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