File-KDBX
view release on metacpan or search on metacpan
lib/File/KDBX/Loader/V3.pm view on Meta::CPAN
}
elsif ($type == HEADER_INNER_RANDOM_STREAM_ID) {
($val) = unpack('L<', $val);
}
elsif ($type == HEADER_KDF_PARAMETERS ||
$type == HEADER_PUBLIC_CUSTOM_DATA) {
throw "Unexpected KDBX4 header: $type", type => $type;
}
else {
alert "Unknown header: $type", type => $type;
}
return wantarray ? ($type => $val, $buf) : $buf;
}
sub _read_body {
my $self = shift;
my $fh = shift;
my $key = shift;
my $header_data = shift;
my $kdbx = $self->kdbx;
# assert all required headers present
for my $field (
HEADER_CIPHER_ID,
HEADER_ENCRYPTION_IV,
HEADER_MASTER_SEED,
HEADER_INNER_RANDOM_STREAM_KEY,
HEADER_STREAM_START_BYTES,
) {
defined $kdbx->headers->{$field} or throw "Missing $field";
}
$kdbx->kdf_parameters({
KDF_PARAM_UUID() => KDF_UUID_AES,
KDF_PARAM_AES_ROUNDS() => delete $kdbx->headers->{+HEADER_TRANSFORM_ROUNDS},
KDF_PARAM_AES_SEED() => delete $kdbx->headers->{+HEADER_TRANSFORM_SEED},
});
my $master_seed = $kdbx->headers->{+HEADER_MASTER_SEED};
my @cleanup;
$key = $kdbx->composite_key($key);
my $response = $key->challenge($master_seed);
push @cleanup, erase_scoped $response;
my $transformed_key = $kdbx->kdf->transform($key);
push @cleanup, erase_scoped $transformed_key;
my $final_key = digest_data('SHA256', $master_seed, $response, $transformed_key);
push @cleanup, erase_scoped $final_key;
my $cipher = $kdbx->cipher(key => $final_key);
$fh = File::KDBX::IO::Crypt->new($fh, cipher => $cipher);
read_all $fh, my $start_bytes, 32 or throw 'Failed to read starting bytes';
my $expected_start_bytes = $kdbx->headers->{stream_start_bytes};
$start_bytes eq $expected_start_bytes
or throw "Invalid credentials or data is corrupt (wrong starting bytes)\n",
got => $start_bytes, expected => $expected_start_bytes, headers => $kdbx->headers;
$kdbx->key($key);
$fh = File::KDBX::IO::HashBlock->new($fh);
my $compress = $kdbx->headers->{+HEADER_COMPRESSION_FLAGS};
if ($compress == COMPRESSION_GZIP) {
load_optional('IO::Uncompress::Gunzip');
$fh = IO::Uncompress::Gunzip->new($fh)
or throw "Failed to initialize compression library: $IO::Uncompress::Gunzip::GunzipError",
error => $IO::Uncompress::Gunzip::GunzipError;
}
elsif ($compress != COMPRESSION_NONE) {
throw "Unsupported compression ($compress)\n", compression_flags => $compress;
}
$self->_read_inner_body($fh);
close($fh);
if (my $header_hash = $kdbx->meta->{header_hash}) {
my $got_header_hash = digest_data('SHA256', $header_data);
$header_hash eq $got_header_hash
or throw 'Header hash does not match', got => $got_header_hash, expected => $header_hash;
}
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
File::KDBX::Loader::V3 - Load KDBX3 files
=head1 VERSION
version 0.906
=head1 BUGS
Please report any bugs or feature requests on the bugtracker website
L<https://github.com/chazmcgarvey/File-KDBX/issues>
When submitting a bug or request, please include a test-file or a
patch to an existing test-file that illustrates the bug or desired
feature.
=head1 AUTHOR
Charles McGarvey <ccm@cpan.org>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2022 by Charles McGarvey.
( run in 0.578 second using v1.01-cache-2.11-cpan-39bf76dae61 )