File Coverage

blib/lib/Crypt/Tea.pm
Criterion Covered Total %
statement 202 216 93.5
branch 36 56 64.2
condition 7 9 77.7
subroutine 20 23 86.9
pod 6 17 35.2
total 271 321 84.4


line stmt bran cond sub pod time code
1             # Tea.pm
2             #########################################################################
3             # This Perl module is Copyright (c) 2000, Peter J Billam #
4             # c/o P J B Computing, www.pjb.com.au #
5             # #
6             # This module is free software; you can redistribute it and/or #
7             # modify it under the same terms as Perl itself. #
8             #########################################################################
9             #
10             # implements TEA, the Tiny Encryption Algorithm, in Perl and Javascript.
11             # http://www.cl.cam.ac.uk/ftp/papers/djw-rmn/djw-rmn-tea.html
12             #
13             # Usage:
14             # use Tea;
15             # $key = 'PUFgob$*LKDF D)(F IDD&P?/';
16             # $ascii_cyphertext = &encrypt ($plaintext, $key);
17             # ...
18             # $plaintext_again = &decrypt ($ascii_cyphertext, $key);
19             # ...
20             # $signature = &asciidigest ($text);
21             #
22             # The $key is a sufficiently longish string; at least 17 random 8-bit bytes
23             #
24             # Written by Peter J Billam, http://www.pjb.com.au
25              
26             package Crypt::Tea;
27             $VERSION = '2.12';
28              
29             # Don't like depending on externals; this is strong encrytion ... but ...
30 1     1   14621 use Exporter; @ISA = qw(Exporter);
  1         2  
  1         334  
