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.
"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",
"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",
"boolean" : "0",
"namespace::clean" : "0",
"overload" : "0",
+ "perl" : "5.010",
"strict" : "0",
"warnings" : "0"
}
"Test::More" : "0",
"Test::Warnings" : "0",
"lib" : "0",
- "perl" : "5.006",
"utf8" : "0"
},
"suggests" : {
"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",
"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",
Test::More: '0'
Test::Warnings: '0'
lib: '0'
- perl: '5.006'
utf8: '0'
configure_requires:
ExtUtils::MakeMaker: '0'
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'
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'
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'
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'
use strict;
use warnings;
-use 5.006;
+use 5.010;
use ExtUtils::MakeMaker;
},
"DISTNAME" => "File-KDBX",
"LICENSE" => "perl",
- "MIN_PERL_VERSION" => "5.006",
+ "MIN_PERL_VERSION" => "5.010",
"NAME" => "File::KDBX",
"PREREQ_PM" => {
"Carp" => 0,
"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,
"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,
"lib" => 0,
"utf8" => 0
},
- "VERSION" => "0.901",
+ "VERSION" => "0.902",
"test" => {
"TESTS" => "t/*.t"
}
"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,
"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,
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;
});
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
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
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)
* 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 <https://keepass.info/> - The original
package File::KDBX;
# ABSTRACT: Encrypted database to store secret text and files
+use 5.010;
use warnings;
use strict;
use boolean;
use namespace::clean;
-our $VERSION = '0.901'; # VERSION
+our $VERSION = '0.902'; # VERSION
our $WARNINGS = 1;
fieldhashes \my (%SAFE, %KEYS);
my %args = @_ % 2 == 0 ? @_ : (base => shift, @_);
my $base = delete $args{base} // $self->root;
- return $base->groups_deeply(%args);
+ return $base->all_groups(%args);
}
##############################################################################
my %args = @_ % 2 == 0 ? @_ : (base => shift, @_);
my $base = delete $args{base} // $self->root;
- return $base->entries_deeply(%args);
+ return $base->all_entries(%args);
}
##############################################################################
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 }
=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;
});
The UUID of a cipher used to encrypt the database when stored as a file.
-See L</File::KDBX::Cipher>.
+See L<File::KDBX::Cipher>.
=head2 compression_flags
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<File::KDBX> does NOT enforce it.
+B<File::KDBX> does NOT enforce it.
=head2 custom_icons
least C<KDBX_VERSION_4_0> (i.e. C<0x00040000>) because Argon2 was introduced with KDBX4.
This method never returns less than C<KDBX_VERSION_3_1> (i.e. C<0x00030001>). That file version is so
-ubiquitious and well-supported, there are seldom reasons to dump in a lesser format nowadays.
+ubiquitous and well-supported, there are seldom reasons to dump in a lesser format nowadays.
B<WARNING:> If you dump a database with a minimum version higher than the current L</version>, the dumper will
typically issue a warning and automatically upgrade the database. This seems like the safest behavior in order
Iterators are the built-in way to navigate or walk the database tree. You get an iterator from L</entries>,
L</groups> and L</objects>. You can specify the search algorithm to iterate over objects in different orders
-using the C<algorith> option, which can be one of these L<constants|File::KDBX::Constants/":iteration">:
+using the C<algorithm> option, which can be one of these L<constants|File::KDBX::Constants/":iteration">:
=over 4
=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<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.
-
=head1 SEE ALSO
=over 4
use Scalar::Util qw(looks_like_number);
use namespace::clean;
-our $VERSION = '0.901'; # VERSION
+our $VERSION = '0.902'; # VERSION
my %CIPHERS;
=head1 VERSION
-version 0.901
+version 0.902
=head1 SYNOPSIS
extends 'File::KDBX::Cipher';
-our $VERSION = '0.901'; # VERSION
+our $VERSION = '0.902'; # VERSION
has key_size => 32;
sub iv_size { 16 }
=head1 VERSION
-version 0.901
+version 0.902
=head1 SYNOPSIS
extends 'File::KDBX::Cipher';
-our $VERSION = '0.901'; # VERSION
+our $VERSION = '0.902'; # VERSION
has 'counter', is => 'ro', default => 0;
=head1 VERSION
-version 0.901
+version 0.902
=head1 SYNOPSIS
# 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 = (
},
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'),
=head1 VERSION
-version 0.901
+version 0.902
=head1 SYNOPSIS
use Scalar::Util qw(looks_like_number openhandle);
use namespace::clean;
-our $VERSION = '0.901'; # VERSION
+our $VERSION = '0.902'; # VERSION
sub new {
=head1 VERSION
-version 0.901
+version 0.902
=head1 ATTRIBUTES
extends 'File::KDBX::Dumper';
-our $VERSION = '0.901'; # VERSION
+our $VERSION = '0.902'; # VERSION
sub _write_magic_numbers { '' }
sub _write_headers { '' }
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);
=head1 VERSION
-version 0.901
+version 0.902
=head1 DESCRIPTION
extends 'File::KDBX::Dumper';
-our $VERSION = '0.901'; # VERSION
+our $VERSION = '0.902'; # VERSION
sub _dump {
my $self = shift;
=head1 VERSION
-version 0.901
+version 0.902
=head1 SYNOPSIS
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;
# nothing
}
elsif ($type == HEADER_TRANSFORM_ROUNDS) {
- assert_64bit;
- $val = pack('Q<', $val);
+ $val = pack_Ql($val);
}
elsif ($type == HEADER_ENCRYPTION_IV) {
# nothing
$fh->print($kdbx->headers->{+HEADER_STREAM_START_BYTES})
or throw 'Failed to write start bytes';
- $fh->flush;
$kdbx->key($key);
=head1 VERSION
-version 0.901
+version 0.902
=head1 BUGS
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);
extends 'File::KDBX::Dumper';
-our $VERSION = '0.901'; # VERSION
+our $VERSION = '0.902'; # VERSION
has _binaries_written => {}, is => 'ro';
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;
$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);
$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);
=head1 VERSION
-version 0.901
+version 0.902
=head1 BUGS
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;
extends 'File::KDBX::Dumper';
-our $VERSION = '0.901'; # VERSION
+our $VERSION = '0.902'; # VERSION
has allow_protection => 1;
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) };
}
=head1 VERSION
-version 0.901
+version 0.902
=head1 ATTRIBUTES
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);
extends 'File::KDBX::Object';
-our $VERSION = '0.901'; # VERSION
+our $VERSION = '0.902'; # VERSION
my $PLACEHOLDER_MAX_DEPTH = 10;
my %PLACEHOLDERS;
=head1 VERSION
-version 0.901
+version 0.902
=head1 DESCRIPTION
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<File::KDBX::Entry> is a subclass of L<File::KDBX::Object>.
+A B<File::KDBX::Entry> is a subclass of L<File::KDBX::Object>. View its documentation to see other attributes
+and methods available on entries.
=head2 Placeholders
package File::KDBX::Error;
# ABSTRACT: Represents something bad that happened
+use 5.010;
use warnings;
use strict;
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);
=head1 VERSION
-version 0.901
+version 0.902
=head1 ATTRIBUTES
extends 'File::KDBX::Object';
-our $VERSION = '0.901'; # VERSION
+our $VERSION = '0.902'; # VERSION
# has uuid => sub { generate_uuid(printable => 1) };
}
-sub entries_deeply {
+sub all_entries {
my $self = shift;
my %args = @_;
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 {
}
-sub groups_deeply {
+sub all_groups {
my $self = shift;
my %args = @_;
##############################################################################
-sub objects_deeply {
+sub all_objects {
my $self = shift;
my %args = @_;
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 {
=head1 VERSION
-version 0.901
+version 0.902
=head1 DESCRIPTION
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<File::KDBX::Group> is a subclass of L<File::KDBX::Object>. View its documentation to see other attributes
+and methods available on groups.
+
=head1 ATTRIBUTES
=head2 name
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<File::KDBX::Iterator> over I<entries> within a group. Supports the same options as L</groups>,
plus some new ones:
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<File::KDBX::Iterator> over I<groups> within a groups, deeply. Options:
Remove a group from a group's array of subgroups. Returns the group removed or C<undef> if nothing removed.
-=head2 objects_deeply
+=head2 all_objects
- \&iterator = $groups->objects_deeply(%options);
+ \&iterator = $groups->all_objects(%options);
Get an L<File::KDBX::Iterator> over I<objects> within a group, deeply. Groups and entries are considered
objects, so this is essentially a combination of L</groups> and L</entries>. This won't often be useful, but
extends 'IO::Handle';
-our $VERSION = '0.901'; # VERSION
+our $VERSION = '0.902'; # VERSION
sub _croak { require Carp; goto &Carp::croak }
=head1 VERSION
-version 0.901
+version 0.902
=head1 DESCRIPTION
extends 'File::KDBX::IO';
-our $VERSION = '0.901'; # VERSION
+our $VERSION = '0.902'; # VERSION
our $BUFFER_SIZE = 16384;
our $ERROR;
=head1 VERSION
-version 0.901
+version 0.902
=head1 SYNOPSIS
extends 'File::KDBX::IO';
-our $VERSION = '0.901'; # VERSION
+our $VERSION = '0.902'; # VERSION
our $ALGORITHM = 'SHA256';
our $BLOCK_SIZE = 1048576; # 1MiB
our $ERROR;
=head1 VERSION
-version 0.901
+version 0.902
=head1 DESCRIPTION
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 },
sub new {
- assert_64bit;
-
my $class = shift;
my %args = @_ % 2 == 1 ? (fh => shift, @_) : @_;
my $self = $class->SUPER::new;
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,
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,
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;
}
=head1 VERSION
-version 0.901
+version 0.902
=head1 DESCRIPTION
BEGIN { mark_as_loaded('Iterator::Simple::Iterator') }
extends 'Iterator::Simple::Iterator';
-our $VERSION = '0.901'; # VERSION
+our $VERSION = '0.902'; # VERSION
sub new {
=head1 VERSION
-version 0.901
+version 0.902
=head1 SYNOPSIS
use Scalar::Util qw(blessed);
use namespace::clean;
-our $VERSION = '0.901'; # VERSION
+our $VERSION = '0.902'; # VERSION
my %KDFS;
=head1 VERSION
-version 0.901
+version 0.902
=head1 DESCRIPTION
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;
=head1 VERSION
-version 0.901
+version 0.902
=head1 DESCRIPTION
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' }
=head1 VERSION
-version 0.901
+version 0.902
=head1 DESCRIPTION
use Scalar::Util qw(blessed openhandle);
use namespace::clean;
-our $VERSION = '0.901'; # VERSION
+our $VERSION = '0.902'; # VERSION
fieldhashes \my %SAFE;
=head1 VERSION
-version 0.901
+version 0.902
=head1 DESCRIPTION
extends 'File::KDBX::Key';
-our $VERSION = '0.901'; # VERSION
+our $VERSION = '0.902'; # VERSION
sub init {
my $self = shift;
=head1 VERSION
-version 0.901
+version 0.902
=head1 SYNOPSIS
extends 'File::KDBX::Key';
-our $VERSION = '0.901'; # VERSION
+our $VERSION = '0.902'; # VERSION
sub init {
my $self = shift;
=head1 VERSION
-version 0.901
+version 0.902
=head1 SYNOPSIS
extends 'File::KDBX::Key';
-our $VERSION = '0.901'; # VERSION
+our $VERSION = '0.902'; # VERSION
has 'type', is => 'ro';
=head1 VERSION
-version 0.901
+version 0.902
=head1 SYNOPSIS
extends 'File::KDBX::Key';
-our $VERSION = '0.901'; # VERSION
+our $VERSION = '0.902'; # VERSION
sub init {
my $self = shift;
=head1 VERSION
-version 0.901
+version 0.902
=head1 SYNOPSIS
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;
=head1 VERSION
-version 0.901
+version 0.902
=head1 SYNOPSIS
various quirks to L<IPC::Open3> and L<IPC::Cmd> 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
use Scalar::Util qw(looks_like_number openhandle);
use namespace::clean;
-our $VERSION = '0.901'; # VERSION
+our $VERSION = '0.902'; # VERSION
sub new {
=head1 VERSION
-version 0.901
+version 0.902
=head1 DESCRIPTION
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
=head1 VERSION
-version 0.901
+version 0.902
=head1 DESCRIPTION
extends 'File::KDBX::Loader';
-our $VERSION = '0.901'; # VERSION
+our $VERSION = '0.902'; # VERSION
sub _read {
my $self = shift;
=head1 VERSION
-version 0.901
+version 0.902
=head1 SYNOPSIS
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;
# nothing
}
elsif ($type == HEADER_TRANSFORM_ROUNDS) {
- assert_64bit;
- $val = unpack('Q<', $val);
+ ($val) = unpack_Ql($val);
}
elsif ($type == HEADER_ENCRYPTION_IV) {
# nothing
# 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) {
=head1 VERSION
-version 0.901
+version 0.902
=head1 BUGS
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;
extends 'File::KDBX::Loader';
-our $VERSION = '0.901'; # VERSION
+our $VERSION = '0.902'; # VERSION
sub _read_header {
my $self = shift;
($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);
($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);
=head1 VERSION
-version 0.901
+version 0.902
=head1 BUGS
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;
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) };
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);
}
=head1 VERSION
-version 0.901
+version 0.902
=head1 BUGS
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);
=head1 VERSION
-version 0.901
+version 0.902
=head1 DESCRIPTION
use Scalar::Util qw(refaddr);
use namespace::clean;
-our $VERSION = '0.901'; # VERSION
+our $VERSION = '0.902'; # VERSION
sub new {
=head1 VERSION
-version 0.901
+version 0.902
=head1 SYNOPSIS
use File::KDBX::Util qw(:class);
use namespace::clean;
-our $VERSION = '0.901'; # VERSION
+our $VERSION = '0.902'; # VERSION
sub new {
=head1 VERSION
-version 0.901
+version 0.902
=head1 ATTRIBUTES
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;
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)],
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)],
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 };
}
-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};
}
-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 }
=head1 VERSION
-version 0.901
+version 0.902
=head1 FUNCTIONS
$bool = load_xs();
$bool = load_xs($version);
-Attempt to load L<File::KDBX::XS>. Return truthy if C<XS> is loaded. If C<$version> is given, it will check
-that at least the given version is loaded.
+Attempt to load L<File::KDBX::XS>. Return truthy if it is loaded. If C<$version> is given, it will check that
+at least the given version is loaded.
=head2 assert
Write an executable comment. Only executed if C<DEBUG> 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;
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<Math::BigInt>.
+
+=head2 pack_Ql
+
+ $bytes = pack_Ql($int);
+
+Like C<pack('QE<lt>', $int)>, but also works on 32-bit perls.
+
+=head2 pack_ql
+
+ $bytes = pack_ql($int);
+
+Like C<pack('qE<lt>', $int)>, but also works on 32-bit perls.
+
+=head2 unpack_Ql
+
+ $int = unpack_Ql($bytes);
+
+Like C<unpack('QE<lt>', $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<fopen>-style mode is readable, writable or both.
+Like C<unpack('qE<lt>', $bytes)>, but also works on 32-bit perls.
=head2 is_uuid
'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',
'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',
'boolean' => '0',
'namespace::clean' => '0',
'overload' => '0',
+ 'perl' => '5.010',
'strict' => '0',
'warnings' => '0'
}
'Test::More' => '0',
'Test::Warnings' => '0',
'lib' => '0',
- 'perl' => '5.006',
'utf8' => '0'
},
'suggests' => {
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';
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';
};
}
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'
}
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';
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",
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<E\252\314k\20\257\302\343p\"y\5sfw ",
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",
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",
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",
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;
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
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;