]>
Dogcows Code - chaz/p5-File-KDBX/blob - lib/File/KDBX/IO/HmacBlock.pm
1 package File
::KDBX
::IO
::HmacBlock
;
2 # ABSTRACT: HMAC block stream IO handle
7 use Crypt
::Digest
qw(digest_data);
8 use Crypt
::Mac
::HMAC
qw(hmac);
10 use File
::KDBX
::Error
;
11 use File
::KDBX
::Util
qw(:class :io assert_64bit);
14 extends
'File::KDBX::IO';
16 our $VERSION = '0.900'; # VERSION
17 our $BLOCK_SIZE = 1048576; # 1MiB
23 _buffer
=> sub { \
(my $buf = '') },
25 block_size
=> sub { $BLOCK_SIZE },
28 while (my ($attr, $default) = each %ATTRS) {
29 no strict
'refs'; ## no critic (ProhibitNoStrict)
32 *$self->{$attr} = shift if @_;
33 *$self->{$attr} //= (ref $default eq 'CODE') ? $default->($self) : $default;
42 my %args = @_ % 2 == 1 ? (fh
=> shift, @_) : @_;
43 my $self = $class->SUPER::new
;
44 $self->_fh($args{fh
}) or throw
'IO handle required';
45 $self->key($args{key
}) or throw
'Key required';
46 $self->block_size($args{block_size
});
54 $ENV{DEBUG_STREAM
} and print STDERR
"FILL\t$self\n";
55 return if $self->_finished;
57 my $block = eval { $self->_read_hashed_block($fh) };
59 $self->_set_error($err);
62 if (length($block) == 0) {
70 my ($self, $buf, $fh) = @_;
72 $ENV{DEBUG_STREAM
} and print STDERR
"WRITE\t$self ($fh)\n";
73 return 0 if $self->_finished;
75 ${*$self->{_buffer
}} .= $buf;
77 $self->_FLUSH($fh); # TODO only if autoflush?
85 $ENV{DEBUG_STREAM
} and print STDERR
"POPPED\t$self ($fh)\n";
86 return if $self->_mode ne 'w';
90 $self->_write_next_hmac_block($fh); # partial block with remaining content
91 $self->_write_final_hmac_block($fh); # terminating block
93 $self->_set_error($@) if $@;
99 $ENV{DEBUG_STREAM
} and print STDERR
"FLUSH\t$self ($fh)\n";
100 return if $self->_mode ne 'w';
103 while ($self->block_size <= length(${*$self->{_buffer
}})) {
104 $self->_write_next_hmac_block($fh);
108 $self->_set_error($err);
117 $ENV{DEBUG_STREAM
} and print STDERR
"err\t$self\n";
118 if (exists &Errno
::EPROTO
) {
121 elsif (exists &Errno
::EIO
) {
124 $self->_error($ERROR = error
(@_));
127 ##############################################################################
129 sub _read_hashed_block
{
133 read_all
$fh, my $hmac, 32 or throw
'Failed to read HMAC';
135 read_all
$fh, my $packed_size, 4 or throw
'Failed to read HMAC block size';
136 my ($size) = unpack('L<', $packed_size);
140 read_all
$fh, $block, $size
141 or throw
'Failed to read HMAC block', index => $self->_block_index, size
=> $size;
144 my $packed_index = pack('Q<', $self->_block_index);
145 my $got_hmac = hmac
('SHA256', $self->_hmac_key,
152 or throw
'Block authentication failed', index => $self->_block_index, got
=> $got_hmac, expected
=> $hmac;
154 *$self->{_block_index
}++;
158 sub _write_next_hmac_block
{
161 my $buffer = shift // $self->_buffer;
162 my $allow_empty = shift;
164 my $size = length($$buffer);
165 $size = $self->block_size if $self->block_size < $size;
166 return 0 if $size == 0 && !$allow_empty;
169 $block = substr($$buffer, 0, $size, '') if 0 < $size;
171 my $packed_index = pack('Q<', $self->_block_index);
172 my $packed_size = pack('L<', $size);
173 my $hmac = hmac
('SHA256', $self->_hmac_key,
179 $fh->print($hmac, $packed_size, $block)
180 or throw
'Failed to write HMAC block', hmac
=> $hmac, block_size
=> $size;
182 *$self->{_block_index
}++;
186 sub _write_final_hmac_block
{
190 $self->_write_next_hmac_block($fh, \'', 1);
195 my $key = shift // $self->key;
196 my $index = shift // $self->_block_index;
198 my $packed_index = pack('Q
<', $index);
199 my $hmac_key = digest_data('SHA512
', $packed_index, $key);
213 File::KDBX::IO::HmacBlock - HMAC block stream IO handle
221 Writing to a HMAC-block stream handle will transform the data into a series of blocks. An HMAC is calculated
222 for each block and is included in the output.
224 Reading from a handle, each block will be verified and authenticated as the blocks are disassembled back into
227 This format helps ensure data integrity and authenticity of KDBX4 files.
229 Each block is encoded thusly:
235 HMAC - 32 bytes, calculated over [block index (increments starting with 0), block size and data]
239 Block size - Little-endian unsigned 32-bit (counting only the data)
243 Data - String of bytes
247 The terminating block is an empty block encoded as usual but block size is 0 and there is no data.
253 Desired block size when writing (default: C<$File::KDBX::IO::HmacBlock::BLOCK_SIZE> or 1,048,576 bytes)
257 HMAC-SHA256 key for authenticating the data stream (required)
263 $fh = File::KDBX::IO::HmacBlock->new(%attributes);
264 $fh = File::KDBX::IO::HmacBlock->new($fh, %attributes);
266 Construct a new HMAC-block stream IO handle.
270 Please report any bugs or feature requests on the bugtracker website
271 L<https://github.com/chazmcgarvey/File-KDBX/issues>
273 When submitting a bug or request, please include a test-file or a
274 patch to an existing test-file that illustrates the bug or desired
279 Charles McGarvey <ccm@cpan.org>
281 =head1 COPYRIGHT AND LICENSE
283 This software is copyright (c) 2022 by Charles McGarvey.
285 This is free software; you can redistribute it and/or modify it under
286 the same terms as the Perl 5 programming language system itself.
This page took 0.046925 seconds and 4 git commands to generate.