| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397 | // 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);	}}
 |