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 | }
|
---|