]> Dogcows Code - chaz/p5-File-KDBX/commitdiff
Version 0.906 dist
authorCharles McGarvey <ccm@cpan.org>
Wed, 17 Aug 2022 01:44:59 +0000 (19:44 -0600)
committerCharles McGarvey <ccm@cpan.org>
Wed, 17 Aug 2022 01:44:59 +0000 (19:44 -0600)
47 files changed:
Changes
META.json
META.yml
Makefile.PL
README
lib/File/KDBX.pm
lib/File/KDBX/Cipher.pm
lib/File/KDBX/Cipher/CBC.pm
lib/File/KDBX/Cipher/Stream.pm
lib/File/KDBX/Constants.pm
lib/File/KDBX/Dumper.pm
lib/File/KDBX/Dumper/KDB.pm
lib/File/KDBX/Dumper/Raw.pm
lib/File/KDBX/Dumper/V3.pm
lib/File/KDBX/Dumper/V4.pm
lib/File/KDBX/Dumper/XML.pm
lib/File/KDBX/Entry.pm
lib/File/KDBX/Error.pm
lib/File/KDBX/Group.pm
lib/File/KDBX/IO.pm
lib/File/KDBX/IO/Crypt.pm
lib/File/KDBX/IO/HashBlock.pm
lib/File/KDBX/IO/HmacBlock.pm
lib/File/KDBX/Iterator.pm
lib/File/KDBX/KDF.pm
lib/File/KDBX/KDF/AES.pm
lib/File/KDBX/KDF/Argon2.pm
lib/File/KDBX/Key.pm
lib/File/KDBX/Key/ChallengeResponse.pm
lib/File/KDBX/Key/Composite.pm
lib/File/KDBX/Key/File.pm
lib/File/KDBX/Key/Password.pm
lib/File/KDBX/Key/YubiKey.pm
lib/File/KDBX/Loader.pm
lib/File/KDBX/Loader/KDB.pm
lib/File/KDBX/Loader/Raw.pm
lib/File/KDBX/Loader/V3.pm
lib/File/KDBX/Loader/V4.pm
lib/File/KDBX/Loader/XML.pm
lib/File/KDBX/Object.pm
lib/File/KDBX/Safe.pm
lib/File/KDBX/Transaction.pm
lib/File/KDBX/Util.pm
t/database.t
t/entry.t
t/kdbx4.t
t/lib/TestCommon.pm

diff --git a/Changes b/Changes
index 8a206139ed9bc36ed0fe89144075354d1d0c65e4..36c88887347dcbd7a8b707804fc71d1ee8ef2fb3 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,8 +1,15 @@
 Revision history for File-KDBX.
 
+0.906     2022-08-16 19:44:09-0600
+  * Fixed bug where dumping a fresh database could write wrong-sized encryption IV, making the resulting
+    serialization unreadable by some KeePass implementations. Thanks HIGHTOWE.
+  * Fixed bugs preventing the use of memory protection with fresh databases. Thanks HIGHTOWE.
+  * Fixed the transform_rounds method to work with Argon KDF; this now maps to the Argon iterations value if
+    the current KDF is Argon. 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
@@ -18,7 +25,7 @@ Revision history for File-KDBX.
   * Added support for 32-bit perls.
   * API change: Rename iterator accessors on group to all_*.
   * Declared perl 5.10.0 prerequisite. I have no intention of supporting 5.8 or earlier.
-  * Fixed more other broken tests -- thanks CPAN testers.
+  * Fixed more other broken tests. Thanks CPAN testers.
 
 0.901     2022-05-02 01:18:13-0600
 
