dhe.js (10600B)
1 // Copyright 2017 Joyent, Inc. 2 3 module.exports = { 4 DiffieHellman: DiffieHellman, 5 generateECDSA: generateECDSA, 6 generateED25519: generateED25519 7 }; 8 9 var assert = require('assert-plus'); 10 var crypto = require('crypto'); 11 var Buffer = require('safer-buffer').Buffer; 12 var algs = require('./algs'); 13 var utils = require('./utils'); 14 var nacl = require('tweetnacl'); 15 16 var Key = require('./key'); 17 var PrivateKey = require('./private-key'); 18 19 var CRYPTO_HAVE_ECDH = (crypto.createECDH !== undefined); 20 21 var ecdh = require('ecc-jsbn'); 22 var ec = require('ecc-jsbn/lib/ec'); 23 var jsbn = require('jsbn').BigInteger; 24 25 function DiffieHellman(key) { 26 utils.assertCompatible(key, Key, [1, 4], 'key'); 27 this._isPriv = PrivateKey.isPrivateKey(key, [1, 3]); 28 this._algo = key.type; 29 this._curve = key.curve; 30 this._key = key; 31 if (key.type === 'dsa') { 32 if (!CRYPTO_HAVE_ECDH) { 33 throw (new Error('Due to bugs in the node 0.10 ' + 34 'crypto API, node 0.12.x or later is required ' + 35 'to use DH')); 36 } 37 this._dh = crypto.createDiffieHellman( 38 key.part.p.data, undefined, 39 key.part.g.data, undefined); 40 this._p = key.part.p; 41 this._g = key.part.g; 42 if (this._isPriv) 43 this._dh.setPrivateKey(key.part.x.data); 44 this._dh.setPublicKey(key.part.y.data); 45 46 } else if (key.type === 'ecdsa') { 47 if (!CRYPTO_HAVE_ECDH) { 48 this._ecParams = new X9ECParameters(this._curve); 49 50 if (this._isPriv) { 51 this._priv = new ECPrivate( 52 this._ecParams, key.part.d.data); 53 } 54 return; 55 } 56 57 var curve = { 58 'nistp256': 'prime256v1', 59 'nistp384': 'secp384r1', 60 'nistp521': 'secp521r1' 61 }[key.curve]; 62 this._dh = crypto.createECDH(curve); 63 if (typeof (this._dh) !== 'object' || 64 typeof (this._dh.setPrivateKey) !== 'function') { 65 CRYPTO_HAVE_ECDH = false; 66 DiffieHellman.call(this, key); 67 return; 68 } 69 if (this._isPriv) 70 this._dh.setPrivateKey(key.part.d.data); 71 this._dh.setPublicKey(key.part.Q.data); 72 73 } else if (key.type === 'curve25519') { 74 if (this._isPriv) { 75 utils.assertCompatible(key, PrivateKey, [1, 5], 'key'); 76 this._priv = key.part.k.data; 77 } 78 79 } else { 80 throw (new Error('DH not supported for ' + key.type + ' keys')); 81 } 82 } 83 84 DiffieHellman.prototype.getPublicKey = function () { 85 if (this._isPriv) 86 return (this._key.toPublic()); 87 return (this._key); 88 }; 89 90 DiffieHellman.prototype.getPrivateKey = function () { 91 if (this._isPriv) 92 return (this._key); 93 else 94 return (undefined); 95 }; 96 DiffieHellman.prototype.getKey = DiffieHellman.prototype.getPrivateKey; 97 98 DiffieHellman.prototype._keyCheck = function (pk, isPub) { 99 assert.object(pk, 'key'); 100 if (!isPub) 101 utils.assertCompatible(pk, PrivateKey, [1, 3], 'key'); 102 utils.assertCompatible(pk, Key, [1, 4], 'key'); 103 104 if (pk.type !== this._algo) { 105 throw (new Error('A ' + pk.type + ' key cannot be used in ' + 106 this._algo + ' Diffie-Hellman')); 107 } 108 109 if (pk.curve !== this._curve) { 110 throw (new Error('A key from the ' + pk.curve + ' curve ' + 111 'cannot be used with a ' + this._curve + 112 ' Diffie-Hellman')); 113 } 114 115 if (pk.type === 'dsa') { 116 assert.deepEqual(pk.part.p, this._p, 117 'DSA key prime does not match'); 118 assert.deepEqual(pk.part.g, this._g, 119 'DSA key generator does not match'); 120 } 121 }; 122 123 DiffieHellman.prototype.setKey = function (pk) { 124 this._keyCheck(pk); 125 126 if (pk.type === 'dsa') { 127 this._dh.setPrivateKey(pk.part.x.data); 128 this._dh.setPublicKey(pk.part.y.data); 129 130 } else if (pk.type === 'ecdsa') { 131 if (CRYPTO_HAVE_ECDH) { 132 this._dh.setPrivateKey(pk.part.d.data); 133 this._dh.setPublicKey(pk.part.Q.data); 134 } else { 135 this._priv = new ECPrivate( 136 this._ecParams, pk.part.d.data); 137 } 138 139 } else if (pk.type === 'curve25519') { 140 var k = pk.part.k; 141 if (!pk.part.k) 142 k = pk.part.r; 143 this._priv = k.data; 144 if (this._priv[0] === 0x00) 145 this._priv = this._priv.slice(1); 146 this._priv = this._priv.slice(0, 32); 147 } 148 this._key = pk; 149 this._isPriv = true; 150 }; 151 DiffieHellman.prototype.setPrivateKey = DiffieHellman.prototype.setKey; 152 153 DiffieHellman.prototype.computeSecret = function (otherpk) { 154 this._keyCheck(otherpk, true); 155 if (!this._isPriv) 156 throw (new Error('DH exchange has not been initialized with ' + 157 'a private key yet')); 158 159 var pub; 160 if (this._algo === 'dsa') { 161 return (this._dh.computeSecret( 162 otherpk.part.y.data)); 163 164 } else if (this._algo === 'ecdsa') { 165 if (CRYPTO_HAVE_ECDH) { 166 return (this._dh.computeSecret( 167 otherpk.part.Q.data)); 168 } else { 169 pub = new ECPublic( 170 this._ecParams, otherpk.part.Q.data); 171 return (this._priv.deriveSharedSecret(pub)); 172 } 173 174 } else if (this._algo === 'curve25519') { 175 pub = otherpk.part.A.data; 176 while (pub[0] === 0x00 && pub.length > 32) 177 pub = pub.slice(1); 178 var priv = this._priv; 179 assert.strictEqual(pub.length, 32); 180 assert.strictEqual(priv.length, 32); 181 182 var secret = nacl.box.before(new Uint8Array(pub), 183 new Uint8Array(priv)); 184 185 return (Buffer.from(secret)); 186 } 187 188 throw (new Error('Invalid algorithm: ' + this._algo)); 189 }; 190 191 DiffieHellman.prototype.generateKey = function () { 192 var parts = []; 193 var priv, pub; 194 if (this._algo === 'dsa') { 195 this._dh.generateKeys(); 196 197 parts.push({name: 'p', data: this._p.data}); 198 parts.push({name: 'q', data: this._key.part.q.data}); 199 parts.push({name: 'g', data: this._g.data}); 200 parts.push({name: 'y', data: this._dh.getPublicKey()}); 201 parts.push({name: 'x', data: this._dh.getPrivateKey()}); 202 this._key = new PrivateKey({ 203 type: 'dsa', 204 parts: parts 205 }); 206 this._isPriv = true; 207 return (this._key); 208 209 } else if (this._algo === 'ecdsa') { 210 if (CRYPTO_HAVE_ECDH) { 211 this._dh.generateKeys(); 212 213 parts.push({name: 'curve', 214 data: Buffer.from(this._curve)}); 215 parts.push({name: 'Q', data: this._dh.getPublicKey()}); 216 parts.push({name: 'd', data: this._dh.getPrivateKey()}); 217 this._key = new PrivateKey({ 218 type: 'ecdsa', 219 curve: this._curve, 220 parts: parts 221 }); 222 this._isPriv = true; 223 return (this._key); 224 225 } else { 226 var n = this._ecParams.getN(); 227 var r = new jsbn(crypto.randomBytes(n.bitLength())); 228 var n1 = n.subtract(jsbn.ONE); 229 priv = r.mod(n1).add(jsbn.ONE); 230 pub = this._ecParams.getG().multiply(priv); 231 232 priv = Buffer.from(priv.toByteArray()); 233 pub = Buffer.from(this._ecParams.getCurve(). 234 encodePointHex(pub), 'hex'); 235 236 this._priv = new ECPrivate(this._ecParams, priv); 237 238 parts.push({name: 'curve', 239 data: Buffer.from(this._curve)}); 240 parts.push({name: 'Q', data: pub}); 241 parts.push({name: 'd', data: priv}); 242 243 this._key = new PrivateKey({ 244 type: 'ecdsa', 245 curve: this._curve, 246 parts: parts 247 }); 248 this._isPriv = true; 249 return (this._key); 250 } 251 252 } else if (this._algo === 'curve25519') { 253 var pair = nacl.box.keyPair(); 254 priv = Buffer.from(pair.secretKey); 255 pub = Buffer.from(pair.publicKey); 256 priv = Buffer.concat([priv, pub]); 257 assert.strictEqual(priv.length, 64); 258 assert.strictEqual(pub.length, 32); 259 260 parts.push({name: 'A', data: pub}); 261 parts.push({name: 'k', data: priv}); 262 this._key = new PrivateKey({ 263 type: 'curve25519', 264 parts: parts 265 }); 266 this._isPriv = true; 267 return (this._key); 268 } 269 270 throw (new Error('Invalid algorithm: ' + this._algo)); 271 }; 272 DiffieHellman.prototype.generateKeys = DiffieHellman.prototype.generateKey; 273 274 /* These are helpers for using ecc-jsbn (for node 0.10 compatibility). */ 275 276 function X9ECParameters(name) { 277 var params = algs.curves[name]; 278 assert.object(params); 279 280 var p = new jsbn(params.p); 281 var a = new jsbn(params.a); 282 var b = new jsbn(params.b); 283 var n = new jsbn(params.n); 284 var h = jsbn.ONE; 285 var curve = new ec.ECCurveFp(p, a, b); 286 var G = curve.decodePointHex(params.G.toString('hex')); 287 288 this.curve = curve; 289 this.g = G; 290 this.n = n; 291 this.h = h; 292 } 293 X9ECParameters.prototype.getCurve = function () { return (this.curve); }; 294 X9ECParameters.prototype.getG = function () { return (this.g); }; 295 X9ECParameters.prototype.getN = function () { return (this.n); }; 296 X9ECParameters.prototype.getH = function () { return (this.h); }; 297 298 function ECPublic(params, buffer) { 299 this._params = params; 300 if (buffer[0] === 0x00) 301 buffer = buffer.slice(1); 302 this._pub = params.getCurve().decodePointHex(buffer.toString('hex')); 303 } 304 305 function ECPrivate(params, buffer) { 306 this._params = params; 307 this._priv = new jsbn(utils.mpNormalize(buffer)); 308 } 309 ECPrivate.prototype.deriveSharedSecret = function (pubKey) { 310 assert.ok(pubKey instanceof ECPublic); 311 var S = pubKey._pub.multiply(this._priv); 312 return (Buffer.from(S.getX().toBigInteger().toByteArray())); 313 }; 314 315 function generateED25519() { 316 var pair = nacl.sign.keyPair(); 317 var priv = Buffer.from(pair.secretKey); 318 var pub = Buffer.from(pair.publicKey); 319 assert.strictEqual(priv.length, 64); 320 assert.strictEqual(pub.length, 32); 321 322 var parts = []; 323 parts.push({name: 'A', data: pub}); 324 parts.push({name: 'k', data: priv.slice(0, 32)}); 325 var key = new PrivateKey({ 326 type: 'ed25519', 327 parts: parts 328 }); 329 return (key); 330 } 331 332 /* Generates a new ECDSA private key on a given curve. */ 333 function generateECDSA(curve) { 334 var parts = []; 335 var key; 336 337 if (CRYPTO_HAVE_ECDH) { 338 /* 339 * Node crypto doesn't expose key generation directly, but the 340 * ECDH instances can generate keys. It turns out this just 341 * calls into the OpenSSL generic key generator, and we can 342 * read its output happily without doing an actual DH. So we 343 * use that here. 344 */ 345 var osCurve = { 346 'nistp256': 'prime256v1', 347 'nistp384': 'secp384r1', 348 'nistp521': 'secp521r1' 349 }[curve]; 350 351 var dh = crypto.createECDH(osCurve); 352 dh.generateKeys(); 353 354 parts.push({name: 'curve', 355 data: Buffer.from(curve)}); 356 parts.push({name: 'Q', data: dh.getPublicKey()}); 357 parts.push({name: 'd', data: dh.getPrivateKey()}); 358 359 key = new PrivateKey({ 360 type: 'ecdsa', 361 curve: curve, 362 parts: parts 363 }); 364 return (key); 365 } else { 366 367 var ecParams = new X9ECParameters(curve); 368 369 /* This algorithm taken from FIPS PUB 186-4 (section B.4.1) */ 370 var n = ecParams.getN(); 371 /* 372 * The crypto.randomBytes() function can only give us whole 373 * bytes, so taking a nod from X9.62, we round up. 374 */ 375 var cByteLen = Math.ceil((n.bitLength() + 64) / 8); 376 var c = new jsbn(crypto.randomBytes(cByteLen)); 377 378 var n1 = n.subtract(jsbn.ONE); 379 var priv = c.mod(n1).add(jsbn.ONE); 380 var pub = ecParams.getG().multiply(priv); 381 382 priv = Buffer.from(priv.toByteArray()); 383 pub = Buffer.from(ecParams.getCurve(). 384 encodePointHex(pub), 'hex'); 385 386 parts.push({name: 'curve', data: Buffer.from(curve)}); 387 parts.push({name: 'Q', data: pub}); 388 parts.push({name: 'd', data: priv}); 389 390 key = new PrivateKey({ 391 type: 'ecdsa', 392 curve: curve, 393 parts: parts 394 }); 395 return (key); 396 } 397 }