| // Copyright 2017 Joyent, Inc. | 
|   | 
| module.exports = { | 
|     DiffieHellman: DiffieHellman, | 
|     generateECDSA: generateECDSA, | 
|     generateED25519: generateED25519 | 
| }; | 
|   | 
| var assert = require('assert-plus'); | 
| var crypto = require('crypto'); | 
| var Buffer = require('safer-buffer').Buffer; | 
| var algs = require('./algs'); | 
| var utils = require('./utils'); | 
| var nacl = require('tweetnacl'); | 
|   | 
| var Key = require('./key'); | 
| var PrivateKey = require('./private-key'); | 
|   | 
| var CRYPTO_HAVE_ECDH = (crypto.createECDH !== undefined); | 
|   | 
| var ecdh = require('ecc-jsbn'); | 
| var ec = require('ecc-jsbn/lib/ec'); | 
| var jsbn = require('jsbn').BigInteger; | 
|   | 
| function DiffieHellman(key) { | 
|     utils.assertCompatible(key, Key, [1, 4], 'key'); | 
|     this._isPriv = PrivateKey.isPrivateKey(key, [1, 3]); | 
|     this._algo = key.type; | 
|     this._curve = key.curve; | 
|     this._key = key; | 
|     if (key.type === 'dsa') { | 
|         if (!CRYPTO_HAVE_ECDH) { | 
|             throw (new Error('Due to bugs in the node 0.10 ' + | 
|                 'crypto API, node 0.12.x or later is required ' + | 
|                 'to use DH')); | 
|         } | 
|         this._dh = crypto.createDiffieHellman( | 
|             key.part.p.data, undefined, | 
|             key.part.g.data, undefined); | 
|         this._p = key.part.p; | 
|         this._g = key.part.g; | 
|         if (this._isPriv) | 
|             this._dh.setPrivateKey(key.part.x.data); | 
|         this._dh.setPublicKey(key.part.y.data); | 
|   | 
|     } else if (key.type === 'ecdsa') { | 
|         if (!CRYPTO_HAVE_ECDH) { | 
|             this._ecParams = new X9ECParameters(this._curve); | 
|   | 
|             if (this._isPriv) { | 
|                 this._priv = new ECPrivate( | 
|                     this._ecParams, key.part.d.data); | 
|             } | 
|             return; | 
|         } | 
|   | 
|         var curve = { | 
|             'nistp256': 'prime256v1', | 
|             'nistp384': 'secp384r1', | 
|             'nistp521': 'secp521r1' | 
|         }[key.curve]; | 
|         this._dh = crypto.createECDH(curve); | 
|         if (typeof (this._dh) !== 'object' || | 
|             typeof (this._dh.setPrivateKey) !== 'function') { | 
|             CRYPTO_HAVE_ECDH = false; | 
|             DiffieHellman.call(this, key); | 
|             return; | 
|         } | 
|         if (this._isPriv) | 
|             this._dh.setPrivateKey(key.part.d.data); | 
|         this._dh.setPublicKey(key.part.Q.data); | 
|   | 
|     } else if (key.type === 'curve25519') { | 
|         if (this._isPriv) { | 
|             utils.assertCompatible(key, PrivateKey, [1, 5], 'key'); | 
|             this._priv = key.part.k.data; | 
|         } | 
|   | 
|     } else { | 
|         throw (new Error('DH not supported for ' + key.type + ' keys')); | 
|     } | 
| } | 
|   | 
| DiffieHellman.prototype.getPublicKey = function () { | 
|     if (this._isPriv) | 
|         return (this._key.toPublic()); | 
|     return (this._key); | 
| }; | 
|   | 
| DiffieHellman.prototype.getPrivateKey = function () { | 
|     if (this._isPriv) | 
|         return (this._key); | 
|     else | 
|         return (undefined); | 
| }; | 
| DiffieHellman.prototype.getKey = DiffieHellman.prototype.getPrivateKey; | 
|   | 
| DiffieHellman.prototype._keyCheck = function (pk, isPub) { | 
|     assert.object(pk, 'key'); | 
|     if (!isPub) | 
|         utils.assertCompatible(pk, PrivateKey, [1, 3], 'key'); | 
|     utils.assertCompatible(pk, Key, [1, 4], 'key'); | 
|   | 
|     if (pk.type !== this._algo) { | 
|         throw (new Error('A ' + pk.type + ' key cannot be used in ' + | 
|             this._algo + ' Diffie-Hellman')); | 
|     } | 
|   | 
|     if (pk.curve !== this._curve) { | 
|         throw (new Error('A key from the ' + pk.curve + ' curve ' + | 
|             'cannot be used with a ' + this._curve + | 
|             ' Diffie-Hellman')); | 
|     } | 
|   | 
|     if (pk.type === 'dsa') { | 
|         assert.deepEqual(pk.part.p, this._p, | 
|             'DSA key prime does not match'); | 
|         assert.deepEqual(pk.part.g, this._g, | 
|             'DSA key generator does not match'); | 
|     } | 
| }; | 
|   | 
| DiffieHellman.prototype.setKey = function (pk) { | 
|     this._keyCheck(pk); | 
|   | 
|     if (pk.type === 'dsa') { | 
|         this._dh.setPrivateKey(pk.part.x.data); | 
|         this._dh.setPublicKey(pk.part.y.data); | 
|   | 
|     } else if (pk.type === 'ecdsa') { | 
|         if (CRYPTO_HAVE_ECDH) { | 
|             this._dh.setPrivateKey(pk.part.d.data); | 
|             this._dh.setPublicKey(pk.part.Q.data); | 
|         } else { | 
|             this._priv = new ECPrivate( | 
|                 this._ecParams, pk.part.d.data); | 
|         } | 
|   | 
|     } else if (pk.type === 'curve25519') { | 
|         var k = pk.part.k; | 
|         if (!pk.part.k) | 
|             k = pk.part.r; | 
|         this._priv = k.data; | 
|         if (this._priv[0] === 0x00) | 
|             this._priv = this._priv.slice(1); | 
|         this._priv = this._priv.slice(0, 32); | 
|     } | 
|     this._key = pk; | 
|     this._isPriv = true; | 
| }; | 
| DiffieHellman.prototype.setPrivateKey = DiffieHellman.prototype.setKey; | 
|   | 
| DiffieHellman.prototype.computeSecret = function (otherpk) { | 
|     this._keyCheck(otherpk, true); | 
|     if (!this._isPriv) | 
|         throw (new Error('DH exchange has not been initialized with ' + | 
|             'a private key yet')); | 
|   | 
|     var pub; | 
|     if (this._algo === 'dsa') { | 
|         return (this._dh.computeSecret( | 
|             otherpk.part.y.data)); | 
|   | 
|     } else if (this._algo === 'ecdsa') { | 
|         if (CRYPTO_HAVE_ECDH) { | 
|             return (this._dh.computeSecret( | 
|                 otherpk.part.Q.data)); | 
|         } else { | 
|             pub = new ECPublic( | 
|                 this._ecParams, otherpk.part.Q.data); | 
|             return (this._priv.deriveSharedSecret(pub)); | 
|         } | 
|   | 
|     } else if (this._algo === 'curve25519') { | 
|         pub = otherpk.part.A.data; | 
|         while (pub[0] === 0x00 && pub.length > 32) | 
|             pub = pub.slice(1); | 
|         var priv = this._priv; | 
|         assert.strictEqual(pub.length, 32); | 
|         assert.strictEqual(priv.length, 32); | 
|   | 
|         var secret = nacl.box.before(new Uint8Array(pub), | 
|             new Uint8Array(priv)); | 
|   | 
|         return (Buffer.from(secret)); | 
|     } | 
|   | 
|     throw (new Error('Invalid algorithm: ' + this._algo)); | 
| }; | 
|   | 
| DiffieHellman.prototype.generateKey = function () { | 
|     var parts = []; | 
|     var priv, pub; | 
|     if (this._algo === 'dsa') { | 
|         this._dh.generateKeys(); | 
|   | 
|         parts.push({name: 'p', data: this._p.data}); | 
|         parts.push({name: 'q', data: this._key.part.q.data}); | 
|         parts.push({name: 'g', data: this._g.data}); | 
|         parts.push({name: 'y', data: this._dh.getPublicKey()}); | 
|         parts.push({name: 'x', data: this._dh.getPrivateKey()}); | 
|         this._key = new PrivateKey({ | 
|             type: 'dsa', | 
|             parts: parts | 
|         }); | 
|         this._isPriv = true; | 
|         return (this._key); | 
|   | 
|     } else if (this._algo === 'ecdsa') { | 
|         if (CRYPTO_HAVE_ECDH) { | 
|             this._dh.generateKeys(); | 
|   | 
|             parts.push({name: 'curve', | 
|                 data: Buffer.from(this._curve)}); | 
|             parts.push({name: 'Q', data: this._dh.getPublicKey()}); | 
|             parts.push({name: 'd', data: this._dh.getPrivateKey()}); | 
|             this._key = new PrivateKey({ | 
|                 type: 'ecdsa', | 
|                 curve: this._curve, | 
|                 parts: parts | 
|             }); | 
|             this._isPriv = true; | 
|             return (this._key); | 
|   | 
|         } else { | 
|             var n = this._ecParams.getN(); | 
|             var r = new jsbn(crypto.randomBytes(n.bitLength())); | 
|             var n1 = n.subtract(jsbn.ONE); | 
|             priv = r.mod(n1).add(jsbn.ONE); | 
|             pub = this._ecParams.getG().multiply(priv); | 
|   | 
|             priv = Buffer.from(priv.toByteArray()); | 
|             pub = Buffer.from(this._ecParams.getCurve(). | 
|                 encodePointHex(pub), 'hex'); | 
|   | 
|             this._priv = new ECPrivate(this._ecParams, priv); | 
|   | 
|             parts.push({name: 'curve', | 
|                 data: Buffer.from(this._curve)}); | 
|             parts.push({name: 'Q', data: pub}); | 
|             parts.push({name: 'd', data: priv}); | 
|   | 
|             this._key = new PrivateKey({ | 
|                 type: 'ecdsa', | 
|                 curve: this._curve, | 
|                 parts: parts | 
|             }); | 
|             this._isPriv = true; | 
|             return (this._key); | 
|         } | 
|   | 
|     } else if (this._algo === 'curve25519') { | 
|         var pair = nacl.box.keyPair(); | 
|         priv = Buffer.from(pair.secretKey); | 
|         pub = Buffer.from(pair.publicKey); | 
|         priv = Buffer.concat([priv, pub]); | 
|         assert.strictEqual(priv.length, 64); | 
|         assert.strictEqual(pub.length, 32); | 
|   | 
|         parts.push({name: 'A', data: pub}); | 
|         parts.push({name: 'k', data: priv}); | 
|         this._key = new PrivateKey({ | 
|             type: 'curve25519', | 
|             parts: parts | 
|         }); | 
|         this._isPriv = true; | 
|         return (this._key); | 
|     } | 
|   | 
|     throw (new Error('Invalid algorithm: ' + this._algo)); | 
| }; | 
| DiffieHellman.prototype.generateKeys = DiffieHellman.prototype.generateKey; | 
|   | 
| /* These are helpers for using ecc-jsbn (for node 0.10 compatibility). */ | 
|   | 
| function X9ECParameters(name) { | 
|     var params = algs.curves[name]; | 
|     assert.object(params); | 
|   | 
|     var p = new jsbn(params.p); | 
|     var a = new jsbn(params.a); | 
|     var b = new jsbn(params.b); | 
|     var n = new jsbn(params.n); | 
|     var h = jsbn.ONE; | 
|     var curve = new ec.ECCurveFp(p, a, b); | 
|     var G = curve.decodePointHex(params.G.toString('hex')); | 
|   | 
|     this.curve = curve; | 
|     this.g = G; | 
|     this.n = n; | 
|     this.h = h; | 
| } | 
| X9ECParameters.prototype.getCurve = function () { return (this.curve); }; | 
| X9ECParameters.prototype.getG = function () { return (this.g); }; | 
| X9ECParameters.prototype.getN = function () { return (this.n); }; | 
| X9ECParameters.prototype.getH = function () { return (this.h); }; | 
|   | 
| function ECPublic(params, buffer) { | 
|     this._params = params; | 
|     if (buffer[0] === 0x00) | 
|         buffer = buffer.slice(1); | 
|     this._pub = params.getCurve().decodePointHex(buffer.toString('hex')); | 
| } | 
|   | 
| function ECPrivate(params, buffer) { | 
|     this._params = params; | 
|     this._priv = new jsbn(utils.mpNormalize(buffer)); | 
| } | 
| ECPrivate.prototype.deriveSharedSecret = function (pubKey) { | 
|     assert.ok(pubKey instanceof ECPublic); | 
|     var S = pubKey._pub.multiply(this._priv); | 
|     return (Buffer.from(S.getX().toBigInteger().toByteArray())); | 
| }; | 
|   | 
| function generateED25519() { | 
|     var pair = nacl.sign.keyPair(); | 
|     var priv = Buffer.from(pair.secretKey); | 
|     var pub = Buffer.from(pair.publicKey); | 
|     assert.strictEqual(priv.length, 64); | 
|     assert.strictEqual(pub.length, 32); | 
|   | 
|     var parts = []; | 
|     parts.push({name: 'A', data: pub}); | 
|     parts.push({name: 'k', data: priv.slice(0, 32)}); | 
|     var key = new PrivateKey({ | 
|         type: 'ed25519', | 
|         parts: parts | 
|     }); | 
|     return (key); | 
| } | 
|   | 
| /* Generates a new ECDSA private key on a given curve. */ | 
| function generateECDSA(curve) { | 
|     var parts = []; | 
|     var key; | 
|   | 
|     if (CRYPTO_HAVE_ECDH) { | 
|         /* | 
|          * Node crypto doesn't expose key generation directly, but the | 
|          * ECDH instances can generate keys. It turns out this just | 
|          * calls into the OpenSSL generic key generator, and we can | 
|          * read its output happily without doing an actual DH. So we | 
|          * use that here. | 
|          */ | 
|         var osCurve = { | 
|             'nistp256': 'prime256v1', | 
|             'nistp384': 'secp384r1', | 
|             'nistp521': 'secp521r1' | 
|         }[curve]; | 
|   | 
|         var dh = crypto.createECDH(osCurve); | 
|         dh.generateKeys(); | 
|   | 
|         parts.push({name: 'curve', | 
|             data: Buffer.from(curve)}); | 
|         parts.push({name: 'Q', data: dh.getPublicKey()}); | 
|         parts.push({name: 'd', data: dh.getPrivateKey()}); | 
|   | 
|         key = new PrivateKey({ | 
|             type: 'ecdsa', | 
|             curve: curve, | 
|             parts: parts | 
|         }); | 
|         return (key); | 
|     } else { | 
|   | 
|         var ecParams = new X9ECParameters(curve); | 
|   | 
|         /* This algorithm taken from FIPS PUB 186-4 (section B.4.1) */ | 
|         var n = ecParams.getN(); | 
|         /* | 
|          * The crypto.randomBytes() function can only give us whole | 
|          * bytes, so taking a nod from X9.62, we round up. | 
|          */ | 
|         var cByteLen = Math.ceil((n.bitLength() + 64) / 8); | 
|         var c = new jsbn(crypto.randomBytes(cByteLen)); | 
|   | 
|         var n1 = n.subtract(jsbn.ONE); | 
|         var priv = c.mod(n1).add(jsbn.ONE); | 
|         var pub = ecParams.getG().multiply(priv); | 
|   | 
|         priv = Buffer.from(priv.toByteArray()); | 
|         pub = Buffer.from(ecParams.getCurve(). | 
|             encodePointHex(pub), 'hex'); | 
|   | 
|         parts.push({name: 'curve', data: Buffer.from(curve)}); | 
|         parts.push({name: 'Q', data: pub}); | 
|         parts.push({name: 'd', data: priv}); | 
|   | 
|         key = new PrivateKey({ | 
|             type: 'ecdsa', | 
|             curve: curve, | 
|             parts: parts | 
|         }); | 
|         return (key); | 
|     } | 
| } |