index 73423f2accb4984e3e16324179eec52f1ea9b9ec..296e7f1b87b0572cf730588361ede250dfab50b5 100644 (file)
--- a/META.json
+++ b/META.json
    "provides" : {
       "File::KDBX" : {
          "file" : "lib/File/KDBX.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Cipher" : {
          "file" : "lib/File/KDBX/Cipher.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Cipher::CBC" : {
          "file" : "lib/File/KDBX/Cipher/CBC.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Cipher::Stream" : {
          "file" : "lib/File/KDBX/Cipher/Stream.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Constants" : {
          "file" : "lib/File/KDBX/Constants.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Dumper" : {
          "file" : "lib/File/KDBX/Dumper.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Dumper::KDB" : {
          "file" : "lib/File/KDBX/Dumper/KDB.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Dumper::Raw" : {
          "file" : "lib/File/KDBX/Dumper/Raw.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Dumper::V3" : {
          "file" : "lib/File/KDBX/Dumper/V3.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Dumper::V4" : {
          "file" : "lib/File/KDBX/Dumper/V4.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Dumper::XML" : {
          "file" : "lib/File/KDBX/Dumper/XML.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Entry" : {
          "file" : "lib/File/KDBX/Entry.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Error" : {
          "file" : "lib/File/KDBX/Error.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Group" : {
          "file" : "lib/File/KDBX/Group.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::IO" : {
          "file" : "lib/File/KDBX/IO.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::IO::Crypt" : {
          "file" : "lib/File/KDBX/IO/Crypt.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::IO::HashBlock" : {
          "file" : "lib/File/KDBX/IO/HashBlock.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::IO::HmacBlock" : {
          "file" : "lib/File/KDBX/IO/HmacBlock.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Iterator" : {
          "file" : "lib/File/KDBX/Iterator.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::KDF" : {
          "file" : "lib/File/KDBX/KDF.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::KDF::AES" : {
          "file" : "lib/File/KDBX/KDF/AES.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::KDF::Argon2" : {
          "file" : "lib/File/KDBX/KDF/Argon2.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Key" : {
          "file" : "lib/File/KDBX/Key.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Key::ChallengeResponse" : {
          "file" : "lib/File/KDBX/Key/ChallengeResponse.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Key::Composite" : {
          "file" : "lib/File/KDBX/Key/Composite.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Key::File" : {
          "file" : "lib/File/KDBX/Key/File.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Key::Password" : {
          "file" : "lib/File/KDBX/Key/Password.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Key::YubiKey" : {
          "file" : "lib/File/KDBX/Key/YubiKey.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Loader" : {
          "file" : "lib/File/KDBX/Loader.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Loader::KDB" : {
          "file" : "lib/File/KDBX/Loader/KDB.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Loader::Raw" : {
          "file" : "lib/File/KDBX/Loader/Raw.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Loader::V3" : {
          "file" : "lib/File/KDBX/Loader/V3.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Loader::V4" : {
          "file" : "lib/File/KDBX/Loader/V4.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Loader::XML" : {
          "file" : "lib/File/KDBX/Loader/XML.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Object" : {
          "file" : "lib/File/KDBX/Object.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Safe" : {
          "file" : "lib/File/KDBX/Safe.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Transaction" : {
          "file" : "lib/File/KDBX/Transaction.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       },
       "File::KDBX::Util" : {
          "file" : "lib/File/KDBX/Util.pm",
-         "version" : "0.905"
+         "version" : "0.906"
       }
    },
    "release_status" : "stable",
          "web" : "https://github.com/chazmcgarvey/File-KDBX"
       }
    },
-   "version" : "0.905",
+   "version" : "0.906",
    "x_authority" : "cpan:CCM",
    "x_generated_by_perl" : "v5.36.0",
    "x_serialization_backend" : "Cpanel::JSON::XS version 4.30",
index 42e4c886994fbb77f02d86616bdee825df094e64..d9c0b2140383f86555b0f7930dc9e7f263f3b7fa 100644 (file)
--- a/META.yml
+++ b/META.yml
@@ -50,118 +50,118 @@ optional_features:
 provides:
   File::KDBX:
     file: lib/File/KDBX.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Cipher:
     file: lib/File/KDBX/Cipher.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Cipher::CBC:
     file: lib/File/KDBX/Cipher/CBC.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Cipher::Stream:
     file: lib/File/KDBX/Cipher/Stream.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Constants:
     file: lib/File/KDBX/Constants.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Dumper:
     file: lib/File/KDBX/Dumper.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Dumper::KDB:
     file: lib/File/KDBX/Dumper/KDB.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Dumper::Raw:
     file: lib/File/KDBX/Dumper/Raw.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Dumper::V3:
     file: lib/File/KDBX/Dumper/V3.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Dumper::V4:
     file: lib/File/KDBX/Dumper/V4.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Dumper::XML:
     file: lib/File/KDBX/Dumper/XML.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Entry:
     file: lib/File/KDBX/Entry.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Error:
     file: lib/File/KDBX/Error.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Group:
     file: lib/File/KDBX/Group.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::IO:
     file: lib/File/KDBX/IO.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::IO::Crypt:
     file: lib/File/KDBX/IO/Crypt.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::IO::HashBlock:
     file: lib/File/KDBX/IO/HashBlock.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::IO::HmacBlock:
     file: lib/File/KDBX/IO/HmacBlock.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Iterator:
     file: lib/File/KDBX/Iterator.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::KDF:
     file: lib/File/KDBX/KDF.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::KDF::AES:
     file: lib/File/KDBX/KDF/AES.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::KDF::Argon2:
     file: lib/File/KDBX/KDF/Argon2.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Key:
     file: lib/File/KDBX/Key.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Key::ChallengeResponse:
     file: lib/File/KDBX/Key/ChallengeResponse.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Key::Composite:
     file: lib/File/KDBX/Key/Composite.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Key::File:
     file: lib/File/KDBX/Key/File.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Key::Password:
     file: lib/File/KDBX/Key/Password.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Key::YubiKey:
     file: lib/File/KDBX/Key/YubiKey.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Loader:
     file: lib/File/KDBX/Loader.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Loader::KDB:
     file: lib/File/KDBX/Loader/KDB.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Loader::Raw:
     file: lib/File/KDBX/Loader/Raw.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Loader::V3:
     file: lib/File/KDBX/Loader/V3.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Loader::V4:
     file: lib/File/KDBX/Loader/V4.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Loader::XML:
     file: lib/File/KDBX/Loader/XML.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Object:
     file: lib/File/KDBX/Object.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Safe:
     file: lib/File/KDBX/Safe.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Transaction:
     file: lib/File/KDBX/Transaction.pm
-    version: '0.905'
+    version: '0.906'
   File::KDBX::Util:
     file: lib/File/KDBX/Util.pm
-    version: '0.905'
+    version: '0.906'
 recommends:
   Compress::Raw::Zlib: '0'
   File::KDBX::XS: '0'
@@ -215,7 +215,7 @@ resources:
   bugtracker: https://github.com/chazmcgarvey/File-KDBX/issues
   homepage: https://github.com/chazmcgarvey/File-KDBX
   repository: https://github.com/chazmcgarvey/File-KDBX.git
-version: '0.905'
+version: '0.906'
 x_authority: cpan:CCM
 x_generated_by_perl: v5.36.0
 x_serialization_backend: 'YAML::Tiny version 1.73'
index df6d11945b27275768ddee6a216f6a7efb89bfee..a85a1539efe0819875a697ae7942075f623cb58a 100644 (file)
@@ -73,7 +73,7 @@ my %WriteMakefileArgs = (
     "lib" => 0,
     "utf8" => 0
   },
-  "VERSION" => "0.905",
+  "VERSION" => "0.906",
   "test" => {
     "TESTS" => "t/*.t"
   }
diff --git a/README b/README
index 40a20507ef8aedf898c6a7c5588c026c69b62332..8af7459aaa05b8b64b3a24435c8e621fc43b794d 100644 (file)
--- a/README
+++ b/README
@@ -4,7 +4,7 @@ NAME
 
 VERSION
 
-    version 0.905
+    version 0.906
 
 SYNOPSIS
 
@@ -145,7 +145,8 @@ ATTRIBUTES
 
  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.
 
  cipher_id
 
@@ -181,8 +182,8 @@ ATTRIBUTES
  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.
+    Increasing this number makes loading and saving the database slower in
+    order to make dictionary and brute force attacks more costly.
 
  encryption_iv
 
index 2da002b02906ffebacfad3c733d416c1f2d20df6..85a2a74e584326813f95b0c699021399a9059d5f 100644 (file)
@@ -20,7 +20,7 @@ use Time::Piece 1.33;
 use boolean;
 use namespace::clean;
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 our $WARNINGS = 1;
 
 fieldhashes \my (%SAFE, %KEYS);
@@ -32,9 +32,12 @@ sub new {
     # copy constructor
     return $_[0]->clone if @_ == 1 && blessed $_[0] && $_[0]->isa($class);
 
-    my $self = bless {}, $class;
+    my $data;
+    $data = shift if is_plain_hashref($_[0]);
+
+    my $self = bless $data // {}, $class;
     $self->init(@_);
-    $self->_set_nonlazy_attributes if empty $self;
+    $self->_set_nonlazy_attributes if !$data;
     return $self;
 }
 
@@ -144,10 +147,12 @@ has raw             => coerce => \&to_string;
 
 # HEADERS
 has 'headers.comment'               => '',                          coerce => \&to_string;
-has 'headers.cipher_id'             => CIPHER_UUID_CHACHA20,        coerce => \&to_uuid;
+has 'headers.cipher_id'             => sub { $_[0]->version < KDBX_VERSION_4_0 ? CIPHER_UUID_AES256 : CIPHER_UUID_CHACHA20 },
+                                                                    coerce => \&to_uuid;
 has 'headers.compression_flags'     => COMPRESSION_GZIP,            coerce => \&to_compression_constant;
 has 'headers.master_seed'           => sub { random_bytes(32) },    coerce => \&to_string;
-has 'headers.encryption_iv'         => sub { random_bytes(16) },    coerce => \&to_string;
+has 'headers.encryption_iv'         => sub { random_bytes($_[0]->version < KDBX_VERSION_4_0 ? 16 : 12) },
+                                                                    coerce => \&to_string;
 has 'headers.stream_start_bytes'    => sub { random_bytes(32) },    coerce => \&to_string;
 has 'headers.kdf_parameters'        => sub {
     +{
@@ -753,16 +758,24 @@ sub _remove_safe { delete $SAFE{$_[0]} }
 sub lock {
     my $self = shift;
 
-    $self->_safe and return $self;
-
+    # Find things to lock:
     my @strings;
-
     $self->entries(history => 1)->each(sub {
-        push @strings, grep { $_->{protect} } values %{$_->strings}, values %{$_->binaries};
+        my $strings = $_->strings;
+        for my $string_key (keys %$strings) {
+            my $string = $strings->{$string_key};
+            push @strings, $string if $string->{protect} // $self->memory_protection($string_key);
+        }
+        push @strings, grep { $_->{protect} } values %{$_->binaries};
     });
+    return $self if !@strings;  # nothing to do
 
-    $self->_safe(File::KDBX::Safe->new(\@strings));
-
+    if (my $safe = $self->_safe) {
+        $safe->add(\@strings);
+    }
+    else {
+        $self->_safe(File::KDBX::Safe->new(\@strings));
+    }
     return $self;
 }
 
@@ -892,7 +905,9 @@ sub prune_history {
 
 sub randomize_seeds {
     my $self = shift;
-    $self->encryption_iv(random_bytes(16));
+    my $iv_size = 16;
+    $iv_size = $self->cipher(key => "\0" x 32)->iv_size if KDBX_VERSION_4_0 <= $self->version;
+    $self->encryption_iv(random_bytes($iv_size));
     $self->inner_random_stream_key(random_bytes(64));
     $self->master_seed(random_bytes(32));
     $self->stream_start_bytes(random_bytes(32));
@@ -921,7 +936,6 @@ sub kdf {
     my %args = @_ % 2 == 1 ? (params => shift, @_) : @_;
 
     my $params = $args{params};
-    my $compat = $args{compatible} // 1;
 
     $params //= $self->kdf_parameters;
     $params = {%{$params || {}}};
@@ -947,18 +961,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};
 }
 
 
@@ -966,8 +984,8 @@ sub cipher {
     my $self = shift;
     my %args = @_;
 
-    $args{uuid} //= $self->headers->{+HEADER_CIPHER_ID};
-    $args{iv}   //= $self->headers->{+HEADER_ENCRYPTION_IV};
+    $args{uuid} //= $self->cipher_id;
+    $args{iv}   //= $self->encryption_iv;
 
     require File::KDBX::Cipher;
     return File::KDBX::Cipher->new(%args);
@@ -1121,7 +1139,7 @@ File::KDBX - Encrypted database to store secret text and files
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 SYNOPSIS
 
@@ -1278,7 +1296,7 @@ L<File::KDBX::Loader::Raw>.
 
 =head2 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.
 
 =head2 cipher_id
 
@@ -1309,7 +1327,7 @@ The transform seed I<should> be changed each time the database is saved to file.
 =head2 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.
 
 =head2 encryption_iv
 
index 75ea6a8bec5bd02b1f36f950d510c77760f1cb21..7b0e6f3a087c5aa12429fda494556f35095c3425 100644 (file)
@@ -12,7 +12,7 @@ use Module::Load;
 use Scalar::Util qw(looks_like_number);
 use namespace::clean;
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 my %CIPHERS;
 
@@ -158,7 +158,7 @@ File::KDBX::Cipher - A block cipher mode or cipher stream
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 SYNOPSIS
 
@@ -304,7 +304,8 @@ This is a factory method which returns a subclass.
 
     $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.
 
 =head2 encrypt
 
index 9b0e226e88a8e74c09fdd1cab994c7563aff6f4b..c5d8eabe4e75fc23845054d5557335f7deb97837 100644 (file)
@@ -11,7 +11,7 @@ use namespace::clean;
 
 extends 'File::KDBX::Cipher';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 has key_size => 32;
 sub iv_size     { 16 }
@@ -63,7 +63,7 @@ File::KDBX::Cipher::CBC - A CBC block cipher mode encrypter/decrypter
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 SYNOPSIS
 
index 3dfa07a57aa1a75433a13abb3e93b889dab226c4..fce89adc5f01fb88bba82147edad91318cba5514 100644 (file)
@@ -14,7 +14,7 @@ use namespace::clean;
 
 extends 'File::KDBX::Cipher';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 
 has 'counter',  is => 'ro', default => 0;
@@ -124,7 +124,7 @@ File::KDBX::Cipher::Stream - A cipher stream encrypter/decrypter
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 SYNOPSIS
 
index d41b3512f77a6abac3bede8c10a190c2e6a95021..0274597bcda95ef5a3c6e1049058fecd394f8a6d 100644 (file)
@@ -15,7 +15,7 @@ use File::KDBX::Util qw(int64);
 use Scalar::Util qw(dualvar);
 use namespace::clean -except => 'import';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 BEGIN {
     my %CONSTANTS = (
@@ -348,7 +348,7 @@ File::KDBX::Constants - All the KDBX-related constants you could ever want
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 SYNOPSIS
 
index 421776dfda911c3140298eb9f9f1186e991c8929..7f305c76d47cb514a67a2d2b50671b22e1d2c917 100644 (file)
@@ -15,7 +15,7 @@ use Ref::Util qw(is_ref is_scalarref);
 use Scalar::Util qw(looks_like_number openhandle);
 use namespace::clean;
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 
 sub new {
@@ -287,7 +287,7 @@ File::KDBX::Dumper - Write KDBX files
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 ATTRIBUTES
 
index d83bb0c9140e33845c620d66549f28f870b13df3..226902f22b31775d7f4b00d435f4e8d3cfad2174 100644 (file)
@@ -14,7 +14,7 @@ use namespace::clean;
 
 extends 'File::KDBX::Dumper';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 sub _write_magic_numbers { '' }
 sub _write_headers { '' }
@@ -136,7 +136,7 @@ File::KDBX::Dumper::KDB - Write KDB files
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 DESCRIPTION
 
index 5223def84c8c87dc9f848fb5ebf85768484f9aa0..c838b613fe091cfffa95e4536f41486e538f6c03 100644 (file)
@@ -9,7 +9,7 @@ use namespace::clean;
 
 extends 'File::KDBX::Dumper';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 sub _dump {
     my $self = shift;
@@ -48,7 +48,7 @@ File::KDBX::Dumper::Raw - A no-op dumper that dumps content as-is
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 SYNOPSIS
 
index d5ff5d6968379826e165134a7241356dd491dee2..e769df4302070f0f5a0cb3065b6fcd3022014a41 100644 (file)
@@ -16,7 +16,7 @@ use namespace::clean;
 
 extends 'File::KDBX::Dumper';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 sub _write_headers {
     my $self = shift;
@@ -31,6 +31,11 @@ sub _write_headers {
     local $headers->{+HEADER_TRANSFORM_SEED} = $kdbx->transform_seed;
     local $headers->{+HEADER_TRANSFORM_ROUNDS} = $kdbx->transform_rounds;
 
+    my $got_iv_size = length($headers->{+HEADER_ENCRYPTION_IV});
+    alert 'Encryption IV should be exactly 16 bytes long',
+        got         => $got_iv_size,
+        expected    => 16 if $got_iv_size != 16;
+
     if (nonempty (my $comment = $headers->{+HEADER_COMMENT})) {
         $buf .= $self->_write_header($fh, HEADER_COMMENT, $comment);
     }
@@ -187,7 +192,7 @@ File::KDBX::Dumper::V3 - Dump KDBX3 files
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 BUGS
 
index 31f48e12cac90e2192f9c67374b687e617c0a953..9e2fc2545657495a6ca1230737f3989ced49d2d3 100644 (file)
@@ -19,7 +19,7 @@ use namespace::clean;
 
 extends 'File::KDBX::Dumper';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 has _binaries_written => {}, is => 'ro';
 
@@ -238,6 +238,12 @@ sub _write_body {
     my $cipher = $kdbx->cipher(key => $final_key);
     $fh = File::KDBX::IO::Crypt->new($fh, cipher => $cipher);
 
+    my $got_iv_size = length($kdbx->headers->{+HEADER_ENCRYPTION_IV});
+    my $iv_size = $cipher->iv_size;
+    alert "Encryption IV should be $iv_size bytes long",
+        got         => $got_iv_size,
+        expected    => $iv_size if $got_iv_size != $iv_size;
+
     my $compress = $kdbx->headers->{+HEADER_COMPRESSION_FLAGS};
     if ($compress == COMPRESSION_GZIP) {
         load_optional('IO::Compress::Gzip');
@@ -374,7 +380,7 @@ File::KDBX::Dumper::V4 - Dump KDBX4 files
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 BUGS
 
index b5cd47c84bc70657c59b45df3f742522576bc885..71b9999b37683148efbbe2430a322fce16e037ae 100644 (file)
@@ -19,7 +19,7 @@ use namespace::clean;
 
 extends 'File::KDBX::Dumper';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 
 has allow_protection => 1;
@@ -563,7 +563,7 @@ File::KDBX::Dumper::XML - Dump unencrypted XML KeePass files
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 ATTRIBUTES
 
index 6ca2051a0ab72d382bfa194a374f31be4b49fdbf..76f2318a929c14fa98917489f0aeb3a81d342105 100644 (file)
@@ -21,7 +21,7 @@ use namespace::clean;
 
 extends 'File::KDBX::Object';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 my $PLACEHOLDER_MAX_DEPTH = 10;
 my %PLACEHOLDERS;
@@ -129,14 +129,16 @@ sub string {
 
     return $self->{strings}{$key} = $args{value} if is_plain_hashref($args{value});
 
+    # Auto-vivify the standard strings.
+    if (!exists $self->{strings}{$key} && $STANDARD_STRINGS{$key}) {
+        $args{value} //= '';
+        $args{protect} //= true if $self->_protect($key);
+    }
+
     while (my ($field, $value) = each %args) {
         $self->{strings}{$key}{$field} = $value;
     }
 
-    # Auto-vivify the standard strings.
-    if ($STANDARD_STRINGS{$key}) {
-        return $self->{strings}{$key} //= {value => '', $self->_protect($key) ? (protect => true) : ()};
-    }
     return $self->{strings}{$key};
 }
 
@@ -681,7 +683,7 @@ File::KDBX::Entry - A KDBX database entry
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 DESCRIPTION
 
index f041968994dd132165cb776d6d10619b6738ca07..f64cb71ec7ca23b0b321dedf84c7975aa212447a 100644 (file)
@@ -9,7 +9,7 @@ use Exporter qw(import);
 use Scalar::Util qw(blessed looks_like_number);
 use namespace::clean -except => 'import';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 our @EXPORT = qw(alert error throw);
 
@@ -167,7 +167,7 @@ File::KDBX::Error - Represents something bad that happened
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 ATTRIBUTES
 
index 1e7e1c5266df6a581dc11215896b99c41270c8b2..531210c9cf8dc1b0dc614628a7d16acdce3ecae1 100644 (file)
@@ -19,7 +19,7 @@ use namespace::clean;
 
 extends 'File::KDBX::Object';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 
 # has uuid                        => sub { generate_uuid(printable => 1) };
@@ -398,7 +398,7 @@ File::KDBX::Group - A KDBX database group
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 DESCRIPTION
 
index cffcf88d35424ef3efc0b29d39e31ab0241e7966..54ce5a7c792ccc6b4bdcd5bbb071e7506cd2d9fb 100644 (file)
@@ -14,7 +14,7 @@ use namespace::clean;
 
 extends 'IO::Handle';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 sub _croak { require Carp; goto &Carp::croak }
 
@@ -390,7 +390,7 @@ File::KDBX::IO - Base IO class for KDBX-related streams
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 DESCRIPTION
 
index 3b2636f32c1c44462c1ef81f04cd9bab633c0ae5..e2fd8f7bdd79277673211f0bae614b5d570f118d 100644 (file)
@@ -11,7 +11,7 @@ use namespace::clean;
 
 extends 'File::KDBX::IO';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 our $BUFFER_SIZE = 16384;
 our $ERROR;
 
@@ -139,7 +139,7 @@ File::KDBX::IO::Crypt - Encrypter/decrypter IO handle
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 SYNOPSIS
 
index ae8aac1194673ec392ab2cb68e5983b37e6246b8..9b6764df64866d9c58e7c89b7c042738621193b1 100644 (file)
@@ -13,7 +13,7 @@ use namespace::clean;
 
 extends 'File::KDBX::IO';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 our $ALGORITHM = 'SHA256';
 our $BLOCK_SIZE = 1048576;  # 1MiB
 our $ERROR;
@@ -208,7 +208,7 @@ File::KDBX::IO::HashBlock - Hash block stream IO handle
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 DESCRIPTION
 
index 69afacdfd83d08cdc3dcd540061feb15931c26a3..33395423826c5df0f69703d5e18cfcf43b95b81a 100644 (file)
@@ -13,7 +13,7 @@ use namespace::clean;
 
 extends 'File::KDBX::IO';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 our $BLOCK_SIZE = 1048576;  # 1MiB
 our $ERROR;
 
@@ -212,7 +212,7 @@ File::KDBX::IO::HmacBlock - HMAC block stream IO handle
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 DESCRIPTION
 
index 6b9a153890d464308ad9a68905224d4bb7f38204..de197623d05f7864e53494249120883e1033f9b4 100644 (file)
@@ -14,7 +14,7 @@ use namespace::clean;
 BEGIN { mark_as_loaded('Iterator::Simple::Iterator') }
 extends 'Iterator::Simple::Iterator';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 
 sub new {
@@ -221,7 +221,7 @@ File::KDBX::Iterator - KDBX database iterator
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 SYNOPSIS
 
index 91d9bacfaac4cb194df1f4390385a1f097e6f045..41407bde35bed2c510bfb675a929a56517d0dd31 100644 (file)
@@ -12,10 +12,19 @@ use Module::Load;
 use Scalar::Util qw(blessed);
 use namespace::clean;
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # 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,
+};
+
 
 sub new {
     my $class = shift;
@@ -120,7 +129,7 @@ File::KDBX::KDF - A key derivation function
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 DESCRIPTION
 
@@ -194,7 +203,8 @@ Construct a new KDF.
 
     $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.
 
 =head2 transform
 
@@ -213,7 +223,7 @@ so challenge-response keys can produce raw keys. See L<File::KDBX::Key/raw_key>.
 
     $kdf->randomize_seed;
 
-Generate a new random seed/salt.
+Generate and set a new random seed/salt.
 
 =head2 register
 
index 05d707fce59ed56a0b73ef9d5fa07416f75ac9e3..41863ddd188543044c8c6eaa38dacda39b1c89c1 100644 (file)
@@ -13,7 +13,7 @@ use namespace::clean;
 
 extends 'File::KDBX::KDF';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 # Rounds higher than this are eligible for forking:
 my $FORK_OPTIMIZATION_THRESHOLD = 100_000;
@@ -111,7 +111,7 @@ File::KDBX::KDF::AES - Using the AES cipher as a key derivation function
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 DESCRIPTION
 
index e1c8b44ab332132d5be9b3023363e649bd3fe5f8..30514e2edd8ec0b9a4763553d029a88bf3804e96 100644 (file)
@@ -12,7 +12,7 @@ use namespace::clean;
 
 extends 'File::KDBX::KDF';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 
 sub salt        { $_[0]->{+KDF_PARAM_ARGON2_SALT} or throw 'Salt is not set' }
@@ -69,7 +69,7 @@ File::KDBX::KDF::Argon2 - The Argon2 family of key derivation functions
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 DESCRIPTION
 
index 7e9a17e28522cf080686eb80144dd79161128ba2..478d615908822442ffeaa24040487ee068472d70 100644 (file)
@@ -14,7 +14,7 @@ use Ref::Util qw(is_arrayref is_coderef is_hashref is_ref is_scalarref);
 use Scalar::Util qw(blessed openhandle);
 use namespace::clean;
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 fieldhashes \my %SAFE;
 
@@ -140,7 +140,7 @@ File::KDBX::Key - A credential that can protect a KDBX file
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 DESCRIPTION
 
index e9c47fa1e3326c1a11fb458356f8413e23eb717a..dc220dcbf12d2369b037668619b951281b384757 100644 (file)
@@ -10,7 +10,7 @@ use namespace::clean;
 
 extends 'File::KDBX::Key';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 sub init {
     my $self = shift;
@@ -56,7 +56,7 @@ File::KDBX::Key::ChallengeResponse - A challenge-response key
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 SYNOPSIS
 
index 8d8737082f740096e2e79fddcc2bb40406b29552..0aaa2618ddcaf52cfa77f1c10e0e20bc57102373 100644 (file)
@@ -13,7 +13,7 @@ use namespace::clean;
 
 extends 'File::KDBX::Key';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 sub init {
     my $self = shift;
@@ -92,7 +92,7 @@ File::KDBX::Key::Composite - A composite key made up of component keys
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 SYNOPSIS
 
index 55dbb263347572f4a7ef59979a9e0b31e6834cb0..ad45f53391dcc968ca7d67ede43838a70e4bb9e4 100644 (file)
@@ -17,7 +17,7 @@ use namespace::clean;
 
 extends 'File::KDBX::Key';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 
 has 'type',     is => 'ro';
@@ -262,7 +262,7 @@ File::KDBX::Key::File - A file key
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 SYNOPSIS
 
index 1a899fcc804ebbaf814065d7445435fa2b680124..4aa7eeb9138c8cd499d81f2bee4d1a2e733742b3 100644 (file)
@@ -12,7 +12,7 @@ use namespace::clean;
 
 extends 'File::KDBX::Key';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 sub init {
     my $self = shift;
@@ -37,7 +37,7 @@ File::KDBX::Key::Password - A password key
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 SYNOPSIS
 
index 515b23ca514fcf0c060bd34df24c8a3a2506e759..1b7d0df0bdde50804c3a869e6bc9cd8e4efca778 100644 (file)
@@ -14,7 +14,7 @@ use namespace::clean;
 
 extends 'File::KDBX::Key::ChallengeResponse';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 # It can take some time for the USB device to be ready again, so we can retry a few times.
 our $RETRY_COUNT    = 5;
@@ -296,7 +296,7 @@ File::KDBX::Key::YubiKey - A Yubico challenge-response key
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 SYNOPSIS
 
index 1be16a54fe60d8ef6688af7f147ac43c3b9d033c..0ff3933708c45ac11366d6240939a33845d5643b 100644 (file)
@@ -14,7 +14,7 @@ use Ref::Util qw(is_ref is_scalarref);
 use Scalar::Util qw(looks_like_number openhandle);
 use namespace::clean;
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 
 sub new {
@@ -249,7 +249,7 @@ File::KDBX::Loader - Load KDBX files
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 DESCRIPTION
 
index b4f662058e223e856a4f31fc9298f874b14b323d..0635ce4a91ef7e3b4e22e362dd670df9990727d7 100644 (file)
@@ -17,7 +17,7 @@ use namespace::clean;
 
 extends 'File::KDBX::Loader';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 my $DEFAULT_EXPIRATION = Time::Piece->strptime('2999-12-31 23:59:59', '%Y-%m-%d %H:%M:%S');
 
@@ -397,7 +397,7 @@ File::KDBX::Loader::KDB - Read KDB files
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 DESCRIPTION
 
index c7921d1105dc1ff9d7e06b87ba6565f4c6d08549..15ebb54807d2cf6ae22e8f6bf7c16bfc82fe61c0 100644 (file)
@@ -9,7 +9,7 @@ use namespace::clean;
 
 extends 'File::KDBX::Loader';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 sub _read {
     my $self = shift;
@@ -47,7 +47,7 @@ File::KDBX::Loader::Raw - A no-op loader that doesn't do any parsing
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 SYNOPSIS
 
index 0de336f5837c3716e6f78d4b6c85592a11b8c8f8..865ae5aa062d9ba4ae3140c12dffc40125b24491 100644 (file)
@@ -27,7 +27,7 @@ use namespace::clean;
 
 extends 'File::KDBX::Loader';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 sub _read_header {
     my $self = shift;
@@ -174,7 +174,7 @@ File::KDBX::Loader::V3 - Load KDBX3 files
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 BUGS
 
index 1ef3c82eccba9fea0bd64f8b1a78a532f7fb8262..882ac1b51be58f4e4cf9026c759a46f652b05c62 100644 (file)
@@ -30,7 +30,7 @@ use namespace::clean;
 
 extends 'File::KDBX::Loader';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 sub _read_header {
     my $self = shift;
@@ -273,7 +273,7 @@ File::KDBX::Loader::V4 - Load KDBX4 files
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 BUGS
 
index 3736ba132d19034aa57b1f2887d05af6765560c7..81d548fe334e513291061015dbe2d89df990affc 100644 (file)
@@ -18,7 +18,7 @@ use namespace::clean;
 
 extends 'File::KDBX::Loader';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 has '_reader',  is => 'ro';
 has '_safe',    is => 'ro', default => sub { File::KDBX::Safe->new(cipher => $_[0]->kdbx->random_stream) };
@@ -589,7 +589,7 @@ File::KDBX::Loader::XML - Load unencrypted XML KeePass files
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 BUGS
 
index 39c3eb1e80e75ac7ba7d180ab8745ed07b2a2302..38fa1e48e4a0d59645acb033338fae79c0e982a5 100644 (file)
@@ -14,7 +14,7 @@ use Ref::Util qw(is_arrayref is_plain_arrayref is_plain_hashref is_ref);
 use Scalar::Util qw(blessed weaken);
 use namespace::clean;
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 fieldhashes \my (%KDBX, %PARENT, %TXNS, %REFS, %SIGNALS);
 
@@ -391,9 +391,9 @@ sub _txns   { $TXNS{$_[0]} //= [] }
 sub _commit { die 'Not implemented' }
 
 # Get a reference to an object that represents an object's committed state. If there is no pending
-# transaction, this is just $self. If there is a transaction, this is the snapshot take before the transaction
-# began. This method is private because it provides direct access to the actual snapshot. It is important that
-# the snapshot not be changed or a rollback would roll back to an altered state.
+# transaction, this is just $self. If there is a transaction, this is the snapshot taken immediately before
+# the transaction began. This method is private because it provides direct access to the actual snapshot. It
+# is important that the snapshot not be changed or a rollback would roll back to an altered state.
 # This is used by File::KDBX::Dumper::XML so as to not dump uncommitted changes.
 sub _committed {
     my $self = shift;
@@ -526,7 +526,7 @@ File::KDBX::Object - A KDBX database object
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 DESCRIPTION
 
index b56ffc2f142253c1f2ab274d477c530e5e399e81..e8c4f947a789f67e556b907da9273a317af543f6 100644 (file)
@@ -14,7 +14,7 @@ use Ref::Util qw(is_arrayref is_coderef is_hashref is_scalarref);
 use Scalar::Util qw(refaddr);
 use namespace::clean;
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 
 sub new {
@@ -217,7 +217,7 @@ File::KDBX::Safe - Keep strings encrypted while in memory
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 SYNOPSIS
 
index 1c0cb4f5137dce308c444cd747ad3a1b36b3c53a..6a4769cb5f86f37c2453aeb3e6d30ccae00054ed 100644 (file)
@@ -8,7 +8,7 @@ use Devel::GlobalDestruction;
 use File::KDBX::Util qw(:class);
 use namespace::clean;
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 
 sub new {
@@ -59,7 +59,7 @@ File::KDBX::Transaction - Make multiple database edits atomically
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 ATTRIBUTES
 
index d0e467382470510118e17df0cc28258ccf61f803..af04f151fdc2028b5ed69ab744758ef9ad63e312 100644 (file)
@@ -17,7 +17,7 @@ use Time::Piece 1.33;
 use boolean;
 use namespace::clean -except => 'import';
 
-our $VERSION = '0.905'; # VERSION
+our $VERSION = '0.906'; # VERSION
 
 our %EXPORT_TAGS = (
     assert      => [qw(DEBUG assert)],
@@ -870,7 +870,7 @@ File::KDBX::Util - Utility functions for working with KDBX files
 
 =head1 VERSION
 
-version 0.905
+version 0.906
 
 =head1 FUNCTIONS
 
index 997d04c1a7d70d0bdd895fcb19e3940197369db0..8b26f6aac3d5ef9f6103934b8a6a9334c2ca50e7 100644 (file)
@@ -8,6 +8,7 @@ use FindBin qw($Bin);
 use lib "$Bin/lib";
 use TestCommon;
 
+use File::KDBX::Constants qw(:cipher :version);
 use File::KDBX;
 use File::Temp qw(tempfile);
 use Test::Deep;
@@ -29,6 +30,14 @@ subtest 'Create a new database' => sub {
 
     $entry->remove;
     ok $kdbx->_has_implicit_root, 'Removing group makes the root group implicit again';
+
+    cmp_ok $kdbx->version, '==', KDBX_VERSION_3_1, 'Default KDBX file version is 3.1';
+    is $kdbx->cipher_id, CIPHER_UUID_AES256, 'Cipher of new database is AES256';
+    cmp_ok length($kdbx->encryption_iv), '==', 16, 'Encryption IV of new databse is 16 bytes';
+
+    my $kdbx2 = File::KDBX->new(version => KDBX_VERSION_4_0);
+    is $kdbx2->cipher_id, CIPHER_UUID_CHACHA20, 'Cipher of new v4 database is ChaCha20';
+    cmp_ok length($kdbx2->encryption_iv), '==', 12, 'Encryption IV of new databse is 12 bytes';
 };
 
 subtest 'Clone' => sub {
index 1581608520220b84ba71f530c6008d0922ab08e1..a5700c90eec4b300511da7cd34a8e0b8304fc08e 100644 (file)
--- a/t/entry.t
+++ b/t/entry.t
@@ -84,6 +84,17 @@ subtest 'Accessors' => sub {
     $entry->creation_time('2022-02-02 12:34:56');
     cmp_ok $entry->creation_time->epoch, '==', 1643805296, 'Creation time coerced into a Time::Piece (epoch)';
     is $entry->creation_time->datetime, '2022-02-02T12:34:56', 'Creation time coerced into a Time::Piece';
+
+    $entry->username('foo');
+    cmp_deeply $entry->strings->{UserName}, {
+        value   => 'foo',
+    }, 'Username setter works';
+
+    $entry->password('bar');
+    cmp_deeply $entry->strings->{Password}, {
+        value   => 'bar',
+        protect => bool(1),
+    }, 'Password setter works';
 };
 
 subtest 'Custom icons' => sub {
@@ -169,4 +180,32 @@ subtest 'Auto-type' => sub {
     is $keys, 'blah', 'Select the correct association';
 };
 
+subtest 'Memory protection' => sub {
+    my $kdbx = File::KDBX->new;
+
+    is exception { $kdbx->lock }, undef, 'Can lock empty database';
+    $kdbx->unlock;  # should be no-op since nothing was locked
+
+    my $entry = $kdbx->root->add_entry(
+        title    => 'My Bank',
+        username => 'mreynolds',
+        password => 's3cr3t',
+    );
+    $entry->string(Custom => 'foo', protect => 1);
+    $entry->binary(Binary => 'bar', protect => 1);
+    $entry->binary(UnprotectedBinary => 'baz');
+
+    is exception { $kdbx->lock }, undef, 'Can lock new database';
+    is $entry->username, 'mreynolds', 'UserName does not get locked';
+    is $entry->password, undef, 'Password is lockable';
+    is $entry->string_value('Custom'), undef, 'Custom is lockable';
+    is $entry->binary_value('Binary'), undef, 'Binary is lockable';
+    is $entry->binary_value('UnprotectedBinary'), 'baz', 'Unprotected binary does not get locked';
+
+    $kdbx->unlock;
+    is $entry->password, 's3cr3t', 'Password is unlockable';
+    is $entry->string_value('Custom'), 'foo', 'Custom is unlockable';
+    is $entry->binary_value('Binary'), 'bar', 'Binary is unlockable';
+};
+
 done_testing;
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.089413 seconds and 4 git commands to generate.