[6a3a178] | 1 | /**
|
---|
| 2 | * Javascript implementation of RSA-KEM.
|
---|
| 3 | *
|
---|
| 4 | * @author Lautaro Cozzani Rodriguez
|
---|
| 5 | * @author Dave Longley
|
---|
| 6 | *
|
---|
| 7 | * Copyright (c) 2014 Lautaro Cozzani <lautaro.cozzani@scytl.com>
|
---|
| 8 | * Copyright (c) 2014 Digital Bazaar, Inc.
|
---|
| 9 | */
|
---|
| 10 | var forge = require('./forge');
|
---|
| 11 | require('./util');
|
---|
| 12 | require('./random');
|
---|
| 13 | require('./jsbn');
|
---|
| 14 |
|
---|
| 15 | module.exports = forge.kem = forge.kem || {};
|
---|
| 16 |
|
---|
| 17 | var BigInteger = forge.jsbn.BigInteger;
|
---|
| 18 |
|
---|
| 19 | /**
|
---|
| 20 | * The API for the RSA Key Encapsulation Mechanism (RSA-KEM) from ISO 18033-2.
|
---|
| 21 | */
|
---|
| 22 | forge.kem.rsa = {};
|
---|
| 23 |
|
---|
| 24 | /**
|
---|
| 25 | * Creates an RSA KEM API object for generating a secret asymmetric key.
|
---|
| 26 | *
|
---|
| 27 | * The symmetric key may be generated via a call to 'encrypt', which will
|
---|
| 28 | * produce a ciphertext to be transmitted to the recipient and a key to be
|
---|
| 29 | * kept secret. The ciphertext is a parameter to be passed to 'decrypt' which
|
---|
| 30 | * will produce the same secret key for the recipient to use to decrypt a
|
---|
| 31 | * message that was encrypted with the secret key.
|
---|
| 32 | *
|
---|
| 33 | * @param kdf the KDF API to use (eg: new forge.kem.kdf1()).
|
---|
| 34 | * @param options the options to use.
|
---|
| 35 | * [prng] a custom crypto-secure pseudo-random number generator to use,
|
---|
| 36 | * that must define "getBytesSync".
|
---|
| 37 | */
|
---|
| 38 | forge.kem.rsa.create = function(kdf, options) {
|
---|
| 39 | options = options || {};
|
---|
| 40 | var prng = options.prng || forge.random;
|
---|
| 41 |
|
---|
| 42 | var kem = {};
|
---|
| 43 |
|
---|
| 44 | /**
|
---|
| 45 | * Generates a secret key and its encapsulation.
|
---|
| 46 | *
|
---|
| 47 | * @param publicKey the RSA public key to encrypt with.
|
---|
| 48 | * @param keyLength the length, in bytes, of the secret key to generate.
|
---|
| 49 | *
|
---|
| 50 | * @return an object with:
|
---|
| 51 | * encapsulation: the ciphertext for generating the secret key, as a
|
---|
| 52 | * binary-encoded string of bytes.
|
---|
| 53 | * key: the secret key to use for encrypting a message.
|
---|
| 54 | */
|
---|
| 55 | kem.encrypt = function(publicKey, keyLength) {
|
---|
| 56 | // generate a random r where 1 < r < n
|
---|
| 57 | var byteLength = Math.ceil(publicKey.n.bitLength() / 8);
|
---|
| 58 | var r;
|
---|
| 59 | do {
|
---|
| 60 | r = new BigInteger(
|
---|
| 61 | forge.util.bytesToHex(prng.getBytesSync(byteLength)),
|
---|
| 62 | 16).mod(publicKey.n);
|
---|
| 63 | } while(r.compareTo(BigInteger.ONE) <= 0);
|
---|
| 64 |
|
---|
| 65 | // prepend r with zeros
|
---|
| 66 | r = forge.util.hexToBytes(r.toString(16));
|
---|
| 67 | var zeros = byteLength - r.length;
|
---|
| 68 | if(zeros > 0) {
|
---|
| 69 | r = forge.util.fillString(String.fromCharCode(0), zeros) + r;
|
---|
| 70 | }
|
---|
| 71 |
|
---|
| 72 | // encrypt the random
|
---|
| 73 | var encapsulation = publicKey.encrypt(r, 'NONE');
|
---|
| 74 |
|
---|
| 75 | // generate the secret key
|
---|
| 76 | var key = kdf.generate(r, keyLength);
|
---|
| 77 |
|
---|
| 78 | return {encapsulation: encapsulation, key: key};
|
---|
| 79 | };
|
---|
| 80 |
|
---|
| 81 | /**
|
---|
| 82 | * Decrypts an encapsulated secret key.
|
---|
| 83 | *
|
---|
| 84 | * @param privateKey the RSA private key to decrypt with.
|
---|
| 85 | * @param encapsulation the ciphertext for generating the secret key, as
|
---|
| 86 | * a binary-encoded string of bytes.
|
---|
| 87 | * @param keyLength the length, in bytes, of the secret key to generate.
|
---|
| 88 | *
|
---|
| 89 | * @return the secret key as a binary-encoded string of bytes.
|
---|
| 90 | */
|
---|
| 91 | kem.decrypt = function(privateKey, encapsulation, keyLength) {
|
---|
| 92 | // decrypt the encapsulation and generate the secret key
|
---|
| 93 | var r = privateKey.decrypt(encapsulation, 'NONE');
|
---|
| 94 | return kdf.generate(r, keyLength);
|
---|
| 95 | };
|
---|
| 96 |
|
---|
| 97 | return kem;
|
---|
| 98 | };
|
---|
| 99 |
|
---|
| 100 | // TODO: add forge.kem.kdf.create('KDF1', {md: ..., ...}) API?
|
---|
| 101 |
|
---|
| 102 | /**
|
---|
| 103 | * Creates a key derivation API object that implements KDF1 per ISO 18033-2.
|
---|
| 104 | *
|
---|
| 105 | * @param md the hash API to use.
|
---|
| 106 | * @param [digestLength] an optional digest length that must be positive and
|
---|
| 107 | * less than or equal to md.digestLength.
|
---|
| 108 | *
|
---|
| 109 | * @return a KDF1 API object.
|
---|
| 110 | */
|
---|
| 111 | forge.kem.kdf1 = function(md, digestLength) {
|
---|
| 112 | _createKDF(this, md, 0, digestLength || md.digestLength);
|
---|
| 113 | };
|
---|
| 114 |
|
---|
| 115 | /**
|
---|
| 116 | * Creates a key derivation API object that implements KDF2 per ISO 18033-2.
|
---|
| 117 | *
|
---|
| 118 | * @param md the hash API to use.
|
---|
| 119 | * @param [digestLength] an optional digest length that must be positive and
|
---|
| 120 | * less than or equal to md.digestLength.
|
---|
| 121 | *
|
---|
| 122 | * @return a KDF2 API object.
|
---|
| 123 | */
|
---|
| 124 | forge.kem.kdf2 = function(md, digestLength) {
|
---|
| 125 | _createKDF(this, md, 1, digestLength || md.digestLength);
|
---|
| 126 | };
|
---|
| 127 |
|
---|
| 128 | /**
|
---|
| 129 | * Creates a KDF1 or KDF2 API object.
|
---|
| 130 | *
|
---|
| 131 | * @param md the hash API to use.
|
---|
| 132 | * @param counterStart the starting index for the counter.
|
---|
| 133 | * @param digestLength the digest length to use.
|
---|
| 134 | *
|
---|
| 135 | * @return the KDF API object.
|
---|
| 136 | */
|
---|
| 137 | function _createKDF(kdf, md, counterStart, digestLength) {
|
---|
| 138 | /**
|
---|
| 139 | * Generate a key of the specified length.
|
---|
| 140 | *
|
---|
| 141 | * @param x the binary-encoded byte string to generate a key from.
|
---|
| 142 | * @param length the number of bytes to generate (the size of the key).
|
---|
| 143 | *
|
---|
| 144 | * @return the key as a binary-encoded string.
|
---|
| 145 | */
|
---|
| 146 | kdf.generate = function(x, length) {
|
---|
| 147 | var key = new forge.util.ByteBuffer();
|
---|
| 148 |
|
---|
| 149 | // run counter from counterStart to ceil(length / Hash.len)
|
---|
| 150 | var k = Math.ceil(length / digestLength) + counterStart;
|
---|
| 151 |
|
---|
| 152 | var c = new forge.util.ByteBuffer();
|
---|
| 153 | for(var i = counterStart; i < k; ++i) {
|
---|
| 154 | // I2OSP(i, 4): convert counter to an octet string of 4 octets
|
---|
| 155 | c.putInt32(i);
|
---|
| 156 |
|
---|
| 157 | // digest 'x' and the counter and add the result to the key
|
---|
| 158 | md.start();
|
---|
| 159 | md.update(x + c.getBytes());
|
---|
| 160 | var hash = md.digest();
|
---|
| 161 | key.putBytes(hash.getBytes(digestLength));
|
---|
| 162 | }
|
---|
| 163 |
|
---|
| 164 | // truncate to the correct key length
|
---|
| 165 | key.truncate(key.length() - length);
|
---|
| 166 | return key.getBytes();
|
---|
| 167 | };
|
---|
| 168 | }
|
---|