/*
* Copyright (c) 2015 Eric Wilde.
* Copyright 1998-2015 David Shapiro.
* 
* RSA.js is a suite of routines for performing RSA public-key computations
* in JavaScript.  The cryptographic functions herein are used for encoding
* and decoding strings to be sent over unsecure channels.
*
* To use these routines, a pair of public/private keys is created through a
* number of means (OpenSSL tools on Linux/Unix, Dave Shapiro's
* RSAKeyGenerator program on Windows).  These keys are passed to RSAKeyPair
* as hexadecimal strings to create an encryption key object.  This key object
* is then used with encryptedString to encrypt blocks of plaintext using the
* public key.  The resulting cyphertext blocks can be decrypted with
* decryptedString.
*
* Note that the cryptographic functions herein are complementary to those
* found in CryptoFuncs.php and CryptoFuncs.pm.  Hence, encrypted messages may
* be sent between programs written in any of those languages.  The most
* useful, of course is to send messages encrypted by a Web page using RSA.js
* to a PHP or Perl script running on a Web servitron.
*
* Also, the optional padding flag may be specified on the call to
* encryptedString, in which case blocks of cyphertext that are compatible
* with real crypto libraries such as OpenSSL or Microsoft will be created.
* These blocks of cyphertext can then be sent to Web servitron that uses one
* of these crypto libraries for decryption.  This allows messages encrypted
* with longer keys to be decrypted quickly on the Web server as well as
* making for more secure communications when a padding algorithm such as
* PKCS1v1.5 is used.
*
* These routines require BigInt.js and Barrett.js.
*/

/*****************************************************************************/

/*
* Modifications
* -------------
*
* 2014 Jan 11  E. Wilde       Add optional padding flag to encryptedString
*                             for compatibility with real crypto libraries
*                             such as OpenSSL or Microsoft.  Add PKCS1v1.5
*                             padding.
*
* 2015 Jan 5  D. Shapiro      Add optional encoding flag for encryptedString
*                             and encapsulate padding and encoding constants
*                             in RSAAPP object.
*
* Original Code
* -------------
*
* 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
*/

/*****************************************************************************/

var RSAAPP = {};

RSAAPP.NoPadding = "NoPadding";
RSAAPP.PKCS1Padding = "PKCS1Padding";
RSAAPP.RawEncoding = "RawEncoding";
RSAAPP.NumericEncoding = "NumericEncoding"

/*****************************************************************************/

