﻿
// Hashing SHA-1

var SHA1_BLOCK_SIZE	 = 64;
var SHA1_DIGEST_SIZE = 20;
var SHA1_LEN_SIZE	 = 8;

var MAX_UNSIGNED = 4294967296;
function SAFE_MOD (x) { while ( x > MAX_UNSIGNED ) x -= MAX_UNSIGNED; while ( x < 0 ) x += MAX_UNSIGNED; return x }

var K1 = SAFE_MOD(0x5A827999);
var K2 = SAFE_MOD(0x6ED9EBA1);
var K3 = SAFE_MOD(0x8F1BBCDC);
var K4 = SAFE_MOD(0xCA62C1D6);

function F1 (x, y, z) { return (((x) & (y)) | ((~x) & (z))) }
function F2 (x, y, z) { return ((x) ^ (y) ^ (z)) }
function F3 (x, y, z) { return (((x) & (y)) | ((x) & (z)) | ((y) &(z))) }
function F4 (x, y, z) { return ((x) ^ (y) ^ (z)) }

var ctx_digest		= new Array(SHA1_DIGEST_SIZE); 
var ctx_running		= false;
var ctx_H			= [0, 0, 0, 0, 0];
var ctx_counter 	= [0, 0];
var ctx_buffer		= new Array(SHA1_BLOCK_SIZE);
var ctx_bufferLen	= 0;

function ROTATE_LEFT (x, n) { return (x << n) | (x >>> (32-n)) }     

function SHA1Transform ()
{
    var i, j, temp;
    var w = new Array(80); 
    var a = ctx_H[0];
    var b = ctx_H[1]; 
    var c = ctx_H[2];
    var d = ctx_H[3]; 
	var e = ctx_H[4];

    for (i=0, j=0; i<16; i++, j+=4)
        w[i] = (ctx_buffer[j]<<24) + (ctx_buffer[j+1]<<16)
                +(ctx_buffer[j+2]<<8) + ctx_buffer[j+3];
    for (i=16; i<80; i++)
    {
        w[i] = w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16];
        w[i] = ROTATE_LEFT(w[i], 1);
    }
    
    for (i=0; i<20; i++)
    {
        temp = SAFE_MOD(ROTATE_LEFT(a, 5) + F1(b, c, d) + e + w[i] + K1);
        e = d;
        d = c;
        c = ROTATE_LEFT(b, 30);
        b = a;
        a = temp;
    }

    for (i=20; i<40; i++)
    {
        temp = SAFE_MOD(ROTATE_LEFT(a, 5) + F2(b, c, d) + e + w[i] + K2);
        e = d;
        d = c;
        c = ROTATE_LEFT(b, 30);
        b = a;
        a = temp;
    }

    for (i=40; i<60; i++)
    {
        temp = SAFE_MOD(ROTATE_LEFT(a, 5) + F3(b, c, d) + e + w[i] + K3);
        e = d;
        d = c;
        c = ROTATE_LEFT(b, 30);
        b = a;
        a = temp;
    }
    
    for (i=60; i<80; i++)
    {
        temp = SAFE_MOD(ROTATE_LEFT(a, 5) + F4(b, c, d) + e + w[i] + K4);
        e = d;
        d = c;
        c = ROTATE_LEFT(b, 30);
        b = a;
        a = temp;
    }
    ctx_H[0] += a; 
    ctx_H[1] += b;
    ctx_H[2] += c;
    ctx_H[3] += d;
    ctx_H[4] += e;

	for (i=0; i<5; i++)
		ctx_H[i] = SAFE_MOD(ctx_H[i]); 
}


function SHA1Encode(aByte, aLong, start, nByte)
{
    var i, j, end;

	end = start + nByte
    for (i=0, j=start; j<end; i++) 
    {
    	aByte[j++] = (aLong[i] >> 24) & 0xff; 
    	aByte[j++] = (aLong[i] >> 16) & 0xff; 
    	aByte[j++] = (aLong[i] >>  8) & 0xff; 
    	aByte[j++] =  aLong[i] & 0xff;
    }
} 


