Login   Register  
PHP Classes
elePHPant
Icontem

File: shaobj.js

Recommend this page to a friend!
Stumble It! Stumble It! Bookmark in del.icio.us Bookmark in del.icio.us
  Classes of Chris Monson  >  SHA  >  shaobj.js  >  Download  
File: shaobj.js
Role: ???
Content type: text/plain
Description: Pure Javascript Implementation of SHA
Class: SHA
Author: By
Last change:
Date: 2000-06-30 11:38
Size: 9,947 bytes
 

Contents

Class file image Download
////////////////////////////////////////////////////////////////////////////
// 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 )
}