File Coverage

blib/lib/File/KDBX/IO/Crypt.pm
Criterion Covered Total %
statement 77 84 91.6
branch 34 54 62.9
condition 6 7 85.7
subroutine 14 14 100.0
pod 1 1 100.0
total 132 160 82.5


line stmt bran cond sub pod time code
1             package File::KDBX::IO::Crypt;
2             # ABSTRACT: Encrypter/decrypter IO handle
3              
4 6     6   434 use warnings;
  6         14  
  6         161  
5 6     6   26 use strict;
  6         11  
  6         98  
6              
7 6     6   401 use Errno;
  6         2103  
  6         212  
8 6     6   31 use File::KDBX::Error;
  6         18  
  6         262  
9 6     6   31 use File::KDBX::Util qw(:class :empty);
  6         9  
  6         579  
10 6     6   33 use namespace::clean;
  6         10  
  6         50  
11              
12             extends 'File::KDBX::IO';
13              
14             our $VERSION = '0.904'; # VERSION
15             our $BUFFER_SIZE = 16384;
16             our $ERROR;
17              
18              
19             my %ATTRS = (
20             cipher => undef,
21             );
22             while (my ($attr, $default) = each %ATTRS) {
23 6     6   1724 no strict 'refs'; ## no critic (ProhibitNoStrict)
  6         10  
  6         5156  
24             *$attr = sub {
25 241     241   317 my $self = shift;
26 241 100       480 *$self->{$attr} = shift if @_;
27 241 50 66     964 *$self->{$attr} //= (ref $default eq 'CODE') ? $default->($self) : $default;
28             };
29             }
30              
31              
32             sub new {
33 44     44 1 2092 my $class = shift;
34 44 50       224 my %args = @_ % 2 == 1 ? (fh => shift, @_) : @_;
35 44         189 my $self = $class->SUPER::new;
36 44 50       170 $self->_fh($args{fh}) or throw 'IO handle required';
37 44 50       154 $self->cipher($args{cipher}) or throw 'Cipher required';
38 44         107 return $self;
39             }
40              
41             sub _FILL {
42 65     65   190 my ($self, $fh) = @_;
43              
44 65 50       156 $ENV{DEBUG_STREAM} and print STDERR "FILL\t$self\n";
45 65 100       133 my $cipher = $self->cipher or return;
46              
47 44         220 $fh->read(my $buf = '', $BUFFER_SIZE);
48 44 100       455 if (0 < length($buf)) {
49 27         60 my $plaintext = eval { $cipher->decrypt($buf) };
  27         113  
50 27 50       89 if (my $err = $@) {
51 0         0 $self->_set_error($err);
52 0         0 return;
53             }
54 27 100       127 return $plaintext if 0 < length($plaintext);
55             }
56              
57             # finish
58 18         38 my $plaintext = eval { $cipher->finish };
  18         69  
59 18 100       51 if (my $err = $@) {
60 1         4 $self->_set_error($err);
61 1         5 return;
62             }
63 17         50 $self->cipher(undef);
64 17         41 return $plaintext;
65             }
66              
67             sub _WRITE {
68 80     80   261 my ($self, $buf, $fh) = @_;
69              
70 80 50       273 $ENV{DEBUG_STREAM} and print STDERR "WRITE\t$self\n";
71 80 50       172 my $cipher = $self->cipher or return 0;
72              
73 80   100     131 my $new_data = eval { $cipher->encrypt($buf) } || '';
74 80 50       224 if (my $err = $@) {
75 0         0 $self->_set_error($err);
76 0         0 return 0;
77             }
78 80 100       222 $self->_buffer_out_add($new_data) if nonempty $new_data;
79 80         338 return length($buf);
80             }
81              
82             sub _POPPED {
83 44     44   112 my ($self, $fh) = @_;
84              
85 44 50       127 $ENV{DEBUG_STREAM} and print STDERR "POPPED\t$self\n";
86 44 100       157 return if $self->_mode ne 'w';
87 17 50       54 my $cipher = $self->cipher or return;
88              
89 17   100     39 my $new_data = eval { $cipher->finish } || '';
90 17 50       54 if (my $err = $@) {
91 0         0 $self->_set_error($err);
92 0         0 return;
93             }
94 17 100       54 $self->_buffer_out_add($new_data) if nonempty $new_data;
95              
96 17         70 $self->cipher(undef);
97 17         53 $self->_FLUSH($fh);
98             }
99              
100             sub _FLUSH {
101 17     17   41 my ($self, $fh) = @_;
102              
103 17 50       56 $ENV{DEBUG_STREAM} and print STDERR "FLUSH\t$self\n";
104 17 50       51 return if $self->_mode ne 'w';
105              
106 17         48 my $buffer = $self->_buffer_out;
107 17         49 while (@$buffer) {
108 78         658 my $read = shift @$buffer;
109 78 50       158 next if empty $read;
110 78 50       232 $fh->print($read) or return -1;
111             }
112 17         167 return 0;
113             }
114              
115             sub _set_error {
116 1     1   2 my $self = shift;
117 1 50       5 $ENV{DEBUG_STREAM} and print STDERR "err\t$self\n";
118 1 50       3 if (exists &Errno::EPROTO) {
    0          
119 1         4 $! = &Errno::EPROTO;
120             }
121             elsif (exists &Errno::EIO) {
122 0         0 $! = &Errno::EIO;
123             }
124 1         4 $self->cipher(undef);
125 1         8 $self->_error($ERROR = File::KDBX::Error->new(@_));
126             }
127              
128             1;
129              
130             __END__