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