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
26package Crypt::Tea_JS;
27$VERSION = '2.23';
28# Don't like depending on externals; this is strong encrytion ... but ...
29require Exporter;
30@ISA = qw(Exporter);
31
32eval { require XSLoader; XSLoader::load('Crypt::Tea_JS', $VERSION); };
33if ($@) {   # 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
44BEGIN {
45	if ($] < 5.006) {
46		$INC{"bytes.pm"} = 1;       # cheating that bytes.pm is loaded
47		*bytes::import   = sub { }; # do nothing
48		*bytes::unimport = sub { };
49	}
50	if ($] > 5.007) { require Encode; }
51}
52if (! defined &tea_code) {
53	die "C library missing, and couldn't eval pp_tea_code\n";
54}
55use bytes;
56
57# begin config
58my %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);
68my %b2a = reverse %a2b;
69# $a2b{'+'}=076;
70# end config
71
72# ------------------ infrastructure ...
73
74sub tea_in_javascript {
75	my @js; while (<DATA>) { last if /^EOT$/; push @js, $_; } join '', @js;
76}
77sub encrypt_and_write { my ($str, $key) = @_;
78	return unless $str; return unless $key;
79	print
80	"<SCRIPT LANGUAGE=\"JavaScript\">\n<!--\nparent.decrypt_and_write('";
81	print encrypt($str,$key);
82	print "');\n// -->\n</SCRIPT>\n";
83}
84sub binary2ascii {
85	return str2ascii(binary2str(@_));
86}
87sub ascii2binary {
88	return str2binary(ascii2str($_[$[]));
89}
90sub str2binary {   my @str = split //, $_[$[];
91	my @intarray = (); my $ii = $[;
92	while (1) {
93		last unless @str; $intarray[$ii]  = (0xFF & ord shift @str)<<24;
94		last unless @str; $intarray[$ii] |= (0xFF & ord shift @str)<<16;
95		last unless @str; $intarray[$ii] |= (0xFF & ord shift @str)<<8;
96		last unless @str; $intarray[$ii] |=  0xFF & ord shift @str;
97		$ii++;
98	}
99	return @intarray;
100}
101sub binary2str {
102	my @str = ();
103	foreach $i (@_) {
104		push @str, chr(0xFF & ($i>>24)), chr(0xFF & ($i>>16)),
105		 chr(0xFF & ($i>>8)), chr(0xFF & $i);
106	}
107	return join '', @str;
108}
109sub ascii2str {   my $a = $_[$[]; # converts pseudo-base64 to string of bytes
110	local $^W = 0;
111	$a =~ tr#-A-Za-z0-9+_##cd;
112	my $ia = $[-1;  my $la = length $a;   # BUG not length, final!
113	my $ib = $[;  my @b = ();
114	my $carry;
115	while (1) {   # reads 4 ascii chars and produces 3 bytes
116		$ia++; last if ($ia>=$la);
117		$b[$ib]  = $a2b{substr $a, $ia+$[, 1}<<2;
118		$ia++; last if ($ia>=$la);
119		$carry=$a2b{substr $a, $ia+$[, 1};  $b[$ib] |= ($carry>>4); $ib++;
120		# if low 4 bits of $carry are 0 and its the last char, then break
121		$carry = 0xF & $carry; last if ($carry == 0 && $ia == ($la-1));
122		$b[$ib]  = $carry<<4;
123		$ia++; last if ($ia>=$la);
124		$carry=$a2b{substr $a, $ia+$[, 1};  $b[$ib] |= ($carry>>2); $ib++;
125		# if low 2 bits of $carry are 0 and its the last char, then break
126		$carry = 03 & $carry; last if ($carry == 0 && $ia == ($la-1));
127		$b[$ib]  = $carry<<6;
128		$ia++; last if ($ia>=$la);
129		$b[$ib] |= $a2b{substr $a, $ia+$[, 1}; $ib++;
130	}
131	return pack 'C*', @b;   # 2.16
132}
133sub str2ascii {   my $b = $_[$[]; # converts string of bytes to pseudo-base64
134	my $ib = $[;  my $lb = length $b;  my @s = ();
135	my $b1; my $b2; my $b3;
136	my $carry;
137	while (1) {   # reads 3 bytes and produces 4 ascii chars
138		if ($ib >= $lb) { last; };
139		$b1 = ord substr $b, $ib+$[, 1;  $ib++;
140		push @s, $b2a{$b1>>2}; $carry = 03 & $b1;
141		if ($ib >= $lb) { push @s, $b2a{$carry<<4}; last; }
142		$b2 = ord substr $b, $ib+$[, 1;  $ib++;
143		push @s, $b2a{($b2>>4) | ($carry<<4)}; $carry = 0xF & $b2;
144		if ($ib >= $lb) { push @s, $b2a{$carry<<2}; last; }
145		$b3 = ord substr $b, $ib+$[, 1;  $ib++;
146		push @s, $b2a{($b3>>6) | ($carry<<2)}, $b2a{077 & $b3};
147		if (!$ENV{REMOTE_ADDR} && (($ib % 36) == 0)) { push @s, "\n"; }
148	}
149	return join('', @s);
150}
151sub asciidigest {   # returns 22-char ascii signature
152	return binary2ascii(binarydigest($_[$[]));
153}
154sub binarydigest { my $str = $_[$[];  # returns 4 32-bit-int binary signature
155	# warning: mode of use invented by Peter Billam 1998, needs checking !
156	return '' unless $str;
157	if ($] > 5.007 && Encode::is_utf8($str)) {
158		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	my $npads = 15 - ((length $str) % 16);
163	$str  = chr($npads) . $str;
164	if ($npads) { $str .= "\0" x $npads; }
165	my @str = str2binary($str);
166	my @key = (0x61626364, 0x62636465, 0x63646566, 0x64656667);
167
168	my ($cswap, $v0, $v1, $v2, $v3);
169	my $c0 = 0x61626364; my $c1 = 0x62636465; # CBC Initial Value. Retain !
170	my $c2 = 0x61626364; my $c3 = 0x62636465; # likewise (abcdbcde).
171	while (@str) {
172		# shift 2 blocks off front of str ...
173		$v0 = shift @str; $v1 = shift @str; $v2 = shift @str; $v3 = shift @str;
174		# cipher them XOR'd with previous stage ...
175		($c0,$c1) = tea_code($v0^$c0, $v1^$c1, @key);
176		($c2,$c3) = tea_code($v2^$c2, $v3^$c3, @key);
177		# mix up the two cipher blocks with a 4-byte left rotation ...
178		$cswap  = $c0; $c0=$c1; $c1=$c2; $c2=$c3; $c3=$cswap;
179	}
180	return ($c0,$c1,$c2,$c3);
181}
182sub encrypt { my ($str,$key)=@_; # encodes with CBC (Cipher Block Chaining)
183	return '' unless $str; return '' unless $key;
184	if ($] > 5.007 && Encode::is_utf8($str)) {
185		Encode::_utf8_off($str);
186		# $str = Encode::encode_utf8($str);
187	}
188	use integer;
189	@key = binarydigest($key);
190
191	# add 1 char ('0'..'7') at front to specify no of pad chars at end ...
192	my $npads = 7 - ((length $str) % 8);
193	$str  = chr($npads|(0xF8 & rand_byte())) . $str;
194	if ($npads) {
195		my $padding = pack 'CCCCCCC', rand_byte(), rand_byte(),
196		 rand_byte(), rand_byte(), rand_byte(), rand_byte(), rand_byte();
197		$str  = $str . substr($padding,$[,$npads);
198	}
199	my @pblocks = str2binary($str);
200	my $v0; my $v1;
201	my $c0 = 0x61626364; my $c1 = 0x62636465; # CBC Initial Value. Retain !
202	my @cblocks;
203	while (1) {
204		last unless @pblocks; $v0 = shift @pblocks; $v1 = shift @pblocks;
205		($c0,$c1) = tea_code($v0^$c0, $v1^$c1, @key);
206		push @cblocks, $c0, $c1;
207	}
208	return str2ascii( binary2str(@cblocks) );
209}
210sub decrypt { my ($acstr, $key) = @_;   # decodes with CBC
211	use integer;
212	return '' unless $acstr; return '' unless $key;
213	@key = binarydigest($key);
214	my $v0; my $v1; my $c0; my $c1; my @pblocks = (); my $de0; my $de1;
215	my $lastc0 = 0x61626364; my $lastc1 = 0x62636465; # CBC Init Val. Retain!
216	my @cblocks = str2binary( ascii2str($acstr) );
217	while (1) {
218		last unless @cblocks; $c0 = shift @cblocks; $c1 = shift @cblocks;
219		($de0, $de1) = tea_decode($c0,$c1, @key);
220		$v0 = $lastc0 ^ $de0;   $v1 = $lastc1 ^ $de1;
221		push @pblocks, $v0, $v1;
222		$lastc0 = $c0;   $lastc1 = $c1;
223	}
224	my $str = binary2str(@pblocks);
225	# remove no of pad chars at end specified by 1 char ('0'..'7') at front
226	my $npads = 0x7 & ord $str; substr ($str, $[, 1) = '';
227	if ($npads) { substr ($str, 0 - $npads) = ''; }
228	return $str;
229}
230sub triple_encrypt { my ($plaintext,  $long_key) = @_;  # not yet ...
231}
232sub triple_decrypt { my ($cyphertext, $long_key) = @_;  # not yet ...
233}
234
235# PurePerl versions: introduced in 2.23
236sub 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	use integer;
241	my $sum = 0; my $n = 32;
242	while ($n-- > 0) {
243		$v0 += ((($v1<<4)^(0x07FFFFFF&($v1>>5)))+$v1) ^ ($sum+$k[$sum&3]);
244		$v0 &= 0xFFFFFFFF;
245		$sum += 0x9e3779b9;   # TEA magic number delta
246		# $sum &= 0xFFFFFFFF; # changes nothing
247		$v1 += ((($v0<<4)^(0x07FFFFFF&($v0>>5)))+$v0)^($sum+$k[($sum>>11)&3]);
248		$v1 &= 0xFFFFFFFF;
249	}
250	return ($v0, $v1);
251}
252sub pp_tea_decode  { my ($v0,$v1, @k) = @_;
253	use integer;
254	my $sum = 0; my $n = 32;
255	$sum = 0x9e3779b9 << 5 ;   # TEA magic number delta
256	while ($n-- > 0) {
257		$v1 -= ((($v0<<4)^(0x07FFFFFF&($v0>>5)))+$v0)^($sum+$k[($sum>>11)&3]);
258		$v1 &= 0xFFFFFFFF;
259		$sum -= 0x9e3779b9 ;
260		$v0 -= ((($v1<<4)^(0x07FFFFFF&($v1>>5)))+$v1) ^ ($sum+$k[$sum&3]);
261		$v0 &= 0xFFFFFFFF;
262	}
263	return ($v0, $v1);
264}
265sub pp_oldtea_code  { my ($v0,$v1, $k0,$k1,$k2,$k3) = @_;
266	use integer;
267	my $sum = 0; my $n = 32;
268	while ($n-- > 0) {
269		$sum += 0x9e3779b9;   # TEA magic number delta
270		$v0 += (($v1<<4)+$k0) ^ ($v1+$sum) ^ ((0x07FFFFFF & ($v1>>5))+$k1) ;
271		$v0 &= 0xFFFFFFFF;
272		$v1 += (($v0<<4)+$k2) ^ ($v0+$sum) ^ ((0x07FFFFFF & ($v0>>5))+$k3) ;
273		$v1 &= 0xFFFFFFFF;
274	}
275	return ($v0, $v1);
276}
277sub pp_oldtea_decode  { my ($v0,$v1, $k0,$k1,$k2,$k3) = @_;
278	use integer;
279	my $sum = 0; my $n = 32;
280	$sum = 0x9e3779b9 << 5 ;   # TEA magic number delta
281	while ($n-- > 0) {
282		$v1 -= (($v0<<4)+$k2) ^ ($v0+$sum) ^ ((0x07FFFFFF & ($v0>>5))+$k3) ;
283		$v1 &= 0xFFFFFFFF;
284		$v0 -= (($v1<<4)+$k0) ^ ($v1+$sum) ^ ((0x07FFFFFF & ($v1>>5))+$k1) ;
285		$v0 &= 0xFFFFFFFF;
286		$sum -= 0x9e3779b9 ;
287	}
288	return ($v0, $v1);
289}
290
291sub rand_byte {
292	if (! $rand_byte_already_called) {
293		srand(time() ^ ($$+($$<<15))); # could do better, but its only padding
294		$rand_byte_already_called = 1;
295	}
296	int(rand 256);
297}
2981;
299
300__DATA__
301
302<SCRIPT LANGUAGE="JavaScript">
303<!--
304//        This JavaScript is Copyright (c) 2000, Peter J Billam
305//              c/o P J B Computing, www.pjb.com.au
306// It was generated by the Crypt::Tea_JS.pm Perl module and is free software;
307// you can redistribute and modify it under the same terms as Perl itself.
308
309// -- conversion routines between string, bytes, ascii encoding, & blocks --
310function binary2ascii (s) {
311 return bytes2ascii( blocks2bytes(s) );
312}
313function binary2str (s) {
314 return bytes2str( blocks2bytes(s) );
315}
316function ascii2binary (s) {
317 return bytes2blocks( ascii2bytes(s) );
318}
319function str2binary (s) {
320 return bytes2blocks( str2bytes(s) );
321}
322function str2bytes(s) {   // converts string to array of bytes
323 var is = 0;  var ls = s.length;  var b = new Array();
324 while (1) {
325  if (is >= ls) break;
326  if (c2b[s.charAt(is)] == null) { b[is] = 0xF7;
327   alert ('is = '+is + '\nchar = '+s.charAt(is) + '\nls = '+ls);
328  } else { b[is] = c2b[s.charAt(is)];
329  }
330  is++;
331 }
332 return b;
333}
334function bytes2str(b) {   // converts array of bytes to string
335 var ib = 0;  var lb = b.length;  var s = '';
336 while (1) {
337  if (ib >= lb) break;
338  s += b2c[0xFF&b[ib]];   // if its like perl, could be faster with join
339  ib++;
340 }
341 return s;
342}
343function ascii2bytes(a) { // converts pseudo-base64 to array of bytes
344 var ia = -1;  var la = a.length;
345 var ib = 0;  var b = new Array();
346 var carry;
347 while (1) {   // reads 4 chars and produces 3 bytes
348  while (1) { ia++; if (ia>=la) return b; if (a2b[a.charAt(ia)]!=null) break; }
349  b[ib]  = a2b[a.charAt(ia)]<<2;
350  while (1) { ia++; if (ia>=la) return b; if (a2b[a.charAt(ia)]!=null) break; }
351  carry=a2b[a.charAt(ia)];  b[ib] |= carry>>>4; ib++;
352  // if low 4 bits of carry are 0 and its the last char, then break
353  carry = 0xF & carry;
354  if (carry == 0 && ia == (la-1)) return b;
355  b[ib]  = carry<<4;
356  while (1) { ia++; if (ia>=la) return b; if (a2b[a.charAt(ia)]!=null) break; }
357  carry=a2b[a.charAt(ia)];  b[ib] |= carry>>>2; ib++;
358  // if low 2 bits of carry are 0 and its the last char, then break
359  carry = 3 & carry;
360  if (carry == 0 && ia == (la-1)) return b;
361  b[ib]  = carry<<6;
362  while (1) { ia++; if (ia>=la) return b; if (a2b[a.charAt(ia)]!=null) break; }
363  b[ib] |= a2b[a.charAt(ia)];   ib++;
364 }
365 return b;
366}
367function bytes2ascii(b) { // converts array of bytes to pseudo-base64 ascii
368 var ib = 0;   var lb = b.length;  var s = '';
369 var b1; var b2; var b3;
370 var carry;
371 while (1) {   // reads 3 bytes and produces 4 chars
372  if (ib >= lb) break;   b1 = 0xFF & b[ib];
373  s += b2a[63 & (b1>>>2)];
374  carry = 3 & b1;
375  ib++;  if (ib >= lb) { s += b2a[carry<<4]; break; }  b2 = 0xFF & b[ib];
376  s += b2a[(0xF0 & (carry<<4)) | (b2>>>4)];
377  carry = 0xF & b2;
378  ib++;  if (ib >= lb) { s += b2a[carry<<2]; break; }  b3 = 0xFF & b[ib];
379  s += b2a[(60 & (carry<<2)) | (b3>>>6)] + b2a[63 & b3];
380  ib++;
381  if (ib % 36 == 0) s += "\n";
382 }
383 return s;
384}
385function bytes2blocks(bytes) {
386 var blocks = new Array(); var ibl = 0;
387 var iby = 0; var nby = bytes.length;
388 while (1) {
389  blocks[ibl]  = (0xFF & bytes[iby])<<24; iby++; if (iby >= nby) break;
390  blocks[ibl] |= (0xFF & bytes[iby])<<16; iby++; if (iby >= nby) break;
391  blocks[ibl] |= (0xFF & bytes[iby])<<8;  iby++; if (iby >= nby) break;
392  blocks[ibl] |=  0xFF & bytes[iby];      iby++; if (iby >= nby) break;
393  ibl++;
394 }
395 return blocks;
396}
397function blocks2bytes(blocks) {
398 var bytes = new Array(); var iby = 0;
399 var ibl = 0; var nbl = blocks.length;
400 while (1) {
401  if (ibl >= nbl) break;
402  bytes[iby] = 0xFF & (blocks[ibl] >>> 24); iby++;
403  bytes[iby] = 0xFF & (blocks[ibl] >>> 16); iby++;
404  bytes[iby] = 0xFF & (blocks[ibl] >>> 8);  iby++;
405  bytes[iby] = 0xFF & blocks[ibl]; iby++;
406  ibl++;
407 }
408 return bytes;
409}
410function digest_pad (bytearray) {
411 // add 1 char ('0'..'15') at front to specify no of \x00 pad chars at end
412 var newarray = new Array();  var ina = 0;
413 var iba = 0; var nba = bytearray.length;
414 var npads = 15 - (nba % 16); newarray[ina] = npads; ina++;
415 while (iba < nba) { newarray[ina] = bytearray[iba]; ina++; iba++; }
416 var ip = npads; while (ip>0) { newarray[ina] = 0; ina++; ip--; }
417 return newarray;
418}
419function pad (bytearray) {
420 // add 1 char ('0'..'7') at front to specify no of rand pad chars at end
421 // unshift and push fail on Netscape 4.7 :-(
422 var newarray = new Array();  var ina = 0;
423 var iba = 0; var nba = bytearray.length;
424 var npads = 7 - (nba % 8);
425 newarray[ina] = (0xF8 & rand_byte()) | (7 & npads); ina++;
426 while (iba < nba) { newarray[ina] = bytearray[iba]; ina++; iba++; }
427 var ip = npads; while (ip>0) { newarray[ina] = rand_byte(); ina++; ip--; }
428 return newarray;
429}
430function rand_byte() {   // used by pad
431 return Math.floor( 256*Math.random() );  // Random needs js1.1 . Seed ?
432 // for js1.0 compatibility, could try following ...
433 if (! rand_byte_already_called) {
434  var now = new Date();  seed = now.milliseconds;
435  rand_byte_already_called = true;
436 }
437 seed = (1029*seed + 221591) % 1048576;  // see Fortran77, Wagener, p177
438 return Math.floor(seed / 4096);
439}
440function unpad (bytearray) {
441 // remove no of pad chars at end specified by 1 char ('0'..'7') at front
442 // unshift and push fail on Netscape 4.7 :-(
443 var iba = 0;
444 var newarray = new Array();  var ina = 0;
445 var npads = 0x7 & bytearray[iba]; iba++; var nba = bytearray.length - npads;
446 while (iba < nba) { newarray[ina] = bytearray[iba]; ina++; iba++; }
447 return newarray;
448}
449
450// --- TEA stuff, translated from the Perl Tea_JS.pm see www.pjb.com.au/comp ---
451
452// In JavaScript we express an 8-byte block as an array of 2 32-bit ints
453function asciidigest (str) {
454 return binary2ascii( binarydigest(str) );
455}
456function binarydigest (str, keystr) {  // returns 22-char ascii signature
457 var key = new Array(); // key = binarydigest(keystr);
458 key[0]=0x61626364; key[1]=0x62636465; key[2]=0x63646566; key[3]=0x64656667;
459
460 // Initial Value for CBC mode = "abcdbcde". Retain for interoperability.
461 var c0 = new Array(); c0[0] = 0x61626364; c0[1] = 0x62636465;
462 var c1 = new Array(); c1 = c0;
463
464 var v0 = new Array(); var v1 = new Array(); var swap;
465 var blocks = new Array(); blocks = bytes2blocks(digest_pad(str2bytes(str)));
466 var ibl = 0;   var nbl = blocks.length;
467 while (1) {
468  if (ibl >= nbl) break;
469  v0[0] = blocks[ibl]; ibl++; v0[1] = blocks[ibl]; ibl++;
470  v1[0] = blocks[ibl]; ibl++; v1[1] = blocks[ibl]; ibl++;
471  // cipher them XOR'd with previous stage ...
472  c0 = tea_code( xor_blocks(v0,c0), key );
473  c1 = tea_code( xor_blocks(v1,c1), key );
474  // mix up the two cipher blocks with a 32-bit left rotation ...
475  swap=c0[0]; c0[0]=c0[1]; c0[1]=c1[0]; c1[0]=c1[1]; c1[1]=swap;
476 }
477 var concat = new Array();
478 concat[0]=c0[0]; concat[1]=c0[1]; concat[2]=c1[0]; concat[3]=c1[1];
479 return concat;
480}
481function encrypt (str,keystr) {  // encodes with CBC (Cipher Block Chaining)
482 if (! keystr) { alert("encrypt: no key"); return false; }
483 var key = new Array();  key = binarydigest(keystr);
484 if (! str) return "";
485 var blocks = new Array(); blocks = bytes2blocks(pad(str2bytes(str)));
486 var ibl = 0;  var nbl = blocks.length;
487 // Initial Value for CBC mode = "abcdbcde". Retain for interoperability.
488 var c = new Array(); c[0] = 0x61626364; c[1] = 0x62636465;
489 var v = new Array(); var cblocks = new Array();  var icb = 0;
490 while (1) {
491  if (ibl >= nbl) break;
492  v[0] = blocks[ibl];  ibl++; v[1] = blocks[ibl];  ibl++;
493  c = tea_code( xor_blocks(v,c), key );
494  cblocks[icb] = c[0]; icb++; cblocks[icb] = c[1]; icb++;
495 }
496 return binary2ascii(cblocks);
497}
498function decrypt (ascii, keystr) {   // decodes with CBC
499 if (! keystr) { alert("decrypt: no key"); return false; }
500 var key = new Array();  key = binarydigest(keystr);
501 if (! ascii) return "";
502 var cblocks = new Array(); cblocks = ascii2binary(ascii);
503 var icbl = 0;  var ncbl = cblocks.length;
504 // Initial Value for CBC mode = "abcdbcde". Retain for interoperability.
505 var lastc = new Array(); lastc[0] = 0x61626364; lastc[1] = 0x62636465;
506 var v = new Array(); var c = new Array();
507 var blocks = new Array(); var ibl = 0;
508 while (1) {
509  if (icbl >= ncbl) break;
510  c[0] = cblocks[icbl];  icbl++;  c[1] = cblocks[icbl];  icbl++;
511  v = xor_blocks( lastc, tea_decode(c,key) );
512  blocks[ibl] = v[0];  ibl++;  blocks[ibl] = v[1];  ibl++;
513  lastc[0] = c[0]; lastc[1] = c[1];
514 }
515 return bytes2str(unpad(blocks2bytes(blocks)));
516}
517function xor_blocks(blk1, blk2) { // xor of two 8-byte blocks
518 var blk = new Array();
519 blk[0] = blk1[0]^blk2[0]; blk[1] = blk1[1]^blk2[1];
520 return blk;
521}
522function tea_code (v, k) {
523 // NewTEA. 2-int (64-bit) cyphertext block in v. 4-int (128-bit) key in k.
524 var v0  = v[0]; var v1 = v[1];
525 var sum = 0; var n = 32;
526 while (n-- > 0) {
527  v0 += (((v1<<4)^(v1>>>5))+v1) ^ (sum+k[sum&3]) ; v0 = v0|0 ;
528  sum -= 1640531527; // TEA magic number 0x9e3779b9
529  sum = sum|0;  // force it back to 32-bit int
530  v1 += (((v0<<4)^(v0>>>5))+v0) ^ (sum+k[(sum>>>11)&3]); v1 = v1|0 ;
531 }
532 var w = new Array(); w[0] = v0; w[1] = v1; return w;
533}
534function tea_decode (v, k) {
535 // NewTEA. 2-int (64-bit) cyphertext block in v. 4-int (128-bit) key in k.
536 var v0 = v[0]; var v1 = v[1];
537 var sum = 0; var n = 32;
538 sum = -957401312 ; // TEA magic number 0x9e3779b9<<5
539 while (n-- > 0) {
540  v1 -= (((v0<<4)^(v0>>>5))+v0) ^ (sum+k[(sum>>>11)&3]); v1 = v1|0 ;
541  sum += 1640531527; // TEA magic number 0x9e3779b9 ;
542  sum = sum|0; // force it back to 32-bit int
543  v0 -= (((v1<<4)^(v1>>>5))+v1) ^ (sum+k[sum&3]); v0 = v0|0 ;
544 }
545 var w = new Array(); w[0] = v0; w[1] = v1; return w;
546}
547
548// ------------- assocarys used by the conversion routines -----------
549c2b = new Object();
550c2b["\x00"]=0;  c2b["\x01"]=1;  c2b["\x02"]=2;  c2b["\x03"]=3;
551c2b["\x04"]=4;  c2b["\x05"]=5;  c2b["\x06"]=6;  c2b["\x07"]=7;
552c2b["\x08"]=8;  c2b["\x09"]=9;  c2b["\x0A"]=10; c2b["\x0B"]=11;
553c2b["\x0C"]=12; c2b["\x0D"]=13; c2b["\x0E"]=14; c2b["\x0F"]=15;
554c2b["\x10"]=16; c2b["\x11"]=17; c2b["\x12"]=18; c2b["\x13"]=19;
555c2b["\x14"]=20; c2b["\x15"]=21; c2b["\x16"]=22; c2b["\x17"]=23;
556c2b["\x18"]=24; c2b["\x19"]=25; c2b["\x1A"]=26; c2b["\x1B"]=27;
557c2b["\x1C"]=28; c2b["\x1D"]=29; c2b["\x1E"]=30; c2b["\x1F"]=31;
558c2b["\x20"]=32; c2b["\x21"]=33; c2b["\x22"]=34; c2b["\x23"]=35;
559c2b["\x24"]=36; c2b["\x25"]=37; c2b["\x26"]=38; c2b["\x27"]=39;
560c2b["\x28"]=40; c2b["\x29"]=41; c2b["\x2A"]=42; c2b["\x2B"]=43;
561c2b["\x2C"]=44; c2b["\x2D"]=45; c2b["\x2E"]=46; c2b["\x2F"]=47;
562c2b["\x30"]=48; c2b["\x31"]=49; c2b["\x32"]=50; c2b["\x33"]=51;
563c2b["\x34"]=52; c2b["\x35"]=53; c2b["\x36"]=54; c2b["\x37"]=55;
564c2b["\x38"]=56; c2b["\x39"]=57; c2b["\x3A"]=58; c2b["\x3B"]=59;
565c2b["\x3C"]=60; c2b["\x3D"]=61; c2b["\x3E"]=62; c2b["\x3F"]=63;
566c2b["\x40"]=64; c2b["\x41"]=65; c2b["\x42"]=66; c2b["\x43"]=67;
567c2b["\x44"]=68; c2b["\x45"]=69; c2b["\x46"]=70; c2b["\x47"]=71;
568c2b["\x48"]=72; c2b["\x49"]=73; c2b["\x4A"]=74; c2b["\x4B"]=75;
569c2b["\x4C"]=76; c2b["\x4D"]=77; c2b["\x4E"]=78; c2b["\x4F"]=79;
570c2b["\x50"]=80; c2b["\x51"]=81; c2b["\x52"]=82; c2b["\x53"]=83;
571c2b["\x54"]=84; c2b["\x55"]=85; c2b["\x56"]=86; c2b["\x57"]=87;
572c2b["\x58"]=88; c2b["\x59"]=89; c2b["\x5A"]=90; c2b["\x5B"]=91;
573c2b["\x5C"]=92; c2b["\x5D"]=93; c2b["\x5E"]=94; c2b["\x5F"]=95;
574c2b["\x60"]=96; c2b["\x61"]=97; c2b["\x62"]=98; c2b["\x63"]=99;
575c2b["\x64"]=100; c2b["\x65"]=101; c2b["\x66"]=102; c2b["\x67"]=103;
576c2b["\x68"]=104; c2b["\x69"]=105; c2b["\x6A"]=106; c2b["\x6B"]=107;
577c2b["\x6C"]=108; c2b["\x6D"]=109; c2b["\x6E"]=110; c2b["\x6F"]=111;
578c2b["\x70"]=112; c2b["\x71"]=113; c2b["\x72"]=114; c2b["\x73"]=115;
579c2b["\x74"]=116; c2b["\x75"]=117; c2b["\x76"]=118; c2b["\x77"]=119;
580c2b["\x78"]=120; c2b["\x79"]=121; c2b["\x7A"]=122; c2b["\x7B"]=123;
581c2b["\x7C"]=124; c2b["\x7D"]=125; c2b["\x7E"]=126; c2b["\x7F"]=127;
582c2b["\x80"]=128; c2b["\x81"]=129; c2b["\x82"]=130; c2b["\x83"]=131;
583c2b["\x84"]=132; c2b["\x85"]=133; c2b["\x86"]=134; c2b["\x87"]=135;
584c2b["\x88"]=136; c2b["\x89"]=137; c2b["\x8A"]=138; c2b["\x8B"]=139;
585c2b["\x8C"]=140; c2b["\x8D"]=141; c2b["\x8E"]=142; c2b["\x8F"]=143;
586c2b["\x90"]=144; c2b["\x91"]=145; c2b["\x92"]=146; c2b["\x93"]=147;
587c2b["\x94"]=148; c2b["\x95"]=149; c2b["\x96"]=150; c2b["\x97"]=151;
588c2b["\x98"]=152; c2b["\x99"]=153; c2b["\x9A"]=154; c2b["\x9B"]=155;
589c2b["\x9C"]=156; c2b["\x9D"]=157; c2b["\x9E"]=158; c2b["\x9F"]=159;
590c2b["\xA0"]=160; c2b["\xA1"]=161; c2b["\xA2"]=162; c2b["\xA3"]=163;
591c2b["\xA4"]=164; c2b["\xA5"]=165; c2b["\xA6"]=166; c2b["\xA7"]=167;
592c2b["\xA8"]=168; c2b["\xA9"]=169; c2b["\xAA"]=170; c2b["\xAB"]=171;
593c2b["\xAC"]=172; c2b["\xAD"]=173; c2b["\xAE"]=174; c2b["\xAF"]=175;
594c2b["\xB0"]=176; c2b["\xB1"]=177; c2b["\xB2"]=178; c2b["\xB3"]=179;
595c2b["\xB4"]=180; c2b["\xB5"]=181; c2b["\xB6"]=182; c2b["\xB7"]=183;
596c2b["\xB8"]=184; c2b["\xB9"]=185; c2b["\xBA"]=186; c2b["\xBB"]=187;
597c2b["\xBC"]=188; c2b["\xBD"]=189; c2b["\xBE"]=190; c2b["\xBF"]=191;
598c2b["\xC0"]=192; c2b["\xC1"]=193; c2b["\xC2"]=194; c2b["\xC3"]=195;
599c2b["\xC4"]=196; c2b["\xC5"]=197; c2b["\xC6"]=198; c2b["\xC7"]=199;
600c2b["\xC8"]=200; c2b["\xC9"]=201; c2b["\xCA"]=202; c2b["\xCB"]=203;
601c2b["\xCC"]=204; c2b["\xCD"]=205; c2b["\xCE"]=206; c2b["\xCF"]=207;
602c2b["\xD0"]=208; c2b["\xD1"]=209; c2b["\xD2"]=210; c2b["\xD3"]=211;
603c2b["\xD4"]=212; c2b["\xD5"]=213; c2b["\xD6"]=214; c2b["\xD7"]=215;
604c2b["\xD8"]=216; c2b["\xD9"]=217; c2b["\xDA"]=218; c2b["\xDB"]=219;
605c2b["\xDC"]=220; c2b["\xDD"]=221; c2b["\xDE"]=222; c2b["\xDF"]=223;
606c2b["\xE0"]=224; c2b["\xE1"]=225; c2b["\xE2"]=226; c2b["\xE3"]=227;
607c2b["\xE4"]=228; c2b["\xE5"]=229; c2b["\xE6"]=230; c2b["\xE7"]=231;
608c2b["\xE8"]=232; c2b["\xE9"]=233; c2b["\xEA"]=234; c2b["\xEB"]=235;
609c2b["\xEC"]=236; c2b["\xED"]=237; c2b["\xEE"]=238; c2b["\xEF"]=239;
610c2b["\xF0"]=240; c2b["\xF1"]=241; c2b["\xF2"]=242; c2b["\xF3"]=243;
611c2b["\xF4"]=244; c2b["\xF5"]=245; c2b["\xF6"]=246; c2b["\xF7"]=247;
612c2b["\xF8"]=248; c2b["\xF9"]=249; c2b["\xFA"]=250; c2b["\xFB"]=251;
613c2b["\xFC"]=252; c2b["\xFD"]=253; c2b["\xFE"]=254; c2b["\xFF"]=255;
614b2c = new Object();
615for (b in c2b) { b2c[c2b[b]] = b; }
616
617// ascii to 6-bit bin to ascii
618a2b = new Object();
619a2b["A"]=0;  a2b["B"]=1;  a2b["C"]=2;  a2b["D"]=3;
620a2b["E"]=4;  a2b["F"]=5;  a2b["G"]=6;  a2b["H"]=7;
621a2b["I"]=8;  a2b["J"]=9;  a2b["K"]=10; a2b["L"]=11;
622a2b["M"]=12; a2b["N"]=13; a2b["O"]=14; a2b["P"]=15;
623a2b["Q"]=16; a2b["R"]=17; a2b["S"]=18; a2b["T"]=19;
624a2b["U"]=20; a2b["V"]=21; a2b["W"]=22; a2b["X"]=23;
625a2b["Y"]=24; a2b["Z"]=25; a2b["a"]=26; a2b["b"]=27;
626a2b["c"]=28; a2b["d"]=29; a2b["e"]=30; a2b["f"]=31;
627a2b["g"]=32; a2b["h"]=33; a2b["i"]=34; a2b["j"]=35;
628a2b["k"]=36; a2b["l"]=37; a2b["m"]=38; a2b["n"]=39;
629a2b["o"]=40; a2b["p"]=41; a2b["q"]=42; a2b["r"]=43;
630a2b["s"]=44; a2b["t"]=45; a2b["u"]=46; a2b["v"]=47;
631a2b["w"]=48; a2b["x"]=49; a2b["y"]=50; a2b["z"]=51;
632a2b["0"]=52; a2b["1"]=53; a2b["2"]=54; a2b["3"]=55;
633a2b["4"]=56; a2b["5"]=57; a2b["6"]=58; a2b["7"]=59;
634a2b["8"]=60; a2b["9"]=61; a2b["-"]=62; a2b["_"]=63;
635
636b2a = new Object();
637for (b in a2b) { b2a[a2b[b]] = ''+b; }
638// -->
639</SCRIPT>
640EOT
641
642=pod
643
644=head1 NAME
645
646Tea_JS.pm - The Tiny Encryption Algorithm in Perl and JavaScript
647
648=head1 SYNOPSIS
649
650Usage:
651
652 use Crypt::Tea_JS;
653 $key = 'PUFgob$*LKDF D)(F IDD&P?/';
654 $ascii_cyphertext = encrypt($plaintext, $key);
655 ...
656 $plaintext_again = decrypt($ascii_cyphertext, $key);
657 ...
658 $signature = asciidigest($text);
659
660In CGI scripts:
661
662 use Crypt::Tea_JS;
663 print tea_in_javascript();
664 # now the browser can encrypt and decrypt ! In JS:
665 var ascii_ciphertext = encrypt (plaintext, key);
666 var plaintext_again  = decrypt (ascii_ciphertext, key);
667 var signature = asciidigest (text);
668
669=head1 DESCRIPTION
670
671This module implements TEA, the Tiny Encryption Algorithm,
672and some Modes of Use, in Perl and JavaScript.
673
674The $key is a sufficiently longish string; at least 17 random 8-bit
675bytes for single encryption.
676
677Crypt::Tea_JS can be used for secret-key encryption in general,
678or, in particular, to communicate securely between browser and web-host.
679In this case, the simplest arrangement is for the user to
680enter the key into a JavaScript variable, and for the host to
681retrieve that user's key from a database.
682Or, for extra security, the first message (or even each message)
683between browser and host could contain a random challenge-string,
684which each end would then turn into a signature,
685and use that signature as the encryption-key for the session (or the reply).
686
687If a travelling employee can carry a session-startup file
688(e.g. I<login_on_the_road.html>) on their laptop,
689then they are invulnerable to imposter-web-hosts
690trying to feed them trojan JavaScript.
691
692Version 2.23
693
694(c) Peter J Billam 1998
695
696=head1 SUBROUTINES
697
698=over 3
699
700=item I<encrypt>( $plaintext, $key );
701
702Encrypts with CBC (Cipher Block Chaining)
703
704=item I<decrypt>( $cyphertext, $key );
705
706Decrypts with CBC (Cipher Block Chaining)
707
708=item I<asciidigest>( $a_string );
709
710Returns an asciified binary signature of the argument.
711
712=item I<tea_in_javascript>();
713
714Returns a compatible implementation of TEA in JavaScript,
715for use in CGI scripts to communicate with browsers.
716
717=back
718
719=head1 EXPORT_OK SUBROUTINES
720
721The following routines are not exported by default,
722but are exported under the I<ALL> tag, so if you need them you should:
723
724 import Crypt::Tea_JS qw(:ALL);
725
726=over 3
727
728=item I<binary2ascii>( $a_binary_string );
729
730Provides an ascii text encoding of the binary argument.
731If Tea_JS.pm is not being invoked from a GCI script,
732the ascii is split into lines of 72 characters.
733
734=item I<ascii2binary>( $an_ascii_string );
735
736Provides the binary original of an ascii text encoding.
737
738=back
739
740=head1 JAVASCRIPT
741
742At the browser end, the following functions offer the same
743functionality as their perl equivalents above:
744
745=over 3
746
747=item I<encrypt> ( str, keystr )
748
749=item I<decrypt> ( ascii, keystr )
750
751=item I<asciidigest> ( str );
752
753=back
754
755Of course the same Key must be used by the Perl on the server
756and by the JavaScript in the browser, and of course you
757don't want to transmit the Key in cleartext between them.
758Let's assume you've already asked the user to fill in a form
759asking for their Username, and that this username can be transmitted
760back and forth in cleartext as an ordinary form variable.
761
762On the server, typically you will retrieve the Key from a
763database of some sort, for example:
764
765 dbmopen %keys, "/home/wherever/passwords", 0666;
766 $key = $keys{$username};  dbmclose %keys;
767 $cyphertext = encrypt("<P>Hello World !</P>\n", $key);
768
769At the browser end, just ask the user for their password when
770they load an encrypted page, e.g.
771
772 print tea_in_javascript(), <<EOT;
773 <SCRIPT LANGUAGE="JavaScript">
774 var key = prompt("Password ?","");
775 document.write(decrypt($cyphertext, key));
776 </SCRIPT>
777 EOT
778
779To submit an encrypted FORM, the traditional way is to construct two FORMs;
780an overt one which the user fills in but which never actually gets
781submitted, and a covert one which will hold the cyphertext.
782See the cgi script C<examples/old_tea_demo.cgi> in the distribution directory.
783
784More often you want the browser to remember its Key from page to page, to
785form a session.  If you store the Key in a Cookie, it is vulnerable to any
786imposter server who imitates your IP address, and also to anyone who sits
787down at the user's computer.  Better is to store the Key in a JavaScript
788variable, and communicate with the server in I<Ajax> style, with
789I<XMLHttpRequest> or I<ActiveXObject>, and I<responseText> or I<responseXML>.
790See the cgi script C<examples/tea_demo.cgi> in the distribution directory.
791
792In the distribution directory there is also C<Tea_JS.js>, which is
793simply the output of C<tea_in_javascript()>.  This could be useful
794if your initial login page is an HTML page rather than a CGI script.
795
796=head1 ROADMAP
797
798Crypt::Tea conflicted with a similarly-named Crypt::TEA by Abhijit Menon-Sen.
799Unfortunately, Microsoft operating systems confused the two names and are
800unable to install both.  Version 2.10 of Crypt::Tea is mature, and apart
801perhaps from minor bug fixes will probably remain the final version.
802Further development will take place under the name Crypt::Tea_JS.
803The calling interface is identical.
804
805I've taken advantage of the new name to make two important changes.
806Firstly, Crypt::Tea_JS uses the New Improved version of the Tea algorithm,
807which provides even stronger encryption, though it does surrender
808backward-compatibility for files encrypted by the old Crypt::Tea.
809Secondly, some of the core routines are now implemented in C, for improved
810performance (at the server end, if you're using it in a CGI context).
811
812=head1 AUTHOR
813
814Peter J Billam ( http://www.pjb.com.au/comp/contact.html ).
815
816=head1 CREDITS
817
818Based on TEA, as described in
819http://www.cl.cam.ac.uk/ftp/papers/djw-rmn/djw-rmn-tea.html ,
820and on some help from I<Applied Cryptography> by Bruce Schneier
821as regards the modes of use.
822Thanks also to Neil Watkiss for the MakeMaker packaging,
823to Scott Harrison for suggesting workarounds for MacOS 10.2 browsers,
824to Morgan Burke for pointing out the problem with URL query strings,
825and to Slaven Razic for portability advice in spite of "use bytes".
826
827=head1 SEE ALSO
828
829examples/tea_demo.cgi, perldoc Encode,
830http://www.pjb.com.au/comp, tea(1), perl(1).
831
832=cut
833
834