Crypt-Tea_JS
view release on metacpan or search on metacpan
Crypt::Tea_JS.pm
This module implements TEA, the Tiny Encryption Algorithm, and some
Modes of Use based on CBC, compatibly in both Perl and JavaScript.
This enables CGI scripts to communicate with browsers, as well
as working as a normal private-key strong-encryption engine.
Subroutines offer encryption, decryption & digest, and all cyphertext
is ascii-encoded to prevent munging. Another routine returns JavaScript
code with identical functions, and this can be used by CGIs to feed to
a browser. A wrapper executable 'tea' is included for command-line use.
Crypt::Tea_JS is the continuation of Crypt::Tea under another name.
(Microsoft systems confused the old name with a different module
// --- TEA stuff, translated from the Perl Tea_JS.pm see www.pjb.com.au/comp ---
// In JavaScript we express an 8-byte block as an array of 2 32-bit ints
function asciidigest (str) {
return binary2ascii( binarydigest(str) );
}
function binarydigest (str, keystr) { // returns 22-char ascii signature
var key = new Array(); // key = binarydigest(keystr);
key[0]=0x61626364; key[1]=0x62636465; key[2]=0x63646566; key[3]=0x64656667;
// Initial Value for CBC mode = "abcdbcde". Retain for interoperability.
var c0 = new Array(); c0[0] = 0x61626364; c0[1] = 0x62636465;
var c1 = new Array(); c1 = c0;
var v0 = new Array(); var v1 = new Array(); var swap;
var blocks = new Array(); blocks = bytes2blocks(digest_pad(str2bytes(str)));
var ibl = 0; var nbl = blocks.length;
while (1) {
if (ibl >= nbl) break;
v0[0] = blocks[ibl]; ibl++; v0[1] = blocks[ibl]; ibl++;
v1[0] = blocks[ibl]; ibl++; v1[1] = blocks[ibl]; ibl++;
// cipher them XOR'd with previous stage ...
c0 = tea_code( xor_blocks(v0,c0), key );
c1 = tea_code( xor_blocks(v1,c1), key );
// mix up the two cipher blocks with a 32-bit left rotation ...
swap=c0[0]; c0[0]=c0[1]; c0[1]=c1[0]; c1[0]=c1[1]; c1[1]=swap;
}
var concat = new Array();
concat[0]=c0[0]; concat[1]=c0[1]; concat[2]=c1[0]; concat[3]=c1[1];
return concat;
}
function encrypt (str,keystr) { // encodes with CBC (Cipher Block Chaining)
if (! keystr) { alert("encrypt: no key"); return false; }
var key = new Array(); key = binarydigest(keystr);
if (! str) return "";
var blocks = new Array(); blocks = bytes2blocks(pad(str2bytes(str)));
var ibl = 0; var nbl = blocks.length;
// Initial Value for CBC mode = "abcdbcde". Retain for interoperability.
var c = new Array(); c[0] = 0x61626364; c[1] = 0x62636465;
var v = new Array(); var cblocks = new Array(); var icb = 0;
while (1) {
if (ibl >= nbl) break;
v[0] = blocks[ibl]; ibl++; v[1] = blocks[ibl]; ibl++;
c = tea_code( xor_blocks(v,c), key );
cblocks[icb] = c[0]; icb++; cblocks[icb] = c[1]; icb++;
}
return binary2ascii(cblocks);
}
function decrypt (ascii, keystr) { // decodes with CBC
if (! keystr) { alert("decrypt: no key"); return false; }
var key = new Array(); key = binarydigest(keystr);
if (! ascii) return "";
var cblocks = new Array(); cblocks = ascii2binary(ascii);
var icbl = 0; var ncbl = cblocks.length;
// Initial Value for CBC mode = "abcdbcde". Retain for interoperability.
var lastc = new Array(); lastc[0] = 0x61626364; lastc[1] = 0x62636465;
var v = new Array(); var c = new Array();
var blocks = new Array(); var ibl = 0;
while (1) {
if (icbl >= ncbl) break;
c[0] = cblocks[icbl]; icbl++; c[1] = cblocks[icbl]; icbl++;
v = xor_blocks( lastc, tea_decode(c,key) );
blocks[ibl] = v[0]; ibl++; blocks[ibl] = v[1]; ibl++;
lastc[0] = c[0]; lastc[1] = c[1];
}
# $str = Encode::encode_utf8($str);
}
# add 1 char ('0'..'15') at front to specify no of pad chars at end ...
my $npads = 15 - ((length $str) % 16);
$str = chr($npads) . $str;
if ($npads) { $str .= "\0" x $npads; }
my @str = str2binary($str);
my @key = (0x61626364, 0x62636465, 0x63646566, 0x64656667);
my ($cswap, $v0, $v1, $v2, $v3);
my $c0 = 0x61626364; my $c1 = 0x62636465; # CBC Initial Value. Retain !
my $c2 = 0x61626364; my $c3 = 0x62636465; # likewise (abcdbcde).
while (@str) {
# shift 2 blocks off front of str ...
$v0 = shift @str; $v1 = shift @str; $v2 = shift @str; $v3 = shift @str;
# cipher them XOR'd with previous stage ...
($c0,$c1) = tea_code($v0^$c0, $v1^$c1, @key);
($c2,$c3) = tea_code($v2^$c2, $v3^$c3, @key);
# mix up the two cipher blocks with a 4-byte left rotation ...
$cswap = $c0; $c0=$c1; $c1=$c2; $c2=$c3; $c3=$cswap;
}
return ($c0,$c1,$c2,$c3);
}
sub encrypt { my ($str,$key)=@_; # encodes with CBC (Cipher Block Chaining)
return '' unless $str; return '' unless $key;
if ($] > 5.007 && Encode::is_utf8($str)) {
Encode::_utf8_off($str);
# $str = Encode::encode_utf8($str);
}
use integer;
@key = binarydigest($key);
# add 1 char ('0'..'7') at front to specify no of pad chars at end ...
my $npads = 7 - ((length $str) % 8);
$str = chr($npads|(0xF8 & rand_byte())) . $str;
if ($npads) {
my $padding = pack 'CCCCCCC', rand_byte(), rand_byte(),
rand_byte(), rand_byte(), rand_byte(), rand_byte(), rand_byte();
$str = $str . substr($padding,$[,$npads);
}
my @pblocks = str2binary($str);
my $v0; my $v1;
my $c0 = 0x61626364; my $c1 = 0x62636465; # CBC Initial Value. Retain !
my @cblocks;
while (1) {
last unless @pblocks; $v0 = shift @pblocks; $v1 = shift @pblocks;
($c0,$c1) = tea_code($v0^$c0, $v1^$c1, @key);
push @cblocks, $c0, $c1;
}
return str2ascii( binary2str(@cblocks) );
}
sub decrypt { my ($acstr, $key) = @_; # decodes with CBC
use integer;
return '' unless $acstr; return '' unless $key;
@key = binarydigest($key);
my $v0; my $v1; my $c0; my $c1; my @pblocks = (); my $de0; my $de1;
my $lastc0 = 0x61626364; my $lastc1 = 0x62636465; # CBC Init Val. Retain!
my @cblocks = str2binary( ascii2str($acstr) );
while (1) {
last unless @cblocks; $c0 = shift @cblocks; $c1 = shift @cblocks;
($de0, $de1) = tea_decode($c0,$c1, @key);
$v0 = $lastc0 ^ $de0; $v1 = $lastc1 ^ $de1;
push @pblocks, $v0, $v1;
$lastc0 = $c0; $lastc1 = $c1;
}
my $str = binary2str(@pblocks);
# remove no of pad chars at end specified by 1 char ('0'..'7') at front
// --- TEA stuff, translated from the Perl Tea_JS.pm see www.pjb.com.au/comp ---
// In JavaScript we express an 8-byte block as an array of 2 32-bit ints
function asciidigest (str) {
return binary2ascii( binarydigest(str) );
}
function binarydigest (str, keystr) { // returns 22-char ascii signature
var key = new Array(); // key = binarydigest(keystr);
key[0]=0x61626364; key[1]=0x62636465; key[2]=0x63646566; key[3]=0x64656667;
// Initial Value for CBC mode = "abcdbcde". Retain for interoperability.
var c0 = new Array(); c0[0] = 0x61626364; c0[1] = 0x62636465;
var c1 = new Array(); c1 = c0;
var v0 = new Array(); var v1 = new Array(); var swap;
var blocks = new Array(); blocks = bytes2blocks(digest_pad(str2bytes(str)));
var ibl = 0; var nbl = blocks.length;
while (1) {
if (ibl >= nbl) break;
v0[0] = blocks[ibl]; ibl++; v0[1] = blocks[ibl]; ibl++;
v1[0] = blocks[ibl]; ibl++; v1[1] = blocks[ibl]; ibl++;
// cipher them XOR'd with previous stage ...
c0 = tea_code( xor_blocks(v0,c0), key );
c1 = tea_code( xor_blocks(v1,c1), key );
// mix up the two cipher blocks with a 32-bit left rotation ...
swap=c0[0]; c0[0]=c0[1]; c0[1]=c1[0]; c1[0]=c1[1]; c1[1]=swap;
}
var concat = new Array();
concat[0]=c0[0]; concat[1]=c0[1]; concat[2]=c1[0]; concat[3]=c1[1];
return concat;
}
function encrypt (str,keystr) { // encodes with CBC (Cipher Block Chaining)
if (! keystr) { alert("encrypt: no key"); return false; }
var key = new Array(); key = binarydigest(keystr);
if (! str) return "";
var blocks = new Array(); blocks = bytes2blocks(pad(str2bytes(str)));
var ibl = 0; var nbl = blocks.length;
// Initial Value for CBC mode = "abcdbcde". Retain for interoperability.
var c = new Array(); c[0] = 0x61626364; c[1] = 0x62636465;
var v = new Array(); var cblocks = new Array(); var icb = 0;
while (1) {
if (ibl >= nbl) break;
v[0] = blocks[ibl]; ibl++; v[1] = blocks[ibl]; ibl++;
c = tea_code( xor_blocks(v,c), key );
cblocks[icb] = c[0]; icb++; cblocks[icb] = c[1]; icb++;
}
return binary2ascii(cblocks);
}
function decrypt (ascii, keystr) { // decodes with CBC
if (! keystr) { alert("decrypt: no key"); return false; }
var key = new Array(); key = binarydigest(keystr);
if (! ascii) return "";
var cblocks = new Array(); cblocks = ascii2binary(ascii);
var icbl = 0; var ncbl = cblocks.length;
// Initial Value for CBC mode = "abcdbcde". Retain for interoperability.
var lastc = new Array(); lastc[0] = 0x61626364; lastc[1] = 0x62636465;
var v = new Array(); var c = new Array();
var blocks = new Array(); var ibl = 0;
while (1) {
if (icbl >= ncbl) break;
c[0] = cblocks[icbl]; icbl++; c[1] = cblocks[icbl]; icbl++;
v = xor_blocks( lastc, tea_decode(c,key) );
blocks[ibl] = v[0]; ibl++; blocks[ibl] = v[1]; ibl++;
lastc[0] = c[0]; lastc[1] = c[1];
}
Version 2.23
(c) Peter J Billam 1998
=head1 SUBROUTINES
=over 3
=item I<encrypt>( $plaintext, $key );
Encrypts with CBC (Cipher Block Chaining)
=item I<decrypt>( $cyphertext, $key );
Decrypts with CBC (Cipher Block Chaining)
=item I<asciidigest>( $a_string );
Returns an asciified binary signature of the argument.
=item I<tea_in_javascript>();
Returns a compatible implementation of TEA in JavaScript,
for use in CGI scripts to communicate with browsers.
tea -o # set Old mode, for Crypt::Tea-compatibility
tea -h # prints this Helpful message
ps axww | tea -s # have the best password in town !
For encryption and decryption, you will be asked for a password.
It should be a sufficiently longish string; say 17 random 8-bit bytes.
=head1 DESCRIPTION
This script uses the Crypt::Tea_JS.pm module to offer new TEA, the Tiny
Encryption Algorithm, and some Modes of Use based on CBC, compatibly in
both Perl and JavaScript.
The various options offer encryption, decryption and digest, and all
cyphertext is ascii-encoded to prevent munging. Another option returns
JavaScript code which offers identical functions in JS, and this can
be used by CGIs to feed to the browser.
=head1 AUTHOR
Peter J Billam ( http://www.pjb.com.au/comp/contact.html )
( run in 0.861 second using v1.01-cache-2.11-cpan-e1769b4cff6 )