X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=lib%2FFile%2FKDBX.pm;h=5d188fb9793b628417bbbc586ceabf9a5c875235;hb=b334578b1eb03deabcdcc02f324e7d2323c7965e;hp=d3e501af1e8c562c781c82750a0e4f6bf5b1938a;hpb=63d73bf382edfb0089b36a45193fc2835cb58b6d;p=chaz%2Fp5-File-KDBX diff --git a/lib/File/KDBX.pm b/lib/File/KDBX.pm index d3e501a..5d188fb 100644 --- a/lib/File/KDBX.pm +++ b/lib/File/KDBX.pm @@ -1,6 +1,7 @@ package File::KDBX; # ABSTRACT: Encrypted database to store secret text and files +use 5.010; use warnings; use strict; @@ -264,7 +265,7 @@ has 'meta.database_description' => '', coer has 'meta.database_description_changed' => sub { gmtime }, coerce => \&to_time; has 'meta.default_username' => '', coerce => \&to_string; has 'meta.default_username_changed' => sub { gmtime }, coerce => \&to_time; -has 'meta.maintenance_history_days' => 0, coerce => \&to_number; +has 'meta.maintenance_history_days' => HISTORY_DEFAULT_MAX_AGE, coerce => \&to_number; has 'meta.color' => '', coerce => \&to_string; has 'meta.master_key_changed' => sub { gmtime }, coerce => \&to_time; has 'meta.master_key_change_rec' => -1, coerce => \&to_number; @@ -341,7 +342,7 @@ might increase this value. For example, setting the KDF to Argon2 will increase least C (i.e. C<0x00040000>) because Argon2 was introduced with KDBX4. This method never returns less than C (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 If you dump a database with a minimum version higher than the current L, the dumper will typically issue a warning and automatically upgrade the database. This seems like the safest behavior in order @@ -398,8 +399,9 @@ because it autovivifies when adding entries and groups to the database. Every database has only a single root group at a time. Some old KDB files might have multiple root groups. When reading such files, a single implicit root group is created to contain the actual root groups. When writing to such a format, if the root group looks like it was implicitly created then it won't be written and -the resulting file might have multiple root groups. This allows working with older files without changing -their written internal structure while still adhering to modern semantics while the database is opened. +the resulting file might have multiple root groups, as it was before loading. This allows working with older +files without changing their written internal structure while still adhering to modern semantics while the +database is opened. The root group of a KDBX database contains all of the database's entries and other groups. If you replace the root group, you are essentially replacing the entire database contents with something else. @@ -592,7 +594,7 @@ Add a group to a database. This is equivalent to identifying a parent group and L on the parent group, forwarding the arguments. Available options: =for :list -* C (aka C) - Group object or group UUID to add the group to (default: root group) +* C - Group object or group UUID to add the group to (default: root group) =cut @@ -602,7 +604,7 @@ sub add_group { my %args = @_; # find the right group to add the group to - my $parent = delete $args{group} // delete $args{parent} // $self->root; + my $parent = delete $args{group} // $self->root; $parent = $self->groups->grep({uuid => $parent})->next if !ref $parent; $parent or throw 'Invalid group'; @@ -635,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); } ############################################################################## @@ -649,7 +651,7 @@ Add a entry to a database. This is equivalent to identifying a parent group and L on the parent group, forwarding the arguments. Available options: =for :list -* C (aka C) - Group object or group UUID to add the entry to (default: root group) +* C - Group object or group UUID to add the entry to (default: root group) =cut @@ -659,7 +661,7 @@ sub add_entry { my %args = @_; # find the right group to add the entry to - my $parent = delete $args{group} // delete $args{parent} // $self->root; + my $parent = delete $args{group} // $self->root; $parent = $self->groups->grep({uuid => $parent})->next if !ref $parent; $parent or throw 'Invalid group'; @@ -693,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); } ############################################################################## @@ -714,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 } @@ -1154,11 +1156,11 @@ our %PLACEHOLDERS = ( $kdbx->lock; -Encrypt all protected binaries strings in a database. The encrypted strings are stored in -a L associated with the database and the actual strings will be replaced with C to +Encrypt all protected strings and binaries in a database. The encrypted data is stored in +a L associated with the database and the actual values will be replaced with C to indicate their protected state. Returns itself to allow method chaining. -You can call C on an already-locked database to memory-protect any unprotected strings and binaries +You can call C on an already-locked database to memory-protect any unprotected strings and binaries added after the last time the database was locked. =cut @@ -1191,8 +1193,8 @@ sub lock { $kdbx->unlock; -Decrypt all protected strings in a database, replacing C placeholders with unprotected values. Returns -itself to allow method chaining. +Decrypt all protected strings and binaries in a database, replacing C value placeholders with their +actual, unprotected values. Returns itself to allow method chaining. =cut @@ -1215,6 +1217,14 @@ C if the database is already unlocked. See L and L. +Example: + + { + my $guard = $kdbx->unlock_scoped; + ...; + } + # $kdbx is now memory-locked + =cut sub unlock_scoped { @@ -1248,13 +1258,13 @@ sub peek { $bool = $kdbx->is_locked; -Get whether or not a database's strings are memory-protected. If this is true, then some or all of the -protected strings within the database will be unavailable (literally have C values) until L is -called. +Get whether or not a database's contents are in a locked (i.e. memory-protected) state. If this is true, then +some or all of the protected strings and binaries within the database will be unavailable (literally have +C values) until L is called. =cut -sub is_locked { $_[0]->_safe ? 1 : 0 } +sub is_locked { !!$_[0]->_safe } ############################################################################## @@ -1368,7 +1378,7 @@ sub prune_history { my $max_items = $args{max_items} // $self->history_max_items // HISTORY_DEFAULT_MAX_ITEMS; my $max_size = $args{max_size} // $self->history_max_size // HISTORY_DEFAULT_MAX_SIZE; - my $max_age = $args{max_age} // HISTORY_DEFAULT_MAX_AGE; + my $max_age = $args{max_age} // $self->maintenance_history_days // HISTORY_DEFAULT_MAX_AGE; my @removed; $self->entries->each(sub { @@ -1418,7 +1428,8 @@ sub randomize_seeds { $key = $kdbx->key($primitive); Get or set a L. This is the master key (e.g. a password or a key file that can decrypt -a database). See L for an explanation of what the primitive can be. +a database). You can also pass a primitive castable to a B. See L for an explanation +of what the primitive can be. You generally don't need to call this directly because you can provide the key directly to the loader or dumper when loading or dumping a KDBX file. @@ -1436,10 +1447,11 @@ sub key { $key = $kdbx->composite_key($key); $key = $kdbx->composite_key($primitive); -Construct a L from a primitive. See L for an explanation of -what the primitive can be. If the primitive does not represent a composite key, it will be wrapped. +Construct a L from a B or primitive. See L for an +explanation of what the primitive can be. If the primitive does not represent a composite key, it will be +wrapped. -You generally don't need to call this directly. The parser and writer use it to transform a master key into +You generally don't need to call this directly. The loader and dumper use it to transform a master key into a raw encryption key. =cut @@ -1522,7 +1534,7 @@ cipher), not a L or primitive. If not passed, the UUID comes from C<< $kdbx->headers->{cipher_id} >> and the encryption IV comes from C<< $kdbx->headers->{encryption_iv} >>. -You generally don't need to call this directly. The parser and writer use it to decrypt and encrypt KDBX +You generally don't need to call this directly. The loader and dumper use it to decrypt and encrypt KDBX files. =cut @@ -1550,7 +1562,7 @@ C<< $kdbx->headers->{inner_random_stream_key} >> (respectively) for KDBX3 files C<< $kdbx->inner_headers->{inner_random_stream_key} >> and C<< $kdbx->inner_headers->{inner_random_stream_id} >> (respectively) for KDBX4 files. -You generally don't need to call this directly. The parser and writer use it to scramble protected strings. +You generally don't need to call this directly. The loader and dumper use it to scramble protected strings. =cut @@ -1705,7 +1717,7 @@ A text string associated with the database. Often unset. The UUID of a cipher used to encrypt the database when stored as a file. -See L. +See L. =attr compression_flags @@ -1792,10 +1804,6 @@ When a new entry is created, the I string will be populated with this Timestamp indicating when the default username was last changed. -=attr maintenance_history_days - -TODO... not really sure what this is. 😀 - =attr color A color associated with the database (in the form C<#ffffff> where "f" is a hexidecimal digit). Some agents @@ -1814,7 +1822,7 @@ Number of days until the agent should prompt to recommend changing the master ke Number of days until the agent should prompt to force changing the master key. Note: This is purely advisory. It is up to the individual agent software to actually enforce it. -C does NOT enforce it. +B does NOT enforce it. =attr custom_icons @@ -1832,7 +1840,7 @@ The UUID of a group used to store thrown-away groups and entries. =attr recycle_bin_changed -Timestamp indicating when the recycle bin was last changed. +Timestamp indicating when the recycle bin group was last changed. =attr entry_templates_group @@ -1852,11 +1860,15 @@ The UUID of the group visible at the top of the list. =attr history_max_items -The maximum number of historical entries allowed to be saved for each entry. +The maximum number of historical entries that should be kept for each entry. Default is 10. =attr history_max_size -The maximum total size (in bytes) that each individual entry's history is allowed to grow. +The maximum total size (in bytes) that each individual entry's history is allowed to grow. Default is 6 MiB. + +=attr maintenance_history_days + +The maximum age (in days) historical entries should be kept. Default it 365. =attr settings_changed @@ -1897,22 +1909,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', ); + # Save the database to the filesystem $kdbx->dump_file('passwords.kdbx', 'M@st3rP@ssw0rd!'); - $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', 'M@st3rP@ssw0rd!'); - $kdbx->entries->each(sub { + # Iterate over database entries, print entry titles + $kdbx2->entries->each(sub { my ($entry) = @_; say 'Entry: ', $entry->title; }); @@ -1921,12 +1938,12 @@ See L for more examples. =head1 DESCRIPTION -B provides everything you need to work with a KDBX database. A KDBX database is a hierarchical +B provides everything you need to work with KDBX databases. A KDBX database is a hierarchical object database which is commonly used to store secret information securely. It was developed for the KeePass password safe. See L for more information about KDBX. -This module lets you query entries, create new entries, delete entries and modify entries. The distribution -also includes various parsers and generators for serializing and persisting databases. +This module lets you query entries, create new entries, delete entries, modify entries and more. The +distribution also includes various parsers and generators for serializing and persisting databases. The design of this software was influenced by the L implementation of KeePass as well as the L module. B is an alternative module @@ -2148,9 +2165,9 @@ unfortunately not portable. To find things in a KDBX database, you should use a filtered iterator. If you have an iterator, such as returned by L, L or even L you can filter it using L. - my $filtered_entries = $kdbx->entries->where($query); + my $filtered_entries = $kdbx->entries->where(\&query); -A C<$query> is just a subroutine that you can either write yourself or have generated for you from either +A C<\&query> is just a subroutine that you can either write yourself or have generated for you from either a L or L. It's easier to have your query generated, so I'll cover that first. @@ -2261,7 +2278,7 @@ operators are: * C<==> - Number equal * C - Number not equal * C<< < >> - Number less than -* C<< > >>> - Number greater than +* C<< > >> - Number greater than * C<< <= >> - Number less than or equal * C<< >= >> - Number less than or equal * C<=~> - String match regular expression @@ -2369,7 +2386,7 @@ your own query logic, like this: Iterators are the built-in way to navigate or walk the database tree. You get an iterator from L, L and L. You can specify the search algorithm to iterate over objects in different orders -using the C option, which can be one of these L: +using the C option, which can be one of these L: =for :list * C - Iterative deepening search (default) @@ -2408,12 +2425,12 @@ B - This is a planned feature, not yet implemented. =head1 ERRORS Errors in this package are constructed as L objects and propagated using perl's built-in -mechanisms. Fatal errors are propagated using L and non-fatal errors (a.k.a. warnings) are -propagated using L while adhering to perl's L system. If you're already familiar -with these mechanisms, you can skip this section. +mechanisms. Fatal errors are propagated using L and non-fatal errors (a.k.a. warnings) +are propagated using L while adhering to perl's L system. If you're already +familiar with these mechanisms, you can skip this section. -You can catch fatal errors using L (or something like L) and non-fatal errors using -C<$SIG{__WARN__}> (see L). Examples: +You can catch fatal errors using L (or something like L) and non-fatal +errors using C<$SIG{__WARN__}> (see L). Examples: use File::KDBX::Error qw(error); @@ -2474,13 +2491,6 @@ This software will alter its behavior depending on the value of certain environm * C - Do not use L if true (default: false) * C - Do not fork if true (default: false) -=head1 CAVEATS - -Some features (e.g. parsing) require 64-bit perl. It should be possible and actually pretty easy to make it -work using L, but I need to build a 32-bit perl in order to test it and frankly I'm still -figuring out how. I'm sure it's simple so I'll mark this one "TODO", but for now an exception will be thrown -when trying to use such features with undersized IVs. - =head1 SEE ALSO =for :list