File Coverage

blib/lib/Crypt/Tea_JS.pm
Criterion Covered Total %
statement 236 254 92.9
branch 43 64 67.1
condition 11 15 73.3
subroutine 25 28 89.2
pod 6 19 31.5
total 321 380 84.4


line stmt bran cond sub pod time code
1             # Tea_JS.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_JS;
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_JS;
27             $VERSION = '2.23';
28             # Don't like depending on externals; this is strong encrytion ... but ...
29             require Exporter;
30             @ISA = qw(Exporter);
31              
32             eval { require XSLoader; XSLoader::load('Crypt::Tea_JS', $VERSION); };
33             if ($@) { # 2.23 revert to PurePerl
34             *tea_code = \&pp_tea_code;
35             *tea_decode = \&pp_tea_decode;
36             *oldtea_code = \&pp_oldtea_code;
37             *oldtea_decode = \&pp_oldtea_decode;
38             }
39              
40             @EXPORT = qw(asciidigest encrypt decrypt tea_in_javascript);
41             @EXPORT_OK = qw(str2ascii ascii2str encrypt_and_write);
42             %EXPORT_TAGS = (ALL => [@EXPORT,@EXPORT_OK]);
43              
44             BEGIN {
45 1 50   1   15820 if ($] < 5.006) {
46 0         0 $INC{"bytes.pm"} = 1; # cheating that bytes.pm is loaded
47 0         0 *bytes::import = sub { }; # do nothing
  0         0  
48 0         0 *bytes::unimport = sub { };
  0         0  
49             }
50 1 50       4 if ($] > 5.007) { require Encode; }
  1         810  
51             }
52             if (! defined &tea_code) {
53             die "C library missing, and couldn't eval pp_tea_code\n";
54             }
55 1     1   10807 use bytes;
  1         2  
  1         4  
