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   486 use warnings;
  6         10  
  6         163  
5 6     6   33 use strict;
  6         14  
  6         97  
6              
7 6     6   410 use Errno;
  6         2183  
  6         181  
8 6     6   27 use File::KDBX::Error;
  6         19  
  6         250  
9 6     6   41 use File::KDBX::Util qw(:class :empty);
  6         20  
  6         667  
10 6     6   35 use namespace::clean;
  6         11  
  6         28  
11              
12             extends 'File::KDBX::IO';
13              
14             our $VERSION = '0.906'; # 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   1823 no strict 'refs'; ## no critic (ProhibitNoStrict)
  6         14  
  6         5029  
24             *$attr = sub {
25 256     256   295 my $self = shift;
26 256 100       483 *$self->{$attr} = shift if @_;
27 256 50 66     932 *$self->{$attr} //= (ref $default eq 'CODE') ? $default->($self) : $default;
28             };
29             }
30              
31              
32             sub new {
33 44     44 1 2219 my $class = shift;
34 44 50       198 my %args = @_ % 2 == 1 ? (fh => shift, @_) : @_;
35 44         192 my $self = $class->SUPER::new;
36 44 50       155 $self->_fh($args{fh}) or throw 'IO handle required';
37 44 50       152 $self->cipher($args{cipher}) or throw 'Cipher required';
38 44         112 return $self;
39             }
40              
41             sub _FILL {
42 76     76   143 my ($self, $fh) = @_;
43              
44 76 50       168 $ENV{DEBUG_STREAM} and print STDERR "FILL\t$self\n";
45 76 100       138 my $cipher = $self->cipher or return;
46              
47 48         269 $fh->read(my $buf = '', $BUFFER_SIZE);
48 48 100       513 if (0 < length($buf)) {
49 27         1998 my $plaintext = eval { $cipher->decrypt($buf) };
  27         111  
50 27 50       93 if (my $err = $@) {
51 0         0 $self->_set_error($err);
52 0         0 return;
53             }
54 27 100       136 return $plaintext if 0 < length($plaintext);
55             }
56              
57             # finish
58 22         40 my $plaintext = eval { $cipher->finish };
  22         79  
59 22 100       69 if (my $err = $@) {
60 1         5 $self->_set_error($err);
61 1         5 return;
62             }
63 21         58 $self->cipher(undef);
64 21         48 return $plaintext;
65             }
66              
67             sub _WRITE {
68 80     80   244 my ($self, $buf, $fh) = @_;
69              
70 80 50       231 $ENV{DEBUG_STREAM} and print STDERR "WRITE\t$self\n";
71 80 50       147 my $cipher = $self->cipher or return 0;
72              
73 80   100     127 my $new_data = eval { $cipher->encrypt($buf) } || '';
74 80 50       187 if (my $err = $@) {
75 0         0 $self->_set_error($err);
76 0         0 return 0;
77             }
78 80 100       197 $self->_buffer_out_add($new_data) if nonempty $new_data;
79 80         347 return length($buf);
80             }
81              
82             sub _POPPED {
83 44     44   103 my ($self, $fh) = @_;
84              
85 44 50       115 $ENV{DEBUG_STREAM} and print STDERR "POPPED\t$self\n";
86 44 100       112 return if $self->_mode ne 'w';
87 17 50       49 my $cipher = $self->cipher or return;
88              
89 17   100     33 my $new_data = eval { $cipher->finish } || '';
90 17 50       55 if (my $err = $@) {
91 0         0 $self->_set_error($err);
92 0         0 return;
93             }
94 17 100       50 $self->_buffer_out_add($new_data) if nonempty $new_data;
95              
96 17         75 $self->cipher(undef);
97 17         57 $self->_FLUSH($fh);
98             }
99              
100             sub _FLUSH {
101 17     17   38 my ($self, $fh) = @_;
102              
103 17 50       60 $ENV{DEBUG_STREAM} and print STDERR "FLUSH\t$self\n";
104 17 50       50 return if $self->_mode ne 'w';
105              
106 17         43 my $buffer = $self->_buffer_out;
107 17         52 while (@$buffer) {
108 63         514 my $read = shift @$buffer;
109 63 50       135 next if empty $read;
110 63 50       209 $fh->print($read) or return -1;
111             }
112 17         165 return 0;
113             }
114              
115             sub _set_error {
116 1     1   2 my $self = shift;
117 1 50       4 $ENV{DEBUG_STREAM} and print STDERR "err\t$self\n";
118 1 50       3 if (exists &Errno::EPROTO) {
    0          
119 1         5 $! = &Errno::EPROTO;
120             }
121             elsif (exists &Errno::EIO) {
122 0         0 $! = &Errno::EIO;
123             }
124 1         4 $self->cipher(undef);
125 1         9 $self->_error($ERROR = File::KDBX::Error->new(@_));
126             }
127              
128             1;
129              
130             __END__