File Coverage

blib/lib/Crypt/FileHandle.pm
Criterion Covered Total %
statement 207 274 75.5
branch 83 138 60.1
condition 19 32 59.3
subroutine 27 28 96.4
pod 3 3 100.0
total 339 475 71.3


line stmt bran cond sub pod time code
1             package Crypt::FileHandle;
2              
3             require 5.006000;
4              
5 1     1   5770 use strict;
  1         2  
  1         32  
6 1     1   5 use warnings;
  1         1  
  1         23  
7 1     1   366 use FileHandle;
  1         8736  
  1         5  
8 1     1   237 use Carp qw(croak carp);
  1         2  
  1         47  
9              
10 1     1   5 use vars qw($VERSION);
  1         1  
  1         2637  
11             $VERSION = "0.03";
12              
13             # ensure errors are properly reported to caller
14             $Carp::Internal{"Crypt::FileHandle"}++;
15              
16             # internal variable names
17             our $V_FH = 'fh';
18             our $V_CIPHER = 'cipher';
19             our $V_STATE = 'state';
20             our $V_READ_BUFFER = 'read_buffer';
21             our $V_TOTAL_BYTES = 'total_bytes';
22             our $V_EOF = 'eof';
23              
24             # internal state values
25             our $STATE_CLOSED = 0;
26             our $STATE_ENCRYPT = 1;
27             our $STATE_DECRYPT = 2;
28              
29             # global variables
30             our $READSIZE = 4096;
31              
32             ############################################################
33              
34             sub new {
35 1     1 1 512 my $proto = shift;
36 1   33     7 my $class = ref($proto) || $proto;
37              
38             # create tied FileHandle object
39             # tie() automatically attaches this class type to it via TIEHANDLE()
40             # all methods defined below will be called in place of the real methods
41 1         6 my $fh = new FileHandle;
42 1         47 my $self = tie(*$fh, $class, @_);
43              
44             # WARNING: do not store tied FileHandle object as this will
45             # create a second hidden reference that prevents the tied object
46             # from being destroyed; untie() cannot be called automatically
47              
48             # return tied FileHandle
49 1         2 return $fh;
50             }
51              
52             ############################################################
53              
54             # global method
55             # verifies that the given cipher supports the necessary methods
56             #
57             sub verify_cipher {
58 2     2 1 5 my $class = shift;
59 2         4 my $cipher = shift;
60              
61             # check parameters
62 2 50       3 if (! defined $cipher) {
63 0         0 return !1;
64             }
65              
66             # must at least be a reference to something
67 2 50       5 if (! ref($cipher)) {
68 0         0 return !1;
69             }
70              
71             # verify required methods exist
72 2 50       8 if (! $cipher->can("start")) {
73 0         0 return !1;
74             }
75 2 50       6 if (! $cipher->can("crypt")) {
76 0         0 return !1;
77             }
78 2 50       6 if (! $cipher->can("finish")) {
79 0         0 return !1;
80             }
81              
82 2         15 return 1;
83             }
84              
85             ############################################################
86              
87             # global access method
88             # affects ALL instances of Crypt::FileHandle
89             # WARNING: should not be less than minimum encrypted header length
90             #
91             sub readsize {
92 2     2 1 5 my $class = shift;
93 2         2 my $readsize = shift;
94 2 100 66     10 if (!defined $readsize || $readsize <= 0) {
95 1         4 return $READSIZE;
96             }
97             # provide warning if READSIZE is smaller than minimum
98             # must be able to read at least header on first read
99             # "Salted__" or "RandomIV" plus 8 byte IV equals 16 bytes
100 1 50       3 if ($readsize < 16) {
101 0         0 carp "readsize may be too small for encrypted header"
102             }
103 1         5 return $READSIZE = $readsize;
104             }
105              
106             ############################################################
107              
108             sub DESTROY {
109 1     1   4 my $self = shift;
110              
111             # close the real FileHandle
112 1 50       5 if ($self->{$V_STATE} != $STATE_CLOSED) {
113 0         0 $self->CLOSE();
114             }
115              
116 1         127 return 1;
117             }
118              
119             ############################################################
120              
121             sub TIEHANDLE {
122 1     1   2 my $proto = shift;
123 1   33     4 my $class = ref($proto) || $proto;
124              
125             # required parameters
126 1         2 my $cipher = shift;
127              
128             # create new empty hash reference, bless as class
129 1         2 my $self = {};
130 1         2 bless($self, $class);
131              
132             # verify cipher
133 1 50       3 if (!$self->verify_cipher($cipher)) {
134 0         0 croak "invalid cipher or cipher not defined";
135             }
136              
137             # default options
138 1         5 $self->{$V_CIPHER} = $cipher;
139 1         3 $self->{$V_STATE} = $STATE_CLOSED;
140 1         2 $self->{$V_READ_BUFFER} = "";
141 1         2 $self->{$V_TOTAL_BYTES} = 0;
142 1         2 $self->{$V_EOF} = 0;
143              
144             # create real FileHandle
145             # read/write methods below will utilize this FileHandle
146 1         5 my $fh = new FileHandle;
147 1         23 $self->{$V_FH} = $fh;
148              
149             # automatically call open if additional options are provided
150 1 50       4 if (scalar @_ > 0) {
151 0 0       0 $self->OPEN(@_) || croak $!;
152             }
153              
154 1         3 return $self;
155             }
156              
157             ############################################################
158              
159             # untie gotcha
160             #
161             sub UNTIE {
162 0     0   0 my ($obj, $c) = @_;
163 0 0       0 carp "untie attempted while $c inner references still exist" if $c;
164             }
165              
166             ############################################################
167              
168             # open() FileHandle
169             #
170             sub OPEN {
171 10     10   3622 my $self = shift;
172 10         19 my $fh = $self->{$V_FH};
173              
174             # reset variables
175 10         30 $self->{$V_READ_BUFFER} = "";
176 10         15 $self->{$V_TOTAL_BYTES} = 0;
177 10         11 $self->{$V_EOF} = 0;
178              
179             # open real FileHandle
180             # utilize multiple parameters without directly passing @_
181             # perlio complains if undef is inadvertently passed to open()
182 10         33 my $rtnval;
183              
184             # open FILEHANDLE,MODE,EXPR,LIST
185 10 50       37 if (scalar @_ >= 3) {
    100          
    100          
186 0         0 my $mode = shift;
187 0         0 my $expr = shift;
188 0         0 my $list = shift;
189              
190 0         0 $self->_parse_open_mode($mode);
191              
192 0         0 $rtnval = open($fh, $mode, $expr, $list);
193             }
194              
195             # open FILEHANDLE,MODE,EXPR
196             elsif (scalar @_ == 2) {
197 5         7 my $mode = shift;
198 5         6 my $expr = shift;
199              
200 5         13 $self->_parse_open_mode($mode);
201              
202 2         73 $rtnval = open($fh, $mode, $expr);
203             }
204              
205             # open FILEHANDLE,EXPR
206             elsif (scalar @_ == 1) {
207 4         5 my $expr = shift;
208              
209             # determine mode from expr
210 4         5 my $mode = undef;
211 4 50       9 if (defined $expr) {
212 4 100       16 if ($expr =~ /(?:^([<>\+\-\|]+)|(\|)$)/) {
213 3         14 $mode = $1;
214             }
215             }
216              
217 4         10 $self->_parse_open_mode($mode);
218              
219 1         23 $rtnval = open($fh, $expr);
220             }
221              
222             # open FILEHANDLE
223             else {
224 1         55 croak "Use of uninitialized value in open";
225             }
226              
227             # determine FileHandle flags to start encryption/decryption
228 3 100       19 if ($fh->opened()) {
229              
230             # automatically set real FileHandle to binary mode
231             # necessary for some systems
232 2         13 binmode($fh);
233              
234             # open for encrypting
235 2 100       10 if ($self->{$V_STATE} eq $STATE_ENCRYPT) {
    50          
236 1         5 $self->{$V_CIPHER}->start('encrypting');
237              
238             # an error is presented if no data is ever written
239             # so force an empty string of data to be encrypted
240 1         3 $self->_encrypt_write("");
241             }
242              
243             # open for decrypting
244             elsif ($self->{$V_STATE} eq $STATE_DECRYPT) {
245 1         3 $self->{$V_CIPHER}->start('decrypting');
246             }
247              
248             # unknown
249             else {
250             # this should never occur
251 0         0 croak "Bad state";
252             }
253             }
254              
255 3         19 return $rtnval;
256             }
257              
258             ############################################################
259              
260             # sets binary mode
261             #
262             sub BINMODE {
263 2     2   5 my $self = shift;
264              
265             # set binary mode on real FileHandle
266 2         3 my $fh = $self->{$V_FH};
267 2 50       5 return binmode($fh, @_) if (@_);
268 2         14 return binmode($fh);
269             }
270              
271             ############################################################
272              
273             # print() to FileHandle
274             #
275             sub PRINT {
276 3     3   6 my $self = shift;
277              
278             # check state
279 3 50       9 if ($self->{$V_STATE} == $STATE_CLOSED) {
280 0         0 carp "print() on closed filehandle";
281 0         0 return !1;
282             }
283 3 50       12 if ($self->{$V_STATE} == $STATE_DECRYPT) {
284 0         0 carp "Filehandle opened only for input";
285 0         0 return !1;
286             }
287              
288             # encrypt and write data to real FileHandle
289 3 50       12 if ($self->_encrypt_write(@_)) {
290 3         16 return 1;
291             }
292              
293 0         0 return !1;
294             }
295              
296             ############################################################
297              
298             # printf() to FileHandle
299             #
300             sub PRINTF {
301 1     1   3 my $self = shift;
302 1         1 my $fmt = shift;
303              
304 1         5 return $self->PRINT(sprintf($fmt, @_));
305             }
306              
307             ############################################################
308              
309             # write() to FileHandle
310             #
311             sub WRITE {
312 1     1   3 my $self = shift;
313              
314             # check state
315 1 50       4 if ($self->{$V_STATE} == $STATE_CLOSED) {
316 0         0 carp "syswrite() on closed filehandle";
317 0         0 return undef;
318             }
319 1 50       4 if ($self->{$V_STATE} == $STATE_DECRYPT) {
320 0         0 carp "Filehandle opened only for input";
321 0         0 return undef;
322             }
323              
324             # get parameters
325 1         3 my ($buf, $len, $off) = @_;
326              
327             # check parameters
328 1 50       14 if (! defined $buf) {
329 0         0 carp "Use of uninitialized value";
330 0         0 return undef;
331             }
332 1 50       4 if (! defined $len) {
333 0         0 $len = length($buf);
334             }
335 1 50       3 if ($len < 0) {
336 0         0 carp "Negative length";
337 0         0 return undef;
338             }
339 1 50       10 if (! defined $off) {
340 1         2 $off = 0;
341             }
342              
343             # truncate to length
344 1         2 $buf = substr($buf, $off, $len);
345              
346             # encrypt and write data to real FileHandle
347 1         3 return $self->_encrypt_write($buf);
348             }
349              
350             ############################################################
351              
352             # readline() from FileHandle
353             #
354             sub READLINE {
355 2     2   3 my $self = shift;
356              
357             # check state
358 2 50       13 if ($self->{$V_STATE} == $STATE_CLOSED) {
359 0         0 carp "readline() on closed filehandle";
360 0         0 return undef;
361             }
362 2 50       5 if ($self->{$V_STATE} == $STATE_ENCRYPT) {
363 0         0 carp "Filehandle opened only for output";
364 0         0 return undef;
365             }
366              
367             # EOF and buffer is empty
368 2 50 33     5 if ($self->{$V_EOF} && length($self->{$V_READ_BUFFER}) == 0) {
369 0         0 return undef;
370             }
371              
372             # utilize INPUT_RECORD_SEPARATOR ($/) to determine end of line
373 2         3 my $index = -1;
374 2 50       5 if (defined $/) {
375             # loop reading data until buffer contains $/
376 2         5 $index = index($self->{$V_READ_BUFFER}, $/);
377 2   100     8 while (($index < 0) && $self->_decrypt_read()) {
378 1         4 $index = index($self->{$V_READ_BUFFER}, $/);
379             }
380             }
381             else {
382             # special case if $/ is undef
383             # continue looping until entire file is read
384 0         0 while($self->_decrypt_read()) {
385             }
386             }
387              
388             # if index was found, include $/ in length
389             # otherwise an undef length will extract entire buffer
390             # return value (extracted length) is unused when reading lines
391 2 100       5 my $len = ($index >= 0 ? ($index + length($/)) : undef);
392 2         3 my $buf;
393 2         5 $self->_extract_read_buffer($buf, $len);
394              
395 2         8 return $buf;
396             }
397              
398             ############################################################
399              
400             # read() from FileHandle
401             #
402             sub READ {
403 2     2   4 my $self = shift;
404              
405             # check state
406 2 50       6 if ($self->{$V_STATE} == $STATE_CLOSED) {
407 0         0 carp "sysread() on closed filehandle";
408 0         0 return undef;
409             }
410 2 50       5 if ($self->{$V_STATE} == $STATE_ENCRYPT) {
411 0         0 carp "Filehandle opened only for output";
412 0         0 return undef;
413             }
414              
415             # get parameters
416             # acquire reference to provided scalar
417 2         3 my $buf = \shift;
418 2         4 my ($len, $off) = @_;
419              
420             # check parameters
421 2 50       5 if (! defined $buf) {
422 0         0 carp "Use of uninitialized value";
423 0         0 return undef;
424             }
425 2 50       3 if (! defined $len) {
426 0         0 carp "Use of uninitialized length";
427 0         0 return undef;
428             }
429 2 50       5 if ($len < 0) {
430 0         0 carp "Negative length";
431 0         0 return undef;
432             }
433 2 50       5 if (! defined $off) {
434 2         2 $off = 0;
435             }
436              
437             # read more data until buffer is large enough
438 2         5 while ($len > length($self->{$V_READ_BUFFER})) {
439 1 50       4 last if (! $self->_decrypt_read());
440             }
441              
442             # extract data from buffer
443 2         6 return $self->_extract_read_buffer($$buf, $len, $off);
444             }
445              
446             ############################################################
447              
448             # getc() from FileHandle
449             #
450             sub GETC {
451 1     1   4 my $self = shift;
452              
453 1         2 my $buf;
454 1 50       3 $self->READ($buf, 1) || return undef;
455 1         5 return $buf;
456             }
457              
458             ############################################################
459              
460             # not implemented
461             #
462             #sub SEEK { }
463              
464             ############################################################
465              
466             # returns the total number of cleartext bytes read or
467             # written, or -1 if the file is closed
468             #
469             sub TELL {
470 2     2   4 my $self = shift;
471              
472             # check state
473 2 50       6 if ($self->{$V_STATE} == $STATE_CLOSED) {
474 0         0 return -1;
475             }
476              
477             # return total number of bytes
478 2         7 return $self->{$V_TOTAL_BYTES};
479             }
480              
481             ############################################################
482              
483             # closes the real file
484             #
485             sub CLOSE {
486 2     2   4 my $self = shift;
487              
488             # finish writing leftover data prior to close
489 2         6 $self->_finish();
490              
491             # change state
492 2         3 $self->{$V_STATE} = $STATE_CLOSED;
493              
494             # close the real FileHandle
495 2         9 my $fh = $self->{$V_FH};
496 2 50       5 if (defined $fh) {
497 2         8 return $fh->close();
498             }
499              
500 0         0 return !1;
501             }
502              
503             ############################################################
504              
505             # returns FILENO for real FileHandle
506             # utilized by a call to opened() on tied FileHandle
507             #
508             sub FILENO {
509 2     2   4 my $self = shift;
510 2         15 return fileno($self->{$V_FH});
511             }
512              
513             ############################################################
514              
515             # returns true if EOF or CLOSED
516             #
517             sub EOF {
518 3     3   318 my $self = shift;
519              
520             # CLOSED
521 3 100       36 if ($self->{$V_STATE} == $STATE_CLOSED) {
522 2         56 return 1;
523             }
524              
525             # EOF if real FileHandle is EOF and the buffer is empty
526 1 50 33     16 if ($self->{$V_EOF} && length($self->{$V_READ_BUFFER}) == 0) {
527 1         5 return 1;
528             }
529              
530 0         0 return !1;
531             }
532              
533             ############################################################
534              
535             # finishes up the encryption if there is leftover data to be written
536             #
537             sub _finish {
538 2     2   2 my $self = shift;
539              
540             # check state
541             # only necessary if encrypting
542 2 50       7 if ($self->{$V_STATE} == $STATE_CLOSED) {
543 0         0 return !1;
544             }
545 2 100       5 if ($self->{$V_STATE} == $STATE_DECRYPT) {
546 1         3 return !1;
547             }
548              
549             # finish up the encryption
550 1         3 my $ciphertext = $self->{$V_CIPHER}->finish();
551              
552             # write to real FileHandle
553 1         5 my $fh = $self->{$V_FH};
554 1         6 my $bytes_written = syswrite($fh, $ciphertext, length($ciphertext));
555              
556 1         2 return 1;
557             }
558              
559             ############################################################
560              
561             # parse the mode provided to open()
562             #
563             sub _parse_open_mode {
564 9     9   13 my $self = shift;
565 9         10 my $mode = shift;
566              
567             # determine cipher mode (encrypt or decrypt)
568 9 100 100     95 if (! defined $mode || $mode =~ /^
    100 66        
    100 66        
    50          
569             # update state to reading
570 2         4 $self->{$V_STATE} = $STATE_DECRYPT;
571             }
572              
573             elsif ($mode =~ /^>(?!>)/ || $mode =~ /^\|/) {
574             # update state to writing
575 1         2 $self->{$V_STATE} = $STATE_ENCRYPT;
576             }
577              
578             elsif ($mode =~ /^\+?>>/) {
579 2         213 croak "APPEND mode not supported";
580             }
581              
582             elsif ($mode =~ /^\+[<>]/) {
583 4         252 croak "READ/WRITE mode not supported";
584             }
585              
586             else {
587 0         0 croak "Unknown mode";
588             }
589              
590 3         3 return 1;
591             }
592              
593             ############################################################
594              
595             # encrypts the provided parameters and writes to real FileHandle
596             # returns the number of cleartext bytes processed
597             #
598             sub _encrypt_write {
599 5     5   9 my $self = shift;
600              
601             # check parameters
602 5 50       9 return undef unless (@_);
603              
604             # check state
605 5 50       16 if ($self->{$V_STATE} == $STATE_CLOSED) {
606 0         0 carp "crypt() on closed FileHandle";
607 0         0 return undef;
608             }
609 5 50       9 if ($self->{$V_STATE} == $STATE_DECRYPT) {
610 0         0 carp "FileHandle opened only for input";
611 0         0 return undef;
612             }
613              
614             # append data
615 5         8 my $cleartext = "";
616 5         8 foreach (@_) {
617 5 50       13 $cleartext .= $_ if (defined $_);
618             }
619              
620             # encrypt data
621             # must call crypt() even if cleartext is empty to ensure
622             # header is written to an "empty" file
623 5         11 my $ciphertext = $self->{$V_CIPHER}->crypt($cleartext);
624              
625             # ciphertext is only defined if there is at least one full block
626             # after performing the encryption above
627 5 50       277 if (defined $ciphertext) {
628              
629             # write data to real FileHandle
630 5         8 my $fh = $self->{$V_FH};
631 5         111 my $bw = syswrite($fh, $ciphertext, length($ciphertext));
632              
633             # check bytes written for errors
634 5 50       13 if (! defined $bw) {
635 0         0 carp $!;
636 0         0 return undef;
637             }
638 5 50       12 if ($bw < 0) {
639 0         0 return undef;
640             }
641 5 50 66     37 if ($bw == 0 && length($ciphertext) > 0) {
642 0         0 return undef;
643             }
644             }
645              
646             # increment total number of cleartext bytes
647 5         7 $self->{$V_TOTAL_BYTES} += length($cleartext);
648              
649             # return the number of cleartext characters processed
650 5         35 return length($cleartext);
651             }
652              
653             ############################################################
654              
655             # performs a single read from the real FileHandle
656             # decrypts the data and adds it to the buffer
657             # should be used when more data is needed in the buffer
658             # returns true on success, or false if EOF or error
659             #
660             sub _decrypt_read {
661 3     3   5 my $self = shift;
662              
663             # end of file
664 3 100       6 if ($self->{$V_EOF}) {
665 1         3 return !1;
666             }
667              
668             # reference to buffer
669 2         2 my $rbuf = \$self->{$V_READ_BUFFER};
670              
671             # read and decrypt additional data from real Filehandle
672             # always read in blocks of READSIZE encrypted bytes
673 2         4 my $ciphertext;
674 2         15 my $br = sysread($self->{$V_FH}, $ciphertext, $READSIZE);
675              
676             # check bytes read for errors
677 2 50       6 if (! defined $br) {
678 0         0 carp $!;
679 0         0 return !1;
680             }
681 2 100       6 if ($br == 0) {
682             # end of file (real FileHandle)
683 1         3 $self->{$V_EOF} = 1;
684              
685             # finish decryption, if necessary
686             # should only run once based on loop parameters above
687 1         4 $$rbuf .= $self->{$V_CIPHER}->finish();
688             }
689             else {
690             # decrypt data and append to buffer
691             # cleartext length may be less than bytes read
692 1   50     4 $$rbuf .= $self->{$V_CIPHER}->crypt($ciphertext) || "";
693             }
694              
695 2         213 return 1;
696             }
697              
698             ############################################################
699              
700             # extracts len bytes from the stored buffer
701             # assumes buffer has already been sufficiently populated with _decrypt_read()
702             # returns number of bytes extracted or undef on error
703             #
704             sub _extract_read_buffer {
705 4     4   4 my $self = shift;
706              
707             # get parameters
708             # acquire reference to provided scalar
709 4         5 my $buf = \shift;
710 4         7 my ($len, $off) = @_;
711              
712             # check parameters
713 4 50       7 if (! defined $buf) {
714 0         0 carp "Use of uninitialized value";
715 0         0 return undef;
716             }
717 4 100       13 if (! defined $len) {
718             # return entire buffer
719 1         2 $len = length($self->{$V_READ_BUFFER});
720             }
721 4 50       7 if ($len < 0) {
722 0         0 carp "Negative length";
723 0         0 return undef;
724             }
725 4 100       16 if (! defined $off) {
726 2         2 $off = 0;
727             }
728              
729             # reference to buffer
730 4         11 my $rbuf = \$self->{$V_READ_BUFFER};
731              
732             # extract requested bytes from buffer and replace with empty string
733             # store extracted data in provided scalar at offset position
734             # if offset is 0, same as writing over provided scalar
735             # should be valid even for 0 byte requests
736             # save length of extracted data for increment below
737 4         5 my $xl;
738 4 50       8 if (! defined $$buf) {
739             # offset is ignored if provided scalar is not defined
740 4         10 $$buf = substr($$rbuf, 0, $len, "");
741 4         6 $xl = length($$buf);
742             }
743             else {
744 0         0 $xl = length(substr($$buf, $off) = substr($$rbuf, 0, $len, ""));
745             }
746              
747             # increment total number of bytes extracted
748             # may differ from requested length if _decrypt_read() not called or EOF
749 4         5 $self->{$V_TOTAL_BYTES} += $xl;
750              
751 4         9 return $xl;
752             }
753              
754             ############################################################
755              
756             1;
757              
758             __END__