File Coverage

blib/lib/Secret/Simple.pm
Criterion Covered Total %
statement 98 123 79.6
branch 25 64 39.0
condition 2 15 13.3
subroutine 18 20 90.0
pod 12 12 100.0
total 155 234 66.2


line stmt bran cond sub pod time code
1             package Secret::Simple;
2              
3 3     3   120492 use strict;
  3         8  
  3         122  
4 3     3   16 use warnings;
  3         9  
  3         118  
5 3     3   17 use vars qw( @ISA @EXPORT );
  3         9  
  3         196  
6              
7 3     3   17 use Carp;
  3         5  
  3         286  
8 3     3   3753 use Crypt::CBC;
  3         25554  
  3         129  
9 3     3   37 use Exporter;
  3         8  
  3         160  
10 3     3   3958 use MIME::Base64;
  3         4335  
  3         6482  
11              
12             our $VERSION = '0.11';
13              
14             @ISA = qw( Exporter );
15             @EXPORT = qw( ssdecrypt ssdecryptraw ssencrypt ssencryptraw );
16              
17             my $DEFAULT_CIPHER = 'Rijndael_PP';
18             my $DEFAULT_KEYFILE = '~/.ssh/id_dsa';
19             my $DEFAULT_GARBAGE = 'eLH6eDl7H+Ng07Zj';
20              
21             sub new {
22 3     3 1 24 my ($class, @args) = @_;
23 3         6 my $self = {};
24              
25 3         7 my (%args, %option);
26 3 50       13 @args = ( 'key', '{sskeyfile}' ) unless @args;
27 3 50       15 if (ref($args[0]) eq 'HASH') {
28 0         0 %option = %{$args[0]};
  0         0  
29             } else {
30 3 100       13 @args = ( 'key', $args[0] ) if @args == 1;
31 3 50       14 croak "Invalid arguments passed" if scalar(@args) & 1;
32 3         11 %args = @args;
33             }
34 3         10 my %tmp = map { $_ => 1 } qw( key keyfilesize );
  6         21  
35 3         12 for my $opt (keys %args) {
36 3         6 my $opt2 = $opt;
37 3         12 $opt2 =~ s/^-//;
38 3 50       9 croak "Unrecognized -$opt2 option passed" unless $tmp{$opt2};
39 3         10 $option{$opt2} = $args{$opt};
40             }
41 3 50       12 $option{key} = '{sskeyfile}' unless $option{key};
42 3         13 key($self, $option{key});
43 3         6 $self->{keyfilesize} = 0;
44 3 50       9 keyfilesize($self, $option{keyfilesize}) if $option{keyfilesize};
45 3         34 $self->{keydata} = keydata($self);
46              
47 3         8 bless($self, $class);
48 3         616 return $self;
49             }
50              
51             sub decrypt {
52 1     1 1 9 my ($self, $b64ciphertext) = @_;
53 1 50       5 return unless $b64ciphertext;
54 1         9 my $ciphertext = decode_base64($b64ciphertext);
55 1         5 my $plaintext = decryptraw($self, $ciphertext);
56 1         9 return $plaintext;
57             }
58              
59             sub decryptraw {
60 2     2 1 8 my ($self, $ciphertext) = @_;
61 2 50       9 return unless $ciphertext;
62 2         30 my $cipher = Crypt::CBC->new(
63             -key => $self->{keydata},
64             -cipher => $DEFAULT_CIPHER,
65             -header => 'none',
66             -iv => $DEFAULT_GARBAGE
67             );
68 2         337 my $plaintext = $cipher->decrypt($ciphertext);
69 2         248775 return $plaintext;
70             }
71              
72             sub encrypt {
73 1     1 1 8 my ($self, $plaintext) = @_;
74 1         4 my $ciphertext = encryptraw($self, $plaintext);
75 1 50       8 return unless $ciphertext;
76 1         23 return encode_base64( $ciphertext );
77             }
78              
79             sub encryptraw {
80 2     2 1 3 my ($self, $plaintext) = @_;
81 2 50       7 return unless $plaintext;
82 2         36 my $cipher = Crypt::CBC->new(
83             -key => $self->{keydata},
84             -cipher => $DEFAULT_CIPHER,
85             -header => 'none',
86             -iv => $DEFAULT_GARBAGE
87             );
88 2         62713 my $ciphertext = $cipher->encrypt($plaintext);
89 2         233627 return $ciphertext;
90             }
91              
92             sub key {
93 3     3 1 6 my ($self, $key) = @_;
94 3 50       10 if (defined $key) {
95 3 50 66     21 croak "Bad key specification"
96             if ref($key) && ref($key) ne 'ARRAY';
97 3         7 $self->{key} = $key;
98             }
99 3         7 return $self->{key};
100             }
101              
102             sub keydata {
103 3     3 1 579 my ($self) = @_;
104              
105 3 50       13 unless (defined $self->{keydata}) {
106             # calculate aggregate key data
107 2         5 my @keys = ref($self->{key}) eq 'ARRAY' ?
108 3 100       12 @{$self->{key}} : ( $self->{key} );
109 3         6 my $data = "";
110 3         7 for my $frag (@keys) {
111 3         31 my $piece = $frag;
112 3 50       10 if ($frag =~ /^\{sskeyfile\}/) {
113 0         0 my $fn = $frag;
114 0         0 $fn =~ s/^\{sskeyfile\}//;
115 0 0       0 $fn = $DEFAULT_KEYFILE unless $fn;
116 0         0 my ($fn1) = glob($fn);
117 0 0       0 croak "No access to specified key file '$fn'"
118             unless -r $fn1;
119 0         0 $piece = _read_rawfile($fn1, $self->{keyfilesize});
120             }
121 3         9 $data .= $piece;
122             }
123 3         9 $self->{keydata} = $data;
124             }
125              
126 3         9 return $self->{keydata};
127             }
128              
129             sub keyfilesize {
130 0     0 1 0 my ($self, $num) = @_;
131 0 0 0     0 croak "Bad limit passed" if defined $num && $num !~ /^\d+$/;
132 0 0       0 $self->{keyfilesize} = $num if defined $num;
133 0         0 return $self->{keyfilesize};
134             }
135              
136             # The procedural style function section begins here.
137              
138             sub ssdecrypt {
139 1     1 1 6 my ($b64ciphertext, @keyspec) = @_;
140 1 50       5 return unless $b64ciphertext;
141 1         7 my $ciphertext = decode_base64($b64ciphertext);
142 1         5 my $plaintext = ssdecryptraw($ciphertext, @keyspec);
143 1         35 return $plaintext;
144             }
145              
146             sub ssdecryptraw {
147 1     1 1 3 my ($ciphertext, @keyspec) = @_;
148 1 50       3 return unless $ciphertext;
149 1 50       12 my $ss = @keyspec ?
150             Secret::Simple->new( key => [ @keyspec ] ) :
151             Secret::Simple->new();
152 1         7 my $plaintext = $ss->decryptraw($ciphertext);
153 1         11 return $plaintext;
154             }
155              
156             sub ssencrypt {
157 1     1 1 10 my ($plaintext, @keyspec) = @_;
158 1 50       5 return unless $plaintext;
159 1         4 my $ciphertext = ssencryptraw($plaintext, @keyspec);
160 1 50       6 return unless $ciphertext;
161 1         26 return encode_base64( $ciphertext );
162             }
163              
164             sub ssencryptraw {
165 1     1 1 3 my ($plaintext, @keyspec) = @_;
166 1 50       2 return unless $plaintext;
167 1 50       10 my $ss = @keyspec ?
168             Secret::Simple->new( key => [ @keyspec ] ) :
169             Secret::Simple->new();
170 1         4 my $ciphertext = $ss->encryptraw($plaintext);
171 1         15 return $ciphertext;
172             }
173              
174             # The private module function section begins here.
175              
176             # The _read_rawfile private function accepts a filename and an optional
177             # limit argument. The entire contents of a specified file will be read
178             # and returned as a string if the limit is undefined or zero, but a
179             # maximum of $limit bytes will be read in and returned otherwise.
180              
181             sub _read_rawfile {
182 0     0     my ($fn, $limit) = @_;
183 0 0         croak "No filename argument passed" unless $fn;
184 0 0 0       croak "Bad limit passed" if $limit && $limit !~ /^\d+$/;
185 0           my ($chunk, $num, $data, $buf) = ( 8192, 0, "" );
186 0 0         croak "Unable to read from file" unless
187             open my ($F), $fn;
188 0           binmode($F);
189 0           until ( eof($F) ) {
190 0 0 0       $chunk = $limit - $num if $limit && $num + $chunk > $limit;
191 0           $num += read($F, $buf, $chunk);
192 0           $data .= $buf;
193 0 0 0       last if $limit && $num >= $limit;
194             }
195 0           close $F;
196 0           return $data;
197             }
198              
199             1;
200             __END__