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   446 use warnings;
  6         11  
  6         160  
5 6     6   26 use strict;
  6         7  
  6         95  
6              
7 6     6   389 use Errno;
  6         1993  
  6         205  
8 6     6   31 use File::KDBX::Error;
  6         18  
  6         267  
9 6     6   32 use File::KDBX::Util qw(:class :empty);
  6         8  
  6         590  
10 6     6   37 use namespace::clean;
  6         8  
  6         31  
11              
12             extends 'File::KDBX::IO';
13              
14             our $VERSION = '0.905'; # 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   1729 no strict 'refs'; ## no critic (ProhibitNoStrict)
  6         9  
  6         4956  
24             *$attr = sub {
25 241     241   279 my $self = shift;
26 241 100       421 *$self->{$attr} = shift if @_;
27 241 50 66     824 *$self->{$attr} //= (ref $default eq 'CODE') ? $default->($self) : $default;
28             };
29             }
30              
31              
32             sub new {
33 44     44 1 2090 my $class = shift;
34 44 50       180 my %args = @_ % 2 == 1 ? (fh => shift, @_) : @_;
35 44         180 my $self = $class->SUPER::new;
36 44 50       140 $self->_fh($args{fh}) or throw 'IO handle required';
37 44 50       117 $self->cipher($args{cipher}) or throw 'Cipher required';
38 44         105 return $self;
39             }
40              
41             sub _FILL {
42 65     65   113 my ($self, $fh) = @_;
43              
44 65 50       138 $ENV{DEBUG_STREAM} and print STDERR "FILL\t$self\n";
45 65 100       122 my $cipher = $self->cipher or return;
46              
47 44         217 $fh->read(my $buf = '', $BUFFER_SIZE);
48 44 100       433 if (0 < length($buf)) {
49 27         57 my $plaintext = eval { $cipher->decrypt($buf) };
  27         99  
50 27 50       83 if (my $err = $@) {
51 0         0 $self->_set_error($err);
52 0         0 return;
53             }
54 27 100       125 return $plaintext if 0 < length($plaintext);
55             }
56              
57             # finish
58 18         34 my $plaintext = eval { $cipher->finish };
  18         53  
59 18 100       51 if (my $err = $@) {
60 1         5 $self->_set_error($err);
61 1         5 return;
62             }
63 17         43 $self->cipher(undef);
64 17         72 return $plaintext;
65             }
66              
67             sub _WRITE {
68 80     80   241 my ($self, $buf, $fh) = @_;
69              
70 80 50       223 $ENV{DEBUG_STREAM} and print STDERR "WRITE\t$self\n";
71 80 50       137 my $cipher = $self->cipher or return 0;
72              
73 80   100     151 my $new_data = eval { $cipher->encrypt($buf) } || '';
74 80 50       169 if (my $err = $@) {
75 0         0 $self->_set_error($err);
76 0         0 return 0;
77             }
78 80 100       166 $self->_buffer_out_add($new_data) if nonempty $new_data;
79 80         370 return length($buf);
80             }
81              
82             sub _POPPED {
83 44     44   81 my ($self, $fh) = @_;
84              
85 44 50       115 $ENV{DEBUG_STREAM} and print STDERR "POPPED\t$self\n";
86 44 100       121 return if $self->_mode ne 'w';
87 17 50       57 my $cipher = $self->cipher or return;
88              
89 17   100     40 my $new_data = eval { $cipher->finish } || '';
90 17 50       46 if (my $err = $@) {
91 0         0 $self->_set_error($err);
92 0         0 return;
93             }
94 17 100       46 $self->_buffer_out_add($new_data) if nonempty $new_data;
95              
96 17         49 $self->cipher(undef);
97 17         43 $self->_FLUSH($fh);
98             }
99              
100             sub _FLUSH {
101 17     17   34 my ($self, $fh) = @_;
102              
103 17 50       58 $ENV{DEBUG_STREAM} and print STDERR "FLUSH\t$self\n";
104 17 50       51 return if $self->_mode ne 'w';
105              
106 17         44 my $buffer = $self->_buffer_out;
107 17         47 while (@$buffer) {
108 78         573 my $read = shift @$buffer;
109 78 50       140 next if empty $read;
110 78 50       206 $fh->print($read) or return -1;
111             }
112 17         129 return 0;
113             }
114              
115             sub _set_error {
116 1     1   1 my $self = shift;
117 1 50       4 $ENV{DEBUG_STREAM} and print STDERR "err\t$self\n";
118 1 50       4 if (exists &Errno::EPROTO) {
    0          
119 1         4 $! = &Errno::EPROTO;
120             }
121             elsif (exists &Errno::EIO) {
122 0         0 $! = &Errno::EIO;
123             }
124 1         3 $self->cipher(undef);
125 1         9 $self->_error($ERROR = File::KDBX::Error->new(@_));
126             }
127              
128             1;
129              
130             __END__