function RSAKeyPair(encryptionExponent, decryptionExponent, modulus, keylen)
/*
* encryptionExponent                    The encryption exponent (i.e. public
*                                       encryption key) to be used for
*                                       encrypting messages.  If you aren't
*                                       doing any encrypting, a dummy
*                                       exponent such as "10001" can be
*                                       passed.
*
* decryptionExponent                    The decryption exponent (i.e. private
*                                       decryption key) to be used for
*                                       decrypting messages.  If you aren't
*                                       doing any decrypting, a dummy
*                                       exponent such as "10001" can be
*                                       passed.
*
* modulus                               The modulus to be used both for
*                                       encrypting and decrypting messages.
*
* keylen                                The optional length of the key, in
*                                       bits.  If omitted, RSAKeyPair will
*                                       attempt to derive a key length (but,
*                                       see the notes below).
*
* returns                               The "new" object creator returns an
*                                       instance of a key object that can be
*                                       used to encrypt/decrypt messages.
*
* This routine is invoked as the first step in the encryption or decryption
* process to take the three numbers (expressed as hexadecimal strings) that
* are used for RSA asymmetric encryption/decryption and turn them into a key
* object that can be used for encrypting and decrypting.
*
* The key object is created thusly:
*
*      RSAKey = new RSAKeyPair("ABC12345", 10001, "987654FE");
*
* or:
*
*      RSAKey = new RSAKeyPair("ABC12345", 10001, "987654FE", 64);
*
* Note that RSAKeyPair will try to derive the length of the key that is being
* used, from the key itself.  The key length is especially useful when one of
* the padding options is used and/or when the encrypted messages created by
* the routine encryptedString are exchanged with a real crypto library such
* as OpenSSL or Microsoft, as it determines how many padding characters are
* appended.
*
* Usually, RSAKeyPair can determine the key length from the modulus of the
* key but this doesn't always work properly, depending on the actual value of
* the modulus.  If you are exchanging messages with a real crypto library,
* such as OpenSSL or Microsoft, that depends on the fact that the blocks
* being passed to it are properly padded, you'll want the key length to be
* set properly.  If that's the case, of if you just want to be sure, you
* should specify the key length that you used to generated the key, in bits
* when this routine is invoked.
*/
{
/*
* Convert from hexadecimal and save the encryption/decryption exponents and
* modulus as big integers in the key object.
*/
this.e = biFromHex(encryptionExponent);
this.d = biFromHex(decryptionExponent);
this.m = biFromHex(modulus);
/*
* Using big integers, we can represent two bytes per element in the big
* integer array, so we calculate the chunk size as:
*
*      chunkSize = 2 * (number of digits in modulus - 1)
*
* Since biHighIndex returns the high index, not the number of digits, the
* number 1 has already been subtracted from its answer.
*
* However, having said all this, "User Knows Best".  If our caller passes us
* a key length (in bits), we'll treat it as gospel truth.
*/
if (typeof(keylen) != 'number') { this.chunkSize = 2 * biHighIndex(this.m); }
else { this.chunkSize = keylen / 8; }

this.radix = 16;
/*
* Precalculate the stuff used for Barrett modular reductions.
*/
this.barrett = new BarrettMu(this.m);
}

/*****************************************************************************/

