From: Charles McGarvey Date: Wed, 4 May 2022 01:18:47 +0000 (-0600) Subject: Version 0.902 X-Git-Url: https://git.brokenzipper.com/gitweb?a=commitdiff_plain;h=dcea26ed1350452c08e135ff7ac82350f2faa618;p=chaz%2Fp5-File-KDBX Version 0.902 --- diff --git a/Changes b/Changes index c74d78a..d9e489e 100644 --- a/Changes +++ b/Changes @@ -1,18 +1,24 @@ Revision history for File-KDBX. +0.902 2022-05-03 19:18:06-0600 + + * 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. + 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. + * 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 - unused and unnecessary. - * Now use the database maintenance_history_days value as the default - "max_age" value in prune_history method. + * Removed the min_version methods from dumper and loader because it was unused and unnecessary. + * Now use the database maintenance_history_days value as the default "max_age" value in prune_history + method. * Fixed distribution prereq issues. * Cleaned up a lot of pod typos and other inaccuracies. diff --git a/META.json b/META.json index 7ee8a96..e72d259 100644 --- a/META.json +++ b/META.json @@ -106,7 +106,7 @@ "Crypt::Cipher" : "0", "Crypt::Digest" : "0", "Crypt::Mac::HMAC" : "0", - "Crypt::Misc" : "0.029", + "Crypt::Misc" : "0.049", "Crypt::Mode::CBC" : "0", "Crypt::PRNG" : "0", "Data::Dumper" : "0", @@ -116,9 +116,10 @@ "File::Temp" : "0", "Hash::Util::FieldHash" : "0", "IO::Handle" : "0", - "IPC::Cmd" : "0.52", + "IPC::Cmd" : "0.84", "Iterator::Simple" : "0", "List::Util" : "1.33", + "Math::BigInt" : "0", "Module::Load" : "0", "Module::Loaded" : "0", "POSIX" : "0", @@ -134,6 +135,7 @@ "boolean" : "0", "namespace::clean" : "0", "overload" : "0", + "perl" : "5.010", "strict" : "0", "warnings" : "0" } @@ -155,7 +157,6 @@ "Test::More" : "0", "Test::Warnings" : "0", "lib" : "0", - "perl" : "5.006", "utf8" : "0" }, "suggests" : { @@ -166,155 +167,155 @@ "provides" : { "File::KDBX" : { "file" : "lib/File/KDBX.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Cipher" : { "file" : "lib/File/KDBX/Cipher.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Cipher::CBC" : { "file" : "lib/File/KDBX/Cipher/CBC.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Cipher::Stream" : { "file" : "lib/File/KDBX/Cipher/Stream.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Constants" : { "file" : "lib/File/KDBX/Constants.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Dumper" : { "file" : "lib/File/KDBX/Dumper.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Dumper::KDB" : { "file" : "lib/File/KDBX/Dumper/KDB.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Dumper::Raw" : { "file" : "lib/File/KDBX/Dumper/Raw.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Dumper::V3" : { "file" : "lib/File/KDBX/Dumper/V3.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Dumper::V4" : { "file" : "lib/File/KDBX/Dumper/V4.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Dumper::XML" : { "file" : "lib/File/KDBX/Dumper/XML.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Entry" : { "file" : "lib/File/KDBX/Entry.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Error" : { "file" : "lib/File/KDBX/Error.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Group" : { "file" : "lib/File/KDBX/Group.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::IO" : { "file" : "lib/File/KDBX/IO.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::IO::Crypt" : { "file" : "lib/File/KDBX/IO/Crypt.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::IO::HashBlock" : { "file" : "lib/File/KDBX/IO/HashBlock.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::IO::HmacBlock" : { "file" : "lib/File/KDBX/IO/HmacBlock.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Iterator" : { "file" : "lib/File/KDBX/Iterator.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::KDF" : { "file" : "lib/File/KDBX/KDF.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::KDF::AES" : { "file" : "lib/File/KDBX/KDF/AES.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::KDF::Argon2" : { "file" : "lib/File/KDBX/KDF/Argon2.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Key" : { "file" : "lib/File/KDBX/Key.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Key::ChallengeResponse" : { "file" : "lib/File/KDBX/Key/ChallengeResponse.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Key::Composite" : { "file" : "lib/File/KDBX/Key/Composite.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Key::File" : { "file" : "lib/File/KDBX/Key/File.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Key::Password" : { "file" : "lib/File/KDBX/Key/Password.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Key::YubiKey" : { "file" : "lib/File/KDBX/Key/YubiKey.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Loader" : { "file" : "lib/File/KDBX/Loader.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Loader::KDB" : { "file" : "lib/File/KDBX/Loader/KDB.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Loader::Raw" : { "file" : "lib/File/KDBX/Loader/Raw.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Loader::V3" : { "file" : "lib/File/KDBX/Loader/V3.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Loader::V4" : { "file" : "lib/File/KDBX/Loader/V4.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Loader::XML" : { "file" : "lib/File/KDBX/Loader/XML.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Object" : { "file" : "lib/File/KDBX/Object.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Safe" : { "file" : "lib/File/KDBX/Safe.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Transaction" : { "file" : "lib/File/KDBX/Transaction.pm", - "version" : "0.901" + "version" : "0.902" }, "File::KDBX::Util" : { "file" : "lib/File/KDBX/Util.pm", - "version" : "0.901" + "version" : "0.902" } }, "release_status" : "stable", @@ -329,7 +330,7 @@ "web" : "https://github.com/chazmcgarvey/File-KDBX" } }, - "version" : "0.901", + "version" : "0.902", "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 3473a03..02a301d 100644 --- a/META.yml +++ b/META.yml @@ -14,7 +14,6 @@ build_requires: Test::More: '0' Test::Warnings: '0' lib: '0' - perl: '5.006' utf8: '0' configure_requires: ExtUtils::MakeMaker: '0' @@ -50,118 +49,118 @@ optional_features: provides: File::KDBX: file: lib/File/KDBX.pm - version: '0.901' + version: '0.902' File::KDBX::Cipher: file: lib/File/KDBX/Cipher.pm - version: '0.901' + version: '0.902' File::KDBX::Cipher::CBC: file: lib/File/KDBX/Cipher/CBC.pm - version: '0.901' + version: '0.902' File::KDBX::Cipher::Stream: file: lib/File/KDBX/Cipher/Stream.pm - version: '0.901' + version: '0.902' File::KDBX::Constants: file: lib/File/KDBX/Constants.pm - version: '0.901' + version: '0.902' File::KDBX::Dumper: file: lib/File/KDBX/Dumper.pm - version: '0.901' + version: '0.902' File::KDBX::Dumper::KDB: file: lib/File/KDBX/Dumper/KDB.pm - version: '0.901' + version: '0.902' File::KDBX::Dumper::Raw: file: lib/File/KDBX/Dumper/Raw.pm - version: '0.901' + version: '0.902' File::KDBX::Dumper::V3: file: lib/File/KDBX/Dumper/V3.pm - version: '0.901' + version: '0.902' File::KDBX::Dumper::V4: file: lib/File/KDBX/Dumper/V4.pm - version: '0.901' + version: '0.902' File::KDBX::Dumper::XML: file: lib/File/KDBX/Dumper/XML.pm - version: '0.901' + version: '0.902' File::KDBX::Entry: file: lib/File/KDBX/Entry.pm - version: '0.901' + version: '0.902' File::KDBX::Error: file: lib/File/KDBX/Error.pm - version: '0.901' + version: '0.902' File::KDBX::Group: file: lib/File/KDBX/Group.pm - version: '0.901' + version: '0.902' File::KDBX::IO: file: lib/File/KDBX/IO.pm - version: '0.901' + version: '0.902' File::KDBX::IO::Crypt: file: lib/File/KDBX/IO/Crypt.pm - version: '0.901' + version: '0.902' File::KDBX::IO::HashBlock: file: lib/File/KDBX/IO/HashBlock.pm - version: '0.901' + version: '0.902' File::KDBX::IO::HmacBlock: file: lib/File/KDBX/IO/HmacBlock.pm - version: '0.901' + version: '0.902' File::KDBX::Iterator: file: lib/File/KDBX/Iterator.pm - version: '0.901' + version: '0.902' File::KDBX::KDF: file: lib/File/KDBX/KDF.pm - version: '0.901' + version: '0.902' File::KDBX::KDF::AES: file: lib/File/KDBX/KDF/AES.pm - version: '0.901' + version: '0.902' File::KDBX::KDF::Argon2: file: lib/File/KDBX/KDF/Argon2.pm - version: '0.901' + version: '0.902' File::KDBX::Key: file: lib/File/KDBX/Key.pm - version: '0.901' + version: '0.902' File::KDBX::Key::ChallengeResponse: file: lib/File/KDBX/Key/ChallengeResponse.pm - version: '0.901' + version: '0.902' File::KDBX::Key::Composite: file: lib/File/KDBX/Key/Composite.pm - version: '0.901' + version: '0.902' File::KDBX::Key::File: file: lib/File/KDBX/Key/File.pm - version: '0.901' + version: '0.902' File::KDBX::Key::Password: file: lib/File/KDBX/Key/Password.pm - version: '0.901' + version: '0.902' File::KDBX::Key::YubiKey: file: lib/File/KDBX/Key/YubiKey.pm - version: '0.901' + version: '0.902' File::KDBX::Loader: file: lib/File/KDBX/Loader.pm - version: '0.901' + version: '0.902' File::KDBX::Loader::KDB: file: lib/File/KDBX/Loader/KDB.pm - version: '0.901' + version: '0.902' File::KDBX::Loader::Raw: file: lib/File/KDBX/Loader/Raw.pm - version: '0.901' + version: '0.902' File::KDBX::Loader::V3: file: lib/File/KDBX/Loader/V3.pm - version: '0.901' + version: '0.902' File::KDBX::Loader::V4: file: lib/File/KDBX/Loader/V4.pm - version: '0.901' + version: '0.902' File::KDBX::Loader::XML: file: lib/File/KDBX/Loader/XML.pm - version: '0.901' + version: '0.902' File::KDBX::Object: file: lib/File/KDBX/Object.pm - version: '0.901' + version: '0.902' File::KDBX::Safe: file: lib/File/KDBX/Safe.pm - version: '0.901' + version: '0.902' File::KDBX::Transaction: file: lib/File/KDBX/Transaction.pm - version: '0.901' + version: '0.902' File::KDBX::Util: file: lib/File/KDBX/Util.pm - version: '0.901' + version: '0.902' recommends: Compress::Raw::Zlib: '0' File::KDBX::XS: '0' @@ -175,7 +174,7 @@ requires: Crypt::Cipher: '0' Crypt::Digest: '0' Crypt::Mac::HMAC: '0' - Crypt::Misc: '0.029' + Crypt::Misc: '0.049' Crypt::Mode::CBC: '0' Crypt::PRNG: '0' Data::Dumper: '0' @@ -185,9 +184,10 @@ requires: File::Temp: '0' Hash::Util::FieldHash: '0' IO::Handle: '0' - IPC::Cmd: '0.52' + IPC::Cmd: '0.84' Iterator::Simple: '0' List::Util: '1.33' + Math::BigInt: '0' Module::Load: '0' Module::Loaded: '0' POSIX: '0' @@ -203,13 +203,14 @@ requires: boolean: '0' namespace::clean: '0' overload: '0' + perl: '5.010' strict: '0' warnings: '0' 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.901' +version: '0.902' 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 7c6ce7c..9b13813 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -2,7 +2,7 @@ use strict; use warnings; -use 5.006; +use 5.010; use ExtUtils::MakeMaker; @@ -14,7 +14,7 @@ my %WriteMakefileArgs = ( }, "DISTNAME" => "File-KDBX", "LICENSE" => "perl", - "MIN_PERL_VERSION" => "5.006", + "MIN_PERL_VERSION" => "5.010", "NAME" => "File::KDBX", "PREREQ_PM" => { "Carp" => 0, @@ -22,7 +22,7 @@ my %WriteMakefileArgs = ( "Crypt::Cipher" => 0, "Crypt::Digest" => 0, "Crypt::Mac::HMAC" => 0, - "Crypt::Misc" => "0.029", + "Crypt::Misc" => "0.049", "Crypt::Mode::CBC" => 0, "Crypt::PRNG" => 0, "Data::Dumper" => 0, @@ -32,9 +32,10 @@ my %WriteMakefileArgs = ( "File::Temp" => 0, "Hash::Util::FieldHash" => 0, "IO::Handle" => 0, - "IPC::Cmd" => "0.52", + "IPC::Cmd" => "0.84", "Iterator::Simple" => 0, "List::Util" => "1.33", + "Math::BigInt" => 0, "Module::Load" => 0, "Module::Loaded" => 0, "POSIX" => 0, @@ -67,7 +68,7 @@ my %WriteMakefileArgs = ( "lib" => 0, "utf8" => 0 }, - "VERSION" => "0.901", + "VERSION" => "0.902", "test" => { "TESTS" => "t/*.t" } @@ -80,7 +81,7 @@ my %FallbackPrereqs = ( "Crypt::Cipher" => 0, "Crypt::Digest" => 0, "Crypt::Mac::HMAC" => 0, - "Crypt::Misc" => "0.029", + "Crypt::Misc" => "0.049", "Crypt::Mode::CBC" => 0, "Crypt::PRNG" => 0, "Data::Dumper" => 0, @@ -94,10 +95,11 @@ my %FallbackPrereqs = ( "Getopt::Std" => 0, "Hash::Util::FieldHash" => 0, "IO::Handle" => 0, - "IPC::Cmd" => "0.52", + "IPC::Cmd" => "0.84", "IPC::Open3" => 0, "Iterator::Simple" => 0, "List::Util" => "1.33", + "Math::BigInt" => 0, "Module::Load" => 0, "Module::Loaded" => 0, "POSIX" => 0, diff --git a/README b/README index 06be87c..ec6508e 100644 --- a/README +++ b/README @@ -4,28 +4,33 @@ NAME VERSION - version 0.901 + version 0.902 SYNOPSIS 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; }); @@ -148,7 +153,7 @@ ATTRIBUTES The UUID of a cipher used to encrypt the database when stored as a file. - See "File::KDBX::Cipher". + See File::KDBX::Cipher. compression_flags @@ -464,8 +469,8 @@ METHODS was introduced with KDBX4. This method never returns less than KDBX_VERSION_3_1 (i.e. 0x00030001). - That file version is so ubiquitious and well-supported, there are - seldom reasons to dump in a lesser format nowadays. + That file version is so ubiquitous and well-supported, there are seldom + reasons to dump in a lesser format nowadays. WARNING: If you dump a database with a minimum version higher than the current "version", the dumper will typically issue a warning and @@ -1456,7 +1461,7 @@ QUERY Iterators are the built-in way to navigate or walk the database tree. You get an iterator from "entries", "groups" and "objects". You can specify the search algorithm to iterate over objects in different - orders using the algorith option, which can be one of these constants: + orders using the algorithm option, which can be one of these constants: * ITERATION_IDS - Iterative deepening search (default) @@ -1582,15 +1587,6 @@ ENVIRONMENT * NO_FORK - Do not fork if true (default: false) -CAVEATS - - Some features (e.g. parsing) require 64-bit perl. It should be possible - and actually pretty easy to make it work using Math::BigInt, 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. - SEE ALSO * KeePass Password Safe - The original diff --git a/lib/File/KDBX.pm b/lib/File/KDBX.pm index c8686ef..b628375 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; @@ -19,7 +20,7 @@ use Time::Piece; use boolean; use namespace::clean; -our $VERSION = '0.901'; # VERSION +our $VERSION = '0.902'; # VERSION our $WARNINGS = 1; fieldhashes \my (%SAFE, %KEYS); @@ -419,7 +420,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); } ############################################################################## @@ -451,7 +452,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); } ############################################################################## @@ -462,7 +463,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 } @@ -1120,28 +1121,33 @@ File::KDBX - Encrypted database to store secret text and files =head1 VERSION -version 0.901 +version 0.902 =head1 SYNOPSIS 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; }); @@ -1279,7 +1285,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. =head2 compression_flags @@ -1384,7 +1390,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. =head2 custom_icons @@ -1569,7 +1575,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 @@ -2640,7 +2646,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: =over 4 @@ -2796,13 +2802,6 @@ C - Do not fork if true (default: false) =back -=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 =over 4 diff --git a/lib/File/KDBX/Cipher.pm b/lib/File/KDBX/Cipher.pm index 250b9cf..37bdb58 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.901'; # VERSION +our $VERSION = '0.902'; # VERSION my %CIPHERS; @@ -158,7 +158,7 @@ File::KDBX::Cipher - A block cipher mode or cipher stream =head1 VERSION -version 0.901 +version 0.902 =head1 SYNOPSIS diff --git a/lib/File/KDBX/Cipher/CBC.pm b/lib/File/KDBX/Cipher/CBC.pm index a759452..8f798de 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.901'; # VERSION +our $VERSION = '0.902'; # 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.901 +version 0.902 =head1 SYNOPSIS diff --git a/lib/File/KDBX/Cipher/Stream.pm b/lib/File/KDBX/Cipher/Stream.pm index 2b0c9f2..b511200 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.901'; # VERSION +our $VERSION = '0.902'; # VERSION has 'counter', is => 'ro', default => 0; @@ -123,7 +123,7 @@ File::KDBX::Cipher::Stream - A cipher stream encrypter/decrypter =head1 VERSION -version 0.901 +version 0.902 =head1 SYNOPSIS diff --git a/lib/File/KDBX/Constants.pm b/lib/File/KDBX/Constants.pm index f31cc0c..c7b31bd 100644 --- a/lib/File/KDBX/Constants.pm +++ b/lib/File/KDBX/Constants.pm @@ -6,14 +6,16 @@ package File::KDBX::Constants; # 2. List it in the pod at the bottom of this file in the section corresponding to its tag. # 3. There is no step three. +use 5.010; use warnings; use strict; use Exporter qw(import); +use File::KDBX::Util qw(int64); use Scalar::Util qw(dualvar); use namespace::clean -except => 'import'; -our $VERSION = '0.901'; # VERSION +our $VERSION = '0.902'; # VERSION BEGIN { my %CONSTANTS = ( @@ -213,7 +215,7 @@ BEGIN { }, time => { __prefix => 'TIME', - SECONDS_AD1_TO_UNIX_EPOCH => 62_135_596_800, + SECONDS_AD1_TO_UNIX_EPOCH => int64('62135596800'), }, yubikey => { YUBICO_VID => dualvar( 0x1050, 'Yubico'), @@ -346,7 +348,7 @@ File::KDBX::Constants - All the KDBX-related constants you could ever want =head1 VERSION -version 0.901 +version 0.902 =head1 SYNOPSIS diff --git a/lib/File/KDBX/Dumper.pm b/lib/File/KDBX/Dumper.pm index f6d6657..c73ddcf 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.901'; # VERSION +our $VERSION = '0.902'; # VERSION sub new { @@ -287,7 +287,7 @@ File::KDBX::Dumper - Write KDBX files =head1 VERSION -version 0.901 +version 0.902 =head1 ATTRIBUTES diff --git a/lib/File/KDBX/Dumper/KDB.pm b/lib/File/KDBX/Dumper/KDB.pm index 4f35970..ea0db6f 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.901'; # VERSION +our $VERSION = '0.902'; # VERSION sub _write_magic_numbers { '' } sub _write_headers { '' } @@ -29,8 +29,6 @@ sub _write_body { my $k = File::KeePass::KDBX->new($self->kdbx)->to_fkp; $self->_write_custom_icons($self->kdbx, $k); - # TODO create a KPX_CUSTOM_ICONS_4 meta stream. FKP itself handles KPX_GROUP_TREE_STATE - substr($k->header->{seed_rand}, 16) = ''; $key = $self->kdbx->composite_key($key, keep_primitive => 1); @@ -138,7 +136,7 @@ File::KDBX::Dumper::KDB - Write KDB files =head1 VERSION -version 0.901 +version 0.902 =head1 DESCRIPTION diff --git a/lib/File/KDBX/Dumper/Raw.pm b/lib/File/KDBX/Dumper/Raw.pm index d6d2173..70aa0c6 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.901'; # VERSION +our $VERSION = '0.902'; # 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.901 +version 0.902 =head1 SYNOPSIS diff --git a/lib/File/KDBX/Dumper/V3.pm b/lib/File/KDBX/Dumper/V3.pm index a5f7ef0..f06db6b 100644 --- a/lib/File/KDBX/Dumper/V3.pm +++ b/lib/File/KDBX/Dumper/V3.pm @@ -10,13 +10,13 @@ use File::KDBX::Constants qw(:header :compression); use File::KDBX::Error; use File::KDBX::IO::Crypt; use File::KDBX::IO::HashBlock; -use File::KDBX::Util qw(:class :empty :load assert_64bit erase_scoped); +use File::KDBX::Util qw(:class :empty :int :load erase_scoped); use IO::Handle; use namespace::clean; extends 'File::KDBX::Dumper'; -our $VERSION = '0.901'; # VERSION +our $VERSION = '0.902'; # VERSION sub _write_headers { my $self = shift; @@ -81,8 +81,7 @@ sub _write_header { # nothing } elsif ($type == HEADER_TRANSFORM_ROUNDS) { - assert_64bit; - $val = pack('Q<', $val); + $val = pack_Ql($val); } elsif ($type == HEADER_ENCRYPTION_IV) { # nothing @@ -152,7 +151,6 @@ sub _write_body { $fh->print($kdbx->headers->{+HEADER_STREAM_START_BYTES}) or throw 'Failed to write start bytes'; - $fh->flush; $kdbx->key($key); @@ -189,7 +187,7 @@ File::KDBX::Dumper::V3 - Dump KDBX3 files =head1 VERSION -version 0.901 +version 0.902 =head1 BUGS diff --git a/lib/File/KDBX/Dumper/V4.pm b/lib/File/KDBX/Dumper/V4.pm index c5644d4..0dec651 100644 --- a/lib/File/KDBX/Dumper/V4.pm +++ b/lib/File/KDBX/Dumper/V4.pm @@ -11,7 +11,7 @@ use File::KDBX::Constants qw(:header :inner_header :compression :kdf :variant_ma use File::KDBX::Error; use File::KDBX::IO::Crypt; use File::KDBX::IO::HmacBlock; -use File::KDBX::Util qw(:class :empty :load assert_64bit erase_scoped); +use File::KDBX::Util qw(:class :empty :int :load erase_scoped); use IO::Handle; use Scalar::Util qw(looks_like_number); use boolean qw(:all); @@ -19,7 +19,7 @@ use namespace::clean; extends 'File::KDBX::Dumper'; -our $VERSION = '0.901'; # VERSION +our $VERSION = '0.902'; # VERSION has _binaries_written => {}, is => 'ro'; @@ -129,9 +129,8 @@ sub _intuit_variant_type { return VMAP_TYPE_BOOL; } elsif (looks_like_number($variant) && ($variant + 0) =~ /^\d+$/) { - assert_64bit; my $neg = $variant < 0; - my @b = unpack('L>2', pack('Q>', $variant)); + my @b = unpack('L>2', scalar reverse pack_Ql($variant)); return VMAP_TYPE_INT64 if $b[0] && $neg; return VMAP_TYPE_UINT64 if $b[0]; return VMAP_TYPE_INT32 if $neg; @@ -162,8 +161,7 @@ sub _write_variant_dictionary { $val = pack('L<', $val); } elsif ($type == VMAP_TYPE_UINT64) { - assert_64bit; - $val = pack('Q<', $val); + $val = pack_Ql($val); } elsif ($type == VMAP_TYPE_BOOL) { $val = pack('C', $val); @@ -172,8 +170,7 @@ sub _write_variant_dictionary { $val = pack('l', $val); } elsif ($type == VMAP_TYPE_INT64) { - assert_64bit; - $val = pack('q<', $val); + $val = pack_ql($val); } elsif ($type == VMAP_TYPE_STRING) { $val = encode('UTF-8', $val); @@ -377,7 +374,7 @@ File::KDBX::Dumper::V4 - Dump KDBX4 files =head1 VERSION -version 0.901 +version 0.902 =head1 BUGS diff --git a/lib/File/KDBX/Dumper/XML.pm b/lib/File/KDBX/Dumper/XML.pm index 1d7ec39..ebf6795 100644 --- a/lib/File/KDBX/Dumper/XML.pm +++ b/lib/File/KDBX/Dumper/XML.pm @@ -9,7 +9,7 @@ use Crypt::Misc 0.029 qw(encode_b64); use Encode qw(encode); use File::KDBX::Constants qw(:version :time); use File::KDBX::Error; -use File::KDBX::Util qw(:class assert_64bit erase_scoped gzip snakify); +use File::KDBX::Util qw(:class :int erase_scoped gzip snakify); use IO::Handle; use Scalar::Util qw(blessed isdual looks_like_number); use Time::Piece; @@ -19,7 +19,7 @@ use namespace::clean; extends 'File::KDBX::Dumper'; -our $VERSION = '0.901'; # VERSION +our $VERSION = '0.902'; # VERSION has allow_protection => 1; @@ -526,9 +526,8 @@ sub _encode_datetime { sub _encode_datetime_binary { local $_ = shift; - assert_64bit; my $seconds_since_ad1 = $_ + TIME_SECONDS_AD1_TO_UNIX_EPOCH; - my $buf = pack('Q<', $seconds_since_ad1->epoch); + my $buf = pack_Ql($seconds_since_ad1->epoch); return eval { encode_b64($buf) }; } @@ -564,7 +563,7 @@ File::KDBX::Dumper::XML - Dump unencrypted XML KeePass files =head1 VERSION -version 0.901 +version 0.902 =head1 ATTRIBUTES diff --git a/lib/File/KDBX/Entry.pm b/lib/File/KDBX/Entry.pm index 8012372..df8cf4d 100644 --- a/lib/File/KDBX/Entry.pm +++ b/lib/File/KDBX/Entry.pm @@ -4,7 +4,7 @@ package File::KDBX::Entry; use warnings; use strict; -use Crypt::Misc 0.029 qw(decode_b64 encode_b32r); +use Crypt::Misc 0.049 qw(decode_b64 encode_b32r); use Devel::GlobalDestruction; use Encode qw(encode); use File::KDBX::Constants qw(:history :icon); @@ -21,7 +21,7 @@ use namespace::clean; extends 'File::KDBX::Object'; -our $VERSION = '0.901'; # VERSION +our $VERSION = '0.902'; # VERSION my $PLACEHOLDER_MAX_DEPTH = 10; my %PLACEHOLDERS; @@ -681,7 +681,7 @@ File::KDBX::Entry - A KDBX database entry =head1 VERSION -version 0.901 +version 0.902 =head1 DESCRIPTION @@ -720,7 +720,8 @@ There is also some metadata associated with an entry. Each entry in a database i a UUID. An entry can also have an icon associated with it, and there are various timestamps. Take a look at the attributes to see what's available. -A B is a subclass of L. +A B is a subclass of L. View its documentation to see other attributes +and methods available on entries. =head2 Placeholders diff --git a/lib/File/KDBX/Error.pm b/lib/File/KDBX/Error.pm index 5e3de90..ae02c5f 100644 --- a/lib/File/KDBX/Error.pm +++ b/lib/File/KDBX/Error.pm @@ -1,6 +1,7 @@ package File::KDBX::Error; # ABSTRACT: Represents something bad that happened +use 5.010; use warnings; use strict; @@ -8,7 +9,7 @@ use Exporter qw(import); use Scalar::Util qw(blessed looks_like_number); use namespace::clean -except => 'import'; -our $VERSION = '0.901'; # VERSION +our $VERSION = '0.902'; # VERSION our @EXPORT = qw(alert error throw); @@ -166,7 +167,7 @@ File::KDBX::Error - Represents something bad that happened =head1 VERSION -version 0.901 +version 0.902 =head1 ATTRIBUTES diff --git a/lib/File/KDBX/Group.pm b/lib/File/KDBX/Group.pm index c81f34b..cf24d71 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.901'; # VERSION +our $VERSION = '0.902'; # VERSION # has uuid => sub { generate_uuid(printable => 1) }; @@ -78,7 +78,7 @@ sub entries { } -sub entries_deeply { +sub all_entries { my $self = shift; my %args = @_; @@ -86,7 +86,7 @@ sub entries_deeply { my $auto_type = delete $args{auto_type}; my $history = delete $args{history}; - my $groups = $self->groups_deeply(%args); + my $groups = $self->all_groups(%args); my @entries; return File::KDBX::Iterator->new(sub { @@ -149,7 +149,7 @@ sub groups { } -sub groups_deeply { +sub all_groups { my $self = shift; my %args = @_; @@ -220,7 +220,7 @@ sub remove_group { ############################################################################## -sub objects_deeply { +sub all_objects { my $self = shift; my %args = @_; @@ -228,7 +228,7 @@ sub objects_deeply { my $auto_type = delete $args{auto_type}; my $history = delete $args{history}; - my $groups = $self->groups_deeply(%args); + my $groups = $self->all_groups(%args); my @entries; return File::KDBX::Iterator->new(sub { @@ -398,7 +398,7 @@ File::KDBX::Group - A KDBX database group =head1 VERSION -version 0.901 +version 0.902 =head1 DESCRIPTION @@ -408,6 +408,9 @@ There is also some metadata associated with a group. Each group in a database is a UUID. An entry can also have an icon associated with it, and there are various timestamps. Take a look at the attributes to see what's available. +A B is a subclass of L. View its documentation to see other attributes +and methods available on groups. + =head1 ATTRIBUTES =head2 name @@ -454,9 +457,9 @@ Array of subgroups contained within the group. Get an array of direct child entries within a group. -=head2 entries_deeply +=head2 all_entries - \&iterator = $kdbx->entries_deeply(%options); + \&iterator = $kdbx->all_entries(%options); Get an L over I within a group. Supports the same options as L, plus some new ones: @@ -498,9 +501,9 @@ Remove an entry from a group's array of entries. Returns the entry removed or C< Get an array of direct subgroups within a group. -=head2 groups_deeply +=head2 all_groups - \&iterator = $group->groups_deeply(%options); + \&iterator = $group->all_groups(%options); Get an L over I within a groups, deeply. Options: @@ -531,9 +534,9 @@ being added to C<$group>. Remove a group from a group's array of subgroups. Returns the group removed or C if nothing removed. -=head2 objects_deeply +=head2 all_objects - \&iterator = $groups->objects_deeply(%options); + \&iterator = $groups->all_objects(%options); Get an L over I within a group, deeply. Groups and entries are considered objects, so this is essentially a combination of L and L. This won't often be useful, but diff --git a/lib/File/KDBX/IO.pm b/lib/File/KDBX/IO.pm index 0ac4529..135820e 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.901'; # VERSION +our $VERSION = '0.902'; # 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.901 +version 0.902 =head1 DESCRIPTION diff --git a/lib/File/KDBX/IO/Crypt.pm b/lib/File/KDBX/IO/Crypt.pm index 0974933..7675776 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.901'; # VERSION +our $VERSION = '0.902'; # VERSION our $BUFFER_SIZE = 16384; our $ERROR; @@ -139,7 +139,7 @@ File::KDBX::IO::Crypt - Encrypter/decrypter IO handle =head1 VERSION -version 0.901 +version 0.902 =head1 SYNOPSIS diff --git a/lib/File/KDBX/IO/HashBlock.pm b/lib/File/KDBX/IO/HashBlock.pm index f1bdf92..2d83249 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.901'; # VERSION +our $VERSION = '0.902'; # 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.901 +version 0.902 =head1 DESCRIPTION diff --git a/lib/File/KDBX/IO/HmacBlock.pm b/lib/File/KDBX/IO/HmacBlock.pm index ae36c95..e14100e 100644 --- a/lib/File/KDBX/IO/HmacBlock.pm +++ b/lib/File/KDBX/IO/HmacBlock.pm @@ -8,18 +8,18 @@ use Crypt::Digest qw(digest_data); use Crypt::Mac::HMAC qw(hmac); use Errno; use File::KDBX::Error; -use File::KDBX::Util qw(:class :io assert_64bit); +use File::KDBX::Util qw(:class :int :io); use namespace::clean; extends 'File::KDBX::IO'; -our $VERSION = '0.901'; # VERSION +our $VERSION = '0.902'; # VERSION our $BLOCK_SIZE = 1048576; # 1MiB our $ERROR; my %ATTRS = ( - _block_index => 0, + _block_index => int64(0), _buffer => sub { \(my $buf = '') }, _finished => 0, block_size => sub { $BLOCK_SIZE }, @@ -36,8 +36,6 @@ while (my ($attr, $default) = each %ATTRS) { sub new { - assert_64bit; - my $class = shift; my %args = @_ % 2 == 1 ? (fh => shift, @_) : @_; my $self = $class->SUPER::new; @@ -141,7 +139,7 @@ sub _read_hashed_block { or throw 'Failed to read HMAC block', index => $self->_block_index, size => $size; } - my $packed_index = pack('Q<', $self->_block_index); + my $packed_index = pack_Ql($self->_block_index); my $got_hmac = hmac('SHA256', $self->_hmac_key, $packed_index, $packed_size, @@ -168,7 +166,7 @@ sub _write_next_hmac_block { my $block = ''; $block = substr($$buffer, 0, $size, '') if 0 < $size; - my $packed_index = pack('Q<', $self->_block_index); + my $packed_index = pack_Ql($self->_block_index); my $packed_size = pack('L<', $size); my $hmac = hmac('SHA256', $self->_hmac_key, $packed_index, @@ -195,7 +193,7 @@ sub _hmac_key { my $key = shift // $self->key; my $index = shift // $self->_block_index; - my $packed_index = pack('Q<', $index); + my $packed_index = pack_Ql($index); my $hmac_key = digest_data('SHA512', $packed_index, $key); return $hmac_key; } @@ -214,7 +212,7 @@ File::KDBX::IO::HmacBlock - HMAC block stream IO handle =head1 VERSION -version 0.901 +version 0.902 =head1 DESCRIPTION diff --git a/lib/File/KDBX/Iterator.pm b/lib/File/KDBX/Iterator.pm index 72de001..b5d0b91 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.901'; # VERSION +our $VERSION = '0.902'; # VERSION sub new { @@ -221,7 +221,7 @@ File::KDBX::Iterator - KDBX database iterator =head1 VERSION -version 0.901 +version 0.902 =head1 SYNOPSIS diff --git a/lib/File/KDBX/KDF.pm b/lib/File/KDBX/KDF.pm index f1e0196..1cf335d 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.901'; # VERSION +our $VERSION = '0.902'; # VERSION my %KDFS; @@ -120,7 +120,7 @@ File::KDBX::KDF - A key derivation function =head1 VERSION -version 0.901 +version 0.902 =head1 DESCRIPTION diff --git a/lib/File/KDBX/KDF/AES.pm b/lib/File/KDBX/KDF/AES.pm index b5d22c9..d86ce48 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.901'; # VERSION +our $VERSION = '0.902'; # 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.901 +version 0.902 =head1 DESCRIPTION diff --git a/lib/File/KDBX/KDF/Argon2.pm b/lib/File/KDBX/KDF/Argon2.pm index 4c9146f..9f88f74 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.901'; # VERSION +our $VERSION = '0.902'; # 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.901 +version 0.902 =head1 DESCRIPTION diff --git a/lib/File/KDBX/Key.pm b/lib/File/KDBX/Key.pm index 12fa430..1fa09f6 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.901'; # VERSION +our $VERSION = '0.902'; # VERSION fieldhashes \my %SAFE; @@ -140,7 +140,7 @@ File::KDBX::Key - A credential that can protect a KDBX file =head1 VERSION -version 0.901 +version 0.902 =head1 DESCRIPTION diff --git a/lib/File/KDBX/Key/ChallengeResponse.pm b/lib/File/KDBX/Key/ChallengeResponse.pm index 843c892..74555f4 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.901'; # VERSION +our $VERSION = '0.902'; # VERSION sub init { my $self = shift; @@ -56,7 +56,7 @@ File::KDBX::Key::ChallengeResponse - A challenge-response key =head1 VERSION -version 0.901 +version 0.902 =head1 SYNOPSIS diff --git a/lib/File/KDBX/Key/Composite.pm b/lib/File/KDBX/Key/Composite.pm index e6f3d93..fcaef54 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.901'; # VERSION +our $VERSION = '0.902'; # 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.901 +version 0.902 =head1 SYNOPSIS diff --git a/lib/File/KDBX/Key/File.pm b/lib/File/KDBX/Key/File.pm index 2223870..10b57d4 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.901'; # VERSION +our $VERSION = '0.902'; # VERSION has 'type', is => 'ro'; @@ -262,7 +262,7 @@ File::KDBX::Key::File - A file key =head1 VERSION -version 0.901 +version 0.902 =head1 SYNOPSIS diff --git a/lib/File/KDBX/Key/Password.pm b/lib/File/KDBX/Key/Password.pm index fe0face..ba2809d 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.901'; # VERSION +our $VERSION = '0.902'; # VERSION sub init { my $self = shift; @@ -37,7 +37,7 @@ File::KDBX::Key::Password - A password key =head1 VERSION -version 0.901 +version 0.902 =head1 SYNOPSIS diff --git a/lib/File/KDBX/Key/YubiKey.pm b/lib/File/KDBX/Key/YubiKey.pm index 1185fa1..d55cdaf 100644 --- a/lib/File/KDBX/Key/YubiKey.pm +++ b/lib/File/KDBX/Key/YubiKey.pm @@ -7,14 +7,14 @@ use strict; use File::KDBX::Constants qw(:yubikey); use File::KDBX::Error; use File::KDBX::Util qw(:class :io pad_pkcs7); -use IPC::Cmd 0.52 qw(run_forked); +use IPC::Cmd 0.84 qw(run_forked); use Ref::Util qw(is_arrayref); use Symbol qw(gensym); use namespace::clean; extends 'File::KDBX::Key::ChallengeResponse'; -our $VERSION = '0.901'; # VERSION +our $VERSION = '0.902'; # 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.901 +version 0.902 =head1 SYNOPSIS @@ -487,7 +487,7 @@ C from L worked in Windows, but it probably doesn't. I spe various quirks to L and L implementations but never quite got it to worked reliably without deadlocks. Maybe I'll revisit this later. Hit me up so I know if there's demand. -It would also be possible to implement this is an XS module that incorporated ykcore, using libusb-1 which +It would also be possible to implement this as an XS module that incorporated ykcore, using libusb-1 which would probably make it more portable with Windows. Perhaps if I get around to it. =head1 BUGS diff --git a/lib/File/KDBX/Loader.pm b/lib/File/KDBX/Loader.pm index 69a7bfb..727f4c6 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.901'; # VERSION +our $VERSION = '0.902'; # VERSION sub new { @@ -249,7 +249,7 @@ File::KDBX::Loader - Load KDBX files =head1 VERSION -version 0.901 +version 0.902 =head1 DESCRIPTION diff --git a/lib/File/KDBX/Loader/KDB.pm b/lib/File/KDBX/Loader/KDB.pm index 7dad5d2..1edccb2 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.901'; # VERSION +our $VERSION = '0.902'; # 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.901 +version 0.902 =head1 DESCRIPTION diff --git a/lib/File/KDBX/Loader/Raw.pm b/lib/File/KDBX/Loader/Raw.pm index ddb64a7..0352c62 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.901'; # VERSION +our $VERSION = '0.902'; # 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.901 +version 0.902 =head1 SYNOPSIS diff --git a/lib/File/KDBX/Loader/V3.pm b/lib/File/KDBX/Loader/V3.pm index 6be4090..1d42365 100644 --- a/lib/File/KDBX/Loader/V3.pm +++ b/lib/File/KDBX/Loader/V3.pm @@ -22,12 +22,12 @@ use File::KDBX::Constants qw(:header :compression :kdf); use File::KDBX::Error; use File::KDBX::IO::Crypt; use File::KDBX::IO::HashBlock; -use File::KDBX::Util qw(:class :io :load assert_64bit erase_scoped); +use File::KDBX::Util qw(:class :int :io :load erase_scoped); use namespace::clean; extends 'File::KDBX::Loader'; -our $VERSION = '0.901'; # VERSION +our $VERSION = '0.902'; # VERSION sub _read_header { my $self = shift; @@ -62,8 +62,7 @@ sub _read_header { # nothing } elsif ($type == HEADER_TRANSFORM_ROUNDS) { - assert_64bit; - $val = unpack('Q<', $val); + ($val) = unpack_Ql($val); } elsif ($type == HEADER_ENCRYPTION_IV) { # nothing @@ -75,7 +74,7 @@ sub _read_header { # nothing } elsif ($type == HEADER_INNER_RANDOM_STREAM_ID) { - $val = unpack('L<', $val); + ($val) = unpack('L<', $val); } elsif ($type == HEADER_KDF_PARAMETERS || $type == HEADER_PUBLIC_CUSTOM_DATA) { @@ -175,7 +174,7 @@ File::KDBX::Loader::V3 - Load KDBX3 files =head1 VERSION -version 0.901 +version 0.902 =head1 BUGS diff --git a/lib/File/KDBX/Loader/V4.pm b/lib/File/KDBX/Loader/V4.pm index 0e33fc9..8f73e42 100644 --- a/lib/File/KDBX/Loader/V4.pm +++ b/lib/File/KDBX/Loader/V4.pm @@ -22,7 +22,7 @@ use Crypt::Mac::HMAC qw(hmac); use Encode qw(decode); use File::KDBX::Constants qw(:header :inner_header :variant_map :compression); use File::KDBX::Error; -use File::KDBX::Util qw(:class :io :load assert_64bit erase_scoped); +use File::KDBX::Util qw(:class :int :io :load erase_scoped); use File::KDBX::IO::Crypt; use File::KDBX::IO::HmacBlock; use boolean; @@ -30,7 +30,7 @@ use namespace::clean; extends 'File::KDBX::Loader'; -our $VERSION = '0.901'; # VERSION +our $VERSION = '0.902'; # VERSION sub _read_header { my $self = shift; @@ -116,8 +116,7 @@ sub _read_variant_dictionary { ($val) = unpack('L<', $val); } elsif ($type == VMAP_TYPE_UINT64) { - assert_64bit; - ($val) = unpack('Q<', $val); + ($val) = unpack_Ql($val); } elsif ($type == VMAP_TYPE_BOOL) { ($val) = unpack('C', $val); @@ -127,8 +126,7 @@ sub _read_variant_dictionary { ($val) = unpack('l<', $val); } elsif ($type == VMAP_TYPE_INT64) { - assert_64bit; - ($val) = unpack('q<', $val); + ($val) = unpack_ql($val); } elsif ($type == VMAP_TYPE_STRING) { $val = decode('UTF-8', $val); @@ -275,7 +273,7 @@ File::KDBX::Loader::V4 - Load KDBX4 files =head1 VERSION -version 0.901 +version 0.902 =head1 BUGS diff --git a/lib/File/KDBX/Loader/XML.pm b/lib/File/KDBX/Loader/XML.pm index a0826e9..412de23 100644 --- a/lib/File/KDBX/Loader/XML.pm +++ b/lib/File/KDBX/Loader/XML.pm @@ -9,7 +9,7 @@ use Encode qw(decode); use File::KDBX::Constants qw(:version :time); use File::KDBX::Error; use File::KDBX::Safe; -use File::KDBX::Util qw(:class :text assert_64bit gunzip erase_scoped); +use File::KDBX::Util qw(:class :int :text gunzip erase_scoped); use Scalar::Util qw(looks_like_number); use Time::Piece; use XML::LibXML::Reader; @@ -18,7 +18,7 @@ use namespace::clean; extends 'File::KDBX::Loader'; -our $VERSION = '0.901'; # VERSION +our $VERSION = '0.902'; # VERSION has '_reader', is => 'ro'; has '_safe', is => 'ro', default => sub { File::KDBX::Safe->new(cipher => $_[0]->kdbx->random_stream) }; @@ -533,9 +533,8 @@ sub _decode_datetime { throw 'Failed to parse binary datetime', text => $_, error => $err; } throw $@ if $@; - assert_64bit; $binary .= \0 x (8 - length($binary)) if length($binary) < 8; - my ($seconds_since_ad1) = unpack('Q<', $binary); + my ($seconds_since_ad1) = unpack_Ql($binary); my $epoch = $seconds_since_ad1 - TIME_SECONDS_AD1_TO_UNIX_EPOCH; return Time::Piece->new($epoch); } @@ -591,7 +590,7 @@ File::KDBX::Loader::XML - Load unencrypted XML KeePass files =head1 VERSION -version 0.901 +version 0.902 =head1 BUGS diff --git a/lib/File/KDBX/Object.pm b/lib/File/KDBX/Object.pm index b7b9644..33d1e03 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.901'; # VERSION +our $VERSION = '0.902'; # VERSION fieldhashes \my (%KDBX, %PARENT, %TXNS, %REFS, %SIGNALS); @@ -526,7 +526,7 @@ File::KDBX::Object - A KDBX database object =head1 VERSION -version 0.901 +version 0.902 =head1 DESCRIPTION diff --git a/lib/File/KDBX/Safe.pm b/lib/File/KDBX/Safe.pm index b88b86c..4052404 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.901'; # VERSION +our $VERSION = '0.902'; # VERSION sub new { @@ -217,7 +217,7 @@ File::KDBX::Safe - Keep strings encrypted while in memory =head1 VERSION -version 0.901 +version 0.902 =head1 SYNOPSIS diff --git a/lib/File/KDBX/Transaction.pm b/lib/File/KDBX/Transaction.pm index 86c380f..d71863c 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.901'; # VERSION +our $VERSION = '0.902'; # VERSION sub new { @@ -59,7 +59,7 @@ File::KDBX::Transaction - Make multiple database edits atomically =head1 VERSION -version 0.901 +version 0.902 =head1 ATTRIBUTES diff --git a/lib/File/KDBX/Util.pm b/lib/File/KDBX/Util.pm index 7223f6a..57e0e18 100644 --- a/lib/File/KDBX/Util.pm +++ b/lib/File/KDBX/Util.pm @@ -1,13 +1,13 @@ package File::KDBX::Util; # ABSTRACT: Utility functions for working with KDBX files +use 5.010; use warnings; use strict; use Crypt::PRNG qw(random_bytes random_string); use Encode qw(decode encode); use Exporter qw(import); -use File::KDBX::Constants qw(:bool); use File::KDBX::Error; use List::Util 1.33 qw(any all); use Module::Load; @@ -17,10 +17,10 @@ use Time::Piece; use boolean; use namespace::clean -except => 'import'; -our $VERSION = '0.901'; # VERSION +our $VERSION = '0.902'; # VERSION our %EXPORT_TAGS = ( - assert => [qw(DEBUG assert assert_64bit)], + assert => [qw(DEBUG assert)], class => [qw(extends has list_attributes)], clone => [qw(clone clone_nomagic)], coercion => [qw(to_bool to_number to_string to_time to_tristate to_uuid)], @@ -31,7 +31,8 @@ our %EXPORT_TAGS = ( empty => [qw(empty nonempty)], erase => [qw(erase erase_scoped)], gzip => [qw(gzip gunzip)], - io => [qw(is_readable is_writable read_all)], + int => [qw(int64 pack_ql pack_Ql unpack_ql unpack_Ql)], + io => [qw(read_all)], load => [qw(load_optional load_xs try_load_optional)], search => [qw(query query_any search simple_expression_query)], text => [qw(snakify trim)], @@ -104,7 +105,7 @@ sub load_xs { goto IS_LOADED if defined $XS_LOADED; if ($ENV{PERL_ONLY} || (exists $ENV{PERL_FILE_KDBX_XS} && !$ENV{PERL_FILE_KDBX_XS})) { - return $XS_LOADED = FALSE; + return $XS_LOADED = !1; } $XS_LOADED = !!eval { require File::KDBX::XS; 1 }; @@ -137,13 +138,6 @@ sub assert(&) { ## no critic (ProhibitSubroutinePrototypes) } -sub assert_64bit() { - require Config; - $Config::Config{ivsize} < 8 - and throw "64-bit perl is required to use this feature.\n", ivsize => $Config::Config{ivsize}; -} - - sub can_fork { require Config; return 1 if $Config::Config{d_fork}; @@ -413,8 +407,80 @@ sub gzip { } -sub is_readable { $_[0] !~ /^[aw]b?$/ } -sub is_writable { $_[0] !~ /^rb?$/ } +sub int64 { + require Config; + if ($Config::Config{ivsize} < 8) { + require Math::BigInt; + return Math::BigInt->new(@_); + } + return 0 + shift; +} + + +sub pack_Ql { + my $num = shift; + require Config; + if ($Config::Config{ivsize} < 8) { + if (blessed $num && $num->can('as_hex')) { + return "\xff\xff\xff\xff\xff\xff\xff\xff" if Math::BigInt->new('18446744073709551615') <= $num; + return "\x00\x00\x00\x00\x00\x00\x00\x80" if $num <= Math::BigInt->new('-9223372036854775808'); + my $neg; + if ($num < 0) { + $neg = 1; + $num = -$num; + } + my $hex = $num->as_hex; + $hex =~ s/^0x/000000000000000/; + my $bytes = reverse pack('H16', substr($hex, -16)); + $bytes .= "\0" x (8 - length $bytes) if length $bytes < 8; + if ($neg) { + # two's compliment + $bytes = join('', map { chr(~ord($_) & 0xff) } split(//, $bytes)); + substr($bytes, 0, 1, chr(ord(substr($bytes, 0, 1)) + 1)); + } + return $bytes; + } + else { + my $pad = $num < 0 ? "\xff" : "\0"; + return pack('L<', $num) . ($pad x 4); + }; + } + return pack('Q<', $num); +} + + +sub pack_ql { goto &pack_Ql } + + +sub unpack_Ql { + my $bytes = shift; + require Config; + if ($Config::Config{ivsize} < 8) { + require Math::BigInt; + return Math::BigInt->new('0x' . unpack('H*', scalar reverse $bytes)); + } + return unpack('Q<', $bytes); +} + + +sub unpack_ql { + my $bytes = shift; + require Config; + if ($Config::Config{ivsize} < 8) { + require Math::BigInt; + if (ord(substr($bytes, -1, 1)) & 128) { + return Math::BigInt->new('-9223372036854775808') if $bytes eq "\x00\x00\x00\x00\x00\x00\x00\x80"; + # two's compliment + substr($bytes, 0, 1, chr(ord(substr($bytes, 0, 1)) - 1)); + $bytes = join('', map { chr(~ord($_) & 0xff) } split(//, $bytes)); + return -Math::BigInt->new('0x' . unpack('H*', scalar reverse $bytes)); + } + else { + return Math::BigInt->new('0x' . unpack('H*', scalar reverse $bytes)); + } + } + return unpack('q<', $bytes); +} sub is_uuid { defined $_[0] && !is_ref($_[0]) && length($_[0]) == 16 } @@ -803,7 +869,7 @@ File::KDBX::Util - Utility functions for working with KDBX files =head1 VERSION -version 0.901 +version 0.902 =head1 FUNCTIONS @@ -812,8 +878,8 @@ version 0.901 $bool = load_xs(); $bool = load_xs($version); -Attempt to load L. Return truthy if C is loaded. If C<$version> is given, it will check -that at least the given version is loaded. +Attempt to load L. Return truthy if it is loaded. If C<$version> is given, it will check that +at least the given version is loaded. =head2 assert @@ -821,12 +887,6 @@ that at least the given version is loaded. Write an executable comment. Only executed if C is set in the environment. -=head2 assert_64bit - - assert_64bit(); - -Throw if perl doesn't support 64-bit IVs. - =head2 can_fork $bool = can_fork; @@ -992,14 +1052,36 @@ Decompress an octet stream. Compress an octet stream. -=head2 is_readable +=head2 int64 + + $int = int64($string); + +Get a scalar integer capable of holding 64-bit values, initialized with a given default value. On a 64-bit +perl, it will return a regular SvIV. On a 32-bit perl it will return a L. + +=head2 pack_Ql + + $bytes = pack_Ql($int); + +Like C', $int)>, but also works on 32-bit perls. + +=head2 pack_ql + + $bytes = pack_ql($int); + +Like C', $int)>, but also works on 32-bit perls. + +=head2 unpack_Ql + + $int = unpack_Ql($bytes); + +Like C', $bytes)>, but also works on 32-bit perls. -=head2 is_writable +=head2 unpack_ql - $bool = is_readable($mode); - $bool = is_writable($mode); + $int = unpack_ql($bytes); -Determine of an C-style mode is readable, writable or both. +Like C', $bytes)>, but also works on 32-bit perls. =head2 is_uuid diff --git a/t/00-report-prereqs.dd b/t/00-report-prereqs.dd index b34f59d..3979100 100644 --- a/t/00-report-prereqs.dd +++ b/t/00-report-prereqs.dd @@ -48,7 +48,7 @@ do { my $x = { 'Crypt::Cipher' => '0', 'Crypt::Digest' => '0', 'Crypt::Mac::HMAC' => '0', - 'Crypt::Misc' => '0.029', + 'Crypt::Misc' => '0.049', 'Crypt::Mode::CBC' => '0', 'Crypt::PRNG' => '0', 'Data::Dumper' => '0', @@ -58,9 +58,10 @@ do { my $x = { 'File::Temp' => '0', 'Hash::Util::FieldHash' => '0', 'IO::Handle' => '0', - 'IPC::Cmd' => '0.52', + 'IPC::Cmd' => '0.84', 'Iterator::Simple' => '0', 'List::Util' => '1.33', + 'Math::BigInt' => '0', 'Module::Load' => '0', 'Module::Loaded' => '0', 'POSIX' => '0', @@ -76,6 +77,7 @@ do { my $x = { 'boolean' => '0', 'namespace::clean' => '0', 'overload' => '0', + 'perl' => '5.010', 'strict' => '0', 'warnings' => '0' } @@ -97,7 +99,6 @@ do { my $x = { 'Test::More' => '0', 'Test::Warnings' => '0', 'lib' => '0', - 'perl' => '5.006', 'utf8' => '0' }, 'suggests' => { diff --git a/t/database.t b/t/database.t index 8bed335..94e1ea8 100644 --- a/t/database.t +++ b/t/database.t @@ -133,7 +133,7 @@ subtest 'Recycle bin' => sub { is $kdbx->entries->size, 1, 'Database is not empty'; is $kdbx->entries(searching => 1)->size, 0, 'Database has no entries if searching'; - cmp_ok $bin->entries_deeply->size, '==', 1, 'Recycle bin has an entry'; + cmp_ok $bin->all_entries->size, '==', 1, 'Recycle bin has an entry'; $entry->recycle_or_remove; is $kdbx->entries->size, 0, 'Remove entry if it is already in the recycle bin'; diff --git a/t/entry.t b/t/entry.t index f08b683..1581608 100644 --- a/t/entry.t +++ b/t/entry.t @@ -82,7 +82,7 @@ subtest 'Accessors' => sub { my $entry = File::KDBX::Entry->new; $entry->creation_time('2022-02-02 12:34:56'); - cmp_ok $entry->creation_time, '==', 1643805296, 'Creation time coerced into a Time::Piece (epoch)'; + 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'; }; diff --git a/t/error.t b/t/error.t index fabaa17..79ad54f 100644 --- a/t/error.t +++ b/t/error.t @@ -80,7 +80,7 @@ subtest 'Warnings' => sub { } SKIP: { - skip 'warnings::warnif_at_level is required', 1 if !warnings->can('warnif_at_level'); + skip 'warnings::warnif_at_level is required', 1 if !warnings::->can('warnif_at_level'); no warnings 'File::KDBX'; my @warnings = warnings { alert 'uh oh' }; is @warnings, 0, 'Warnings can be disabled lexically' @@ -88,7 +88,7 @@ subtest 'Warnings' => sub { } SKIP: { - skip 'warnings::fatal_enabled_at_level is required', 1 if !warnings->can('fatal_enabled_at_level'); + skip 'warnings::fatal_enabled_at_level is required', 1 if !warnings::->can('fatal_enabled_at_level'); use warnings FATAL => 'File::KDBX'; my $exception = exception { alert 'uh oh' }; like $exception, qr/uh oh/, 'Warnings can be fatal'; diff --git a/t/kdbx2.t b/t/kdbx2.t index 958348a..af67061 100644 --- a/t/kdbx2.t +++ b/t/kdbx2.t @@ -38,7 +38,7 @@ sub verify_kdbx2 { inner_random_stream_key => "\214\aW\253\362\177<\346n`\263l\245\353T\25\261BnFp\177\357\335\36(b\372z\231b\355", kdf_parameters => { "\$UUID" => "\311\331\363\232b\212D`\277t\r\b\301\212O\352", - R => 6000, + R => num(6000), S => "S\202\207A\3475\265\177\220\331\263[\334\326\365\324B\\\2222zb-f\263m\220\333S\361L\332", }, master_seed => "\253!\2\241\r*|{\227\0276Lx\215\32\\\17\372d\254\255*\21r\376\251\313+gMI\343", diff --git a/t/kdbx3.t b/t/kdbx3.t index fac6101..e1e5838 100644 --- a/t/kdbx3.t +++ b/t/kdbx3.t @@ -25,7 +25,7 @@ subtest 'Verify Format300' => sub { inner_random_stream_key => "\346\n8\2\322\264i\5\5\274\22\377+\16tB\353\210\1\2m\2U%\326\347\355\313\313\340A\305", kdf_parameters => { "\$UUID" => "\311\331\363\232b\212D`\277t\r\b\301\212O\352", - R => 6000, + R => num(6000), S => "\340\377\235\255\222o\1(\226m\373\tC{K\352\f\332M\302|~P\e\346J\@\275A\227\236\366", }, master_seed => "Z\230\355\353\2303\361\237-p\345\27nM\22 sub { inner_random_stream_key => "Z\244]\373\13`\2108=>\r\224\351\373\316\276\253\6\317z\356\302\36\fW\1776Q\366\32\34,", kdf_parameters => { "\$UUID" => "\311\331\363\232b\212D`\277t\r\b\301\212O\352", - R => 6000, + R => num(6000), S => "l\254\250\255\240U\313\364\336\316#\254\306\231\f%U\207J\235\275\34\b\25036\26\241\a\300\26\332", }, master_seed => "\13\350\370\214{\0276\17dv\31W[H\26\272\4\335\377\356\275N\"\2A1\364\213\226\237\303M", @@ -72,7 +72,7 @@ subtest 'Verify Compressed' => sub { inner_random_stream_key => "+\232\222\302\20\333\254\342YD\371\34\373,\302:\303\247\t\26\$\a\370g\314\32J\240\371;U\234", kdf_parameters => { "\$UUID" => "\311\331\363\232b\212D`\277t\r\b\301\212O\352", - R => 6000, + R => num(6000), S => "\3!\230hx\363\220nV\23\340\316\262\210\26Z\al?\343\240\260\325\262\31i\223y\b\306\344V", }, master_seed => "\0206\244\265\203m14\257T\372o\16\271\306\347\215\365\376\304\20\356\344\3713\3\303\363\a\5\205\325", @@ -96,7 +96,7 @@ subtest 'Verify ProtectedStrings' => sub { kdf_parameters => ignore(), kdf_parameters => { "\$UUID" => "\311\331\363\232b\212D`\277t\r\b\301\212O\352", - R => 6000, + R => num(6000), S => "y\251\327\312mW8B\351\273\364#T#m:\370k1\240v\360E\245\304\325\265\313\337\245\211E", }, master_seed => "\355\32<1\311\320\315\24\204\325\250\35+\2525\321\224x?\361\355\310V\322\20\331\324\"\372\334\210\233", diff --git a/t/kdbx4.t b/t/kdbx4.t index f1e9cbc..5afceeb 100644 --- a/t/kdbx4.t +++ b/t/kdbx4.t @@ -25,11 +25,11 @@ subtest 'Verify Format400' => sub { encryption_iv => "3?\207P\233or\220\215h\2240", kdf_parameters => { "\$UUID" => "\357cm\337\214)DK\221\367\251\244\3\343\n\f", - I => 2, - M => 1048576, - P => 2, + I => num(2), + M => num(1048576), + P => num(2), S => "V\254\6m-\206*\260\305\f\0\366\24:4\235\364A\362\346\221\13)}\250\217P\303\303\2\331\245", - V => 19, + V => num(19), }, 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; diff --git a/t/lib/TestCommon.pm b/t/lib/TestCommon.pm index e499251..33438d3 100644 --- a/t/lib/TestCommon.pm +++ b/t/lib/TestCommon.pm @@ -98,4 +98,5 @@ sub fast_kdf { } return $params; } + 1; diff --git a/t/util.t b/t/util.t index 5ea4359..a785b2f 100644 --- a/t/util.t +++ b/t/util.t @@ -7,10 +7,11 @@ use lib 't/lib'; use TestCommon; use File::KDBX::Util qw(:all); +use Math::BigInt; +use Scalar::Util qw(blessed); use Test::More; can_ok('File::KDBX::Util', qw{ - assert_64bit can_fork dumper empty @@ -132,4 +133,58 @@ subtest 'Padding' => sub { like exception { pad_pkcs7('bar', 0) }, qr/must provide block size/i, 'Size must be non-zero'; }; +subtest '64-bit packing' => sub { + for my $test ( + # bytes, value + ["\xfe\xff\xff\xff\xff\xff\xff\xff", -2], + ["\xff\xff\xff\xff\xff\xff\xff\xff", -1], + ["\x00\x00\x00\x00\x00\x00\x00\x00", 0], + ["\x01\x00\x00\x00\x00\x00\x00\x00", 1], + ["\x02\x00\x00\x00\x00\x00\x00\x00", 2], + ["\x01\x01\x00\x00\x00\x00\x00\x00", 257], + ["\xfe\xff\xff\xff\xff\xff\xff\xff", Math::BigInt->new('-2')], + ["\xff\xff\xff\xff\xff\xff\xff\xff", Math::BigInt->new('-1')], + ["\x00\x00\x00\x00\x00\x00\x00\x00", Math::BigInt->new('0')], + ["\x01\x00\x00\x00\x00\x00\x00\x00", Math::BigInt->new('1')], + ["\x02\x00\x00\x00\x00\x00\x00\x00", Math::BigInt->new('2')], + ["\x01\x01\x00\x00\x00\x00\x00\x00", Math::BigInt->new('257')], + ["\xfe\xff\xff\xff\xff\xff\xff\xff", Math::BigInt->new('18446744073709551614')], + ["\xff\xff\xff\xff\xff\xff\xff\xff", Math::BigInt->new('18446744073709551615')], + ["\xff\xff\xff\xff\xff\xff\xff\xff", Math::BigInt->new('18446744073709551616')], # overflow + ["\x02\x00\x00\x00\x00\x00\x00\x80", Math::BigInt->new('-9223372036854775806')], + ["\x01\x00\x00\x00\x00\x00\x00\x80", Math::BigInt->new('-9223372036854775807')], + ["\x00\x00\x00\x00\x00\x00\x00\x80", Math::BigInt->new('-9223372036854775808')], + ["\x00\x00\x00\x00\x00\x00\x00\x80", Math::BigInt->new('-9223372036854775809')], # overflow + ) { + my ($bytes, $num) = @$test; + my $desc = sprintf('Pack %s => %s', $num, unpack('H*', $bytes)); + $desc =~ s/^(Pack)/$1 bigint/ if blessed $num; + my $p = pack_Ql($num); + is $p, $bytes, $desc or diag unpack('H*', $p); + } + + for my $test ( + # bytes, unsigned value, signed value + ["\x00\x00\x00\x00\x00\x00\x00\x00", 0, 0], + ["\x01\x00\x00\x00\x00\x00\x00\x00", 1, 1], + ["\x02\x00\x00\x00\x00\x00\x00\x00", 2, 2], + ["\xfe\xff\xff\xff\xff\xff\xff\xff", Math::BigInt->new('18446744073709551614'), -2], + ["\xff\xff\xff\xff\xff\xff\xff\xff", Math::BigInt->new('18446744073709551615'), -1], + ["\x02\x00\x00\x00\x00\x00\x00\x80", Math::BigInt->new('9223372036854775810'), + Math::BigInt->new('-9223372036854775806')], + ["\x01\x00\x00\x00\x00\x00\x00\x80", Math::BigInt->new('9223372036854775809'), + Math::BigInt->new('-9223372036854775807')], + ["\x00\x00\x00\x00\x00\x00\x00\x80", Math::BigInt->new('9223372036854775808'), + Math::BigInt->new('-9223372036854775808')], + ) { + my ($bytes, $num1, $num2) = @$test; + my $desc = sprintf('Unpack %s => %s', unpack('H*', $bytes), $num1); + my $p = unpack_Ql($bytes); + cmp_ok $p, '==', $num1, $desc or diag $p; + $desc = sprintf('Unpack signed %s => %s', unpack('H*', $bytes), $num2); + my $q = unpack_ql($bytes); + cmp_ok $q, '==', $num2, $desc or diag $q; + }; +}; + done_testing;