31             @EXPORT=qw(asciidigest encrypt decrypt tea_in_javascript);
32             @EXPORT_OK = qw(str2ascii ascii2str encrypt_and_write);
33             %EXPORT_TAGS = (ALL => [@EXPORT,@EXPORT_OK]);
34              
35             # begin config
36             my %a2b = (
37             A=>000, B=>001, C=>002, D=>003, E=>004, F=>005, G=>006, H=>007,
38             I=>010, J=>011, K=>012, L=>013, M=>014, N=>015, O=>016, P=>017,
39             Q=>020, R=>021, S=>022, T=>023, U=>024, V=>025, W=>026, X=>027,
40             Y=>030, Z=>031, a=>032, b=>033, c=>034, d=>035, e=>036, f=>037,
41             g=>040, h=>041, i=>042, j=>043, k=>044, l=>045, m=>046, n=>047,
42             o=>050, p=>051, q=>052, r=>053, s=>054, t=>055, u=>056, v=>057,
43             w=>060, x=>061, y=>062, z=>063, '0'=>064, '1'=>065, '2'=>066, '3'=>067,
44             '4'=>070,'5'=>071,'6'=>072,'7'=>073,'8'=>074,'9'=>075,'-'=>076,'_'=>077,
45             );
46             my %b2a = reverse %a2b;
47             $a2b{'+'}=076;
48             # end config
49              
50             # ------------------ infrastructure ...
51              
52             sub tea_in_javascript {
53 1 100   1 1 6 my @js; while () { last if /^EOT$/; push @js, $_; } join '', @js;
  1         5  
  342         505  
  341         704  
  1         134  
54             }
55 0     0 0 0 sub encrypt_and_write { my ($str, $key) = @_;
56 0 0       0 return unless $str; return unless $key;
  0 0       0  
57 0         0 print
58             "\n";
61             }
62             sub binary2ascii {
63 5     5 1 18 return &str2ascii(&binary2str(@_));
64             }
65             sub ascii2binary {
66 1     1 1 728 return &str2binary(&ascii2str($_[$[]));
  1     1   383  
  1         953  
  1         6  
67             }
68 15     15 0 820 sub str2binary { my @str = split //, $_[$[];
69 15         106 my @intarray = (); my $ii = $[;
  15         34  
70 15         18 while (1) {
71 851 100       1364 last unless @str; $intarray[$ii] = (0xFF & ord shift @str)<<24;
  836         958  
72 836 50       1481 last unless @str; $intarray[$ii] |= (0xFF & ord shift @str)<<16;
  836         887  
73 836 50       1290 last unless @str; $intarray[$ii] |= (0xFF & ord shift @str)<<8;
  836         869  
74 836 50       1344 last unless @str; $intarray[$ii] |= 0xFF & ord shift @str;
  836         878  
75 836         760 $ii++;
76             }
77 15         143 return @intarray;
78             }
79             sub binary2str {
80 13     13 0 23 my @str = ();
81 13         27 foreach $i (@_) {
82 1124         2269 push @str, chr (0xFF & ($i>>24)), chr (0xFF & ($i>>16)),
83             chr (0xFF & ($i>>8)), chr (0xFF & $i);
84             }
85 13         395 return join '', @str;
86             }
87 3     3 0 10 sub ascii2str { my $a = $_[$[]; # converts pseudo-base64 to string of bytes
88             # no warnings;
89 3         13 local $^W = 0;
90 3         14 $a =~ tr#-A-Za-z0-9+_##cd;
91 3         9 my $ia = $[-1; my $la = length $a; # BUG not length, final!
  3         4  
92 3         7 my $ib = $[; my @b = ();
  3         6  
93 3         2 my $carry;
94 3         4 while (1) { # reads 4 ascii chars and produces 3 bytes
95 552 100       452 $ia++; last if ($ia>=$la);
  552         874  
96 550         1178 $b[$ib] = $a2b{substr $a, $ia+$[, 1}<<2;
97 550 100       490 $ia++; last if ($ia>=$la);
  550         863  
98 549         1083 $carry=$a2b{substr $a, $ia+$[, 1}; $b[$ib] |= ($carry>>4); $ib++;
  549         569  
  549         446  
99             # if low 4 bits of $carry are 0 and its the last char, then break
100 549 50 66     484 $carry = 0xF & $carry; last if ($carry == 0 && $ia == ($la-1));
  549         1124  
101 549         619 $b[$ib] = $carry<<4;
102 549 50       414 $ia++; last if ($ia>=$la);
  549         852  
103 549         1095 $carry=$a2b{substr $a, $ia+$[, 1}; $b[$ib] |= ($carry>>2); $ib++;
  549         571  
  549         465  
104             # if low 2 bits of $carry are 0 and its the last char, then break
105 549 50 66     491 $carry = 03 & $carry; last if ($carry == 0 && $ia == ($la-1));
  549         1292  
106 549         560 $b[$ib] = $carry<<6;
107 549 50       459 $ia++; last if ($ia>=$la);
  549         837  
108 549         1135 $b[$ib] |= $a2b{substr $a, $ia+$[, 1}; $ib++;
  549         496  
109             }
110 3         66 return pack 'C*', @b;
111             }
112 8     8 0 32 sub str2ascii { my $b = $_[$[]; # converts string of bytes to pseudo-base64
113 8         16 my $ib = $[; my $lb = length $b; my @s = ();
  8         10  
  8         10  
114 8         9 my $b1; my $b2; my $b3;
  0         0  
115 0         0 my $carry;
116 8         6 while (1) { # reads 3 bytes and produces 4 ascii chars
117 497 100       792 if ($ib >= $lb) { last; };
  3         5  
118 494         890 $b1 = ord substr $b, $ib+$[, 1; $ib++;
  494         954  
119 494         639 push @s, $b2a{$b1>>2}; $carry = 03 & $b1;
  494         461  
120 494 100       829 if ($ib >= $lb) { push @s, $b2a{$carry<<4}; last; }
  5         6  
  5         8  
121 489         821 $b2 = ord substr $b, $ib+$[, 1; $ib++;
  489         426  
122 489         681 push @s, $b2a{($b2>>4) | ($carry<<4)}; $carry = 0xF & $b2;
  489         447  
123 489 50       770 if ($ib >= $lb) { push @s, $b2a{$carry<<2}; last; }
  0         0  
  0         0  
124 489         873 $b3 = ord substr $b, $ib+$[, 1; $ib++;
  489         420  
125 489         834 push @s, $b2a{($b3>>6) | ($carry<<2)}, $b2a{077 & $b3};
126 489 100 100     1601 if (!$ENV{REMOTE_ADDR} && (($ib % 36) == 0)) { push @s, "\n"; }
  22         26  
127             }
128 8         208 return join ('', @s);
129             }
130             sub asciidigest { # returns 22-char ascii signature
131 4     4 1 552 return &binary2ascii(&binarydigest($_[$[]));
132             }
133 9     9 0 25 sub binarydigest { my $str = $_[$[]; # returns 4 32-bit-int binary signature
134             # warning: mode of use invented by Peter Billam 1998, needs checking !
135 9 50       22 return '' unless $str;
136             # add 1 char ('0'..'15') at front to specify no of pad chars at end ...
137 9         19 my $npads = 15 - ((length $str) % 16);
138 9         19 $str = chr($npads) . $str;
139 9 50       17 if ($npads) { $str .= "\0" x $npads; }
  9         18  
140 9         15 my @str = &str2binary($str);
141 9         14 my @key = (0x61626364, 0x62636465, 0x63646566, 0x64656667);
142              
143 9         8 my ($cswap, $v0, $v1, $v2, $v3);
144 9         10 my $c0 = 0x61626364; my $c1 = 0x62636465; # CBC Initial Value. Retain !
  9         10  
145 9         9 my $c2 = 0x61626364; my $c3 = 0x62636465; # likewise (abcdbcde).
  9         9  
146 9         15 while (@str) {
147             # shift 2 blocks off front of str ...
148 19         19 $v0 = shift @str; $v1 = shift @str; $v2 = shift @str; $v3 = shift @str;
  19         14  
  19         16  
  19         16  
149             # cipher them XOR'd with previous stage ...
150 19         37 ($c0,$c1) = &tea_code ($v0^$c0, $v1^$c1, @key);
151 19         36 ($c2,$c3) = &tea_code ($v2^$c2, $v3^$c3, @key);
152             # mix up the two cipher blocks with a 4-byte left rotation ...
153 19         21 $cswap = $c0; $c0=$c1; $c1=$c2; $c2=$c3; $c3=$cswap;
  19         16  
  19         17  
  19         13  
  19         36  
154             }
155 9         29 return ($c0,$c1,$c2,$c3);
156             }
157 3     3 1 195 sub encrypt { my ($str,$key)=@_; # encodes with CBC (Cypher Block Chaining)
158 1     1   733 use integer;
  1         13  
  1         4  
159 3 50       8 return '' unless $str; return '' unless $key;
  3 50       6  
160 3         6 @key = &binarydigest($key);
161              
162             # add 1 char ('0'..'7') at front to specify no of pad chars at end ...
163 3         7 my $npads = 7 - ((length $str) % 8);
164 3         6 $str = chr($npads|(0xF8 & &rand_byte)) . $str;
165 3 50       7 if ($npads) {
166 3         9 my $padding = pack 'CCCCCCC', &rand_byte, &rand_byte,
167             &rand_byte, &rand_byte, &rand_byte, &rand_byte, &rand_byte;
168 3         11 $str = $str . substr($padding,$[,$npads);
169             }
170 3         6 my @pblocks = &str2binary($str);
171 3         10 my $v0; my $v1;
172 3         6 my $c0 = 0x61626364; my $c1 = 0x62636465; # CBC Initial Value. Retain !
  3         3  
173 3         3 my @cblocks;
174 3         4 while (1) {
175 177 100       303 last unless @pblocks; $v0 = shift @pblocks; $v1 = shift @pblocks;
  174         172  
  174         169  
176 174         296 ($c0,$c1) = &tea_code ($v0^$c0, $v1^$c1, @key);
177 174         229 push @cblocks, $c0, $c1;
178             }
179 3         14 my $btmp = &binary2str(@cblocks);
180 3         14 return &str2ascii( &binary2str(@cblocks) );
181             }
182 2     2 1 361 sub decrypt { my ($acstr, $key) = @_; # decodes with CBC
183 1     1   224 use integer;
  1         1  
  1         3  
184 2 50       7 return '' unless $acstr; return '' unless $key;
  2 50       8  
185 2         17 @key = &binarydigest($key);
186 2         5 my $v0; my $v1; my $c0; my $c1; my @pblocks = (); my $de0; my $de1;
  0         0  
  0         0  
  2         3  
  2         2  
187 2         2 my $lastc0 = 0x61626364; my $lastc1 = 0x62636465; # CBC Init Val. Retain!
  2         3  
188 2         4 my @cblocks = &str2binary( &ascii2str($acstr) );
189 2         11 while (1) {
190 206 100       321 last unless @cblocks; $c0 = shift @cblocks; $c1 = shift @cblocks;
  204         285  
  204         194  
191 204         313 ($de0, $de1) = &tea_decode ($c0,$c1, @key);
192 204         218 $v0 = $lastc0 ^ $de0; $v1 = $lastc1 ^ $de1;
  204         201  
193 204         204 push @pblocks, $v0, $v1;
194 204         180 $lastc0 = $c0; $lastc1 = $c1;
  204         196  
195             }
196 2         14 my $str = &binary2str( @pblocks );
197             # remove no of pad chars at end specified by 1 char ('0'..'7') at front
198 2         5 my $npads = 0x7 & ord $str; substr ($str, $[, 1) = '';
  2         10  
199 2 50       6 if ($npads) { substr ($str, 0 - $npads) = ''; }
  2         4  
200 2         19 return $str;
201             }
202 0     0 0 0 sub triple_encrypt { my ($plaintext, $long_key) = @_; # not yet ...
203             }
204 0     0 0 0 sub triple_decrypt { my ($cyphertext, $long_key) = @_; # not yet ...
205             }
206 213     213 0 493 sub tea_code { my ($v0,$v1, $k0,$k1,$k2,$k3) = @_;
207             # TEA. 64-bit cleartext block in $v0,$v1. 128-bit key in $k0..$k3.
208             # &prn("tea_code: v0=$v0 v1=$v1");
209 1     1   278 use integer;
  1         3  
  1         3  
210 213         182 my $sum = 0; my $n = 32;
  213         208  
211 213         319 while ($n-- > 0) {
212 6816         5376 $sum += 0x9e3779b9; # TEA magic number delta
213 6816         7302 $v0 += (($v1<<4)+$k0) ^ ($v1+$sum) ^ ((0x07FFFFFF & ($v1>>5))+$k1) ;
214 6816         5578 $v0 &= 0xFFFFFFFF;
215 6816         7372 $v1 += (($v0<<4)+$k2) ^ ($v0+$sum) ^ ((0x07FFFFFF & ($v0>>5))+$k3) ;
216 6816         9829 $v1 &= 0xFFFFFFFF;
217             }
218 213         341 return ($v0, $v1);
219             }
220 205     205 0 464 sub tea_decode { my ($v0,$v1, $k0,$k1,$k2,$k3) = @_;
221             # TEA. 64-bit cyphertext block in $v0,$v1. 128-bit key in $k0..$k3.
222 1     1   141 use integer;
  1         1  
  1         4  
223 205         185 my $sum = 0; my $n = 32;
  205         176  
224 205         170 $sum = 0x9e3779b9 << 5 ; # TEA magic number delta
225 205         338 while ($n-- > 0) {
226 6560         7060 $v1 -= (($v0<<4)+$k2) ^ ($v0+$sum) ^ ((0x07FFFFFF & ($v0>>5))+$k3) ;
227 6560         5238 $v1 &= 0xFFFFFFFF;
228 6560         7039 $v0 -= (($v1<<4)+$k0) ^ ($v1+$sum) ^ ((0x07FFFFFF & ($v1>>5))+$k1) ;
229 6560         5007 $v0 &= 0xFFFFFFFF;
230 6560         9940 $sum -= 0x9e3779b9 ;
231             }
232 205         313 return ($v0, $v1);
233             }
234             sub rand_byte {
235 24 100   24 0 38 if (! $rand_byte_already_called) {
236 1         5 srand(time() ^ ($$+($$<<15))); # could do better, but its only padding
237 1         2 $rand_byte_already_called = 1;
238             }
239 24         53 int(rand 256);
240             }
241             1;
242              
243             __DATA__