function encryptedString(key, s, pad, encoding)
/*
* key                                   The previously-built RSA key whose
*                                       public key component is to be used to
*                                       encrypt the plaintext string.
*
* s                                     The plaintext string that is to be
*                                       encrypted, using the RSA assymmetric
*                                       encryption method.
*
* pad                                   The optional padding method to use
*                                       when extending the plaintext to the
*                                       full chunk size required by the RSA
*                                       algorithm.  To maintain compatibility
*                                       with other crypto libraries, the
*                                       padding method is described by a
*                                       string.  The default, if not
*                                       specified is "OHDave".  Here are the
*                                       choices:
*
*                                         OHDave - this is the original
*                                           padding method employed by Dave
*                                           Shapiro and Rob Saunders.  If
*                                           this method is chosen, the
*                                           plaintext can be of any length.
*                                           It will be padded to the correct
*                                           length with zeros and then broken
*                                           up into chunks of the correct
*                                           length before being encrypted.
*                                           The resultant cyphertext blocks
*                                           will be separated by blanks.
*
*                                           Note that the original code by
*                                           Dave Shapiro reverses the byte
*                                           order to little-endian, as the
*                                           plaintext is encrypted.  If
*                                           either these JavaScript routines
*                                           or one of the complementary
*                                           PHP/Perl routines derived from
*                                           this code is used for decryption,
*                                           the byte order will be reversed
*                                           again upon decryption so as to
*                                           come out correctly.
*                                           
*                                           Also note that this padding
*                                           method is claimed to be less
*                                           secure than PKCS1Padding.
*
*                                         NoPadding - this method truncates
*                                           the plaintext to the length of
*                                           the RSA key, if it is longer.  If
*                                           its length is shorter, it is
*                                           padded with zeros.  In either
*                                           case, the plaintext string is
*                                           reversed to preserve big-endian
*                                           order before it is encrypted to
*                                           maintain compatibility with real
*                                           crypto libraries such as OpenSSL
*                                           or Microsoft.  When the
*                                           cyphertext is to be decrypted
*                                           by a crypto library, the
*                                           library routine's RSAAPP.NoPadding
*                                           flag, or its equivalent, should
*                                           be used.
*
*                                           Note that this padding method is
*                                           claimed to be less secure than
*                                           PKCS1Padding.
*
*                                         PKCS1Padding - the PKCS1v1.5
*                                           padding method (as described in
*                                           RFC 2313) is employed to pad the
*                                           plaintext string.  The plaintext
*                                           string must be no longer than the
*                                           length of the RSA key minus 11,
*                                           since PKCS1v1.5 requires 3 bytes
*                                           of overhead and specifies a
*                                           minimum pad of 8 bytes.  The
*                                           plaintext string is padded with
*                                           randomly-generated bytes and then
*                                           its order is reversed to preserve
*                                           big-endian order before it is
*                                           encrypted to maintain
*                                           compatibility with real crypto
*                                           libraries such as OpenSSL or
*                                           Microsoft.  When the cyphertext
*                                           is to be decrypted by a crypto
*                                           library, the library routine's
*                                           RSAAPP.PKCS1Padding flag, or its
*                                           equivalent, should be used.
*
* encoding                              The optional encoding scheme to use
*                                       for the return value. If ommitted,
*                                       numeric encoding will be used.
*
*                                           RawEncoding - The return value
*                                           is given as its raw value.
*                                           This is the easiest method when
*                                           interoperating with server-side
*                                           OpenSSL, as no additional conversion
*                                           is required. Use the constant
*                                           RSAAPP.RawEncoding for this option.
*
*                                           NumericEncoding - The return value
*                                           is given as a number in hexadecimal.
*                                           Perhaps useful for debugging, but
*                                           will need to be translated back to
*                                           its raw equivalent (e.g. using
*                                           PHP's hex2bin) before using with
*                                           OpenSSL. Use the constant
*                                           RSAAPP.NumericEncoding for this option.
*
* returns                               The cyphertext block that results
*                                       from encrypting the plaintext string
*                                       s with the RSA key.
*
* This routine accepts a plaintext string that is to be encrypted with the
* public key component of the previously-built RSA key using the RSA
* assymmetric encryption method.  Before it is encrypted, the plaintext
* string is padded to the same length as the encryption key for proper
* encryption.
*
* Depending on the padding method chosen, an optional header with block type
* is prepended, the plaintext is padded using zeros or randomly-generated
* bytes, and then the plaintext is possibly broken up into chunks.
*
* Note that, for padding with zeros, this routine was altered by Rob Saunders
* (rob@robsaunders.net). The new routine pads the string after it has been
* converted to an array. This fixes an incompatibility with Flash MX's
* ActionScript.
*
* The various padding schemes employed by this routine, and as presented to
* RSA for encryption, are shown below.  Note that the RSA encryption done
* herein reverses the byte order as encryption is done:
*
*      Plaintext In
*      ------------
*
*      d5 d4 d3 d2 d1 d0
*
*      OHDave
*      ------
*
*      d5 d4 d3 d2 d1 d0 00 00 00 /.../ 00 00 00 00 00 00 00 00
*
*      NoPadding
*      ---------
*
*      00 00 00 00 00 00 00 00 00 /.../ 00 00 d0 d1 d2 d3 d4 d5
*
*      PKCS1Padding
*      ------------
*
*      d0 d1 d2 d3 d4 d5 00 p0 p1 /.../ p2 p3 p4 p5 p6 p7 02 00
*                            \------------  ------------/
*                                         \/
*                             Minimum 8 bytes pad length
*/
{
var a = new Array();                    // The usual Alice and Bob stuff
var sl = s.length;                      // Plaintext string length
var i, j, k;                            // The usual Fortran index stuff
var padtype;                            // Type of padding to do
var encodingtype;                       // Type of output encoding
var rpad;                               // Random pad
var al;                                 // Array length
var result = "";                        // Cypthertext result
var block;                              // Big integer block to encrypt
var crypt;                              // Big integer result
var text;                               // Text result
/*
* Figure out the padding type.
*/
if (typeof(pad) == 'string') {
  if (pad == RSAAPP.NoPadding) { padtype = 1; }
  else if (pad == RSAAPP.PKCS1Padding) { padtype = 2; }
  else { padtype = 0; }
}
else { padtype = 0; }
/*
* Determine encoding type.
*/
if (typeof(encoding) == 'string' && encoding == RSAAPP.RawEncoding) {
	encodingtype = 1;
}
else { encodingtype = 0; }

/*
* If we're not using Dave's padding method, we need to truncate long
* plaintext blocks to the correct length for the padding method used:
*
*       NoPadding    - key length
*       PKCS1Padding - key length - 11
*/
if (padtype == 1) {
  if (sl > key.chunkSize) { sl = key.chunkSize; }
}
else if (padtype == 2) {
  if (sl > (key.chunkSize-11)) { sl = key.chunkSize - 11; }
}
/*
* Convert the plaintext string to an array of characters so that we can work
* with individual characters.
*
* Note that, if we're talking to a real crypto library at the other end, we
* reverse the plaintext order to preserve big-endian order.
*/
i = 0;

if (padtype == 2) { j = sl - 1; }
else { j = key.chunkSize - 1; }

while (i < sl) {
  if (padtype) { a[j] = s.charCodeAt(i); }
  else { a[i] = s.charCodeAt(i); }

  i++; j--;
}
/*
* Now is the time to add the padding.
*
* If we're doing PKCS1v1.5 padding, we pick up padding where we left off and
* pad the remainder of the block.  Otherwise, we pad at the front of the
* block.  This gives us the correct padding for big-endian blocks.
*
* The padding is either a zero byte or a randomly-generated non-zero byte.
*/
if (padtype == 1) { i = 0; }

j = key.chunkSize - (sl % key.chunkSize);

while (j > 0) {
  if (padtype == 2) {
    rpad = Math.floor(Math.random() * 256);

    while (!rpad) { rpad = Math.floor(Math.random() * 256); }

    a[i] = rpad;
  }
  else { a[i] = 0; }

  i++; j--;
}
/*
* For PKCS1v1.5 padding, we need to fill in the block header.
*
* According to RFC 2313, a block type, a padding string, and the data shall
* be formatted into the encryption block:
*
*      EncrBlock = 00 || BlockType || PadString || 00 || Data
*
* The block type shall be a single octet indicating the structure of the
* encryption block. For this version of the document it shall have value 00,
* 01, or 02. For a private-key operation, the block type shall be 00 or 01.
* For a public-key operation, it shall be 02.
*
* The padding string shall consist of enough octets to pad the encryption
* block to the length of the encryption key.  For block type 00, the octets
* shall have value 00; for block type 01, they shall have value FF; and for
* block type 02, they shall be pseudorandomly generated and nonzero.
*
* Note that in a previous step, we wrote padding bytes into the first three
* bytes of the encryption block because it was simpler to do so.  We now
* overwrite them.
*/
if (padtype == 2)
  {
  a[sl] = 0;
  a[key.chunkSize-2] = 2;
  a[key.chunkSize-1] = 0;
  }
/*
* Carve up the plaintext and encrypt each of the resultant blocks.
*/
al = a.length;

for (i = 0; i < al; i += key.chunkSize) {
  /*
  * Get a block.
  */
  block = new BigInt();

  j = 0;

  for (k = i; k < (i+key.chunkSize); ++j) {
    block.digits[j] = a[k++];
    block.digits[j] += a[k++] << 8;
  }
  /*
  * Encrypt it, convert it to text, and append it to the result.
  */
  crypt = key.barrett.powMod(block, key.e);
  if (encodingtype == 1) {
	  text = biToBytes(crypt);
  }
  else {
	  text = (key.radix == 16) ? biToHex(crypt) : biToString(crypt, key.radix);
  }
  result += text;
}
/*
* Return the result, removing the last space.
*/
//result = (result.substring(0, result.length - 1));
return result;
}

