From d6b235ceb4b84d5eca94868aec850b203e373357 Mon Sep 17 00:00:00 2001 From: Charles McGarvey Date: Mon, 2 May 2022 01:19:01 -0600 Subject: [PATCH] Version 0.901 --- Changes | 9 +- META.json | 78 ++++++------- META.yml | 78 ++++++------- Makefile.PL | 2 +- README | 48 ++++---- lib/File/KDBX.pm | 44 +++++--- lib/File/KDBX/Cipher.pm | 4 +- lib/File/KDBX/Cipher/CBC.pm | 4 +- lib/File/KDBX/Cipher/Stream.pm | 4 +- lib/File/KDBX/Constants.pm | 4 +- lib/File/KDBX/Dumper.pm | 145 ++++++++++++++++++++----- lib/File/KDBX/Dumper/KDB.pm | 4 +- lib/File/KDBX/Dumper/Raw.pm | 4 +- lib/File/KDBX/Dumper/V3.pm | 4 +- lib/File/KDBX/Dumper/V4.pm | 4 +- lib/File/KDBX/Dumper/XML.pm | 4 +- lib/File/KDBX/Entry.pm | 4 +- lib/File/KDBX/Error.pm | 4 +- lib/File/KDBX/Group.pm | 4 +- lib/File/KDBX/IO.pm | 4 +- lib/File/KDBX/IO/Crypt.pm | 4 +- lib/File/KDBX/IO/HashBlock.pm | 4 +- lib/File/KDBX/IO/HmacBlock.pm | 4 +- lib/File/KDBX/Iterator.pm | 4 +- lib/File/KDBX/KDF.pm | 4 +- lib/File/KDBX/KDF/AES.pm | 4 +- lib/File/KDBX/KDF/Argon2.pm | 4 +- lib/File/KDBX/Key.pm | 4 +- lib/File/KDBX/Key/ChallengeResponse.pm | 4 +- lib/File/KDBX/Key/Composite.pm | 4 +- lib/File/KDBX/Key/File.pm | 28 +++-- lib/File/KDBX/Key/Password.pm | 4 +- lib/File/KDBX/Key/YubiKey.pm | 4 +- lib/File/KDBX/Loader.pm | 58 ++++++++-- lib/File/KDBX/Loader/KDB.pm | 4 +- lib/File/KDBX/Loader/Raw.pm | 4 +- lib/File/KDBX/Loader/V3.pm | 4 +- lib/File/KDBX/Loader/V4.pm | 4 +- lib/File/KDBX/Loader/XML.pm | 4 +- lib/File/KDBX/Object.pm | 4 +- lib/File/KDBX/Safe.pm | 6 +- lib/File/KDBX/Transaction.pm | 4 +- lib/File/KDBX/Util.pm | 8 +- t/database.t | 21 ++++ t/hash-block.t | 6 +- t/hmac-block.t | 6 +- t/kdbx3.t | 6 +- t/keys.t | 6 +- 48 files changed, 429 insertions(+), 248 deletions(-) diff --git a/Changes b/Changes index 6475c06..c74d78a 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,12 @@ Revision history for File-KDBX. +0.901 2022-05-02 01:18:13-0600 + + * Fixed a bug where peeking at memory-protected strings and binaries does + not work without unlocking the database at least once. + * Added an option for writing files non-atomically. + * Fixed broken tests on Windows. + 0.900 2022-05-01 12:55:59-0600 * Removed the min_version methods from dumper and loader because it was @@ -7,7 +14,7 @@ Revision history for File-KDBX. * Now use the database maintenance_history_days value as the default "max_age" value in prune_history method. * Fixed distribution prereq issues. - * Clean up a lot of pod typos and other inaccuracies. + * Cleaned up a lot of pod typos and other inaccuracies. 0.800 2022-04-30 21:14:30-0600 diff --git a/META.json b/META.json index 995a8d9..7ee8a96 100644 --- a/META.json +++ b/META.json @@ -166,155 +166,155 @@ "provides" : { "File::KDBX" : { "file" : "lib/File/KDBX.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Cipher" : { "file" : "lib/File/KDBX/Cipher.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Cipher::CBC" : { "file" : "lib/File/KDBX/Cipher/CBC.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Cipher::Stream" : { "file" : "lib/File/KDBX/Cipher/Stream.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Constants" : { "file" : "lib/File/KDBX/Constants.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Dumper" : { "file" : "lib/File/KDBX/Dumper.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Dumper::KDB" : { "file" : "lib/File/KDBX/Dumper/KDB.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Dumper::Raw" : { "file" : "lib/File/KDBX/Dumper/Raw.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Dumper::V3" : { "file" : "lib/File/KDBX/Dumper/V3.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Dumper::V4" : { "file" : "lib/File/KDBX/Dumper/V4.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Dumper::XML" : { "file" : "lib/File/KDBX/Dumper/XML.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Entry" : { "file" : "lib/File/KDBX/Entry.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Error" : { "file" : "lib/File/KDBX/Error.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Group" : { "file" : "lib/File/KDBX/Group.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::IO" : { "file" : "lib/File/KDBX/IO.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::IO::Crypt" : { "file" : "lib/File/KDBX/IO/Crypt.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::IO::HashBlock" : { "file" : "lib/File/KDBX/IO/HashBlock.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::IO::HmacBlock" : { "file" : "lib/File/KDBX/IO/HmacBlock.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Iterator" : { "file" : "lib/File/KDBX/Iterator.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::KDF" : { "file" : "lib/File/KDBX/KDF.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::KDF::AES" : { "file" : "lib/File/KDBX/KDF/AES.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::KDF::Argon2" : { "file" : "lib/File/KDBX/KDF/Argon2.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Key" : { "file" : "lib/File/KDBX/Key.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Key::ChallengeResponse" : { "file" : "lib/File/KDBX/Key/ChallengeResponse.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Key::Composite" : { "file" : "lib/File/KDBX/Key/Composite.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Key::File" : { "file" : "lib/File/KDBX/Key/File.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Key::Password" : { "file" : "lib/File/KDBX/Key/Password.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Key::YubiKey" : { "file" : "lib/File/KDBX/Key/YubiKey.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Loader" : { "file" : "lib/File/KDBX/Loader.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Loader::KDB" : { "file" : "lib/File/KDBX/Loader/KDB.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Loader::Raw" : { "file" : "lib/File/KDBX/Loader/Raw.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Loader::V3" : { "file" : "lib/File/KDBX/Loader/V3.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Loader::V4" : { "file" : "lib/File/KDBX/Loader/V4.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Loader::XML" : { "file" : "lib/File/KDBX/Loader/XML.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Object" : { "file" : "lib/File/KDBX/Object.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Safe" : { "file" : "lib/File/KDBX/Safe.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Transaction" : { "file" : "lib/File/KDBX/Transaction.pm", - "version" : "0.900" + "version" : "0.901" }, "File::KDBX::Util" : { "file" : "lib/File/KDBX/Util.pm", - "version" : "0.900" + "version" : "0.901" } }, "release_status" : "stable", @@ -329,7 +329,7 @@ "web" : "https://github.com/chazmcgarvey/File-KDBX" } }, - "version" : "0.900", + "version" : "0.901", "x_authority" : "cpan:CCM", "x_generated_by_perl" : "v5.34.1", "x_serialization_backend" : "Cpanel::JSON::XS version 4.27", diff --git a/META.yml b/META.yml index 5f0f5ca..3473a03 100644 --- a/META.yml +++ b/META.yml @@ -50,118 +50,118 @@ optional_features: provides: File::KDBX: file: lib/File/KDBX.pm - version: '0.900' + version: '0.901' File::KDBX::Cipher: file: lib/File/KDBX/Cipher.pm - version: '0.900' + version: '0.901' File::KDBX::Cipher::CBC: file: lib/File/KDBX/Cipher/CBC.pm - version: '0.900' + version: '0.901' File::KDBX::Cipher::Stream: file: lib/File/KDBX/Cipher/Stream.pm - version: '0.900' + version: '0.901' File::KDBX::Constants: file: lib/File/KDBX/Constants.pm - version: '0.900' + version: '0.901' File::KDBX::Dumper: file: lib/File/KDBX/Dumper.pm - version: '0.900' + version: '0.901' File::KDBX::Dumper::KDB: file: lib/File/KDBX/Dumper/KDB.pm - version: '0.900' + version: '0.901' File::KDBX::Dumper::Raw: file: lib/File/KDBX/Dumper/Raw.pm - version: '0.900' + version: '0.901' File::KDBX::Dumper::V3: file: lib/File/KDBX/Dumper/V3.pm - version: '0.900' + version: '0.901' File::KDBX::Dumper::V4: file: lib/File/KDBX/Dumper/V4.pm - version: '0.900' + version: '0.901' File::KDBX::Dumper::XML: file: lib/File/KDBX/Dumper/XML.pm - version: '0.900' + version: '0.901' File::KDBX::Entry: file: lib/File/KDBX/Entry.pm - version: '0.900' + version: '0.901' File::KDBX::Error: file: lib/File/KDBX/Error.pm - version: '0.900' + version: '0.901' File::KDBX::Group: file: lib/File/KDBX/Group.pm - version: '0.900' + version: '0.901' File::KDBX::IO: file: lib/File/KDBX/IO.pm - version: '0.900' + version: '0.901' File::KDBX::IO::Crypt: file: lib/File/KDBX/IO/Crypt.pm - version: '0.900' + version: '0.901' File::KDBX::IO::HashBlock: file: lib/File/KDBX/IO/HashBlock.pm - version: '0.900' + version: '0.901' File::KDBX::IO::HmacBlock: file: lib/File/KDBX/IO/HmacBlock.pm - version: '0.900' + version: '0.901' File::KDBX::Iterator: file: lib/File/KDBX/Iterator.pm - version: '0.900' + version: '0.901' File::KDBX::KDF: file: lib/File/KDBX/KDF.pm - version: '0.900' + version: '0.901' File::KDBX::KDF::AES: file: lib/File/KDBX/KDF/AES.pm - version: '0.900' + version: '0.901' File::KDBX::KDF::Argon2: file: lib/File/KDBX/KDF/Argon2.pm - version: '0.900' + version: '0.901' File::KDBX::Key: file: lib/File/KDBX/Key.pm - version: '0.900' + version: '0.901' File::KDBX::Key::ChallengeResponse: file: lib/File/KDBX/Key/ChallengeResponse.pm - version: '0.900' + version: '0.901' File::KDBX::Key::Composite: file: lib/File/KDBX/Key/Composite.pm - version: '0.900' + version: '0.901' File::KDBX::Key::File: file: lib/File/KDBX/Key/File.pm - version: '0.900' + version: '0.901' File::KDBX::Key::Password: file: lib/File/KDBX/Key/Password.pm - version: '0.900' + version: '0.901' File::KDBX::Key::YubiKey: file: lib/File/KDBX/Key/YubiKey.pm - version: '0.900' + version: '0.901' File::KDBX::Loader: file: lib/File/KDBX/Loader.pm - version: '0.900' + version: '0.901' File::KDBX::Loader::KDB: file: lib/File/KDBX/Loader/KDB.pm - version: '0.900' + version: '0.901' File::KDBX::Loader::Raw: file: lib/File/KDBX/Loader/Raw.pm - version: '0.900' + version: '0.901' File::KDBX::Loader::V3: file: lib/File/KDBX/Loader/V3.pm - version: '0.900' + version: '0.901' File::KDBX::Loader::V4: file: lib/File/KDBX/Loader/V4.pm - version: '0.900' + version: '0.901' File::KDBX::Loader::XML: file: lib/File/KDBX/Loader/XML.pm - version: '0.900' + version: '0.901' File::KDBX::Object: file: lib/File/KDBX/Object.pm - version: '0.900' + version: '0.901' File::KDBX::Safe: file: lib/File/KDBX/Safe.pm - version: '0.900' + version: '0.901' File::KDBX::Transaction: file: lib/File/KDBX/Transaction.pm - version: '0.900' + version: '0.901' File::KDBX::Util: file: lib/File/KDBX/Util.pm - version: '0.900' + version: '0.901' recommends: Compress::Raw::Zlib: '0' File::KDBX::XS: '0' @@ -209,7 +209,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.900' +version: '0.901' x_authority: cpan:CCM x_generated_by_perl: v5.34.1 x_serialization_backend: 'YAML::Tiny version 1.73' diff --git a/Makefile.PL b/Makefile.PL index 40792ca..7c6ce7c 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -67,7 +67,7 @@ my %WriteMakefileArgs = ( "lib" => 0, "utf8" => 0 }, - "VERSION" => "0.900", + "VERSION" => "0.901", "test" => { "TESTS" => "t/*.t" } diff --git a/README b/README index 42cd205..06be87c 100644 --- a/README +++ b/README @@ -4,7 +4,7 @@ NAME VERSION - version 0.900 + version 0.901 SYNOPSIS @@ -784,12 +784,12 @@ METHODS $kdbx->lock; - Encrypt all protected binaries strings in a database. The encrypted - strings are stored in a File::KDBX::Safe associated with the database - and the actual strings will be replaced with undef to indicate their + Encrypt all protected strings and binaries in a database. The encrypted + data is stored in a File::KDBX::Safe associated with the database and + the actual values will be replaced with undef to indicate their protected state. Returns itself to allow method chaining. - You can call code on an already-locked database to memory-protect any + You can call lock on an already-locked database to memory-protect any unprotected strings and binaries added after the last time the database was locked. @@ -797,9 +797,9 @@ METHODS $kdbx->unlock; - Decrypt all protected strings in a database, replacing undef - placeholders with unprotected values. Returns itself to allow method - chaining. + Decrypt all protected strings and binaries in a database, replacing + undef value placeholders with their actual, unprotected values. Returns + itself to allow method chaining. unlock_scoped @@ -811,6 +811,14 @@ METHODS See "lock" and "unlock". + Example: + + { + my $guard = $kdbx->unlock_scoped; + ...; + } + # $kdbx is now memory-locked + peek $string = $kdbx->peek(\%string); @@ -825,10 +833,10 @@ METHODS $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 undef values) until "unlock" 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 undef values) until "unlock" is called. remove_empty_groups @@ -896,8 +904,8 @@ METHODS Get or set a File::KDBX::Key. This is the master key (e.g. a password or a key file that can decrypt a database). You can also pass a - primitive that can be cast to a Key. See "new" in File::KDBX::Key for - an explanation of what the primitive can be. + primitive castable to a Key. See "new" in File::KDBX::Key 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 @@ -1500,14 +1508,14 @@ ERRORS Errors in this package are constructed as File::KDBX::Error objects and propagated using perl's built-in mechanisms. Fatal errors are - propagated using "die" in functions and non-fatal errors (a.k.a. - warnings) are propagated using "warn" in functions while adhering to - perl's warnings system. If you're already familiar with these + propagated using "die LIST" in perlfunc and non-fatal errors (a.k.a. + warnings) are propagated using "warn LIST" in perlfunc while adhering + to perl's warnings system. If you're already familiar with these mechanisms, you can skip this section. - You can catch fatal errors using "eval" in functions (or something like - Try::Tiny) and non-fatal errors using $SIG{__WARN__} (see "%SIG" in - variables). Examples: + You can catch fatal errors using "eval BLOCK" in perlfunc (or something + like Try::Tiny) and non-fatal errors using $SIG{__WARN__} (see "%SIG" + in perlvar). Examples: use File::KDBX::Error qw(error); diff --git a/lib/File/KDBX.pm b/lib/File/KDBX.pm index d2dc04f..c8686ef 100644 --- a/lib/File/KDBX.pm +++ b/lib/File/KDBX.pm @@ -19,7 +19,7 @@ use Time::Piece; use boolean; use namespace::clean; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # VERSION our $WARNINGS = 1; fieldhashes \my (%SAFE, %KEYS); @@ -796,7 +796,7 @@ sub peek { } -sub is_locked { $_[0]->_safe ? 1 : 0 } +sub is_locked { !!$_[0]->_safe } ############################################################################## @@ -1120,7 +1120,7 @@ File::KDBX - Encrypted database to store secret text and files =head1 VERSION -version 0.900 +version 0.901 =head1 SYNOPSIS @@ -1933,19 +1933,19 @@ To get the I string of a specific entry (identified by its UUID): $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. =head2 unlock $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. =head2 unlock_scoped @@ -1956,6 +1956,14 @@ C if the database is already unlocked. See L and L. +Example: + + { + my $guard = $kdbx->unlock_scoped; + ...; + } + # $kdbx is now memory-locked + =head2 peek $string = $kdbx->peek(\%string); @@ -1968,9 +1976,9 @@ a string or binary hashref as returned by L or Lis_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. =head2 remove_empty_groups @@ -2053,8 +2061,8 @@ You normally do not need to call this method explicitly because the dumper does $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). You can also pass a primitive that can be cast to a B. 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. @@ -2711,12 +2719,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); diff --git a/lib/File/KDBX/Cipher.pm b/lib/File/KDBX/Cipher.pm index fd335a3..250b9cf 100644 --- a/lib/File/KDBX/Cipher.pm +++ b/lib/File/KDBX/Cipher.pm @@ -12,7 +12,7 @@ use Module::Load; use Scalar::Util qw(looks_like_number); use namespace::clean; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # VERSION my %CIPHERS; @@ -158,7 +158,7 @@ File::KDBX::Cipher - A block cipher mode or cipher stream =head1 VERSION -version 0.900 +version 0.901 =head1 SYNOPSIS diff --git a/lib/File/KDBX/Cipher/CBC.pm b/lib/File/KDBX/Cipher/CBC.pm index 0dbdc6c..a759452 100644 --- a/lib/File/KDBX/Cipher/CBC.pm +++ b/lib/File/KDBX/Cipher/CBC.pm @@ -11,7 +11,7 @@ use namespace::clean; extends 'File::KDBX::Cipher'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # 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.900 +version 0.901 =head1 SYNOPSIS diff --git a/lib/File/KDBX/Cipher/Stream.pm b/lib/File/KDBX/Cipher/Stream.pm index 2cd4033..2b0c9f2 100644 --- a/lib/File/KDBX/Cipher/Stream.pm +++ b/lib/File/KDBX/Cipher/Stream.pm @@ -14,7 +14,7 @@ use namespace::clean; extends 'File::KDBX::Cipher'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # VERSION has 'counter', is => 'ro', default => 0; @@ -123,7 +123,7 @@ File::KDBX::Cipher::Stream - A cipher stream encrypter/decrypter =head1 VERSION -version 0.900 +version 0.901 =head1 SYNOPSIS diff --git a/lib/File/KDBX/Constants.pm b/lib/File/KDBX/Constants.pm index a3d5baa..f31cc0c 100644 --- a/lib/File/KDBX/Constants.pm +++ b/lib/File/KDBX/Constants.pm @@ -13,7 +13,7 @@ use Exporter qw(import); use Scalar::Util qw(dualvar); use namespace::clean -except => 'import'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # VERSION BEGIN { my %CONSTANTS = ( @@ -346,7 +346,7 @@ File::KDBX::Constants - All the KDBX-related constants you could ever want =head1 VERSION -version 0.900 +version 0.901 =head1 SYNOPSIS diff --git a/lib/File/KDBX/Dumper.pm b/lib/File/KDBX/Dumper.pm index 3b3d311..f6d6657 100644 --- a/lib/File/KDBX/Dumper.pm +++ b/lib/File/KDBX/Dumper.pm @@ -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.900'; # VERSION +our $VERSION = '0.901'; # VERSION sub new { @@ -118,16 +118,27 @@ sub dump_file { my $filepath = shift; my %args = @_ % 2 == 0 ? @_ : (key => shift, @_); - my $key = delete $args{key}; + my $key = delete $args{key}; + my $mode = delete $args{mode}; + my $uid = delete $args{uid}; + my $gid = delete $args{gid}; + my $atomic = delete $args{atomic} // 1; + $args{kdbx} //= $self->kdbx; - require File::Temp; - my ($fh, $filepath_temp) = eval { File::Temp::tempfile("${filepath}-XXXXXX", CLEANUP => 1) }; - if (!$fh or my $err = $@) { - $err //= 'Unknown error'; - throw sprintf('Open file failed (%s): %s', $filepath_temp, $err), - error => $err, - filepath => $filepath_temp; + my ($fh, $filepath_temp); + if ($atomic) { + require File::Temp; + ($fh, $filepath_temp) = eval { File::Temp::tempfile("${filepath}-XXXXXX", UNLINK => 1) }; + if (!$fh or my $err = $@) { + $err //= 'Unknown error'; + throw sprintf('Open file failed (%s): %s', $filepath_temp, $err), + error => $err, + filepath => $filepath_temp; + } + } + else { + open($fh, '>:raw', $filepath) or throw "Open file failed ($filepath): $!", filepath => $filepath; } $fh->autoflush(1); @@ -138,12 +149,15 @@ sub dump_file { my ($file_mode, $file_uid, $file_gid) = (stat($filepath))[2, 4, 5]; - my $mode = $args{mode} // $file_mode // do { my $m = umask; defined $m ? oct(666) &~ $m : undef }; - my $uid = $args{uid} // $file_uid // -1; - my $gid = $args{gid} // $file_gid // -1; - chmod($mode, $filepath_temp) if defined $mode; - chown($uid, $gid, $filepath_temp); - rename($filepath_temp, $filepath) or throw "Failed to write file ($filepath): $!", filepath => $filepath; + if ($filepath_temp) { + $mode //= $file_mode // do { my $m = umask; defined $m ? oct(666) &~ $m : undef }; + $uid //= $file_uid // -1; + $gid //= $file_gid // -1; + chmod($mode, $filepath_temp) if defined $mode; + chown($uid, $gid, $filepath_temp); + rename($filepath_temp, $filepath) or throw "Failed to write file ($filepath): $!", + filepath => $filepath; + } return $self; } @@ -273,7 +287,7 @@ File::KDBX::Dumper - Write KDBX files =head1 VERSION -version 0.900 +version 0.901 =head1 ATTRIBUTES @@ -379,33 +393,108 @@ Set a L to a blank state, ready to dump another KDBX file. =head2 dump - $dumper->dump(\$string, $key); - $dumper->dump(*IO, $key); - $dumper->dump($filepath, $key); + $dumper->dump(\$string, %options); + $dumper->dump(\$string, $key, %options); + $dumper->dump(*IO, %options); + $dumper->dump(*IO, $key, %options); + $dumper->dump($filepath, %options); + $dumper->dump($filepath, $key, %options); Dump a KDBX file. -The C<$key> is either a L or a primitive that can be cast to a Key object. +The C<$key> is either a L or a primitive castable to a Key object. Available options: + +=over 4 + +=item * + +C - Database to dump (default: value of L) + +=item * + +C - Alternative way to specify C<$key> (default: value of L) + +=back + +Other options are supported depending on the first argument. See L, L and +L. =head2 dump_string - $dumper->dump_string(\$string, $key); - \$string = $dumper->dump_string($key); + $dumper->dump_string(\$string, %options); + $dumper->dump_string(\$string, $key, %options); + \$string = $dumper->dump_string(%options); + \$string = $dumper->dump_string($key, %options); + +Dump a KDBX file to a string / memory buffer. Available options: + +=over 4 + +=item * -Dump a KDBX file to a string / memory buffer. +C - Database to dump (default: value of L) + +=item * + +C - Alternative way to specify C<$key> (default: value of L) + +=back =head2 dump_file - $dumper->dump_file($filepath, $key); + $dumper->dump_file($filepath, %options); + $dumper->dump_file($filepath, $key, %options); -Dump a KDBX file to a filesystem. +Dump a KDBX file to a filesystem. Available options: + +=over 4 + +=item * + +C - Database to dump (default: value of L) + +=item * + +C - Alternative way to specify C<$key> (default: value of L) + +=item * + +C - File mode / permissions (see L + +=item * + +C - User ID (see L) + +=item * + +C - Group ID (see L) + +=item * + +C - Write to the filepath atomically (default: true) + +=back =head2 dump_handle - $dumper->dump_handle($fh, $key); - $dumper->dump_handle(*IO, $key); + $dumper->dump_handle($fh, %options); + $dumper->dump_handle(*IO, $key, %options); + $dumper->dump_handle($fh, %options); + $dumper->dump_handle(*IO, $key, %options); + +Dump a KDBX file to an output stream / file handle. Available options: + +=over 4 + +=item * + +C - Database to dump (default: value of L) -Dump a KDBX file to an output stream / file handle. +=item * + +C - Alternative way to specify C<$key> (default: value of L) + +=back =head1 BUGS diff --git a/lib/File/KDBX/Dumper/KDB.pm b/lib/File/KDBX/Dumper/KDB.pm index 39ab4f6..4f35970 100644 --- a/lib/File/KDBX/Dumper/KDB.pm +++ b/lib/File/KDBX/Dumper/KDB.pm @@ -14,7 +14,7 @@ use namespace::clean; extends 'File::KDBX::Dumper'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # VERSION sub _write_magic_numbers { '' } sub _write_headers { '' } @@ -138,7 +138,7 @@ File::KDBX::Dumper::KDB - Write KDB files =head1 VERSION -version 0.900 +version 0.901 =head1 DESCRIPTION diff --git a/lib/File/KDBX/Dumper/Raw.pm b/lib/File/KDBX/Dumper/Raw.pm index 1153fcc..d6d2173 100644 --- a/lib/File/KDBX/Dumper/Raw.pm +++ b/lib/File/KDBX/Dumper/Raw.pm @@ -9,7 +9,7 @@ use namespace::clean; extends 'File::KDBX::Dumper'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # 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.900 +version 0.901 =head1 SYNOPSIS diff --git a/lib/File/KDBX/Dumper/V3.pm b/lib/File/KDBX/Dumper/V3.pm index 394dce3..a5f7ef0 100644 --- a/lib/File/KDBX/Dumper/V3.pm +++ b/lib/File/KDBX/Dumper/V3.pm @@ -16,7 +16,7 @@ use namespace::clean; extends 'File::KDBX::Dumper'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # VERSION sub _write_headers { my $self = shift; @@ -189,7 +189,7 @@ File::KDBX::Dumper::V3 - Dump KDBX3 files =head1 VERSION -version 0.900 +version 0.901 =head1 BUGS diff --git a/lib/File/KDBX/Dumper/V4.pm b/lib/File/KDBX/Dumper/V4.pm index 061bd08..c5644d4 100644 --- a/lib/File/KDBX/Dumper/V4.pm +++ b/lib/File/KDBX/Dumper/V4.pm @@ -19,7 +19,7 @@ use namespace::clean; extends 'File::KDBX::Dumper'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # VERSION has _binaries_written => {}, is => 'ro'; @@ -377,7 +377,7 @@ File::KDBX::Dumper::V4 - Dump KDBX4 files =head1 VERSION -version 0.900 +version 0.901 =head1 BUGS diff --git a/lib/File/KDBX/Dumper/XML.pm b/lib/File/KDBX/Dumper/XML.pm index f5b62f0..1d7ec39 100644 --- a/lib/File/KDBX/Dumper/XML.pm +++ b/lib/File/KDBX/Dumper/XML.pm @@ -19,7 +19,7 @@ use namespace::clean; extends 'File::KDBX::Dumper'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # VERSION has allow_protection => 1; @@ -564,7 +564,7 @@ File::KDBX::Dumper::XML - Dump unencrypted XML KeePass files =head1 VERSION -version 0.900 +version 0.901 =head1 ATTRIBUTES diff --git a/lib/File/KDBX/Entry.pm b/lib/File/KDBX/Entry.pm index be4a6a3..8012372 100644 --- a/lib/File/KDBX/Entry.pm +++ b/lib/File/KDBX/Entry.pm @@ -21,7 +21,7 @@ use namespace::clean; extends 'File::KDBX::Object'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # VERSION my $PLACEHOLDER_MAX_DEPTH = 10; my %PLACEHOLDERS; @@ -681,7 +681,7 @@ File::KDBX::Entry - A KDBX database entry =head1 VERSION -version 0.900 +version 0.901 =head1 DESCRIPTION diff --git a/lib/File/KDBX/Error.pm b/lib/File/KDBX/Error.pm index f902f79..5e3de90 100644 --- a/lib/File/KDBX/Error.pm +++ b/lib/File/KDBX/Error.pm @@ -8,7 +8,7 @@ use Exporter qw(import); use Scalar::Util qw(blessed looks_like_number); use namespace::clean -except => 'import'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # VERSION our @EXPORT = qw(alert error throw); @@ -166,7 +166,7 @@ File::KDBX::Error - Represents something bad that happened =head1 VERSION -version 0.900 +version 0.901 =head1 ATTRIBUTES diff --git a/lib/File/KDBX/Group.pm b/lib/File/KDBX/Group.pm index fe087d2..c81f34b 100644 --- a/lib/File/KDBX/Group.pm +++ b/lib/File/KDBX/Group.pm @@ -19,7 +19,7 @@ use namespace::clean; extends 'File::KDBX::Object'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # VERSION # has uuid => sub { generate_uuid(printable => 1) }; @@ -398,7 +398,7 @@ File::KDBX::Group - A KDBX database group =head1 VERSION -version 0.900 +version 0.901 =head1 DESCRIPTION diff --git a/lib/File/KDBX/IO.pm b/lib/File/KDBX/IO.pm index 6dc5c9b..0ac4529 100644 --- a/lib/File/KDBX/IO.pm +++ b/lib/File/KDBX/IO.pm @@ -14,7 +14,7 @@ use namespace::clean; extends 'IO::Handle'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # 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.900 +version 0.901 =head1 DESCRIPTION diff --git a/lib/File/KDBX/IO/Crypt.pm b/lib/File/KDBX/IO/Crypt.pm index 0d59723..0974933 100644 --- a/lib/File/KDBX/IO/Crypt.pm +++ b/lib/File/KDBX/IO/Crypt.pm @@ -11,7 +11,7 @@ use namespace::clean; extends 'File::KDBX::IO'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # VERSION our $BUFFER_SIZE = 16384; our $ERROR; @@ -139,7 +139,7 @@ File::KDBX::IO::Crypt - Encrypter/decrypter IO handle =head1 VERSION -version 0.900 +version 0.901 =head1 SYNOPSIS diff --git a/lib/File/KDBX/IO/HashBlock.pm b/lib/File/KDBX/IO/HashBlock.pm index 21e8a10..f1bdf92 100644 --- a/lib/File/KDBX/IO/HashBlock.pm +++ b/lib/File/KDBX/IO/HashBlock.pm @@ -13,7 +13,7 @@ use namespace::clean; extends 'File::KDBX::IO'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # 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.900 +version 0.901 =head1 DESCRIPTION diff --git a/lib/File/KDBX/IO/HmacBlock.pm b/lib/File/KDBX/IO/HmacBlock.pm index 650d681..ae36c95 100644 --- a/lib/File/KDBX/IO/HmacBlock.pm +++ b/lib/File/KDBX/IO/HmacBlock.pm @@ -13,7 +13,7 @@ use namespace::clean; extends 'File::KDBX::IO'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # VERSION our $BLOCK_SIZE = 1048576; # 1MiB our $ERROR; @@ -214,7 +214,7 @@ File::KDBX::IO::HmacBlock - HMAC block stream IO handle =head1 VERSION -version 0.900 +version 0.901 =head1 DESCRIPTION diff --git a/lib/File/KDBX/Iterator.pm b/lib/File/KDBX/Iterator.pm index 3f0124d..72de001 100644 --- a/lib/File/KDBX/Iterator.pm +++ b/lib/File/KDBX/Iterator.pm @@ -14,7 +14,7 @@ use namespace::clean; BEGIN { mark_as_loaded('Iterator::Simple::Iterator') } extends 'Iterator::Simple::Iterator'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # VERSION sub new { @@ -221,7 +221,7 @@ File::KDBX::Iterator - KDBX database iterator =head1 VERSION -version 0.900 +version 0.901 =head1 SYNOPSIS diff --git a/lib/File/KDBX/KDF.pm b/lib/File/KDBX/KDF.pm index 4b7c920..f1e0196 100644 --- a/lib/File/KDBX/KDF.pm +++ b/lib/File/KDBX/KDF.pm @@ -12,7 +12,7 @@ use Module::Load; use Scalar::Util qw(blessed); use namespace::clean; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # VERSION my %KDFS; @@ -120,7 +120,7 @@ File::KDBX::KDF - A key derivation function =head1 VERSION -version 0.900 +version 0.901 =head1 DESCRIPTION diff --git a/lib/File/KDBX/KDF/AES.pm b/lib/File/KDBX/KDF/AES.pm index 07ff122..b5d22c9 100644 --- a/lib/File/KDBX/KDF/AES.pm +++ b/lib/File/KDBX/KDF/AES.pm @@ -13,7 +13,7 @@ use namespace::clean; extends 'File::KDBX::KDF'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # 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.900 +version 0.901 =head1 DESCRIPTION diff --git a/lib/File/KDBX/KDF/Argon2.pm b/lib/File/KDBX/KDF/Argon2.pm index 9951473..4c9146f 100644 --- a/lib/File/KDBX/KDF/Argon2.pm +++ b/lib/File/KDBX/KDF/Argon2.pm @@ -12,7 +12,7 @@ use namespace::clean; extends 'File::KDBX::KDF'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # 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.900 +version 0.901 =head1 DESCRIPTION diff --git a/lib/File/KDBX/Key.pm b/lib/File/KDBX/Key.pm index 9333072..12fa430 100644 --- a/lib/File/KDBX/Key.pm +++ b/lib/File/KDBX/Key.pm @@ -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.900'; # VERSION +our $VERSION = '0.901'; # VERSION fieldhashes \my %SAFE; @@ -140,7 +140,7 @@ File::KDBX::Key - A credential that can protect a KDBX file =head1 VERSION -version 0.900 +version 0.901 =head1 DESCRIPTION diff --git a/lib/File/KDBX/Key/ChallengeResponse.pm b/lib/File/KDBX/Key/ChallengeResponse.pm index 1dd88ee..843c892 100644 --- a/lib/File/KDBX/Key/ChallengeResponse.pm +++ b/lib/File/KDBX/Key/ChallengeResponse.pm @@ -10,7 +10,7 @@ use namespace::clean; extends 'File::KDBX::Key'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # VERSION sub init { my $self = shift; @@ -56,7 +56,7 @@ File::KDBX::Key::ChallengeResponse - A challenge-response key =head1 VERSION -version 0.900 +version 0.901 =head1 SYNOPSIS diff --git a/lib/File/KDBX/Key/Composite.pm b/lib/File/KDBX/Key/Composite.pm index 9ecc0f1..e6f3d93 100644 --- a/lib/File/KDBX/Key/Composite.pm +++ b/lib/File/KDBX/Key/Composite.pm @@ -13,7 +13,7 @@ use namespace::clean; extends 'File::KDBX::Key'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # 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.900 +version 0.901 =head1 SYNOPSIS diff --git a/lib/File/KDBX/Key/File.pm b/lib/File/KDBX/Key/File.pm index 6e4173e..2223870 100644 --- a/lib/File/KDBX/Key/File.pm +++ b/lib/File/KDBX/Key/File.pm @@ -17,7 +17,7 @@ use namespace::clean; extends 'File::KDBX::Key'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # VERSION has 'type', is => 'ro'; @@ -99,18 +99,24 @@ sub save { my $version = $args{version} // $self->version // 2; my $filepath = $args{filepath} // $self->filepath; my $fh = $args{fh}; + my $atomic = $args{atomic} // 1; my $filepath_temp; if (!openhandle($fh)) { $filepath or throw 'Must specify where to safe the key file to'; - require File::Temp; - ($fh, $filepath_temp) = eval { File::Temp::tempfile("${filepath}-XXXXXX", CLEANUP => 1) }; - if (!$fh or my $err = $@) { - $err //= 'Unknown error'; - throw sprintf('Open file failed (%s): %s', $filepath_temp, $err), - error => $err, - filepath => $filepath_temp; + if ($atomic) { + require File::Temp; + ($fh, $filepath_temp) = eval { File::Temp::tempfile("${filepath}-XXXXXX", UNLINK => 1) }; + if (!$fh or my $err = $@) { + $err //= 'Unknown error'; + throw sprintf('Open file failed (%s): %s', $filepath_temp, $err), + error => $err, + filepath => $filepath_temp; + } + } + else { + open($fh, '>:raw', $filepath) or throw "Open file failed ($filepath): $!", filepath => $filepath; } } @@ -256,7 +262,7 @@ File::KDBX::Key::File - A file key =head1 VERSION -version 0.900 +version 0.901 =head1 SYNOPSIS @@ -377,6 +383,10 @@ C - IO handle to write to (overrides C, one of which must be defin C - Raw key (default: value of L) +=item * + +C - Write to the filepath atomically (default: true) + =back =head1 BUGS diff --git a/lib/File/KDBX/Key/Password.pm b/lib/File/KDBX/Key/Password.pm index d7aa7d5..fe0face 100644 --- a/lib/File/KDBX/Key/Password.pm +++ b/lib/File/KDBX/Key/Password.pm @@ -12,7 +12,7 @@ use namespace::clean; extends 'File::KDBX::Key'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # VERSION sub init { my $self = shift; @@ -37,7 +37,7 @@ File::KDBX::Key::Password - A password key =head1 VERSION -version 0.900 +version 0.901 =head1 SYNOPSIS diff --git a/lib/File/KDBX/Key/YubiKey.pm b/lib/File/KDBX/Key/YubiKey.pm index 949f348..1185fa1 100644 --- a/lib/File/KDBX/Key/YubiKey.pm +++ b/lib/File/KDBX/Key/YubiKey.pm @@ -14,7 +14,7 @@ use namespace::clean; extends 'File::KDBX::Key::ChallengeResponse'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # 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.900 +version 0.901 =head1 SYNOPSIS diff --git a/lib/File/KDBX/Loader.pm b/lib/File/KDBX/Loader.pm index dd0caff..69a7bfb 100644 --- a/lib/File/KDBX/Loader.pm +++ b/lib/File/KDBX/Loader.pm @@ -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.900'; # VERSION +our $VERSION = '0.901'; # VERSION sub new { @@ -249,7 +249,7 @@ File::KDBX::Loader - Load KDBX files =head1 VERSION -version 0.900 +version 0.901 =head1 DESCRIPTION @@ -333,37 +333,73 @@ Set a L to a blank state, ready to load another KDBX file. =head2 load + $kdbx = File::KDBX::Loader->load(\$string, %options); $kdbx = File::KDBX::Loader->load(\$string, $key); + $kdbx = File::KDBX::Loader->load(*IO, %options); $kdbx = File::KDBX::Loader->load(*IO, $key); + $kdbx = File::KDBX::Loader->load($filepath, %options); $kdbx = File::KDBX::Loader->load($filepath, $key); - $kdbx = $loader->load(...); # also instance method -Load a KDBX file. +Load a KDBX file. This works as an instance or a class method. The C<$key> is either +a L or a primitive castable to a Key object. Available options: -The C<$key> is either a L or a primitive that can be cast to a Key object. +=over 4 + +=item * + +C - Alternative way to specify C<$key> + +=back =head2 load_string + $kdbx = File::KDBX::Loader->load_string($string, %options); $kdbx = File::KDBX::Loader->load_string($string, $key); + $kdbx = File::KDBX::Loader->load_string(\$string, %options); $kdbx = File::KDBX::Loader->load_string(\$string, $key); - $kdbx = $loader->load_string(...); # also instance method -Load a KDBX file from a string / memory buffer. +Load a KDBX file from a string / memory buffer. This works as an instance or class method. Available options: + +=over 4 + +=item * + +C - Alternative way to specify C<$key> + +=back =head2 load_file + $kdbx = File::KDBX::Loader->load_file($filepath, %options); $kdbx = File::KDBX::Loader->load_file($filepath, $key); - $kdbx = $loader->load_file(...); # also instance method -Read a KDBX file from a filesystem. +Read a KDBX file from a filesystem. This works as an instance or class method. Available options: + +=over 4 + +=item * + +C - Alternative way to specify C<$key> + +=back =head2 load_handle + $kdbx = File::KDBX::Loader->load_handle($fh, %options); $kdbx = File::KDBX::Loader->load_handle($fh, $key); + $kdbx = File::KDBX::Loader->load_handle(*IO, %options); $kdbx = File::KDBX::Loader->load_handle(*IO, $key); - $kdbx->load_handle(...); # also instance method -Read a KDBX file from an input stream / file handle. +Read a KDBX file from an input stream / file handle. This works as an instance or class method. Available +options: + +=over 4 + +=item * + +C - Alternative way to specify C<$key> + +=back =head2 read_magic_numbers diff --git a/lib/File/KDBX/Loader/KDB.pm b/lib/File/KDBX/Loader/KDB.pm index 3e1e35e..7dad5d2 100644 --- a/lib/File/KDBX/Loader/KDB.pm +++ b/lib/File/KDBX/Loader/KDB.pm @@ -17,7 +17,7 @@ use namespace::clean; extends 'File::KDBX::Loader'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # VERSION my $DEFAULT_EXPIRATION = Time::Piece->new(32503677839); # 2999-12-31 23:59:59 @@ -397,7 +397,7 @@ File::KDBX::Loader::KDB - Read KDB files =head1 VERSION -version 0.900 +version 0.901 =head1 DESCRIPTION diff --git a/lib/File/KDBX/Loader/Raw.pm b/lib/File/KDBX/Loader/Raw.pm index d86b782..ddb64a7 100644 --- a/lib/File/KDBX/Loader/Raw.pm +++ b/lib/File/KDBX/Loader/Raw.pm @@ -9,7 +9,7 @@ use namespace::clean; extends 'File::KDBX::Loader'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # 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.900 +version 0.901 =head1 SYNOPSIS diff --git a/lib/File/KDBX/Loader/V3.pm b/lib/File/KDBX/Loader/V3.pm index 8d2047c..6be4090 100644 --- a/lib/File/KDBX/Loader/V3.pm +++ b/lib/File/KDBX/Loader/V3.pm @@ -27,7 +27,7 @@ use namespace::clean; extends 'File::KDBX::Loader'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # VERSION sub _read_header { my $self = shift; @@ -175,7 +175,7 @@ File::KDBX::Loader::V3 - Load KDBX3 files =head1 VERSION -version 0.900 +version 0.901 =head1 BUGS diff --git a/lib/File/KDBX/Loader/V4.pm b/lib/File/KDBX/Loader/V4.pm index 3cf1a15..0e33fc9 100644 --- a/lib/File/KDBX/Loader/V4.pm +++ b/lib/File/KDBX/Loader/V4.pm @@ -30,7 +30,7 @@ use namespace::clean; extends 'File::KDBX::Loader'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # VERSION sub _read_header { my $self = shift; @@ -275,7 +275,7 @@ File::KDBX::Loader::V4 - Load KDBX4 files =head1 VERSION -version 0.900 +version 0.901 =head1 BUGS diff --git a/lib/File/KDBX/Loader/XML.pm b/lib/File/KDBX/Loader/XML.pm index 89234b5..a0826e9 100644 --- a/lib/File/KDBX/Loader/XML.pm +++ b/lib/File/KDBX/Loader/XML.pm @@ -18,7 +18,7 @@ use namespace::clean; extends 'File::KDBX::Loader'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # VERSION has '_reader', is => 'ro'; has '_safe', is => 'ro', default => sub { File::KDBX::Safe->new(cipher => $_[0]->kdbx->random_stream) }; @@ -591,7 +591,7 @@ File::KDBX::Loader::XML - Load unencrypted XML KeePass files =head1 VERSION -version 0.900 +version 0.901 =head1 BUGS diff --git a/lib/File/KDBX/Object.pm b/lib/File/KDBX/Object.pm index bdf86a8..b7b9644 100644 --- a/lib/File/KDBX/Object.pm +++ b/lib/File/KDBX/Object.pm @@ -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.900'; # VERSION +our $VERSION = '0.901'; # VERSION fieldhashes \my (%KDBX, %PARENT, %TXNS, %REFS, %SIGNALS); @@ -526,7 +526,7 @@ File::KDBX::Object - A KDBX database object =head1 VERSION -version 0.900 +version 0.901 =head1 DESCRIPTION diff --git a/lib/File/KDBX/Safe.pm b/lib/File/KDBX/Safe.pm index ea0c402..b88b86c 100644 --- a/lib/File/KDBX/Safe.pm +++ b/lib/File/KDBX/Safe.pm @@ -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.900'; # VERSION +our $VERSION = '0.901'; # VERSION sub new { @@ -110,7 +110,7 @@ sub add_protected { @strings or throw 'Must provide strings to lock'; for my $string (@strings) { - my $item = {str => $string}; + my $item = {str => $string, off => $self->{counter}}; $item->{filter} = $filter if defined $filter; if (is_scalarref($string)) { next if !defined $$string; @@ -217,7 +217,7 @@ File::KDBX::Safe - Keep strings encrypted while in memory =head1 VERSION -version 0.900 +version 0.901 =head1 SYNOPSIS diff --git a/lib/File/KDBX/Transaction.pm b/lib/File/KDBX/Transaction.pm index d33a48f..86c380f 100644 --- a/lib/File/KDBX/Transaction.pm +++ b/lib/File/KDBX/Transaction.pm @@ -8,7 +8,7 @@ use Devel::GlobalDestruction; use File::KDBX::Util qw(:class); use namespace::clean; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # VERSION sub new { @@ -59,7 +59,7 @@ File::KDBX::Transaction - Make multiple database edits atomically =head1 VERSION -version 0.900 +version 0.901 =head1 ATTRIBUTES diff --git a/lib/File/KDBX/Util.pm b/lib/File/KDBX/Util.pm index 948cf16..7223f6a 100644 --- a/lib/File/KDBX/Util.pm +++ b/lib/File/KDBX/Util.pm @@ -17,7 +17,7 @@ use Time::Piece; use boolean; use namespace::clean -except => 'import'; -our $VERSION = '0.900'; # VERSION +our $VERSION = '0.901'; # VERSION our %EXPORT_TAGS = ( assert => [qw(DEBUG assert assert_64bit)], @@ -803,7 +803,7 @@ File::KDBX::Util - Utility functions for working with KDBX files =head1 VERSION -version 0.900 +version 0.901 =head1 FUNCTIONS @@ -1054,8 +1054,8 @@ Get either a L or L, depending on the argument $size = read_all($fh, my $buffer, $size); $size = read_all($fh, my $buffer, $size, $offset); -Like L but returns C if not all C<$size> bytes are read. This is considered an error, -distinguishable from other errors by C<$!> not being set. +Like L but returns C if not all C<$size> bytes are +read. This is considered an error, distinguishable from other errors by C<$!> not being set. =head2 recurse_limit diff --git a/t/database.t b/t/database.t index d4edfb2..8bed335 100644 --- a/t/database.t +++ b/t/database.t @@ -9,6 +9,7 @@ use lib "$Bin/lib"; use TestCommon; use File::KDBX; +use File::Temp qw(tempfile); use Test::Deep; use Test::More; use Time::Piece; @@ -170,4 +171,24 @@ subtest 'Maintenance' => sub { is $entry->custom_icon_uuid, $icon_uuid, 'Uses of removed icon change'; }; +subtest 'Dumping to filesystem' => sub { + my $kdbx = File::KDBX->new; + $kdbx->add_entry(title => 'Foo', password => 'whatever'); + + my ($fh, $filepath) = tempfile('kdbx-XXXXXX', TMPDIR => 1, UNLINK => 1); + close($fh); + + $kdbx->dump($filepath, 'a'); + + my $kdbx2 = File::KDBX->load($filepath, 'a'); + my $entry = $kdbx2->entries->map(sub { $_->title.'/'.$_->expand_password })->next; + is $entry, 'Foo/whatever', 'Dump and load an entry'; + + $kdbx->dump($filepath, key => 'a', atomic => 0); + + $kdbx2 = File::KDBX->load($filepath, 'a'); + $entry = $kdbx2->entries->map(sub { $_->title.'/'.$_->expand_password })->next; + is $entry, 'Foo/whatever', 'Dump and load an entry (non-atomic)'; +}; + done_testing; diff --git a/t/hash-block.t b/t/hash-block.t index b42aa23..3bf3261 100644 --- a/t/hash-block.t +++ b/t/hash-block.t @@ -40,9 +40,9 @@ SKIP: { $write = File::KDBX::IO::HashBlock->new($write); print $write $expected_plaintext; close($write) or die "close failed: $!"; - # exit; - require POSIX; - POSIX::_exit(0); + exit; + # require POSIX; + # POSIX::_exit(0); } $read = File::KDBX::IO::HashBlock->new($read); diff --git a/t/hmac-block.t b/t/hmac-block.t index 87f2809..035d433 100644 --- a/t/hmac-block.t +++ b/t/hmac-block.t @@ -44,9 +44,9 @@ SKIP: { $write = File::KDBX::IO::HmacBlock->new($write, key => $KEY); print $write $expected_plaintext; close($write) or die "close failed: $!"; - # exit; - require POSIX; - POSIX::_exit(0); + exit; + # require POSIX; + # POSIX::_exit(0); } $read = File::KDBX::IO::HmacBlock->new($read, key => $KEY); diff --git a/t/kdbx3.t b/t/kdbx3.t index 5fe53f7..fac6101 100644 --- a/t/kdbx3.t +++ b/t/kdbx3.t @@ -105,10 +105,12 @@ subtest 'Verify ProtectedStrings' => sub { is $kdbx->meta->{database_name}, 'Protected Strings Test', 'Extract database name from meta'; - $kdbx->unlock; - my $entry = $kdbx->entries->next; is $entry->title, 'Sample Entry', 'Get entry title'; + + is $entry->string_peek('Password'), 'ProtectedPassword', 'Peek at password from entry'; + is $entry->string_peek('TestProtected'), 'ABC', 'Peek at protected string from entry'; + $kdbx->unlock; is $entry->username, 'Protected User Name', 'Get protected username from entry'; is $entry->password, 'ProtectedPassword', 'Get protected password from entry'; is $entry->string_value('TestProtected'), 'ABC', 'Get ABC string from entry'; diff --git a/t/keys.t b/t/keys.t index 65658e5..601260c 100644 --- a/t/keys.t +++ b/t/keys.t @@ -55,7 +55,8 @@ for my $test ( subtest "Save $type key file" => sub { my ($type, $filename, $expected_key, $version) = @_; - my ($fh, $filepath) = tempfile('keyfile-XXXXXX', TMPDIR => 1, UNLINK => 1, SUFFIX => '.key'); + my ($fh, $filepath) = tempfile('keyfile-XXXXXX', TMPDIR => 1, UNLINK => 1); + close($fh); note $filepath; my $key = File::KDBX::Key::File->new( filepath => $filepath, @@ -65,7 +66,6 @@ for my $test ( ); my $e = exception { $key->save }; - close($fh); if ($type == KEY_FILE_TYPE_HASHED) { like $e, qr/invalid type/i, "Cannot save $type file"; @@ -88,7 +88,7 @@ subtest 'IO handle key files' => sub { 'Can calculate raw key from file handle' or diag encode_b64($key->raw_key); is $key->type, 'hashed', 'file type is detected as hashed'; - my ($fh_save, $filepath) = tempfile('keyfile-XXXXXX', TMPDIR => 1, UNLINK => 1, SUFFIX => '.key'); + my ($fh_save, $filepath) = tempfile('keyfile-XXXXXX', TMPDIR => 1, UNLINK => 1); is exception { $key->save(fh => $fh_save, type => KEY_FILE_TYPE_XML) }, undef, 'Save key file using IO handle'; close($fh_save); -- 2.44.0