56              
57             # begin config
58             my %a2b = (
59             A=>000, B=>001, C=>002, D=>003, E=>004, F=>005, G=>006, H=>007,
60             I=>010, J=>011, K=>012, L=>013, M=>014, N=>015, O=>016, P=>017,
61             Q=>020, R=>021, S=>022, T=>023, U=>024, V=>025, W=>026, X=>027,
62             Y=>030, Z=>031, a=>032, b=>033, c=>034, d=>035, e=>036, f=>037,
63             g=>040, h=>041, i=>042, j=>043, k=>044, l=>045, m=>046, n=>047,
64             o=>050, p=>051, q=>052, r=>053, s=>054, t=>055, u=>056, v=>057,
65             w=>060, x=>061, y=>062, z=>063, '0'=>064, '1'=>065, '2'=>066, '3'=>067,
66             '4'=>070,'5'=>071,'6'=>072,'7'=>073,'8'=>074,'9'=>075,'-'=>076,'_'=>077,
67             );
68             my %b2a = reverse %a2b;
69             # $a2b{'+'}=076;
70             # end config
71              
72             # ------------------ infrastructure ...
73              
74             sub tea_in_javascript {
75 1 100   1 1 6 my @js; while () { last if /^EOT$/; push @js, $_; } join '', @js;
  1         8  
  340         481  
  339         687  
  1         200  
76             }
77 0     0 0 0 sub encrypt_and_write { my ($str, $key) = @_;
78 0 0       0 return unless $str; return unless $key;
  0 0       0  
79 0         0 print
80             "\n";
83             }
84             sub binary2ascii {
85 5     5 1 19 return str2ascii(binary2str(@_));
86             }
87             sub ascii2binary {
88 1     1 1 1039 return str2binary(ascii2str($_[$[]));
  1     1   356  
  1         1089  
  1         6  
89             }
90 17     17 0 833 sub str2binary { my @str = split //, $_[$[];
91 17         101 my @intarray = (); my $ii = $[;
  17         35  
92 17         18 while (1) {
93 857 100       1250 last unless @str; $intarray[$ii] = (0xFF & ord shift @str)<<24;
  840         1006  
94 840 50       1246 last unless @str; $intarray[$ii] |= (0xFF & ord shift @str)<<16;
  840         868  
95 840 50       1414 last unless @str; $intarray[$ii] |= (0xFF & ord shift @str)<<8;
  840         804  
96 840 50       1195 last unless @str; $intarray[$ii] |= 0xFF & ord shift @str;
  840         795  
97 840         730 $ii++;
98             }
99 17         192 return @intarray;
100             }
101             sub binary2str {
102 11     11 0 20 my @str = ();
103 11         24 foreach $i (@_) {
104 576         1051 push @str, chr(0xFF & ($i>>24)), chr(0xFF & ($i>>16)),
105             chr(0xFF & ($i>>8)), chr(0xFF & $i);
106             }
107 11         216 return join '', @str;
108             }
109 3     3 0 9 sub ascii2str { my $a = $_[$[]; # converts pseudo-base64 to string of bytes
110 3         12 local $^W = 0;
111 3         10 $a =~ tr#-A-Za-z0-9+_##cd;
112 3         8 my $ia = $[-1; my $la = length $a; # BUG not length, final!
  3         5  
113 3         7 my $ib = $[; my @b = ();
  3         4  
114 3         4 my $carry;
115 3         4 while (1) { # reads 4 ascii chars and produces 3 bytes
116 282 100       283 $ia++; last if ($ia>=$la);
  282         412  
117 281         593 $b[$ib] = $a2b{substr $a, $ia+$[, 1}<<2;
118 281 100       264 $ia++; last if ($ia>=$la);
  281         399  
119 280         563 $carry=$a2b{substr $a, $ia+$[, 1}; $b[$ib] |= ($carry>>4); $ib++;
  280         310  
  280         241  
120             # if low 4 bits of $carry are 0 and its the last char, then break
121 280 50 66     235 $carry = 0xF & $carry; last if ($carry == 0 && $ia == ($la-1));
  280         645  
122 280         308 $b[$ib] = $carry<<4;
123 280 50       210 $ia++; last if ($ia>=$la);
  280         427  
124 280         688 $carry=$a2b{substr $a, $ia+$[, 1}; $b[$ib] |= ($carry>>2); $ib++;
  280         324  
  280         218  
125             # if low 2 bits of $carry are 0 and its the last char, then break
126 280 100 100     246 $carry = 03 & $carry; last if ($carry == 0 && $ia == ($la-1));
  280         606  
127 279         288 $b[$ib] = $carry<<6;
128 279 50       223 $ia++; last if ($ia>=$la);
  279         427  
129 279         604 $b[$ib] |= $a2b{substr $a, $ia+$[, 1}; $ib++;
  279         279  
130             }
131 3         50 return pack 'C*', @b; # 2.16
132             }
133 9     9 0 60 sub str2ascii { my $b = $_[$[]; # converts string of bytes to pseudo-base64
134 9         20 my $ib = $[; my $lb = length $b; my @s = ();
  9         11  
  9         12  
135 9         9 my $b1; my $b2; my $b3;
  0         0  
136 0         0 my $carry;
137 9         10 while (1) { # reads 3 bytes and produces 4 ascii chars
138 500 100       691 if ($ib >= $lb) { last; };
  3         23  
139 497         938 $b1 = ord substr $b, $ib+$[, 1; $ib++;
  497         454  
140 497         662 push @s, $b2a{$b1>>2}; $carry = 03 & $b1;
  497         492  
141 497 100       747 if ($ib >= $lb) { push @s, $b2a{$carry<<4}; last; }
  5         7  
  5         7  
142 492         1050 $b2 = ord substr $b, $ib+$[, 1; $ib++;
  492         483  
143 492         682 push @s, $b2a{($b2>>4) | ($carry<<4)}; $carry = 0xF & $b2;
  492         402  
144 492 100       858 if ($ib >= $lb) { push @s, $b2a{$carry<<2}; last; }
  1         2  
  1         2  
145 491         822 $b3 = ord substr $b, $ib+$[, 1; $ib++;
  491         440  
146 491         975 push @s, $b2a{($b3>>6) | ($carry<<2)}, $b2a{077 & $b3};
147 491 100 100     1507 if (!$ENV{REMOTE_ADDR} && (($ib % 36) == 0)) { push @s, "\n"; }
  22         27  
148             }
149 9         187 return join('', @s);
150             }
151             sub asciidigest { # returns 22-char ascii signature
152 4     4 1 596 return binary2ascii(binarydigest($_[$[]));
153             }
154 10     10 0 29 sub binarydigest { my $str = $_[$[]; # returns 4 32-bit-int binary signature
155             # warning: mode of use invented by Peter Billam 1998, needs checking !
156 10 50       17 return '' unless $str;
157 10 50 33     54 if ($] > 5.007 && Encode::is_utf8($str)) {
158 0         0 Encode::_utf8_off($str);
159             # $str = Encode::encode_utf8($str);
160             }
161             # add 1 char ('0'..'15') at front to specify no of pad chars at end ...
162 10         13 my $npads = 15 - ((length $str) % 16);
163 10         21 $str = chr($npads) . $str;
164 10 50       14 if ($npads) { $str .= "\0" x $npads; }
  10         18  
165 10         18 my @str = str2binary($str);
166 10         25 my @key = (0x61626364, 0x62636465, 0x63646566, 0x64656667);
167              
168 10         9 my ($cswap, $v0, $v1, $v2, $v3);
169 10         10 my $c0 = 0x61626364; my $c1 = 0x62636465; # CBC Initial Value. Retain !
  10         9  
170 10         8 my $c2 = 0x61626364; my $c3 = 0x62636465; # likewise (abcdbcde).
  10         11  
171 10         18 while (@str) {
172             # shift 2 blocks off front of str ...
173 70         69 $v0 = shift @str; $v1 = shift @str; $v2 = shift @str; $v3 = shift @str;
  70         82  
  70         69  
  70         61  
174             # cipher them XOR'd with previous stage ...
175 70         191 ($c0,$c1) = tea_code($v0^$c0, $v1^$c1, @key);
176 70         192 ($c2,$c3) = tea_code($v2^$c2, $v3^$c3, @key);
177             # mix up the two cipher blocks with a 4-byte left rotation ...
178 70         62 $cswap = $c0; $c0=$c1; $c1=$c2; $c2=$c3; $c3=$cswap;
  70         49  
  70         107  
  70         77  
  70         141  
179             }
180 10         35 return ($c0,$c1,$c2,$c3);
181             }
182 4     4 1 602 sub encrypt { my ($str,$key)=@_; # encodes with CBC (Cipher Block Chaining)
183 4 50       8 return '' unless $str; return '' unless $key;
  4 50       7  
184 4 100 66     23 if ($] > 5.007 && Encode::is_utf8($str)) {
185 1         4 Encode::_utf8_off($str);
186             # $str = Encode::encode_utf8($str);
187             }
188 1     1   840 use integer;
  1         10  
  1         4  
189 4         8 @key = binarydigest($key);
190              
191             # add 1 char ('0'..'7') at front to specify no of pad chars at end ...
192 4         9 my $npads = 7 - ((length $str) % 8);
193 4         10 $str = chr($npads|(0xF8 & rand_byte())) . $str;
194 4 50       10 if ($npads) {
195 4         8 my $padding = pack 'CCCCCCC', rand_byte(), rand_byte(),
196             rand_byte(), rand_byte(), rand_byte(), rand_byte(), rand_byte();
197 4         15 $str = $str . substr($padding,$[,$npads);
198             }
199 4         6 my @pblocks = str2binary($str);
200 4         11 my $v0; my $v1;
201 4         6 my $c0 = 0x61626364; my $c1 = 0x62636465; # CBC Initial Value. Retain !
  4         4  
202 4         4 my @cblocks;
203 4         4 while (1) {
204 179 100       260 last unless @pblocks; $v0 = shift @pblocks; $v1 = shift @pblocks;
  175         133  
  175         151  
205 175         451 ($c0,$c1) = tea_code($v0^$c0, $v1^$c1, @key);
206 175         215 push @cblocks, $c0, $c1;
207             }
208 4         14 return str2ascii( binary2str(@cblocks) );
209             }
210 2     2 1 11 sub decrypt { my ($acstr, $key) = @_; # decodes with CBC
211 1     1   253 use integer;
  1         2  
  1         4  
212 2 50       5 return '' unless $acstr; return '' unless $key;
  2 50       5  
213 2         5 @key = binarydigest($key);
214 2         4 my $v0; my $v1; my $c0; my $c1; my @pblocks = (); my $de0; my $de1;
  0         0  
  0         0  
  2         4  
  2         3  
215 2         3 my $lastc0 = 0x61626364; my $lastc1 = 0x62636465; # CBC Init Val. Retain!
  2         1  
216 2         7 my @cblocks = str2binary( ascii2str($acstr) );
217 2         8 while (1) {
218 105 100       196 last unless @cblocks; $c0 = shift @cblocks; $c1 = shift @cblocks;
  103         105  
  103         99  
219 103         247 ($de0, $de1) = tea_decode($c0,$c1, @key);
220 103         83 $v0 = $lastc0 ^ $de0; $v1 = $lastc1 ^ $de1;
  103         84  
221 103         102 push @pblocks, $v0, $v1;
222 103         81 $lastc0 = $c0; $lastc1 = $c1;
  103         87  
223             }
224 2         7 my $str = binary2str(@pblocks);
225             # remove no of pad chars at end specified by 1 char ('0'..'7') at front
226 2         5 my $npads = 0x7 & ord $str; substr ($str, $[, 1) = '';
  2         17  
227 2 50       4 if ($npads) { substr ($str, 0 - $npads) = ''; }
  2         3  
228 2         16 return $str;
229             }
230 0     0 0 0 sub triple_encrypt { my ($plaintext, $long_key) = @_; # not yet ...
231             }
232 0     0 0 0 sub triple_decrypt { my ($cyphertext, $long_key) = @_; # not yet ...
233             }
234              
235             # PurePerl versions: introduced in 2.23
236 1     1 0 811 sub pp_tea_code { my ($v0,$v1,@k) = @_;
237             # Note that both "<<" and ">>" in Perl are implemented directly using
238             # "<<" and ">>" in C. If "use integer" (see "Integer Arithmetic") is in
239             # force then signed C integers are used, else unsigned C integers are used.
240 1     1   355 use integer;
  1         1  
  1         3  
241 1         2 my $sum = 0; my $n = 32;
  1         2  
242 1         4 while ($n-- > 0) {
243 32         40 $v0 += ((($v1<<4)^(0x07FFFFFF&($v1>>5)))+$v1) ^ ($sum+$k[$sum&3]);
244 32         27 $v0 &= 0xFFFFFFFF;
245 32         26 $sum += 0x9e3779b9; # TEA magic number delta
246             # $sum &= 0xFFFFFFFF; # changes nothing
247 32         40 $v1 += ((($v0<<4)^(0x07FFFFFF&($v0>>5)))+$v0)^($sum+$k[($sum>>11)&3]);
248 32         52 $v1 &= 0xFFFFFFFF;
249             }
250 1         4 return ($v0, $v1);
251             }
252 1     1 0 258 sub pp_tea_decode { my ($v0,$v1, @k) = @_;
253 1     1   111 use integer;
  1         1  
  1         4  
254 1         2 my $sum = 0; my $n = 32;
  1         1  
255 1         2 $sum = 0x9e3779b9 << 5 ; # TEA magic number delta
256 1         4 while ($n-- > 0) {
257 32         53 $v1 -= ((($v0<<4)^(0x07FFFFFF&($v0>>5)))+$v0)^($sum+$k[($sum>>11)&3]);
258 32         31 $v1 &= 0xFFFFFFFF;
259 32         24 $sum -= 0x9e3779b9 ;
260 32         115 $v0 -= ((($v1<<4)^(0x07FFFFFF&($v1>>5)))+$v1) ^ ($sum+$k[$sum&3]);
261 32         54 $v0 &= 0xFFFFFFFF;
262             }
263 1         10 return ($v0, $v1);
264             }
265 1     1 0 688 sub pp_oldtea_code { my ($v0,$v1, $k0,$k1,$k2,$k3) = @_;
266 1     1   142 use integer;
  1         2  
  1         3  
267 1         2 my $sum = 0; my $n = 32;
  1         2  
268 1         4 while ($n-- > 0) {
269 32         24 $sum += 0x9e3779b9; # TEA magic number delta
270 32         35 $v0 += (($v1<<4)+$k0) ^ ($v1+$sum) ^ ((0x07FFFFFF & ($v1>>5))+$k1) ;
271 32         26 $v0 &= 0xFFFFFFFF;
272 32         33 $v1 += (($v0<<4)+$k2) ^ ($v0+$sum) ^ ((0x07FFFFFF & ($v0>>5))+$k3) ;
273 32         46 $v1 &= 0xFFFFFFFF;
274             }
275 1         3 return ($v0, $v1);
276             }
277 1     1 0 264 sub pp_oldtea_decode { my ($v0,$v1, $k0,$k1,$k2,$k3) = @_;
278 1     1   145 use integer;
  1         1  
  1         4  
279 1         2 my $sum = 0; my $n = 32;
  1         2  
280 1         2 $sum = 0x9e3779b9 << 5 ; # TEA magic number delta
281 1         5 while ($n-- > 0) {
282 32         44 $v1 -= (($v0<<4)+$k2) ^ ($v0+$sum) ^ ((0x07FFFFFF & ($v0>>5))+$k3) ;
283 32         171 $v1 &= 0xFFFFFFFF;
284 32         43 $v0 -= (($v1<<4)+$k0) ^ ($v1+$sum) ^ ((0x07FFFFFF & ($v1>>5))+$k1) ;
285 32         30 $v0 &= 0xFFFFFFFF;
286 32         55 $sum -= 0x9e3779b9 ;
287             }
288 1         5 return ($v0, $v1);
289             }
290              
291             sub rand_byte {
292 32 100   32 0 47 if (! $rand_byte_already_called) {
293 1         5 srand(time() ^ ($$+($$<<15))); # could do better, but its only padding
294 1         2 $rand_byte_already_called = 1;
295             }
296 32         73 int(rand 256);
297             }
298             1;
299              
300             __DATA__