Commit | Line | Data |
---|---|---|
86530b38 AT |
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_ciphertext = &encrypt ($plaintext, $key); | |
17 | # ... | |
18 | # $plaintext_again = &decrypt ($ascii_ciphertext, $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 = '1.43'; | |
28 | ||
29 | # Don't like depending on externals; this is strong encrytion ... but ... | |
30 | use Exporter; @ISA = qw(Exporter); | |
31 | @EXPORT=qw(asciidigest encrypt decrypt | |
32 | tea_in_javascript str2ascii ascii2str encrypt_and_write); | |
33 | ||
34 | # begin config | |
35 | my %a2b = ( | |
36 | A=>000, B=>001, C=>002, D=>003, E=>004, F=>005, G=>006, H=>007, | |
37 | I=>010, J=>011, K=>012, L=>013, M=>014, N=>015, O=>016, P=>017, | |
38 | Q=>020, R=>021, S=>022, T=>023, U=>024, V=>025, W=>026, X=>027, | |
39 | Y=>030, Z=>031, a=>032, b=>033, c=>034, d=>035, e=>036, f=>037, | |
40 | g=>040, h=>041, i=>042, j=>043, k=>044, l=>045, m=>046, n=>047, | |
41 | o=>050, p=>051, q=>052, r=>053, s=>054, t=>055, u=>056, v=>057, | |
42 | w=>060, x=>061, y=>062, z=>063, '0'=>064, '1'=>065, '2'=>066, '3'=>067, | |
43 | '4'=>070,'5'=>071,'6'=>072,'7'=>073,'8'=>074,'9'=>075,'+'=>076,'_'=>077, | |
44 | ); | |
45 | my %b2a = reverse %a2b; | |
46 | # end config | |
47 | ||
48 | # ------------------ infrastructure ... | |
49 | ||
50 | sub tea_in_javascript { | |
51 | my @js; while (<DATA>) { last if /^EOT$/; push @js, $_; } join '', @js; | |
52 | } | |
53 | sub encrypt_and_write { my ($str, $key) = @_; | |
54 | return unless $str; return unless $key; | |
55 | ||
56 | "<SCRIPT LANGUAGE=\"JavaScript\">\n<!--\nparent.decrypt_and_write('"; | |
57 | print &encrypt($str,$key); | |
58 | print "');\n// -->\n</SCRIPT>\n"; | |
59 | } | |
60 | sub binary2ascii { | |
61 | return &str2ascii(&binary2str(@_)); | |
62 | } | |
63 | sub ascii2binary { | |
64 | return &str2binary(&ascii2str($_[$[])); | |
65 | } | |
66 | sub str2binary { my @str = split //, $_[$[]; | |
67 | my @intarray = (); my $ii = $[; | |
68 | while (1) { | |
69 | last unless @str; $intarray[$ii] = (0xFF & ord shift @str)<<24; | |
70 | last unless @str; $intarray[$ii] |= (0xFF & ord shift @str)<<16; | |
71 | last unless @str; $intarray[$ii] |= (0xFF & ord shift @str)<<8; | |
72 | last unless @str; $intarray[$ii] |= 0xFF & ord shift @str; | |
73 | $ii++; | |
74 | } | |
75 | return @intarray; | |
76 | } | |
77 | sub binary2str { | |
78 | my @str = (); | |
79 | foreach $i (@_) { | |
80 | push @str, chr (0xFF & ($i>>24)), chr (0xFF & ($i>>16)), | |
81 | chr (0xFF & ($i>>8)), chr (0xFF & $i); | |
82 | } | |
83 | return join '', @str; | |
84 | } | |
85 | sub ascii2str { my $a = $_[$[]; # converts pseudo-base64 to string of bytes | |
86 | $a =~ tr#A-Za-z0-9+_##cd; | |
87 | my $ia = $[-1; my $la = length $a; # BUG not length, final! | |
88 | my $ib = $[; my @b = (); | |
89 | my $carry; | |
90 | while (1) { # reads 4 ascii chars and produces 3 bytes | |
91 | $ia++; last if ($ia>=$la); | |
92 | $b[$ib] = $a2b{substr $a, $ia+$[, 1}<<2; | |
93 | $ia++; last if ($ia>=$la); | |
94 | $carry=$a2b{substr $a, $ia+$[, 1}; $b[$ib] |= ($carry>>4); $ib++; | |
95 | # if low 4 bits of $carry are 0 and its the last char, then break | |
96 | $carry = 0xF & $carry; last if ($carry == 0 && $ia == ($la-1)); | |
97 | $b[$ib] = $carry<<4; | |
98 | $ia++; last if ($ia>=$la); | |
99 | $carry=$a2b{substr $a, $ia+$[, 1}; $b[$ib] |= ($carry>>2); $ib++; | |
100 | # if low 2 bits of $carry are 0 and its the last char, then break | |
101 | $carry = 03 & $carry; last if ($carry == 0 && $ia == ($la-1)); | |
102 | $b[$ib] = $carry<<6; | |
103 | $ia++; last if ($ia>=$la); | |
104 | $b[$ib] |= $a2b{substr $a, $ia+$[, 1}; $ib++; | |
105 | } | |
106 | return pack 'c*', @b; | |
107 | } | |
108 | sub str2ascii { my $b = $_[$[]; # converts string of bytes to pseudo-base64 | |
109 | my $ib = $[; my $lb = length $b; my @s = (); | |
110 | my $b1; my $b2; my $b3; | |
111 | my $carry; | |
112 | while (1) { # reads 3 bytes and produces 4 ascii chars | |
113 | if ($ib >= $lb) { last; }; | |
114 | $b1 = ord substr $b, $ib+$[, 1; $ib++; | |
115 | push @s, $b2a{$b1>>2}; $carry = 03 & $b1; | |
116 | if ($ib >= $lb) { push @s, $b2a{$carry<<4}; last; } | |
117 | $b2 = ord substr $b, $ib+$[, 1; $ib++; | |
118 | push @s, $b2a{($b2>>4) | ($carry<<4)}; $carry = 0xF & $b2; | |
119 | if ($ib >= $lb) { push @s, $b2a{$carry<<2}; last; } | |
120 | $b3 = ord substr $b, $ib+$[, 1; $ib++; | |
121 | push @s, $b2a{($b3>>6) | ($carry<<2)}, $b2a{077 & $b3}; | |
122 | if (!$ENV{REMOTE_ADDR} && (($ib % 36) == 0)) { push @s, "\n"; } | |
123 | } | |
124 | return join ('', @s); | |
125 | } | |
126 | sub asciidigest { # returns 22-char ascii signature | |
127 | return &binary2ascii(&binarydigest($_[$[])); | |
128 | } | |
129 | sub binarydigest { my $str = $_[$[]; # returns 4 32-bit-int binary signature | |
130 | # warning: mode of use invented by Peter Billam 1998, needs checking ! | |
131 | return '' unless $str; | |
132 | # add 1 char ('0'..'15') at front to specify no of pad chars at end ... | |
133 | my $npads = 15 - ((length $str) % 16); | |
134 | $str = chr($npads) . $str; | |
135 | if ($npads) { $str .= "\0" x $npads; } | |
136 | my @str = &str2binary($str); | |
137 | my @key = (0x61626364, 0x62636465, 0x63646566, 0x64656667); | |
138 | ||
139 | my ($cswap, $v0, $v1, $v2, $v3); | |
140 | my $c0 = 0x61626364; my $c1 = 0x62636465; # CBC Initial Value. Retain ! | |
141 | my $c2 = 0x61626364; my $c3 = 0x62636465; # likewise (abcdbcde). | |
142 | while (@str) { | |
143 | # shift 2 blocks off front of str ... | |
144 | $v0 = shift @str; $v1 = shift @str; $v2 = shift @str; $v3 = shift @str; | |
145 | # cipher them XOR'd with previous stage ... | |
146 | ($c0,$c1) = &tea_code ($v0^$c0, $v1^$c1, @key); | |
147 | ($c2,$c3) = &tea_code ($v2^$c2, $v3^$c3, @key); | |
148 | # mix up the two cipher blocks with a 4-byte left rotation ... | |
149 | $cswap = $c0; $c0=$c1; $c1=$c2; $c2=$c3; $c3=$cswap; | |
150 | } | |
151 | return ($c0,$c1,$c2,$c3); | |
152 | } | |
153 | sub encrypt { my ($str,$key)=@_; # encodes with CBC (Cipher Block Chaining) | |
154 | use integer; | |
155 | return '' unless $str; return '' unless $key; | |
156 | @key = &binarydigest($key); | |
157 | ||
158 | # add 1 char ('0'..'7') at front to specify no of pad chars at end ... | |
159 | my $npads = 7 - ((length $str) % 8); | |
160 | $str = chr($npads|(0xF8 & &rand_byte)) . $str; | |
161 | if ($npads) { | |
162 | my $padding = pack 'CCCCCCC', &rand_byte, &rand_byte, | |
163 | &rand_byte, &rand_byte, &rand_byte, &rand_byte, &rand_byte; | |
164 | $str = $str . substr($padding,$[,$npads); | |
165 | } | |
166 | my @pblocks = &str2binary($str); | |
167 | my $v0; my $v1; | |
168 | my $c0 = 0x61626364; my $c1 = 0x62636465; # CBC Initial Value. Retain ! | |
169 | my @cblocks; | |
170 | while (1) { | |
171 | last unless @pblocks; $v0 = shift @pblocks; $v1 = shift @pblocks; | |
172 | ($c0,$c1) = &tea_code ($v0^$c0, $v1^$c1, @key); | |
173 | push @cblocks, $c0, $c1; | |
174 | } | |
175 | my $btmp = &binary2str(@cblocks); | |
176 | return &str2ascii( &binary2str(@cblocks) ); | |
177 | } | |
178 | sub decrypt { my ($acstr, $key) = @_; # decodes with CBC | |
179 | use integer; | |
180 | return '' unless $acstr; return '' unless $key; | |
181 | @key = &binarydigest($key); | |
182 | my $v0; my $v1; my $c0; my $c1; my @pblocks = (); my $de0; my $de1; | |
183 | my $lastc0 = 0x61626364; my $lastc1 = 0x62636465; # CBC Init Val. Retain! | |
184 | my @cblocks = &str2binary( &ascii2str($acstr) ); | |
185 | while (1) { | |
186 | last unless @cblocks; $c0 = shift @cblocks; $c1 = shift @cblocks; | |
187 | ($de0, $de1) = &tea_decode ($c0,$c1, @key); | |
188 | $v0 = $lastc0 ^ $de0; $v1 = $lastc1 ^ $de1; | |
189 | push @pblocks, $v0, $v1; | |
190 | $lastc0 = $c0; $lastc1 = $c1; | |
191 | } | |
192 | my $str = &binary2str( @pblocks ); | |
193 | # remove no of pad chars at end specified by 1 char ('0'..'7') at front | |
194 | my $npads = 0x7 & ord $str; substr ($str, $[, 1) = ''; | |
195 | if ($npads) { substr ($str, 0 - $npads) = ''; } | |
196 | return $str; | |
197 | } | |
198 | sub triple_encrypt { my ($plaintext, $long_key) = @_; # not yet ... | |
199 | } | |
200 | sub triple_decrypt { my ($cyphertext, $long_key) = @_; # not yet ... | |
201 | } | |
202 | sub tea_code { my ($v0,$v1, $k0,$k1,$k2,$k3) = @_; | |
203 | # TEA. 64-bit cleartext block in $v0,$v1. 128-bit key in $k0..$k3. | |
204 | # &prn("tea_code: v0=$v0 v1=$v1"); | |
205 | use integer; | |
206 | my $sum = 0; my $n = 32; | |
207 | while ($n-- > 0) { | |
208 | $sum += 0x9e3779b9; # TEA magic number delta | |
209 | $v0 += (($v1<<4)+$k0) ^ ($v1+$sum) ^ ((0x07FFFFFF & ($v1>>5))+$k1) ; | |
210 | $v1 += (($v0<<4)+$k2) ^ ($v0+$sum) ^ ((0x07FFFFFF & ($v0>>5))+$k3) ; | |
211 | } | |
212 | return ($v0, $v1); | |
213 | } | |
214 | sub tea_decode { my ($v0,$v1, $k0,$k1,$k2,$k3) = @_; | |
215 | # TEA. 64-bit cyphertext block in $v0,$v1. 128-bit key in $k0..$k3. | |
216 | use integer; | |
217 | my $sum = 0; my $n = 32; | |
218 | $sum = 0x9e3779b9 << 5 ; # TEA magic number delta | |
219 | while ($n-- > 0) { | |
220 | $v1 -= (($v0<<4)+$k2) ^ ($v0+$sum) ^ ((0x07FFFFFF & ($v0>>5))+$k3) ; | |
221 | $v0 -= (($v1<<4)+$k0) ^ ($v1+$sum) ^ ((0x07FFFFFF & ($v1>>5))+$k1) ; | |
222 | $sum -= 0x9e3779b9 ; | |
223 | } | |
224 | return ($v0, $v1); | |
225 | } | |
226 | sub rand_byte { | |
227 | if (! $rand_byte_already_called) { | |
228 | # Jengwei Pan to keep the same seed | |
229 | #srand(time() ^ ($$+($$<<15))); # could do better, but its only padding | |
230 | srand(1121); | |
231 | $rand_byte_already_called = 1; | |
232 | } | |
233 | int(rand 256); | |
234 | } | |
235 | 1; | |
236 | ||
237 | __DATA__ | |
238 | ||
239 | <SCRIPT LANGUAGE="JavaScript"> | |
240 | <!-- | |
241 | // This JavaScript is Copyright (c) 2000, Peter J Billam | |
242 | // c/o P J B Computing, www.pjb.com.au | |
243 | // It was generated by the Crypt::Tea.pm Perl module and is free software; | |
244 | // you can redistribute and modify it under the same terms as Perl itself. | |
245 | ||
246 | // -- conversion routines between string, bytes, ascii encoding, & blocks -- | |
247 | function binary2ascii (s) { | |
248 | return bytes2ascii( blocks2bytes(s) ); | |
249 | } | |
250 | function binary2str (s) { | |
251 | return bytes2str( blocks2bytes(s) ); | |
252 | } | |
253 | function ascii2binary (s) { | |
254 | return bytes2blocks( ascii2bytes(s) ); | |
255 | } | |
256 | function str2binary (s) { | |
257 | return bytes2blocks( str2bytes(s) ); | |
258 | } | |
259 | function str2bytes(s) { // converts string to array of bytes | |
260 | var is = 0; var ls = s.length; var b = new Array(); | |
261 | while (1) { | |
262 | if (is >= ls) break; | |
263 | if (c2b[s.charAt(is)] == null) { b[is] = 0xF7; | |
264 | alert ('is = '+is + '\nchar = '+s.charAt(is) + '\nls = '+ls); | |
265 | } else { b[is] = c2b[s.charAt(is)]; | |
266 | } | |
267 | is++; | |
268 | } | |
269 | return b; | |
270 | } | |
271 | function bytes2str(b) { // converts array of bytes to string | |
272 | var ib = 0; var lb = b.length; var s = ''; | |
273 | while (1) { | |
274 | if (ib >= lb) break; | |
275 | s += b2c[0xFF&b[ib]]; // if its like perl, could be faster with join | |
276 | ib++; | |
277 | } | |
278 | return s; | |
279 | } | |
280 | function ascii2bytes(a) { // converts pseudo-base64 to array of bytes | |
281 | var ia = -1; var la = a.length; | |
282 | var ib = 0; var b = new Array(); | |
283 | var carry; | |
284 | while (1) { // reads 4 chars and produces 3 bytes | |
285 | while (1) { ia++; if (ia>=la) return b; if (a2b[a.charAt(ia)]!=null) break; } | |
286 | b[ib] = a2b[a.charAt(ia)]<<2; | |
287 | while (1) { ia++; if (ia>=la) return b; if (a2b[a.charAt(ia)]!=null) break; } | |
288 | carry=a2b[a.charAt(ia)]; b[ib] |= carry>>>4; ib++; | |
289 | // if low 4 bits of carry are 0 and its the last char, then break | |
290 | carry = 0xF & carry; | |
291 | if (carry == 0 && ia == (la-1)) return b; | |
292 | b[ib] = carry<<4; | |
293 | while (1) { ia++; if (ia>=la) return b; if (a2b[a.charAt(ia)]!=null) break; } | |
294 | carry=a2b[a.charAt(ia)]; b[ib] |= carry>>>2; ib++; | |
295 | // if low 2 bits of carry are 0 and its the last char, then break | |
296 | carry = 03 & carry; | |
297 | if (carry == 0 && ia == (la-1)) return b; | |
298 | b[ib] = carry<<6; | |
299 | while (1) { ia++; if (ia>=la) return b; if (a2b[a.charAt(ia)]!=null) break; } | |
300 | b[ib] |= a2b[a.charAt(ia)]; ib++; | |
301 | } | |
302 | return b; | |
303 | } | |
304 | function bytes2ascii(b) { // converts array of bytes to pseudo-base64 ascii | |
305 | var ib = 0; var lb = b.length; var s = ''; | |
306 | var b1; var b2; var b3; | |
307 | var carry; | |
308 | while (1) { // reads 3 bytes and produces 4 chars | |
309 | if (ib >= lb) break; b1 = 0xFF & b[ib]; | |
310 | s += b2a[077 & (b1>>>2)]; | |
311 | carry = 03 & b1; | |
312 | ib++; if (ib >= lb) { s += b2a[carry<<4]; break; } b2 = 0xFF & b[ib]; | |
313 | s += b2a[(0xF0 & (carry<<4)) | (b2>>>4)]; | |
314 | carry = 0xF & b2; | |
315 | ib++; if (ib >= lb) { s += b2a[carry<<2]; break; } b3 = 0xFF & b[ib]; | |
316 | s += b2a[(074 & (carry<<2)) | (b3>>>6)] + b2a[077 & b3]; | |
317 | ib++; | |
318 | if (ib % 36 == 0) s += "\n"; | |
319 | } | |
320 | return s; | |
321 | } | |
322 | function bytes2blocks(bytes) { | |
323 | var blocks = new Array(); var ibl = 0; | |
324 | var iby = 0; var nby = bytes.length; | |
325 | while (1) { | |
326 | blocks[ibl] = (0xFF & bytes[iby])<<24; iby++; if (iby >= nby) break; | |
327 | blocks[ibl] |= (0xFF & bytes[iby])<<16; iby++; if (iby >= nby) break; | |
328 | blocks[ibl] |= (0xFF & bytes[iby])<<8; iby++; if (iby >= nby) break; | |
329 | blocks[ibl] |= 0xFF & bytes[iby]; iby++; if (iby >= nby) break; | |
330 | ibl++; | |
331 | } | |
332 | return blocks; | |
333 | } | |
334 | function blocks2bytes(blocks) { | |
335 | var bytes = new Array(); var iby = 0; | |
336 | var ibl = 0; var nbl = blocks.length; | |
337 | while (1) { | |
338 | if (ibl >= nbl) break; | |
339 | bytes[iby] = 0xFF & (blocks[ibl] >>> 24); iby++; | |
340 | bytes[iby] = 0xFF & (blocks[ibl] >>> 16); iby++; | |
341 | bytes[iby] = 0xFF & (blocks[ibl] >>> 8); iby++; | |
342 | bytes[iby] = 0xFF & blocks[ibl]; iby++; | |
343 | ibl++; | |
344 | } | |
345 | return bytes; | |
346 | } | |
347 | function digest_pad (bytearray) { | |
348 | // add 1 char ('0'..'15') at front to specify no of \0 pad chars at end | |
349 | var newarray = new Array(); var ina = 0; | |
350 | var iba = 0; var nba = bytearray.length; | |
351 | var npads = 15 - (nba % 16); newarray[ina] = npads; ina++; | |
352 | while (iba < nba) { newarray[ina] = bytearray[iba]; ina++; iba++; } | |
353 | var ip = npads; while (ip>0) { newarray[ina] = 0; ina++; ip--; } | |
354 | return newarray; | |
355 | } | |
356 | function pad (bytearray) { | |
357 | // add 1 char ('0'..'7') at front to specify no of rand pad chars at end | |
358 | // unshift and push fail on Netscape 4.7 :-( | |
359 | var newarray = new Array(); var ina = 0; | |
360 | var iba = 0; var nba = bytearray.length; | |
361 | var npads = 7 - (nba % 8); | |
362 | newarray[ina] = (0xF8 & rand_byte()) | (07 & npads); ina++; | |
363 | while (iba < nba) { newarray[ina] = bytearray[iba]; ina++; iba++; } | |
364 | var ip = npads; while (ip>0) { newarray[ina] = rand_byte(); ina++; ip--; } | |
365 | return newarray; | |
366 | } | |
367 | function rand_byte() { // used by pad | |
368 | return Math.floor( 256*Math.random() ); // Random needs js1.1 . Seed ? | |
369 | // for js1.0 compatibility, could try following ... | |
370 | if (! rand_byte_already_called) { | |
371 | var now = new Date(); seed = now.milliseconds; | |
372 | rand_byte_already_called = true; | |
373 | } | |
374 | seed = (1029*seed + 221591) % 1048576; // see Fortran77, Wagener, p177 | |
375 | return Math.floor(seed / 4096); | |
376 | } | |
377 | function unpad (bytearray) { | |
378 | // remove no of pad chars at end specified by 1 char ('0'..'7') at front | |
379 | // unshift and push fail on Netscape 4.7 :-( | |
380 | var iba = 0; | |
381 | var newarray = new Array(); var ina = 0; | |
382 | var npads = 0x7 & bytearray[iba]; iba++; var nba = bytearray.length - npads; | |
383 | while (iba < nba) { newarray[ina] = bytearray[iba]; ina++; iba++; } | |
384 | return newarray; | |
385 | } | |
386 | ||
387 | // --- TEA stuff, translated from the Perl Tea.pm see www.pjb.com.au/comp --- | |
388 | ||
389 | // In JavaScript we express an 8-byte block as an array of 2 32-bit ints | |
390 | function asciidigest (str) { | |
391 | return binary2ascii( binarydigest(str) ); | |
392 | } | |
393 | function binarydigest (str, keystr) { // returns 22-char ascii signature | |
394 | var key = new Array(); // key = binarydigest(keystr); | |
395 | key[0]=0x61626364; key[1]=0x62636465; key[2]=0x63646566; key[3]=0x64656667; | |
396 | ||
397 | // Initial Value for CBC mode = "abcdbcde". Retain for interoperability. | |
398 | var c0 = new Array(); c0[0] = 0x61626364; c0[1] = 0x62636465; | |
399 | var c1 = new Array(); c1 = c0; | |
400 | ||
401 | var v0 = new Array(); var v1 = new Array(); var swap; | |
402 | var blocks = new Array(); blocks = bytes2blocks(digest_pad(str2bytes(str))); | |
403 | var ibl = 0; var nbl = blocks.length; | |
404 | while (1) { | |
405 | if (ibl >= nbl) break; | |
406 | v0[0] = blocks[ibl]; ibl++; v0[1] = blocks[ibl]; ibl++; | |
407 | v1[0] = blocks[ibl]; ibl++; v1[1] = blocks[ibl]; ibl++; | |
408 | // cipher them XOR'd with previous stage ... | |
409 | c0 = tea_code( xor_blocks(v0,c0), key ); | |
410 | c1 = tea_code( xor_blocks(v1,c1), key ); | |
411 | // mix up the two cipher blocks with a 32-bit left rotation ... | |
412 | swap=c0[0]; c0[0]=c0[1]; c0[1]=c1[0]; c1[0]=c1[1]; c1[1]=swap; | |
413 | } | |
414 | var concat = new Array(); | |
415 | concat[0]=c0[0]; concat[1]=c0[1]; concat[2]=c1[0]; concat[3]=c1[1]; | |
416 | return concat; | |
417 | } | |
418 | function encrypt (str,keystr) { // encodes with CBC (Cipher Block Chaining) | |
419 | if (! keystr) { alert("encrypt: no key"); return false; } | |
420 | var key = new Array(); key = binarydigest(keystr); | |
421 | if (! str) return ""; | |
422 | var blocks = new Array(); blocks = bytes2blocks(pad(str2bytes(str))); | |
423 | var ibl = 0; var nbl = blocks.length; | |
424 | // Initial Value for CBC mode = "abcdbcde". Retain for interoperability. | |
425 | var c = new Array(); c[0] = 0x61626364; c[1] = 0x62636465; | |
426 | var v = new Array(); var cblocks = new Array(); var icb = 0; | |
427 | while (1) { | |
428 | if (ibl >= nbl) break; | |
429 | v[0] = blocks[ibl]; ibl++; v[1] = blocks[ibl]; ibl++; | |
430 | c = tea_code( xor_blocks(v,c), key ); | |
431 | cblocks[icb] = c[0]; icb++; cblocks[icb] = c[1]; icb++; | |
432 | } | |
433 | return binary2ascii(cblocks); | |
434 | } | |
435 | function decrypt (ascii, keystr) { // decodes with CBC | |
436 | if (! keystr) { alert("decrypt: no key"); return false; } | |
437 | var key = new Array(); key = binarydigest(keystr); | |
438 | if (! ascii) return ""; | |
439 | var cblocks = new Array(); cblocks = ascii2binary(ascii); | |
440 | var icbl = 0; var ncbl = cblocks.length; | |
441 | // Initial Value for CBC mode = "abcdbcde". Retain for interoperability. | |
442 | var lastc = new Array(); lastc[0] = 0x61626364; lastc[1] = 0x62636465; | |
443 | var v = new Array(); var c = new Array(); | |
444 | var blocks = new Array(); var ibl = 0; | |
445 | while (1) { | |
446 | if (icbl >= ncbl) break; | |
447 | c[0] = cblocks[icbl]; icbl++; c[1] = cblocks[icbl]; icbl++; | |
448 | v = xor_blocks( lastc, tea_decode(c,key) ); | |
449 | blocks[ibl] = v[0]; ibl++; blocks[ibl] = v[1]; ibl++; | |
450 | lastc[0] = c[0]; lastc[1] = c[1]; | |
451 | } | |
452 | return bytes2str(unpad(blocks2bytes(blocks))); | |
453 | } | |
454 | function xor_blocks(blk1, blk2) { // xor of two 8-byte blocks | |
455 | var blk = new Array(); | |
456 | blk[0] = blk1[0]^blk2[0]; blk[1] = blk1[1]^blk2[1]; | |
457 | return blk; | |
458 | } | |
459 | function tea_code (v, k) { | |
460 | // TEA. 2-int (64-bit) cyphertext block in v. 4-int (128-bit) key in k. | |
461 | var v0 = v[0]; var v1 = v[1]; | |
462 | var k0 = k[0]; var k1 = k[1]; var k2 = k[2]; var k3 = k[3]; | |
463 | var sum = 0; var n = 32; | |
464 | while (n-- > 0) { | |
465 | sum += 0x9e3779b9; // TEA magic number | |
466 | v0 += ((v1<<4)+k0) ^ (v1+sum) ^ ((v1>>>5)+k1) ; | |
467 | v1 += ((v0<<4)+k2) ^ (v0+sum) ^ ((v0>>>5)+k3) ; | |
468 | } | |
469 | var w = new Array(); w[0] = v0; w[1] = v1; return w; | |
470 | } | |
471 | function tea_decode (v, k) { | |
472 | // TEA. 2-int (64-bit) cyphertext block in v. 4-int (128-bit) key in k. | |
473 | var v0 = v[0]; var v1 = v[1]; | |
474 | var k0 = k[0]; var k1 = k[1]; var k2 = k[2]; var k3 = k[3]; | |
475 | var sum = 0; var n = 32; | |
476 | sum = 0x9e3779b9 << 5 ; // TEA magic number | |
477 | while (n-- > 0) { | |
478 | v1 -= ((v0<<4)+k2) ^ (v0+sum) ^ ((v0>>>5)+k3) ; | |
479 | v0 -= ((v1<<4)+k0) ^ (v1+sum) ^ ((v1>>>5)+k1) ; | |
480 | sum -= 0x9e3779b9 ; | |
481 | } | |
482 | var w = new Array(); w[0] = v0; w[1] = v1; return w; | |
483 | } | |
484 | ||
485 | // ------------- assocarys used by the conversion routines ----------- | |
486 | c2b = new Object(); | |
487 | c2b["\000"]=0000; c2b["\001"]=0001; c2b["\002"]=0002; c2b["\003"]=0003; | |
488 | c2b["\004"]=0004; c2b["\005"]=0005; c2b["\006"]=0006; c2b["\007"]=0007; | |
489 | c2b["\010"]=0010; c2b["\011"]=0011; c2b["\012"]=0012; c2b["\013"]=0013; | |
490 | c2b["\014"]=0014; c2b["\015"]=0015; c2b["\016"]=0016; c2b["\017"]=0017; | |
491 | c2b["\020"]=0020; c2b["\021"]=0021; c2b["\022"]=0022; c2b["\023"]=0023; | |
492 | c2b["\024"]=0024; c2b["\025"]=0025; c2b["\026"]=0026; c2b["\027"]=0027; | |
493 | c2b["\030"]=0030; c2b["\031"]=0031; c2b["\032"]=0032; c2b["\033"]=0033; | |
494 | c2b["\034"]=0034; c2b["\035"]=0035; c2b["\036"]=0036; c2b["\037"]=0037; | |
495 | c2b["\040"]=0040; c2b["\041"]=0041; c2b["\042"]=0042; c2b["\043"]=0043; | |
496 | c2b["\044"]=0044; c2b["\045"]=0045; c2b["\046"]=0046; c2b["\047"]=0047; | |
497 | c2b["\050"]=0050; c2b["\051"]=0051; c2b["\052"]=0052; c2b["\053"]=0053; | |
498 | c2b["\054"]=0054; c2b["\055"]=0055; c2b["\056"]=0056; c2b["\057"]=0057; | |
499 | c2b["\060"]=0060; c2b["\061"]=0061; c2b["\062"]=0062; c2b["\063"]=0063; | |
500 | c2b["\064"]=0064; c2b["\065"]=0065; c2b["\066"]=0066; c2b["\067"]=0067; | |
501 | c2b["\070"]=0070; c2b["\071"]=0071; c2b["\072"]=0072; c2b["\073"]=0073; | |
502 | c2b["\074"]=0074; c2b["\075"]=0075; c2b["\076"]=0076; c2b["\077"]=0077; | |
503 | c2b["\100"]=0100; c2b["\101"]=0101; c2b["\102"]=0102; c2b["\103"]=0103; | |
504 | c2b["\104"]=0104; c2b["\105"]=0105; c2b["\106"]=0106; c2b["\107"]=0107; | |
505 | c2b["\110"]=0110; c2b["\111"]=0111; c2b["\112"]=0112; c2b["\113"]=0113; | |
506 | c2b["\114"]=0114; c2b["\115"]=0115; c2b["\116"]=0116; c2b["\117"]=0117; | |
507 | c2b["\120"]=0120; c2b["\121"]=0121; c2b["\122"]=0122; c2b["\123"]=0123; | |
508 | c2b["\124"]=0124; c2b["\125"]=0125; c2b["\126"]=0126; c2b["\127"]=0127; | |
509 | c2b["\130"]=0130; c2b["\131"]=0131; c2b["\132"]=0132; c2b["\133"]=0133; | |
510 | c2b["\134"]=0134; c2b["\135"]=0135; c2b["\136"]=0136; c2b["\137"]=0137; | |
511 | c2b["\140"]=0140; c2b["\141"]=0141; c2b["\142"]=0142; c2b["\143"]=0143; | |
512 | c2b["\144"]=0144; c2b["\145"]=0145; c2b["\146"]=0146; c2b["\147"]=0147; | |
513 | c2b["\150"]=0150; c2b["\151"]=0151; c2b["\152"]=0152; c2b["\153"]=0153; | |
514 | c2b["\154"]=0154; c2b["\155"]=0155; c2b["\156"]=0156; c2b["\157"]=0157; | |
515 | c2b["\160"]=0160; c2b["\161"]=0161; c2b["\162"]=0162; c2b["\163"]=0163; | |
516 | c2b["\164"]=0164; c2b["\165"]=0165; c2b["\166"]=0166; c2b["\167"]=0167; | |
517 | c2b["\170"]=0170; c2b["\171"]=0171; c2b["\172"]=0172; c2b["\173"]=0173; | |
518 | c2b["\174"]=0174; c2b["\175"]=0175; c2b["\176"]=0176; c2b["\177"]=0177; | |
519 | c2b["\200"]=0200; c2b["\201"]=0201; c2b["\202"]=0202; c2b["\203"]=0203; | |
520 | c2b["\204"]=0204; c2b["\205"]=0205; c2b["\206"]=0206; c2b["\207"]=0207; | |
521 | c2b["\210"]=0210; c2b["\211"]=0211; c2b["\212"]=0212; c2b["\213"]=0213; | |
522 | c2b["\214"]=0214; c2b["\215"]=0215; c2b["\216"]=0216; c2b["\217"]=0217; | |
523 | c2b["\220"]=0220; c2b["\221"]=0221; c2b["\222"]=0222; c2b["\223"]=0223; | |
524 | c2b["\224"]=0224; c2b["\225"]=0225; c2b["\226"]=0226; c2b["\227"]=0227; | |
525 | c2b["\230"]=0230; c2b["\231"]=0231; c2b["\232"]=0232; c2b["\233"]=0233; | |
526 | c2b["\234"]=0234; c2b["\235"]=0235; c2b["\236"]=0236; c2b["\237"]=0237; | |
527 | c2b["\240"]=0240; c2b["\241"]=0241; c2b["\242"]=0242; c2b["\243"]=0243; | |
528 | c2b["\244"]=0244; c2b["\245"]=0245; c2b["\246"]=0246; c2b["\247"]=0247; | |
529 | c2b["\250"]=0250; c2b["\251"]=0251; c2b["\252"]=0252; c2b["\253"]=0253; | |
530 | c2b["\254"]=0254; c2b["\255"]=0255; c2b["\256"]=0256; c2b["\257"]=0257; | |
531 | c2b["\260"]=0260; c2b["\261"]=0261; c2b["\262"]=0262; c2b["\263"]=0263; | |
532 | c2b["\264"]=0264; c2b["\265"]=0265; c2b["\266"]=0266; c2b["\267"]=0267; | |
533 | c2b["\270"]=0270; c2b["\271"]=0271; c2b["\272"]=0272; c2b["\273"]=0273; | |
534 | c2b["\274"]=0274; c2b["\275"]=0275; c2b["\276"]=0276; c2b["\277"]=0277; | |
535 | c2b["\300"]=0300; c2b["\301"]=0301; c2b["\302"]=0302; c2b["\303"]=0303; | |
536 | c2b["\304"]=0304; c2b["\305"]=0305; c2b["\306"]=0306; c2b["\307"]=0307; | |
537 | c2b["\310"]=0310; c2b["\311"]=0311; c2b["\312"]=0312; c2b["\313"]=0313; | |
538 | c2b["\314"]=0314; c2b["\315"]=0315; c2b["\316"]=0316; c2b["\317"]=0317; | |
539 | c2b["\320"]=0320; c2b["\321"]=0321; c2b["\322"]=0322; c2b["\323"]=0323; | |
540 | c2b["\324"]=0324; c2b["\325"]=0325; c2b["\326"]=0326; c2b["\327"]=0327; | |
541 | c2b["\330"]=0330; c2b["\331"]=0331; c2b["\332"]=0332; c2b["\333"]=0333; | |
542 | c2b["\334"]=0334; c2b["\335"]=0335; c2b["\336"]=0336; c2b["\337"]=0337; | |
543 | c2b["\340"]=0340; c2b["\341"]=0341; c2b["\342"]=0342; c2b["\343"]=0343; | |
544 | c2b["\344"]=0344; c2b["\345"]=0345; c2b["\346"]=0346; c2b["\347"]=0347; | |
545 | c2b["\350"]=0350; c2b["\351"]=0351; c2b["\352"]=0352; c2b["\353"]=0353; | |
546 | c2b["\354"]=0354; c2b["\355"]=0355; c2b["\356"]=0356; c2b["\357"]=0357; | |
547 | c2b["\360"]=0360; c2b["\361"]=0361; c2b["\362"]=0362; c2b["\363"]=0363; | |
548 | c2b["\364"]=0364; c2b["\365"]=0365; c2b["\366"]=0366; c2b["\367"]=0367; | |
549 | c2b["\370"]=0370; c2b["\371"]=0371; c2b["\372"]=0372; c2b["\373"]=0373; | |
550 | c2b["\374"]=0374; c2b["\375"]=0375; c2b["\376"]=0376; c2b["\377"]=0377; | |
551 | b2c = new Object(); | |
552 | for (b in c2b) { b2c[c2b[b]] = b; } | |
553 | ||
554 | // ascii to 6-bit bin to ascii | |
555 | a2b = new Object(); | |
556 | a2b["A"]=000; a2b["B"]=001; a2b["C"]=002; a2b["D"]=003; | |
557 | a2b["E"]=004; a2b["F"]=005; a2b["G"]=006; a2b["H"]=007; | |
558 | a2b["I"]=010; a2b["J"]=011; a2b["K"]=012; a2b["L"]=013; | |
559 | a2b["M"]=014; a2b["N"]=015; a2b["O"]=016; a2b["P"]=017; | |
560 | a2b["Q"]=020; a2b["R"]=021; a2b["S"]=022; a2b["T"]=023; | |
561 | a2b["U"]=024; a2b["V"]=025; a2b["W"]=026; a2b["X"]=027; | |
562 | a2b["Y"]=030; a2b["Z"]=031; a2b["a"]=032; a2b["b"]=033; | |
563 | a2b["c"]=034; a2b["d"]=035; a2b["e"]=036; a2b["f"]=037; | |
564 | a2b["g"]=040; a2b["h"]=041; a2b["i"]=042; a2b["j"]=043; | |
565 | a2b["k"]=044; a2b["l"]=045; a2b["m"]=046; a2b["n"]=047; | |
566 | a2b["o"]=050; a2b["p"]=051; a2b["q"]=052; a2b["r"]=053; | |
567 | a2b["s"]=054; a2b["t"]=055; a2b["u"]=056; a2b["v"]=057; | |
568 | a2b["w"]=060; a2b["x"]=061; a2b["y"]=062; a2b["z"]=063; | |
569 | a2b["0"]=064; a2b["1"]=065; a2b["2"]=066; a2b["3"]=067; | |
570 | a2b["4"]=070; a2b["5"]=071; a2b["6"]=072; a2b["7"]=073; | |
571 | a2b["8"]=074; a2b["9"]=075; a2b["+"]=076; a2b["_"]=077; | |
572 | ||
573 | b2a = new Object(); | |
574 | for (b in a2b) { b2a[a2b[b]] = ''+b; } | |
575 | // --> | |
576 | </SCRIPT> | |
577 | EOT | |
578 | ||
579 | =pod | |
580 | ||
581 | =head1 NAME | |
582 | ||
583 | Tea.pm - The Tiny Encryption Algorithm in Perl and JavaScript | |
584 | ||
585 | =head1 SYNOPSIS | |
586 | ||
587 | Usage: | |
588 | ||
589 | use Crypt::Tea; | |
590 | $key = 'PUFgob$*LKDF D)(F IDD&P?/'; | |
591 | $ascii_ciphertext = &encrypt ($plaintext, $key); | |
592 | ... | |
593 | $plaintext_again = &decrypt ($ascii_ciphertext, $key); | |
594 | ... | |
595 | $signature = &asciidigest ($text); | |
596 | ||
597 | In CGI scripts: | |
598 | ||
599 | use Crypt::Tea; | |
600 | print &tea_in_javascript; # now the browser can encrypt | |
601 | # and decrypt ! see CGI::Htauth.pm for examples ... | |
602 | ||
603 | =head1 DESCRIPTION | |
604 | ||
605 | This module implements TEA, the Tiny Encryption Algorithm, | |
606 | and some Modes of Use, in Perl and JavaScript. | |
607 | ||
608 | The $key is a sufficiently longish string; at least 17 random 8-bit | |
609 | bytes for single encryption. | |
610 | ||
611 | As of version 1.34, various Htauth-specific hook routines | |
612 | have now been moved out into the I<CGI::Htauth.pm> module. | |
613 | ||
614 | Version 1.43, | |
615 | #COMMENT# | |
616 | ||
617 | (c) Peter J Billam 1998 | |
618 | ||
619 | =head1 SUBROUTINES | |
620 | ||
621 | =over 3 | |
622 | ||
623 | =item I<encrypt>( $plaintext, $key ); | |
624 | ||
625 | Encrypts with CBC (Cipher Block Chaining) | |
626 | ||
627 | =item I<decrypt>( $ciphertext, $key ); | |
628 | ||
629 | Decrypts with CBC (Cipher Block Chaining) | |
630 | ||
631 | =item I<binary2ascii>( $a_binary_string ); | |
632 | ||
633 | Provide an ascii text encoding of the binary argument. | |
634 | If Tea.pm is not being invoked from a GCI script, | |
635 | the ascii is split into lines of 72 characters. | |
636 | ||
637 | =item I<ascii2binary>( $an_ascii_string ); | |
638 | ||
639 | =item I<asciidigest>( $a_string ); | |
640 | ||
641 | Returns an asciified binary signature of the argument. | |
642 | ||
643 | =item I<tea_in_javascript>(); | |
644 | ||
645 | Returns a compatible implementation of TEA in JavaScript, | |
646 | for use in CGI scripts to communicate with browsers. | |
647 | ||
648 | =back | |
649 | ||
650 | =head1 AUTHOR | |
651 | ||
652 | Peter J Billam <peter@pjb.com.au>, | |
653 | with thanks also to Neil Watkiss for MakeMaker packaging. | |
654 | ||
655 | =head1 CREDITS | |
656 | ||
657 | Based on TEA, as described in | |
658 | http://www.cl.cam.ac.uk/ftp/papers/djw-rmn/djw-rmn-tea.html , | |
659 | and on some help from I<Applied Cryptography> by Bruce Schneier | |
660 | as regards the modes of use. | |
661 | ||
662 | =head1 SEE ALSO | |
663 | ||
664 | http://www.pjb.com.au/, CGI::Htauth.pm, perl(1). | |
665 | ||
666 | =cut | |
667 |