function SHA1Start(block, blockSize)
{
	if (blockSize > SHA1_BLOCK_SIZE) 
		return false;
	ctx_running = true;
    ctx_H[0] = SAFE_MOD(0x67452301);
    ctx_H[1] = SAFE_MOD(0xEFCDAB89);
    ctx_H[2] = SAFE_MOD(0x98BADCFE);
    ctx_H[3] = SAFE_MOD(0x10325476);
    ctx_H[4] = SAFE_MOD(0xC3D2E1F0);
    ctx_counter[0] = 0;
    ctx_counter[1] = blockSize * 8;
    for (i=0; i<SHA1_BLOCK_SIZE; i++)
		ctx_buffer[i] = block.charCodeAt(i) & 0xff;
    ctx_bufferLen = blockSize;
    return true;
}


function SHA1Next(block, start, blockSize)
{
	if (ctx_running == false) 
		return false;
	if (blockSize > SHA1_BLOCK_SIZE) 
    {
		ctx_running = false;
		return false;
    }

	if (ctx_bufferLen < SHA1_BLOCK_SIZE) 
    {
		ctx_running = false;
	    return false;
    }

    SHA1Transform();

	for (i=0; i<blockSize; i++)
		ctx_buffer[i] = block.charCodeAt(start+i) & 0xff;	
	ctx_bufferLen = blockSize;

    if ((ctx_counter[1] += blockSize * 8) < (blockSize * 8))
	        ctx_counter[0]++;
	return true;
}


function SHA1End()
{
    if (ctx_running == false)
        return null;

    if (ctx_bufferLen < SHA1_BLOCK_SIZE - SHA1_LEN_SIZE)
    { 
	    ctx_buffer[ctx_bufferLen] = 0x80;
		if (ctx_bufferLen + 1 < SHA1_BLOCK_SIZE - SHA1_LEN_SIZE)
			for (i=0; i<SHA1_BLOCK_SIZE - (SHA1_LEN_SIZE + ctx_bufferLen + 1); i++)
				ctx_buffer[ctx_bufferLen + 1 + i] = 0;
    }
    else if (ctx_bufferLen == SHA1_BLOCK_SIZE) 
    {
	    SHA1Transform();
	    ctx_buffer[0] = 0x80;
		for (i=0; i<SHA1_BLOCK_SIZE - (SHA1_LEN_SIZE + 1); i++)
			ctx_buffer[1+i] = 0;
    }
    else 
    {
   	    ctx_buffer[ctx_bufferLen] = 0x80;
	    if (ctx_bufferLen + 1 < SHA1_BLOCK_SIZE)
			for (i=0; i<SHA1_BLOCK_SIZE - (ctx_bufferLen + 1); i++)
				ctx_buffer[ctx_bufferLen + 1 + i] = 0;
				
        SHA1Transform();
        for (i=0; i<SHA1_BLOCK_SIZE - SHA1_LEN_SIZE; i++)
			ctx_buffer[i] = 0;
    }
    
    SHA1Encode(ctx_buffer, ctx_counter, SHA1_BLOCK_SIZE - SHA1_LEN_SIZE, SHA1_LEN_SIZE);
    SHA1Transform();
    SHA1Encode(ctx_digest, ctx_H, 0, SHA1_DIGEST_SIZE);
    
    ctx_running = false;
    return ctx_digest;
} 


function ByteToEsa(byteArray, start, nLong)
{
	var end = start+nLong;
	var strEsa = "";
	for (i=start; i<end; i++)
	{
		h = (byteArray[i] & 0xf0) >> 4;
		l = (byteArray[i] & 0x0f);
		strEsa += ESA_CHAR[h] + ESA_CHAR[l];
	}
	return strEsa;
}


function SHA1EsaDigest(msg, msgLen)
{
	var i, len, digest;

	esaDigest = "";
	len = (msgLen > SHA1_BLOCK_SIZE) ? SHA1_BLOCK_SIZE : msgLen;
    if ( !SHA1Start(msg, len) )
		return null;
    if ( msgLen > len )
    {
		for (i=SHA1_BLOCK_SIZE; i<msgLen; i+=SHA1_BLOCK_SIZE)
        {
			len = ((msgLen-i) > SHA1_BLOCK_SIZE) ? SHA1_BLOCK_SIZE : (msgLen-i);
            if ( !SHA1Next(msg, i, len) )
				return null;
        }
	}
    digest = SHA1End();
	
    if ( digest == null )
		return null;

    return ByteToEsa(digest, 0, SHA1_DIGEST_SIZE);	 
}

