You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					169 lines
				
				5.1 KiB
			
		
		
			
		
	
	
					169 lines
				
				5.1 KiB
			| 
								 
											2 years ago
										 
									 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Javascript implementation of RSA-KEM.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @author Lautaro Cozzani Rodriguez
							 | 
						||
| 
								 | 
							
								 * @author Dave Longley
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * Copyright (c) 2014 Lautaro Cozzani <lautaro.cozzani@scytl.com>
							 | 
						||
| 
								 | 
							
								 * Copyright (c) 2014 Digital Bazaar, Inc.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								var forge = require('./forge');
							 | 
						||
| 
								 | 
							
								require('./util');
							 | 
						||
| 
								 | 
							
								require('./random');
							 | 
						||
| 
								 | 
							
								require('./jsbn');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								module.exports = forge.kem = forge.kem || {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var BigInteger = forge.jsbn.BigInteger;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * The API for the RSA Key Encapsulation Mechanism (RSA-KEM) from ISO 18033-2.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								forge.kem.rsa = {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Creates an RSA KEM API object for generating a secret asymmetric key.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * The symmetric key may be generated via a call to 'encrypt', which will
							 | 
						||
| 
								 | 
							
								 * produce a ciphertext to be transmitted to the recipient and a key to be
							 | 
						||
| 
								 | 
							
								 * kept secret. The ciphertext is a parameter to be passed to 'decrypt' which
							 | 
						||
| 
								 | 
							
								 * will produce the same secret key for the recipient to use to decrypt a
							 | 
						||
| 
								 | 
							
								 * message that was encrypted with the secret key.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param kdf the KDF API to use (eg: new forge.kem.kdf1()).
							 | 
						||
| 
								 | 
							
								 * @param options the options to use.
							 | 
						||
| 
								 | 
							
								 *          [prng] a custom crypto-secure pseudo-random number generator to use,
							 | 
						||
| 
								 | 
							
								 *            that must define "getBytesSync".
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								forge.kem.rsa.create = function(kdf, options) {
							 | 
						||
| 
								 | 
							
								  options = options || {};
							 | 
						||
| 
								 | 
							
								  var prng = options.prng || forge.random;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var kem = {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Generates a secret key and its encapsulation.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param publicKey the RSA public key to encrypt with.
							 | 
						||
| 
								 | 
							
								   * @param keyLength the length, in bytes, of the secret key to generate.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @return an object with:
							 | 
						||
| 
								 | 
							
								   *   encapsulation: the ciphertext for generating the secret key, as a
							 | 
						||
| 
								 | 
							
								   *     binary-encoded string of bytes.
							 | 
						||
| 
								 | 
							
								   *   key: the secret key to use for encrypting a message.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  kem.encrypt = function(publicKey, keyLength) {
							 | 
						||
| 
								 | 
							
								    // generate a random r where 1 < r < n
							 | 
						||
| 
								 | 
							
								    var byteLength = Math.ceil(publicKey.n.bitLength() / 8);
							 | 
						||
| 
								 | 
							
								    var r;
							 | 
						||
| 
								 | 
							
								    do {
							 | 
						||
| 
								 | 
							
								      r = new BigInteger(
							 | 
						||
| 
								 | 
							
								        forge.util.bytesToHex(prng.getBytesSync(byteLength)),
							 | 
						||
| 
								 | 
							
								        16).mod(publicKey.n);
							 | 
						||
| 
								 | 
							
								    } while(r.compareTo(BigInteger.ONE) <= 0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // prepend r with zeros
							 | 
						||
| 
								 | 
							
								    r = forge.util.hexToBytes(r.toString(16));
							 | 
						||
| 
								 | 
							
								    var zeros = byteLength - r.length;
							 | 
						||
| 
								 | 
							
								    if(zeros > 0) {
							 | 
						||
| 
								 | 
							
								      r = forge.util.fillString(String.fromCharCode(0), zeros) + r;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // encrypt the random
							 | 
						||
| 
								 | 
							
								    var encapsulation = publicKey.encrypt(r, 'NONE');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // generate the secret key
							 | 
						||
| 
								 | 
							
								    var key = kdf.generate(r, keyLength);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return {encapsulation: encapsulation, key: key};
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Decrypts an encapsulated secret key.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param privateKey the RSA private key to decrypt with.
							 | 
						||
| 
								 | 
							
								   * @param encapsulation the ciphertext for generating the secret key, as
							 | 
						||
| 
								 | 
							
								   *          a binary-encoded string of bytes.
							 | 
						||
| 
								 | 
							
								   * @param keyLength the length, in bytes, of the secret key to generate.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @return the secret key as a binary-encoded string of bytes.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  kem.decrypt = function(privateKey, encapsulation, keyLength) {
							 | 
						||
| 
								 | 
							
								    // decrypt the encapsulation and generate the secret key
							 | 
						||
| 
								 | 
							
								    var r = privateKey.decrypt(encapsulation, 'NONE');
							 | 
						||
| 
								 | 
							
								    return kdf.generate(r, keyLength);
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return kem;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// TODO: add forge.kem.kdf.create('KDF1', {md: ..., ...}) API?
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Creates a key derivation API object that implements KDF1 per ISO 18033-2.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param md the hash API to use.
							 | 
						||
| 
								 | 
							
								 * @param [digestLength] an optional digest length that must be positive and
							 | 
						||
| 
								 | 
							
								 *          less than or equal to md.digestLength.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @return a KDF1 API object.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								forge.kem.kdf1 = function(md, digestLength) {
							 | 
						||
| 
								 | 
							
								  _createKDF(this, md, 0, digestLength || md.digestLength);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Creates a key derivation API object that implements KDF2 per ISO 18033-2.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param md the hash API to use.
							 | 
						||
| 
								 | 
							
								 * @param [digestLength] an optional digest length that must be positive and
							 | 
						||
| 
								 | 
							
								 *          less than or equal to md.digestLength.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @return a KDF2 API object.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								forge.kem.kdf2 = function(md, digestLength) {
							 | 
						||
| 
								 | 
							
								  _createKDF(this, md, 1, digestLength || md.digestLength);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Creates a KDF1 or KDF2 API object.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param md the hash API to use.
							 | 
						||
| 
								 | 
							
								 * @param counterStart the starting index for the counter.
							 | 
						||
| 
								 | 
							
								 * @param digestLength the digest length to use.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @return the KDF API object.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function _createKDF(kdf, md, counterStart, digestLength) {
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Generate a key of the specified length.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param x the binary-encoded byte string to generate a key from.
							 | 
						||
| 
								 | 
							
								   * @param length the number of bytes to generate (the size of the key).
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @return the key as a binary-encoded string.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  kdf.generate = function(x, length) {
							 | 
						||
| 
								 | 
							
								    var key = new forge.util.ByteBuffer();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // run counter from counterStart to ceil(length / Hash.len)
							 | 
						||
| 
								 | 
							
								    var k = Math.ceil(length / digestLength) + counterStart;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var c = new forge.util.ByteBuffer();
							 | 
						||
| 
								 | 
							
								    for(var i = counterStart; i < k; ++i) {
							 | 
						||
| 
								 | 
							
								      // I2OSP(i, 4): convert counter to an octet string of 4 octets
							 | 
						||
| 
								 | 
							
								      c.putInt32(i);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // digest 'x' and the counter and add the result to the key
							 | 
						||
| 
								 | 
							
								      md.start();
							 | 
						||
| 
								 | 
							
								      md.update(x + c.getBytes());
							 | 
						||
| 
								 | 
							
								      var hash = md.digest();
							 | 
						||
| 
								 | 
							
								      key.putBytes(hash.getBytes(digestLength));
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // truncate to the correct key length
							 | 
						||
| 
								 | 
							
								    key.truncate(key.length() - length);
							 | 
						||
| 
								 | 
							
								    return key.getBytes();
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								}
							 |