| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 | /** * 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();  };}
 |