123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312 |
- // Copyright 2015 Joyent, Inc.
- module.exports = DiffieHellman;
- var assert = require('assert-plus');
- var crypto = require('crypto');
- var algs = require('./algs');
- var utils = require('./utils');
- var ed;
- var Key = require('./key');
- var PrivateKey = require('./private-key');
- var CRYPTO_HAVE_ECDH = (crypto.createECDH !== undefined);
- var ecdh, ec, jsbn;
- 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) {
- if (ecdh === undefined)
- ecdh = require('ecc-jsbn');
- if (ec === undefined)
- ec = require('ecc-jsbn/lib/ec');
- if (jsbn === undefined)
- jsbn = require('jsbn').BigInteger;
- 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 (ed === undefined)
- ed = require('jodid25519');
- if (this._isPriv) {
- this._priv = key.part.r.data;
- if (this._priv[0] === 0x00)
- this._priv = this._priv.slice(1);
- this._priv = this._priv.slice(0, 32);
- }
- } 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') {
- this._priv = pk.part.r.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.R.data;
- if (pub[0] === 0x00)
- pub = pub.slice(1);
- var secret = ed.dh.computeKey(
- this._priv.toString('binary'),
- pub.toString('binary'));
- return (new Buffer(secret, 'binary'));
- }
- 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: new Buffer(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 = new Buffer(priv.toByteArray());
- pub = new Buffer(this._ecParams.getCurve().
- encodePointHex(pub), 'hex');
- this._priv = new ECPrivate(this._ecParams, priv);
- parts.push({name: 'curve',
- data: new Buffer(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') {
- priv = ed.dh.generateKey();
- pub = ed.dh.publicKey(priv);
- this._priv = priv = new Buffer(priv, 'binary');
- pub = new Buffer(pub, 'binary');
- parts.push({name: 'R', data: pub});
- parts.push({name: 'r', data: Buffer.concat([priv, pub])});
- 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 (new Buffer(S.getX().toBigInteger().toByteArray()));
- };
|