]> Dogcows Code - chaz/p5-File-KDBX/commitdiff
Make transform_rounds work with Argon KDF
authorCharles McGarvey <ccm@cpan.org>
Tue, 16 Aug 2022 21:52:51 +0000 (15:52 -0600)
committerCharles McGarvey <ccm@cpan.org>
Tue, 16 Aug 2022 21:52:51 +0000 (15:52 -0600)
Changes
lib/File/KDBX.pm
lib/File/KDBX/Cipher.pm
lib/File/KDBX/KDF.pm
t/kdbx4.t
t/lib/TestCommon.pm

diff --git a/Changes b/Changes
index 1b27a062ba3dc3a9659bb6939dbec00abdee9ddb..dcce16269a2e4bf258df73a5c6998b6212a870fe 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,10 +1,11 @@
 Revision history for File-KDBX.
 
 {{$NEXT}}
+  * Fixed transform_rounds method to work with Argon KDF. Thanks HIGHTOWE.
 
 0.905     2022-08-06 12:12:42-0600
-  * Declare Time::Local 1.19 as a required dependency.
-  * Declare CryptX 0.055 as a required dependency. Thanks HIGHTOWE.
+  * Declared Time::Local 1.19 as a required dependency.
+  * Declared CryptX 0.055 as a required dependency. Thanks HIGHTOWE.
   * Fixed minor documentation errors.
 
 0.904     2022-07-07 21:51:17-0600
