// This has been slightly modified by Billy Tetrud

// 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 
/*

To use:
	
1. Generate a key from somewhere and return: 
 * n - The modulus (usually labeled n or m)
 * d - The public key (can be called the encryption exponent, usually labeled e)
 * maxDigits - this is the precision needed to deal with the keys (The equation I'm using to calculate maxDigits is from jCryption: $keyLength*2/16+3 where keyLength is the number of bits in the key: 256, 512, 2048, etc)
 * d - Optionally, the private key (can be called the decryptionExponent, usually labeled d)
 * e.g. var keyset = {e: 10001, d:"401d06b9ebda5853d81897e1387ca301", n:"a72637d650403dbe0bc2548f9d6d0df9", maxDigits:19}

2. Create a new RSAKeyPair or RSAencryptionKey object
	* e.g. var keys = new RSAKeys(keyset)

3. Encrypt something with encryptedString
	* e.g. var encString = keys.enc("some string to encrypt");

4. Decrypt it (if you want and if you have the private key)
	* e.g. alert("This is the original string: '"+ keys.dec(encString) +"'");

*/

// javascript class RSAKeys
// encryptionExponent, decryptionExponent, modulus, maxDigits
// keyset should be an object with the following members:
	// e - the public key (optional if d is a member)
	// d - the private key (optional if e is a member)
	// n - the modulus
	// maxDigits - this is the precision needed to deal with the keys (The equation I'm using to calculate maxDigits is from jCryption: $keyLength*2/16+3 where keyLength is the number of bits in the key: 256, 512, 2048, etc)
function RSAKeys(keyset)
{	var self = this;
	
	var encryptionExponent = keyset["e"];
	var decryptionExponent = keyset["d"];
	var modulus = keyset["n"];
	var maxDigits = parseInt(keyset["maxDigits"]);
	
	setMaxDigits(maxDigits);
	if(encryptionExponent != undefined)
	{	this.e = biFromHex(encryptionExponent);
	}
	if(decryptionExponent != undefined)
	{	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;
	this.barrett = new BarrettMu(this.m);
	function twoDigit(n)
	{	return (n < 10 ? "0" : "") + String(n);
	}
	
	// encrypt
	self.enc = function(s)
	{	// 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 % self.chunkSize != 0) {
			a[i++] = 0;
		}
	
		var al = a.length;
		var result = "";
		var j, k, block;
		for (i = 0; i < al; i += self.chunkSize) {
			block = new BigInt();
			j = 0;
			for (k = i; k < i + self.chunkSize; ++j) {
				block.digits[j] = a[k++];
				block.digits[j] += a[k++] << 8;
			}
			var crypt = self.barrett.powMod(block, self.e);
			var text = self.radix == 16 ? biToHex(crypt) : biToString(crypt, self.radix);
			result += text + " ";
		}
		
		return result.substring(0, result.length - 1); // Remove last space.
	}
	
	// decrypt
	self.dec = function(s)
	{
		var blocks = s.split(" ");
		var result = "";
		var i, j, block;
		for (i = 0; i < blocks.length; ++i) {
			var bi;
			if (self.radix == 16) {
				bi = biFromHex(blocks[i]);
			}
			else {
				bi = biFromString(blocks[i], self.radix);
			}
			block = self.barrett.powMod(bi, self.d);
			for (j = 0; j <= biHighIndex(block); ++j) {
				result += String.fromCharCode(block.digits[j] & 255,
				                              block.digits[j] >> 8);
			}
		}
		// Remove trailing null, if any.
		if (result.charCodeAt(result.length - 1) == 0) {
			result = result.substring(0, result.length - 1);
		}
		return result;
	}
}

	
