////////////////////////////////////////////////////////////////////////////
// SHA implementation v1.0
// Based on the SHA algorithm as given in "Applied Cryptography"
// Code written by Chris Monson (chris@bouncingchairs.net)
// Most recent version available on http://bouncingchairs.net
// Licensed under the GNU LGPL (http://www.gnu.org/copyleft/lesser.html)
// March 17, 2000
// License changed (I'm an idiot) June 26, 2000
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// USAGE:
//------------------------------------------------------------------------
//
// Simple text hash:
//
// sha = new SHA;
// hasharray = sha.hash_text( 'hash me!' );
//
// This returns an array of 5 32-bit integers.
// The SHA.hash_bytes function does the same thing, but requires
// an array of bytes as input. Note that the input values will be
// truncated if they are larger than 8 bits.
//
//------------------------------------------------------------------------
//
// There are also some hash to string conversion functions. The
// naming convention admittedly could be better, but it works :).
//
// sha.block_to_string( hasharray )
//
// Converts the hash array to an uppercase hex string.
//
//------------------------------------------------------------------------
//
// Hashing very large blocks a piece at a time:
//
// sha = new SHA;
// sha.init();
// while (blocks_to_process) {
// sha.update( next_byte_array )
// }
// hasharray = sha.finalize()
//
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// NOTES:
// I have done little optimization. Actually, I haven't done any at
// all on this code. I wrote it because I wanted to do a secure
// challenge response login over an insecure connection. To do that,
// I needed a secure hash algorithm on the client and on the server.
// JavaScript was the obvious choice for the client side code, and it
// works very well for hashing things like passwords and random tokens.
// It is relatively small and it runs fast enough that I don't notice
// the download time nor the run time on a PII 266 over a 28.8
// connection.
//
// If anyone wants to make changes to this, they are, of course,
// absolutely welcome to do so, provided the GPL is honored, which
// includes giving the changes back to me so that I can distribute them
// back to the Free Software community.
//
// There may be bugs in this software. If so, also let me know and
// I'll happily fix them as quickly as I can. Patches are always
// more welcome than criticism, of course.
//
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// SHA Object
////////////////////////////////////////////////////////////////////////////
function SHA () {
// Set up the methods
this.init = SHA_init;
this.f00_19 = SHA_f00_19;
this.f20_39 = SHA_f20_39_60_79;
this.f40_59 = SHA_f40_59;
this.f60_79 = SHA_f20_39_60_79;
this.circ_shl = SHA_circ_shl;
this.pad_block = SHA_pad_block;
this.expand_block = SHA_expand_block;
this.to_string = SHA_to_string;
this.block_to_string = SHA_block_to_string;
this.bytes_to_words = SHA_bytes_to_words;
this.process_block = SHA_process_block;
this.update = SHA_update;
this.finalize = SHA_finalize;
this.hash_text = SHA_hash_text;
this.hash_bytes = SHA_hash_bytes;
// Initialize all variables
this.init();
}
////////////////////////////////////////////////////////////////////////////
// SHA Implementation
////////////////////////////////////////////////////////////////////////////
function SHA_init() {
this.A = 0x67452301
this.B = 0xefcdab89
this.C = 0x98badcfe
this.D = 0x10325476
this.E = 0xc3d2e1f0
this.a = this.A
this.b = this.B
this.c = this.C
this.d = this.D
this.e = this.E
this.K0_19 = 0x5a827999
this.K20_39 = 0x6ed9eba1
this.K40_59 = 0x8f1bbcdc
this.K60_79 = 0xca62c1d6
// Buffer for padding and updating
this.buffer = new Array()
// Current number of bytes in the buffer
this.buffsize = 0
// Total size processed so far
this.totalsize = 0
}
function SHA_f00_19( x, y, z )
{
return (x & y) | (~x & z);
}
function SHA_f20_39_60_79( x, y, z )
{
return (x ^ y ^ z);
}
function SHA_f40_59( x, y, z )
{
return (x & y) | (x & z) | (y & z);
}
function SHA_circ_shl( n, amt )
{
var leftmask = 0xFFFFFFFF;
leftmask <<= 32 - amt;
var rightmask = 0xFFFFFFFF;
rightmask <<= amt;
rightmask = ~rightmask;
var remains = n & leftmask;
remains >>= 32 - amt;
remains &= rightmask;
return (n << amt) | remains;
}
// This padding function is limited to a size of 2^32 bits, rather than 2^64
// as dictated by the spec
function SHA_pad_block( last_block, size )
{
// Returns a block that is a multiple of 512 bits (64 bytes) long.
// 'size' is the total number of bytes in the message
var newblock = new Array()
// Adds padding to a block.
var blksize = last_block.length
var bits = size * 8
// Always pad with 0x80, then add as many zeros as necessary to
// make the message 64 bits short of 512. Then add the 64-bit size.
var i
for (i=0; i<blksize; ++i)
{
newblock[i] = last_block[i]
}
// Add 0x80
newblock[blksize] = 0x80
// Add the zeros
while ((newblock.length % 64) != 56)
{
newblock[newblock.length] = 0;
}
// Add the size (in bytes)
for (i=0; i<8; ++i)
{
newblock[newblock.length] =
(i<4) ? 0 : (bits >> ((7-i)*8)) & 0xff
}
return newblock
}
// Converts the message block of 16 words into a block of 80 words
function SHA_expand_block( block )
{
var nblk = new Array()
var i
for (i=0; i<16; ++i)
{
nblk[i] = block[i]
}
for (i=16; i<80; ++i)
{
nblk[i] = this.circ_shl(
nblk[i-3] ^ nblk[i-8] ^ nblk[i-14] ^ nblk[i-16],
1
)
}
return nblk
}
function SHA_to_string( val )
{
var str = ""
for (var i=0; i<8; ++i)
{
var shift = (7-i) * 4;
var nibble = (val >> shift) & 0x0f
str += nibble.toString( 16 )
}
return str.toUpperCase()
}
function SHA_block_to_string( val )
{
var str = ""
for (var v in val)
{
str += this.to_string( val[v] )
}
return str
}
// Requires the block to be 64 bytes long
function SHA_bytes_to_words( block )
{
var nblk = new Array()
var i
for (i=0; i<16; ++i)
{
var index = i*4
nblk[i] = 0;
nblk[i] |= (block[index] & 0xff) << 24
nblk[i] |= (block[index+1] & 0xff) << 16
nblk[i] |= (block[index+2] & 0xff) << 8
nblk[i] |= (block[index+3] & 0xff)
}
return nblk
}
/* Each message block is 16 32-bit words.
It is expected that the array will be of 32-bit words, not of bytes.
*/
function SHA_process_block( block )
{
var blk = this.expand_block( block );
var i
for (i=0; i<80; ++i)
{
with (this)
{
var temp = circ_shl( a, 5 );
if (i<20)
{
temp += f00_19( b, c, d ) + e + blk[i] + K0_19;
}
else if (i<40)
{
temp += f20_39( b, c, d ) + e + blk[i] + K20_39;
}
else if (i<60)
{
temp += f40_59( b, c, d ) + e + blk[i] + K40_59;
}
else
{
temp += f60_79( b, c, d ) + e + blk[i] + K60_79;
}
e = d;
d = c;
c = circ_shl( b, 30 );
b = a;
a = temp;
}
}
with (this)
{
A += a
B += b
C += c
D += d
E += e
}
}
// Pass in a byte array, and update will call process_block as needed
function SHA_update( bytes )
{
// If there are bytes in the buffer and the number of bytes here
// is sufficient to make a block, then process that initial block
var index=0
// Process each full block
while ((bytes.length - index) + this.buffsize >= 64)
{
// Copy the needed parts into the hash buffer
for( var i=this.buffsize; i<64; ++i)
{
this.buffer[i] = bytes[index + i - this.buffsize]
}
this.process_block( this.bytes_to_words( this.buffer ) )
index += 64 - this.buffsize
this.buffsize = 0
}
// Add the remaining bytes into the buffer
var remaining = bytes.length - index
for( var i=0; i<remaining; ++i)
{
this.buffer[this.buffsize + i] = bytes[index + i]
}
this.buffsize += remaining
this.totalsize += bytes.length
}
// Finalize returns the hash value after doing things like padding
function SHA_finalize()
{
// Clear out the hash buffer
var last_block = new Array()
for( var i=0; i<this.buffsize; ++i)
{
last_block[i] = this.buffer[i]
}
this.buffsize = 0
// Pad the last block
last_block = this.pad_block( last_block, this.totalsize )
// Process this last piece
// We do NOT call update here, since it updates the total size count,
// and that is not desired. The process_block function is called
// instead
var index = 0
while (index < last_block.length)
{
this.process_block(
this.bytes_to_words(
last_block.slice( index, index + 64 )
)
)
index += 64
}
var temp = new Array();
with (this)
{
temp[0] = A
temp[1] = B
temp[2] = C
temp[3] = D
temp[4] = E
}
return temp
}
function SHA_hash_bytes( bytes )
{
this.init()
this.update( bytes )
return this.finalize()
}
function SHA_hash_text( text )
{
// Create a byte array from the text (chop off the MSB of each char)
var bytes = new Array()
for (var i=0; i<text.length; ++i)
{
bytes[i] = text.charCodeAt(i) & 0xff
}
return this.hash_bytes( bytes )
}
|