| /** |
| * 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. |
| */ |
| (function() { |
| /* ########## Begin module implementation ########## */ |
| function initModule(forge) { |
| |
| 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.equals(BigInteger.ZERO)); |
| |
| // 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(); |
| }; |
| } |
| |
| } // end module implementation |
| |
| /* ########## Begin module wrapper ########## */ |
| var name = 'kem'; |
| if(typeof define !== 'function') { |
| // NodeJS -> AMD |
| if(typeof module === 'object' && module.exports) { |
| var nodeJS = true; |
| define = function(ids, factory) { |
| factory(require, module); |
| }; |
| } else { |
| // <script> |
| if(typeof forge === 'undefined') { |
| forge = {}; |
| } |
| return initModule(forge); |
| } |
| } |
| // AMD |
| var deps; |
| var defineFunc = function(require, module) { |
| module.exports = function(forge) { |
| var mods = deps.map(function(dep) { |
| return require(dep); |
| }).concat(initModule); |
| // handle circular dependencies |
| forge = forge || {}; |
| forge.defined = forge.defined || {}; |
| if(forge.defined[name]) { |
| return forge[name]; |
| } |
| forge.defined[name] = true; |
| for(var i = 0; i < mods.length; ++i) { |
| mods[i](forge); |
| } |
| return forge[name]; |
| }; |
| }; |
| var tmpDefine = define; |
| define = function(ids, factory) { |
| deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2); |
| if(nodeJS) { |
| delete define; |
| return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0)); |
| } |
| define = tmpDefine; |
| return define.apply(null, Array.prototype.slice.call(arguments, 0)); |
| }; |
| define(['require', 'module', './util','./random','./jsbn'], function() { |
| defineFunc.apply(null, Array.prototype.slice.call(arguments, 0)); |
| }); |
| })(); |