use TestCommon;
use Crypt::Misc 0.029 qw(decode_b64 encode_b64);
-use Test::More;
-
-BEGIN { use_ok 'File::KDBX::Key' }
+use File::KDBX::Constants qw(:key_file);
+use File::KDBX::Key;
+use File::Temp qw(tempfile);
+use Test::More 1.001004_001;
subtest 'Primitives' => sub {
my $pkey = File::KDBX::Key->new('password');
'Can calculate raw key from composite' or diag encode_b64($ckey->raw_key);
};
-subtest 'File keys' => sub {
- my $key = File::KDBX::Key::File->new(testfile(qw{keys xmlv1.key}));
- is $key->raw_key, decode_b64('OF9tj+tfww1kHNWQaJlZWIlBdoTVXOazP8g/vZK7NcI='),
- 'Can calculate raw key from XML file' or diag encode_b64($key->raw_key);
- is $key->type, 'xml', 'file type is detected as xml';
- is $key->version, '1.0', 'file version is detected as xml';
-
- $key = File::KDBX::Key::File->new(testfile(qw{keys xmlv2.key}));
- is $key->raw_key, decode_b64('OF9tj+tfww1kHNWQaJlZWIlBdoTVXOazP8g/vZK7NcI='),
- 'Can calculate raw key from XML file' or diag encode_b64($key->raw_key);
- is $key->type, 'xml', 'file type is detected as xml';
- is $key->version, '2.0', 'file version is detected as xml';
-
- $key = File::KDBX::Key::File->new(testfile(qw{keys binary.key}));
- is $key->raw_key, decode_b64('QlkDxuYbDPDpDXdK1470EwVBL+AJBH2gvPA9lxNkFEk='),
- 'Can calculate raw key from binary file' or diag encode_b64($key->raw_key);
- is $key->type, 'binary', 'file type is detected as binary';
-
- $key = File::KDBX::Key::File->new(testfile(qw{keys hex.key}));
- is $key->raw_key, decode_b64('QlkDxuYbDPDpDXdK1470EwVBL+AJBH2gvPA9lxNkFEk='),
- 'Can calculate raw key from hex file' or diag encode_b64($key->raw_key);
- is $key->type, 'hex', 'file type is detected as hex';
-
- $key = File::KDBX::Key::File->new(testfile(qw{keys hashed.key}));
- is $key->raw_key, decode_b64('8vAO4mrMeq6iCa1FHeWm/Mj5al8HIv2ajqsqsSeUC6U='),
- 'Can calculate raw key from binary file' or diag encode_b64($key->raw_key);
- is $key->type, 'hashed', 'file type is detected as hashed';
-
+for my $test (
+ [KEY_FILE_TYPE_XML, 'xmlv1.key', 'OF9tj+tfww1kHNWQaJlZWIlBdoTVXOazP8g/vZK7NcI=', '1.0'],
+ [KEY_FILE_TYPE_XML, 'xmlv2.key', 'OF9tj+tfww1kHNWQaJlZWIlBdoTVXOazP8g/vZK7NcI=', '2.0'],
+ [KEY_FILE_TYPE_BINARY, 'binary.key', 'QlkDxuYbDPDpDXdK1470EwVBL+AJBH2gvPA9lxNkFEk='],
+ [KEY_FILE_TYPE_HEX, 'hex.key', 'QlkDxuYbDPDpDXdK1470EwVBL+AJBH2gvPA9lxNkFEk='],
+ [KEY_FILE_TYPE_HASHED, 'hashed.key', '8vAO4mrMeq6iCa1FHeWm/Mj5al8HIv2ajqsqsSeUC6U='],
+) {
+ my ($type) = @$test;
+ subtest "Load $type key file" => sub {
+ my ($type, $filename, $expected_key, $version) = @_;
+
+ my $key = File::KDBX::Key::File->new(testfile('keys', $filename));
+ is $key->raw_key, decode_b64($expected_key),
+ "Can calculate raw key from $type file" or diag encode_b64($key->raw_key);
+ is $key->type, $type, "File type is detected as $type";
+ is $key->version, $version, "File version is detected as $version" if defined $version;
+ }, @$test;
+
+ subtest "Save $type key file" => sub {
+ my ($type, $filename, $expected_key, $version) = @_;
+
+ my ($fh, $filepath) = tempfile('keyfile-XXXXXX', TMPDIR => 1, UNLINK => 1);
+ close($fh);
+ note $filepath;
+ my $key = File::KDBX::Key::File->new(
+ filepath => $filepath,
+ type => $type,
+ version => $version,
+ raw_key => decode_b64($expected_key),
+ );
+
+ my $e = exception { $key->save };
+
+ if ($type == KEY_FILE_TYPE_HASHED) {
+ like $e, qr/invalid type/i, "Cannot save $type file";
+ return;
+ }
+ is $e, undef, "Save $type file";
+
+ my $key2 = File::KDBX::Key::File->new($filepath);
+ is $key2->type, $key->type, 'Loaded key file has the same type';
+ is $key2->raw_key, $key->raw_key, 'Loaded key file has the same raw key';
+ }, @$test;
+}
+
+subtest 'IO handle key files' => sub {
my $buf = 'password';
open(my $fh, '<', \$buf) or die "open failed: $!\n";
- $key = File::KDBX::Key::File->new($fh);
+ my $key = File::KDBX::Key::File->new($fh);
is $key->raw_key, decode_b64('XohImNooBHFR0OVvjcYpJ3NgPQ1qq73WKhHvch0VQtg='),
'Can calculate raw key from file handle' or diag encode_b64($key->raw_key);
is $key->type, 'hashed', 'file type is detected as hashed';
- is exception { File::KDBX::Key::File->new }, undef, 'Can instantiate uninitialized';
+ my ($fh_save, $filepath) = tempfile('keyfile-XXXXXX', TMPDIR => 1, UNLINK => 1);
+ is exception { $key->save(fh => $fh_save, type => KEY_FILE_TYPE_XML) }, undef,
+ 'Save key file using IO handle';
+ close($fh_save);
+
+ my $key2 = File::KDBX::Key::File->new($filepath);
+ is $key2->type, KEY_FILE_TYPE_XML, 'Loaded key file has the same type';
+ is $key2->filepath, $filepath, 'Loaded key remembers the filepath';
+ is $key2->raw_key, $key->raw_key, 'Loaded key file has the same raw key';
+ $key2->reload;
+ is $key2->raw_key, $key->raw_key, 'Raw key is the same when reloaded same file';
+
+ my $easy_raw_key = "\1" x 32;
+ $key->init(\$easy_raw_key);
+ $key->save(filepath => $filepath);
+
+ $key2->reload;
+ is $key2->raw_key, "\1" x 32, 'Raw key is changed after reload';
+};
+
+subtest 'Key file error handling' => sub {
+ is exception { File::KDBX::Key::File->new }, undef, 'Cannot instantiate uninitialized';
like exception { File::KDBX::Key::File->init },
- qr/^Missing key primitive/, 'Throws if no primitive is provided';
+ qr/^Missing key primitive/, 'Throw if no primitive is provided';
like exception { File::KDBX::Key::File->new(testfile(qw{keys nonexistent})) },
- qr/^Failed to open key file/, 'Throws if file is missing';
+ qr/^Failed to open key file/, 'Throw if file is missing';
like exception { File::KDBX::Key::File->new({}) },
- qr/^Unexpected primitive type/, 'Throws if primitive is the wrong type';
+ qr/^Unexpected primitive type/, 'Throw if primitive is the wrong type';
};
done_testing;