]> Dogcows Code - chaz/p5-File-KDBX/blobdiff - lib/File/KDBX.pm
Fix memory protection of brand new databases
[chaz/p5-File-KDBX] / lib / File / KDBX.pm
index 6f5c7dc8ff3ba57dcbb78565f1fad948c55cf23f..82796f323a56e473f31c7d1cc37270cfaf10cf51 100644 (file)
@@ -1,6 +1,7 @@
 package File::KDBX;
 # ABSTRACT: Encrypted database to store secret text and files
 
+use 5.010;
 use warnings;
 use strict;
 
@@ -15,7 +16,7 @@ use Hash::Util::FieldHash qw(fieldhashes);
 use List::Util qw(any first);
 use Ref::Util qw(is_ref is_arrayref is_plain_hashref);
 use Scalar::Util qw(blessed);
-use Time::Piece;
+use Time::Piece 1.33;
 use boolean;
 use namespace::clean;
 
@@ -341,7 +342,7 @@ might increase this value. For example, setting the KDF to Argon2 will increase
 least C<KDBX_VERSION_4_0> (i.e. C<0x00040000>) because Argon2 was introduced with KDBX4.
 
 This method never returns less than C<KDBX_VERSION_3_1> (i.e. C<0x00030001>). That file version is so
-ubiquitious and well-supported, there are seldom reasons to dump in a lesser format nowadays.
+ubiquitous and well-supported, there are seldom reasons to dump in a lesser format nowadays.
 
 B<WARNING:> If you dump a database with a minimum version higher than the current L</version>, the dumper will
 typically issue a warning and automatically upgrade the database. This seems like the safest behavior in order
@@ -636,7 +637,7 @@ sub groups {
     my %args = @_ % 2 == 0 ? @_ : (base => shift, @_);
     my $base = delete $args{base} // $self->root;
 
-    return $base->groups_deeply(%args);
+    return $base->all_groups(%args);
 }
 
 ##############################################################################
@@ -646,7 +647,7 @@ sub groups {
     $kdbx->add_entry($entry, %options);
     $kdbx->add_entry(%entry_attributes, %options);
 
-Add a entry to a database. This is equivalent to identifying a parent group and calling
+Add an entry to a database. This is equivalent to identifying a parent group and calling
 L<File::KDBX::Group/add_entry> on the parent group, forwarding the arguments. Available options:
 
 =for :list
@@ -694,7 +695,7 @@ sub entries {
     my %args = @_ % 2 == 0 ? @_ : (base => shift, @_);
     my $base = delete $args{base} // $self->root;
 
-    return $base->entries_deeply(%args);
+    return $base->all_entries(%args);
 }
 
 ##############################################################################
@@ -715,7 +716,7 @@ sub objects {
     my %args = @_ % 2 == 0 ? @_ : (base => shift, @_);
     my $base = delete $args{base} // $self->root;
 
-    return $base->objects_deeply(%args);
+    return $base->all_objects(%args);
 }
 
 sub __iter__ { $_[0]->objects }
@@ -1175,16 +1176,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;
 }
 
@@ -1367,7 +1376,8 @@ Remove just as many older historical entries as necessary to get under certain l
     limit: -1)
 * C<max_size> - Maximum total size (in bytes) of historical entries to keep (default: value of
     L</history_max_size>, no limit: -1)
-* C<max_age> - Maximum age (in days) of historical entries to keep (default: 365, no limit: -1)
+* C<max_age> - Maximum age (in days) of historical entries to keep (default: value of
+    L</maintenance_history_days>, no limit: -1)
 
 =cut
 
@@ -1405,7 +1415,7 @@ secure the database when dumped. The attributes that will be randomized are:
 * L</transform_seed>
 
 Randomizing these values has no effect on a loaded database. These are only used when a database is dumped.
-You normally do not need to call this method explicitly because the dumper does it explicitly by default.
+You normally do not need to call this method explicitly because the dumper does it for you by default.
 
 =cut
 
@@ -1480,7 +1490,6 @@ sub kdf {
     my %args = @_ % 2 == 1 ? (params => shift, @_) : @_;
 
     my $params = $args{params};
-    my $compat = $args{compatible} // 1;
 
     $params //= $self->kdf_parameters;
     $params = {%{$params || {}}};
@@ -1506,18 +1515,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
@@ -1710,7 +1723,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
 
@@ -1741,7 +1754,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
 
@@ -1908,23 +1921,27 @@ __END__
 
     use File::KDBX;
 
+    # Create a new database from scratch
     my $kdbx = File::KDBX->new;
 
+    # Add some objects to the database
     my $group = $kdbx->add_group(
         name => 'Passwords',
     );
-
     my $entry = $group->add_entry(
         title    => 'My Bank',
+        username => 'mreynolds',
         password => 's3cr3t',
     );
 
-    $kdbx->dump_file('passwords.kdbx', 'M@st3rP@ssw0rd!');
+    # Save the database to the filesystem
+    $kdbx->dump_file('passwords.kdbx', 'masterpw changeme');
 
-    $kdbx = File::KDBX->load_file('passwords.kdbx', 'M@st3rP@ssw0rd!');
+    # Load the database from the filesystem into a new database instance
+    my $kdbx2 = File::KDBX->load_file('passwords.kdbx', 'masterpw changeme');
 
-    $kdbx->entries->each(sub {
-        my ($entry) = @_;
+    # Iterate over database entries, print entry titles
+    $kdbx2->entries->each(sub($entry, @) {
         say 'Entry: ', $entry->title;
     });
 
@@ -2006,8 +2023,7 @@ across different websites. See L</SECURITY> for an overview of security consider
     my $kdbx = File::KDBX->load_file('mypasswords.kdbx', 'master password CHANGEME');
     $kdbx->unlock;  # cause $entry->password below to be defined
 
-    $kdbx->entries->each(sub {
-        my ($entry) = @_;
+    $kdbx->entries->each(sub($entry, @) {
         say 'Found password for: ', $entry->title;
         say '  Username: ', $entry->username;
         say '  Password: ', $entry->password;
@@ -2074,7 +2090,7 @@ The first factor is up to you. This module does not enforce strong master keys.
 generate strong keys.
 
 The KDBX format allows for the key derivation function to be tuned. The idea is that you want each single
-brute-foce attempt to be expensive (in terms of time, CPU usage or memory usage), so that making a lot of
+brute-force attempt to be expensive (in terms of time, CPU usage or memory usage), so that making a lot of
 attempts (which would be required if you have a strong master key) gets I<really> expensive.
 
 How expensive you want to make each attempt is up to you and can depend on the application.
@@ -2209,7 +2225,7 @@ expression. For example, to search for any entry that has been used at least fiv
 
 It helps to read it right-to-left, like "usage_count is greater than or equal to 5".
 
-If you find the disambiguating structures to be distracting or confusing, you can also the
+If you find the disambiguating structures to be distracting or confusing, you can also use the
 L<File::KDBX::Util/simple_expression_query> function as a more intuitive alternative. The following example is
 equivalent to the previous:
 
@@ -2258,7 +2274,7 @@ icon:
 Note: L<File::KDBX::Constants/ICON_SMARTPHONE> is just a constant from L<File::KDBX::Constants>. It isn't
 special to this example or to queries generally. We could have just used a literal number.
 
-The important thing to notice here is how we wrapped the condition in another arrayref with a single key-value
+The important thing to notice here is how we wrapped the condition in another hashref with a single key-value
 pair where the key is the name of an operator and the value is the thing to match against. The supported
 operators are:
 
This page took 0.030445 seconds and 4 git commands to generate.