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