File Coverage

blib/lib/CGI/EncryptForm.pm
Criterion Covered Total %
statement 100 119 84.0
branch 24 38 63.1
condition 10 25 40.0
subroutine 16 17 94.1
pod 7 7 100.0
total 157 206 76.2


line stmt bran cond sub pod time code
1             #
2             # Copyright 1999, Peter Marelas. All rights reserved.
3             #
4             # This library is free software; you can redistribute it
5             # and/or modify it under the same terms as Perl itself.
6             #
7             # Bug reports and comments to maral@phase-one.com.au.
8             #
9              
10             package CGI::EncryptForm;
11              
12 1     1   2760 use Crypt::HCE_SHA;
  1         10009  
  1         64  
13 1     1   1233 use Storable qw(freeze thaw);
  1         6845  
  1         133  
14 1     1   15 use Digest::SHA1 qw(sha1);
  1         8  
  1         56  
15              
16 1     1   6 use strict;
  1         2  
  1         52  
17 1     1   13 use vars qw($VERSION $CHARSET);
  1         2  
  1         2864  
18              
19             $VERSION = 1.02;
20              
21             # Default character set we use for encoding/decoding encrypted string
22             #
23             $CHARSET = [qw/AA Aa BA Ba CA Ca DA Da EA Ea AB Ab BB Bb CB Cb DB Db EB Eb AC Ac BC Bc CC Cc DC Dc EC Ec AD Ad BD Bd CD Cd DD Dd ED Ed AE Ae BE Be CE Ce DE De EE Ee AF Af BF Bf CF Cf DF Df EF Ef AG Ag BG Bg CG Cg DG Dg EG Eg AH Ah BH Bh CH Ch DH Dh EH Eh AI Ai BI Bi CI Ci DI Di EI Ei AJ Aj BJ Bj CJ Cj DJ Dj EJ Ej AK Ak BK Bk CK Ck DK Dk EK Ek AL Al BL Bl CL Cl DL Dl EL El AM Am BM Bm CM Cm DM Dm EM Em AN An BN Bn CN Cn DN Dn EN En AO Ao BO Bo CO Co DO Do EO Eo AP Ap BP Bp CP Cp DP Dp EP Ep AQ Aq BQ Bq CQ Cq DQ Dq EQ Eq AR Ar BR Br CR Cr DR Dr ER Er AS As BS Bs CS Cs DS Ds ES Es AT At BT Bt CT Ct DT Dt ET Et AU Au BU Bu CU Cu DU Du EU Eu AV Av BV Bv CV Cv DV Dv EV Ev AW Aw BW Bw CW Cw DW Dw EW Ew AX Ax BX Bx CX Cx DX Dx EX Ex AY Ay BY By CY Cy DY Dy EY Ey yY wW vV uU zU zZ/];
24              
25             sub new {
26 1     1 1 186 my($this) = shift;
27 1   33     8 my $class = ref($this) || $this;
28 1         3 my $self = {};
29 1         4 bless $self, $class;
30 1         5 $self->_initialize(@_);
31 1         3 return($self);
32             }
33              
34 0     0   0 sub DESTROY {
35             }
36              
37             # Public Method:
38             #
39             # encrypt()
40             #
41             # Purpose:
42             #
43             # Encrypt the hash reference
44             #
45             # Constructs:
46             #
47             # encrypt()
48             # Return last encrypted string
49             #
50             # encrypt({a => b})
51             # Encrypt hash reference and return encrypted string
52             #
53             sub encrypt {
54 3     3 1 109 my($self, $decrypted_hashref) = @_;
55              
56 3   50     7 my $secret_key = $self->secret_key() || return undef;
57              
58 3 50 33     16 if (!defined($decrypted_hashref) && !defined($self->{_encrypted_string})) {
    50          
59 0         0 $self->error('encrypt() This is the first time encrypt() has been called and therefore requires an arguement.');
60 0         0 return undef;
61             }
62             elsif (!defined($decrypted_hashref)) {
63 0         0 return($self->{_encrypted_string});
64             }
65              
66 3 50       9 if (ref($decrypted_hashref) ne 'HASH') {
67 0         0 $self->error('encrypt() This method accepts a single hash reference only.');
68 0         0 return undef;
69             }
70              
71 3         9 my $random_key = $self->_random_key();
72              
73 3         12 my $str = freeze($decrypted_hashref);
74 3         299 $str = sha1($str) . $str;
75              
76 3         18 my $cipher = Crypt::HCE_SHA->new($secret_key, $random_key);
77 3 50       95 $self->{_encrypted_string} = $self->usecharset() ?
78             $self->_encode($random_key . $cipher->hce_block_encrypt($str)) : $random_key . $cipher->hce_block_encrypt($str);
79 3         10 $self->error('');
80 3         21 return($self->{_encrypted_string});
81             }
82              
83              
84             # Public Method:
85             #
86             # decrypt()
87             #
88             # Purpose:
89             #
90             # Decryption routine
91             #
92             # Constructs:
93             #
94             # decrypt()
95             # Return last decrypted reference to hash
96             #
97             # decrypt("encrypted string")
98             # Decrypt encrypted string and return reference to hash
99             #
100             sub decrypt {
101 6     6 1 194 my($self, $encrypted_string) = @_;
102              
103 6   50     11 my $secret_key = $self->secret_key() || return undef;
104 6         8 my $random_key;
105              
106 6 50 33     24 if (!defined($encrypted_string) && !defined($self->{_decrypted_hashref})) {
    50          
107 0         0 $self->error('decrypt() This is the first time decrypt() has been called and therefore requires an arguement.');
108 0         0 return undef;
109             }
110             elsif (!defined($encrypted_string)) {
111 0         0 return($self->{_decrypted_hashref});
112             }
113              
114             # if using char set ensure string is even number
115 6 100 66     14 if ($self->usecharset() && (length($encrypted_string) % 2) != 0) {
116 1         3 $self->error('decrypt() Character set is inconsistent.');
117 1         2 return undef;
118             }
119              
120             # decode the encrypted string
121 5 50       11 my $str = $self->usecharset() ? $self->_decode($encrypted_string) :
122             $encrypted_string;
123              
124             # extract the random key (first 4 bytes)
125 5         12 $random_key = substr($str, 0, 4);
126 5 100       16 if (length($random_key) != 4) {
127 1         3 $self->error('decrypt() Random key is inconsistent.');
128 1         3 return undef;
129             }
130 4         6 $str = substr($str, 4);
131              
132             # decrypt
133 4         17 my $cipher = Crypt::HCE_SHA->new($secret_key, $random_key);
134 4         52 my $plaintxt = $cipher->hce_block_decrypt($str);
135              
136             # extract sha1 digest from decrypted string which is always
137             # 20 bytes long
138 4         1148 my $digest = substr($plaintxt, 0, 20);
139 4 100       12 if (length($digest) != 20) {
140 1         3 $self->error('decrypt() Digest is inconsistent.');
141 1         4 return undef;
142             }
143 3         7 $plaintxt = substr($plaintxt, 20);
144              
145             # check stored decrypted digest against digest of decrypted string
146 3 100       17 if ($digest ne sha1($plaintxt)) {
147 1         4 $self->error('decrypt() Encrypted string is inconsistent.');
148 1         5 return undef;
149             }
150              
151 2         7 $self->error('');
152 2         8 return(thaw($plaintxt));
153             }
154              
155             # Public Method:
156             #
157             # secret_key()
158             #
159             # Purpose:
160             #
161             # Set/Return secret key
162             #
163             # Constructs:
164             #
165             # secret_key()
166             # Return current secret_key
167             #
168             # secret_key("secret key")
169             # Set secret key
170             #
171             sub secret_key {
172 12     12 1 157 my($self, $secret_key) = @_;
173              
174 12 100       37 if (defined($secret_key)) {
    50          
175 3         7 $self->{_secret_key} = $secret_key;
176             }
177             elsif (!defined($self->{_secret_key})) {
178 0         0 $self->error('secret_key() No secret key has been defined.');
179 0         0 return undef;
180             }
181              
182 12         31 $self->error('');
183 12         35 return($self->{_secret_key});
184             }
185              
186             # Public Method
187             #
188             # charset()
189             #
190             # Purpose:
191             #
192             # Set character set
193             #
194             # Constructors:
195             #
196             # charset([array of 2 character length elements from 0 to 255])
197             # Set character set
198             #
199             sub charset {
200 2     2 1 296 my($self, $charset) = @_;
201              
202 2 50 33     34 if ((defined($charset) && ref($charset) ne 'ARRAY') || !defined($charset)) {
    50 33        
      33        
203 0         0 $self->error('charset() This methods accepts a single array reference.');
204 0         0 return undef;
205             }
206             elsif (defined($charset) && $#$charset != 255) {
207 0         0 $self->error('charset() The character set is invalid.');
208 0         0 return undef;
209             }
210              
211 2         5 $self->{'_charset'} = $charset;
212 512         1954 $self->{'_charset_hash'} = {
213 2         5 map { $self->{'_charset'}->[$_] => $_; } 0..$#{$self->{'_charset'}}
  2         51  
214             };
215              
216 2         110 $self->error('');
217             }
218              
219             # Public Method:
220             #
221             # usecharset()
222             #
223             # Purpose:
224             #
225             # Enable/Return character set encoding/decoding of encrypted/decrypted string
226             # suitable for storage in form fields, cookies or URL's
227             #
228             # Constructs:
229             #
230             # usecharset()
231             # Return current usecharset value
232             #
233             # usecharset(0 or 1)
234             # Set charset encoding/decoding
235             #
236             sub usecharset {
237 14     14 1 18 my($self, $usecharset) = @_;
238              
239 14         23 $self->error('');
240 14 50       26 $self->{_usecharset} = $usecharset if defined($usecharset);
241              
242 14 50       77 return($self->{_usecharset} ? 1 : 0);
243             }
244              
245             # Public/Private Method:
246             #
247             # error()
248             #
249             # Purpose:
250             #
251             # Set/Clear/Get error message for last operation on object
252             #
253             # Public Constructors:
254             #
255             # error()
256             # Return error from last operation
257             #
258             sub error {
259 40     40 1 55 my($self, $errormsg) = @_;
260              
261 40 100       69 if ($errormsg) {
262 4         14 $self->{_errormsg} = "Error: $errormsg\n";
263             }
264             else {
265 36         69 return($self->{_errormsg});
266             }
267             }
268              
269             #
270             # Private methods - dont call these from your object
271             #
272             sub _initialize {
273 1     1   4 my($self, %opts) = @_;
274              
275 1         118 $self->{'_usecharset'} = 1;
276 1         4 $self->{'_encrypted_string'} = undef;
277 1         2 $self->{'_decrypted_hashref'} = undef;
278 1         3 $self->{'_secret_key'} = undef;
279 1         3 $self->{'_random_key'} = undef;
280 1         3 $self->{'_errormsg'} = '';
281 1         3 $self->{'_charset'} = undef;
282 1         3 $self->{'_charset_hash'} = undef;
283              
284             # Set default charset
285 1         5 $self->charset($CHARSET);
286              
287 1         6 foreach (keys(%opts)) {
288 0 0       0 if (! $self->can($_)) {
289 0         0 $self->error("_initialize() $_ is not a valid option.");
290 0         0 return undef;
291             }
292 0         0 $self->$_($opts{$_});
293             }
294             }
295              
296             #
297             # Autogenerate the current object's random key every time. i.e. Not Persistent!
298             #
299             sub _random_key {
300 3     3   5 my($self) = @_;
301              
302 3         79 $self->{_random_key} = pack("CCCC", rand(255), rand(255), rand(255),
303             rand(255));
304              
305 3         6 $self->error('');
306 3         9 return($self->{_random_key});
307             }
308              
309             # Decode encrypted string using character set
310             #
311             sub _decode {
312 5     5   6 my($self, $decode) = @_;
313              
314 5         22 $decode =~ s/(.{2})/chr($self->{'_charset_hash'}->{$1})/sge;
  274         990  
315 5         73 return($decode);
316             }
317              
318             # Encode encrypted string using character set
319             #
320             sub _encode {
321 3     3   1108 my($self, $encode) = @_;
322              
323 3         19 $encode =~ s/(.)/$self->{'_charset'}->[ord($1)]/sge;
  229         667  
324 3         36 return($encode);
325             }
326              
327             1;
328             __END__