123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411 |
- // Copyright 2017 Joyent, Inc.
- module.exports = {
- DiffieHellman: DiffieHellman,
- generateECDSA: generateECDSA,
- generateED25519: generateED25519
- };
- var assert = require('assert-plus');
- var crypto = require('crypto');
- var algs = require('./algs');
- var utils = require('./utils');
- var nacl;
- 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 (nacl === undefined)
- nacl = require('tweetnacl');
- if (this._isPriv) {
- this._priv = key.part.r.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') {
- 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;
- while (pub[0] === 0x00 && pub.length > 32)
- pub = pub.slice(1);
- assert.strictEqual(pub.length, 32);
- assert.strictEqual(this._priv.length, 64);
- var priv = this._priv.slice(0, 32);
- var secret = nacl.box.before(new Uint8Array(pub),
- new Uint8Array(priv));
- return (new Buffer(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: 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') {
- var pair = nacl.box.keyPair();
- priv = new Buffer(pair.secretKey);
- pub = new Buffer(pair.publicKey);
- priv = Buffer.concat([priv, pub]);
- assert.strictEqual(priv.length, 64);
- assert.strictEqual(pub.length, 32);
- parts.push({name: 'R', data: pub});
- parts.push({name: 'r', 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 (new Buffer(S.getX().toBigInteger().toByteArray()));
- };
- function generateED25519() {
- if (nacl === undefined)
- nacl = require('tweetnacl');
- var pair = nacl.sign.keyPair();
- var priv = new Buffer(pair.secretKey);
- var pub = new Buffer(pair.publicKey);
- assert.strictEqual(priv.length, 64);
- assert.strictEqual(pub.length, 32);
- var parts = [];
- parts.push({name: 'R', data: pub});
- parts.push({name: 'r', data: priv});
- 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: new Buffer(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 {
- if (ecdh === undefined)
- ecdh = require('ecc-jsbn');
- if (ec === undefined)
- ec = require('ecc-jsbn/lib/ec');
- if (jsbn === undefined)
- jsbn = require('jsbn').BigInteger;
- 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 = new Buffer(priv.toByteArray());
- pub = new Buffer(ecParams.getCurve().
- encodePointHex(pub), 'hex');
- parts.push({name: 'curve', data: new Buffer(curve)});
- parts.push({name: 'Q', data: pub});
- parts.push({name: 'd', data: priv});
- key = new PrivateKey({
- type: 'ecdsa',
- curve: curve,
- parts: parts
- });
- return (key);
- }
- }
|