/*****************************************************************************/

function decryptedString(key, c)
/*
* key                                   The previously-built RSA key whose
*                                       private key component is to be used
*                                       to decrypt the cyphertext string.
*
* c                                     The cyphertext string that is to be
*                                       decrypted, using the RSA assymmetric
*                                       encryption method.
*
* returns                               The plaintext block that results from
*                                       decrypting the cyphertext string c
*                                       with the RSA key.
*
* This routine is the complementary decryption routine that is meant to be
* used for JavaScript decryption of cyphertext blocks that were encrypted
* using the OHDave padding method of the encryptedString routine (in this
* module).  It can also decrypt cyphertext blocks that were encrypted by
* RSAEncode (in CryptoFuncs.pm or CryptoFuncs.php) so that encrypted
* messages can be sent of insecure links (e.g. HTTP) to a Web page.
*
* It accepts a cyphertext string that is to be decrypted with the public key
* component of the previously-built RSA key using the RSA assymmetric
* encryption method.  Multiple cyphertext blocks are broken apart, if they
* are found in c, and each block is decrypted.  All of the decrypted blocks
* are concatenated back together to obtain the original plaintext string.
*
* This routine assumes that the plaintext was padded to the same length as
* the encryption key with zeros.  Therefore, it removes any zero bytes that
* are found at the end of the last decrypted block, before it is appended to
* the decrypted plaintext string.
*
* Note that the encryptedString routine (in this module) works fairly quickly
* simply by virtue of the fact that the public key most often chosen is quite
* short (e.g. 0x10001).  This routine does not have that luxury.  The
* decryption key that it must employ is the full key length.  For long keys,
* this can result in serious timing delays (e.g. 7-8 seconds to decrypt using
* 2048 bit keys on a reasonably fast machine, under the Firefox Web browser).
*
* If you intend to send encrypted messagess to a JavaScript program running
* under a Web browser, you might consider using shorter keys to keep the
* decryption times low.  Alternately, a better scheme is to generate a random
* key for use by a symmetric encryption algorithm and transmit it to the
* other end, after encrypting it with encryptedString.  The other end can use
* a real crypto library (e.g. OpenSSL or Microsoft) to decrypt the key and
* then use it to encrypt all of the messages (with a symmetric encryption
* algorithm such as Twofish or AES) bound for the JavaScript program.
* Symmetric decryption is orders of magnitude faster than asymmetric and
* should yield low decryption times, even when executed in JavaScript.
*
* Also note that only the OHDave padding method (e.g. zeros) is supported by
* this routine *AND* that this routine expects little-endian cyphertext, as
* created by the encryptedString routine (in this module) or the RSAEncode
* routine (in either CryptoFuncs.pm or CryptoFuncs.php).  You can use one of
* the real crypto libraries to create cyphertext that can be decrypted by
* this routine, if you reverse the plaintext byte order first and then
* manually pad it with zero bytes.  The plaintext should then be encrypted
* with the NoPadding flag or its equivalent in the crypto library of your
* choice.
*/
{
var blocks = c.split(" ");              // Multiple blocks of cyphertext
var b;                                  // The usual Alice and Bob stuff
var i, j;                               // The usual Fortran index stuff
var bi;                                 // Cyphertext as a big integer
var result = "";                        // Plaintext result
/*
* Carve up the cyphertext into blocks.
*/
for (i = 0; i < blocks.length; ++i) {
  /*
  * Depending on the radix being used for the key, convert this cyphertext
  * block into a big integer.
  */
  if (key.radix == 16) { bi = biFromHex(blocks[i]); }
  else { bi = biFromString(blocks[i], key.radix); }
  /*
  * Decrypt the cyphertext.
  */
  b = key.barrett.powMod(bi, key.d);
  /*
  * Convert the decrypted big integer back to the plaintext string.  Since
  * we are using big integers, each element thereof represents two bytes of
  * plaintext.
  */
  for (j = 0; j <= biHighIndex(b); ++j) {
    result += String.fromCharCode(b.digits[j] & 255, b.digits[j] >> 8);
  }
}
/*
* Remove trailing null, if any.
*/
if (result.charCodeAt(result.length - 1) == 0) {
  result = result.substring(0, result.length - 1);
}
/*
* Return the plaintext.
*/
return (result);
}