index 680c2520b1ff67e4589c7d36f9e05bddd0a0f494..db403667aae0684645fbe2ceae2d829bbeaa8835 100644 (file)
@@ -1482,7 +1482,6 @@ sub kdf {
     my %args = @_ % 2 == 1 ? (params => shift, @_) : @_;
 
     my $params = $args{params};
-    my $compat = $args{compatible} // 1;
 
     $params //= $self->kdf_parameters;
     $params = {%{$params || {}}};
@@ -1508,18 +1507,22 @@ sub kdf {
 
 sub transform_seed {
     my $self = shift;
+    my $param = KDF_PARAM_AES_SEED;     # Short cut: Argon2 uses the same parameter name ("S")
     $self->headers->{+HEADER_TRANSFORM_SEED} =
-        $self->headers->{+HEADER_KDF_PARAMETERS}{+KDF_PARAM_AES_SEED} = shift if @_;
+        $self->headers->{+HEADER_KDF_PARAMETERS}{$param} = shift if @_;
     $self->headers->{+HEADER_TRANSFORM_SEED} =
-        $self->headers->{+HEADER_KDF_PARAMETERS}{+KDF_PARAM_AES_SEED} //= random_bytes(32);
+        $self->headers->{+HEADER_KDF_PARAMETERS}{$param} //= random_bytes(32);
 }
 
 sub transform_rounds {
     my $self = shift;
+    require File::KDBX::KDF;
+    my $info = $File::KDBX::KDF::ROUNDS_INFO{$self->kdf_parameters->{+KDF_PARAM_UUID} // ''} //
+        $File::KDBX::KDF::DEFAULT_ROUNDS_INFO;
     $self->headers->{+HEADER_TRANSFORM_ROUNDS} =
-        $self->headers->{+HEADER_KDF_PARAMETERS}{+KDF_PARAM_AES_ROUNDS} = shift if @_;
+        $self->headers->{+HEADER_KDF_PARAMETERS}{$info->{p}} = shift if @_;
     $self->headers->{+HEADER_TRANSFORM_ROUNDS} =
-        $self->headers->{+HEADER_KDF_PARAMETERS}{+KDF_PARAM_AES_ROUNDS} //= 100_000;
+        $self->headers->{+HEADER_KDF_PARAMETERS}{$info->{p}} //= $info->{d};
 }
 
 =method cipher
@@ -1712,7 +1715,7 @@ L<File::KDBX::Loader::Raw>.
 
 =attr comment
 
-A text string associated with the database. Often unset.
+A text string associated with the database stored unencrypted in the file header. Often unset.
 
 =attr cipher_id
 
@@ -1743,7 +1746,7 @@ The transform seed I<should> be changed each time the database is saved to file.
 =attr transform_rounds
 
 The number of rounds or iterations used in the key derivation function. Increasing this number makes loading
-and saving the database slower by design in order to make dictionary and brute force attacks more costly.
+and saving the database slower in order to make dictionary and brute force attacks more costly.
 
 =attr encryption_iv
 
index 655f8fbe7448a872ba70b80d04d590bd1cb926eb..04be27145ad6341e72a86c780a81ecf01b467f71 100644 (file)
@@ -142,7 +142,8 @@ sub new_from_stream_id {
 
     $self->init;
 
-Initialize the cipher. Called by </new>.
+Called by L</new> to set attributes. You normally shouldn't call this. Returns itself to allow method
+chaining.
 
 =cut
 
index ce32945361ad81ea6114421932b5a1d9e7b19e44..60b4acf516677f15ec1827ab2603196f5612a94f 100644 (file)
@@ -16,6 +16,15 @@ our $VERSION = '999.999'; # VERSION
 
 my %KDFS;
 
+our %ROUNDS_INFO = (
+    KDF_UUID_ARGON2D()  => {p => KDF_PARAM_ARGON2_ITERATIONS, d => KDF_DEFAULT_ARGON2_ITERATIONS},
+    KDF_UUID_ARGON2ID() => {p => KDF_PARAM_ARGON2_ITERATIONS, d => KDF_DEFAULT_ARGON2_ITERATIONS},
+);
+our $DEFAULT_ROUNDS_INFO = {
+    p => KDF_PARAM_AES_ROUNDS,
+    d => KDF_DEFAULT_AES_ROUNDS,
+};
+
 =method new
 
     $kdf = File::KDBX::KDF->new(parameters => \%params);
@@ -43,7 +52,8 @@ sub new {
 
     $kdf = $kdf->init(%attributes);
 
-Called by method to set attributes. You normally shouldn't call this.
+Called by L</new> to set attributes. You normally shouldn't call this. Returns itself to allow method
+chaining.
 
 =cut
 
@@ -109,7 +119,7 @@ sub _transform { die 'Not implemented' }
 
     $kdf->randomize_seed;
 
-Generate a new random seed/salt.
+Generate and set a new random seed/salt.
 
 =cut
 
index ff487003614358615a58ea0eaecdc436d623af6c..b14ddcf2d4042eb57fb572d3cfbf4359dd8898de 100644 (file)
--- a/t/kdbx4.t
+++ b/t/kdbx4.t
@@ -34,6 +34,11 @@ subtest 'Verify Format400' => sub {
         master_seed => ";\372y\300yS%\3331\177\231\364u\265Y\361\225\3273h\332R,\22\240a\240\302\271\357\313\23",
     }, 'Extract headers' or diag explain $kdbx->headers;
 
+    is $kdbx->transform_seed,
+        "V\254\6m-\206*\260\305\f\0\366\24:4\235\364A\362\346\221\13)}\250\217P\303\303\2\331\245",
+        'Get the correct transform seed';
+    cmp_ok $kdbx->transform_rounds, '==', 2, 'Get the correct transform rounds';
+
     is $kdbx->meta->{database_name}, 'Format400', 'Extract database name from meta';
     is $kdbx->root->name, 'Format400', 'Extract name of root group';
 
@@ -132,10 +137,10 @@ sub test_upgrade_master_key_integrity {
     plan tests => $expected_version >= KDBX_VERSION_4_0 ? 6 : 5;
 
     my $kdbx = File::KDBX->new;
-    $kdbx->kdf_parameters(fast_kdf);
-
     is $kdbx->kdf->uuid, KDF_UUID_AES, 'Default KDF is AES';
 
+    $kdbx->kdf_parameters(fast_kdf);
+
     {
         local $_ = $kdbx;
         $modifier->($kdbx);
@@ -216,4 +221,28 @@ subtest 'Custom data' => sub {
     is_deeply $entry2->custom_data_value('bool'), '0', 'Store a boolean in entry custom data';
 };
 
+subtest 'KDF parameters' => sub {
+    my $kdbx = File::KDBX->new;
+    $kdbx->version(KDBX_VERSION_4_0);
+
+    is $kdbx->kdf_parameters->{+KDF_PARAM_UUID}, KDF_UUID_AES, 'Default KDF type is correct';
+    cmp_ok $kdbx->transform_rounds, '==', 100_000, 'Default transform rounds is correct';
+
+    $kdbx->transform_rounds(17);
+    cmp_deeply $kdbx->kdf_parameters, {
+        "\$UUID" => "\311\331\363\232b\212D`\277t\r\b\301\212O\352",
+        R => num(17),
+        S => ignore(),
+    }, 'Set transform rounds for AES KDF';
+
+    $kdbx->kdf_parameters({KDF_PARAM_UUID() => KDF_UUID_ARGON2D});
+    cmp_ok $kdbx->transform_rounds, '==', 10, 'Default Argon2D transform rounds is correct';
+
+    $kdbx->transform_rounds(17);
+    cmp_deeply $kdbx->kdf_parameters, {
+        "\$UUID" => "\357cm\337\214)DK\221\367\251\244\3\343\n\f",
+        I => num(17),
+    }, 'Set transform rounds for Argon KDF';
+};
+
 done_testing;
index 33438d3f9538bd0e1e498d2c4e43fde8d75f6755..195b76ca8364251e38c219b4265be4d0bb7624a9 100644 (file)
@@ -80,6 +80,7 @@ sub ok_magic {
     ], $note // 'KDBX magic numbers are correct';
 }
 
+# Returns parameters for a fast KDF so that running tests isn't pointlessly slow.
 sub fast_kdf {
     my $uuid = shift // KDF_UUID_AES;
     my $params = {
This page took 0.033453 seconds and 4 git commands to generate.