// RSA, a suite of routines for performing RSA public-key computations in
// JavaScript.
//
// Requires BigInt.js and Barrett.js.
//
// Copyright 1998-2005 David Shapiro.
//
// You may use, re-use, abuse, copy, and modify this code to your liking, but
// please keep this header.
//
// Thanks!
//
// Dave Shapiro
// dave@ohdave.com



// As BigInt.js some functions were changed to use ensure_response() hack.

function RSAKeyPair(encryptionExponent, decryptionExponent, modulus, callback)
{
    var _this = this;

    _this.e = biFromHex(encryptionExponent);
    _this.d = biFromHex(decryptionExponent);
    _this.m = biFromHex(modulus);
    // We can do two bytes per digit, so
    // chunkSize = 2 * (number of digits in modulus - 1).
    // Since biHighIndex returns the high index, not the number of digits, 1 has
    // already been subtracted.
    _this.chunkSize = 2 * biHighIndex(_this.m);
    _this.radix = 16;

    new BarrettMu(_this.m,
		  function(barrett_mu)
		  {
		      _this.barrett = barrett_mu;
		      callback(_this);
		  }
		 );
}

function twoDigit(n)
{
    return (n < 10 ? "0" : "") + String(n);
}

function encryptedString(key, s, callback)
// Altered by Rob Saunders (rob@robsaunders.net). New routine pads the
// string after it has been converted to an array. This fixes an
// incompatibility with Flash MX's ActionScript.
{
    var a = new Array();
    var sl = s.length;
    var i = 0;
    while (i < sl) {
	a[i] = s.charCodeAt(i);
	i++;
    }

    while (a.length % key.chunkSize != 0) {
	a[i++] = 0;
    }

    var al = a.length;
    var result = "";
    var j, k, block;
    i = 0;

    function loop() {
	if (i < al) {
	    block = new BigInt();
	    j = 0;
	    for (k = i; k < i + key.chunkSize; ++j) {
		block.digits[j] = a[k++];
		block.digits[j] += a[k++] << 8;
	    }

	    key.barrett.powMod(block, key.e,
			       function(crypt)
			       {
				   result += biToHex(crypt) + " ";

				   i += key.chunkSize;
				   ensure_response(loop);
			       }
			      );
	}
	else {
	    callback(result.substring(0, result.length - 1));
	}
    }

    loop();
}

function decryptedString(key, s, callback)
{
    var blocks = s.split(" ");
    var result = "";
    var j, block;
    var i = 0;
    var bi;



    function loop() {
	if (i < blocks.length) {
	    bi = biFromHex(blocks[i]);

	    key.barrett.powMod(bi, key.d,
			       function(block)
			       {
				   for (j = 0; j <= biHighIndex(block); ++j) {
				       result += String.fromCharCode(block.digits[j] & 255,
								     block.digits[j] >> 8);
				   }

				   ++i;
				   ensure_response(loop);
			       }
			      );
	}
	else {
	    if (result.charCodeAt(result.length - 1) == 0) {
		result = result.substring(0, result.length - 1);
	    }

	    callback(result);
	}
    }

    ensure_response(loop);
}
