twitst4tz

twitter statistics web application
Log | Files | Refs | README | LICENSE

dhe.js (10600B)


      1 // Copyright 2017 Joyent, Inc.
      2 
      3 module.exports = {
      4 	DiffieHellman: DiffieHellman,
      5 	generateECDSA: generateECDSA,
      6 	generateED25519: generateED25519
      7 };
      8 
      9 var assert = require('assert-plus');
     10 var crypto = require('crypto');
     11 var Buffer = require('safer-buffer').Buffer;
     12 var algs = require('./algs');
     13 var utils = require('./utils');
     14 var nacl = require('tweetnacl');
     15 
     16 var Key = require('./key');
     17 var PrivateKey = require('./private-key');
     18 
     19 var CRYPTO_HAVE_ECDH = (crypto.createECDH !== undefined);
     20 
     21 var ecdh = require('ecc-jsbn');
     22 var ec = require('ecc-jsbn/lib/ec');
     23 var jsbn = require('jsbn').BigInteger;
     24 
     25 function DiffieHellman(key) {
     26 	utils.assertCompatible(key, Key, [1, 4], 'key');
     27 	this._isPriv = PrivateKey.isPrivateKey(key, [1, 3]);
     28 	this._algo = key.type;
     29 	this._curve = key.curve;
     30 	this._key = key;
     31 	if (key.type === 'dsa') {
     32 		if (!CRYPTO_HAVE_ECDH) {
     33 			throw (new Error('Due to bugs in the node 0.10 ' +
     34 			    'crypto API, node 0.12.x or later is required ' +
     35 			    'to use DH'));
     36 		}
     37 		this._dh = crypto.createDiffieHellman(
     38 		    key.part.p.data, undefined,
     39 		    key.part.g.data, undefined);
     40 		this._p = key.part.p;
     41 		this._g = key.part.g;
     42 		if (this._isPriv)
     43 			this._dh.setPrivateKey(key.part.x.data);
     44 		this._dh.setPublicKey(key.part.y.data);
     45 
     46 	} else if (key.type === 'ecdsa') {
     47 		if (!CRYPTO_HAVE_ECDH) {
     48 			this._ecParams = new X9ECParameters(this._curve);
     49 
     50 			if (this._isPriv) {
     51 				this._priv = new ECPrivate(
     52 				    this._ecParams, key.part.d.data);
     53 			}
     54 			return;
     55 		}
     56 
     57 		var curve = {
     58 			'nistp256': 'prime256v1',
     59 			'nistp384': 'secp384r1',
     60 			'nistp521': 'secp521r1'
     61 		}[key.curve];
     62 		this._dh = crypto.createECDH(curve);
     63 		if (typeof (this._dh) !== 'object' ||
     64 		    typeof (this._dh.setPrivateKey) !== 'function') {
     65 			CRYPTO_HAVE_ECDH = false;
     66 			DiffieHellman.call(this, key);
     67 			return;
     68 		}
     69 		if (this._isPriv)
     70 			this._dh.setPrivateKey(key.part.d.data);
     71 		this._dh.setPublicKey(key.part.Q.data);
     72 
     73 	} else if (key.type === 'curve25519') {
     74 		if (this._isPriv) {
     75 			utils.assertCompatible(key, PrivateKey, [1, 5], 'key');
     76 			this._priv = key.part.k.data;
     77 		}
     78 
     79 	} else {
     80 		throw (new Error('DH not supported for ' + key.type + ' keys'));
     81 	}
     82 }
     83 
     84 DiffieHellman.prototype.getPublicKey = function () {
     85 	if (this._isPriv)
     86 		return (this._key.toPublic());
     87 	return (this._key);
     88 };
     89 
     90 DiffieHellman.prototype.getPrivateKey = function () {
     91 	if (this._isPriv)
     92 		return (this._key);
     93 	else
     94 		return (undefined);
     95 };
     96 DiffieHellman.prototype.getKey = DiffieHellman.prototype.getPrivateKey;
     97 
     98 DiffieHellman.prototype._keyCheck = function (pk, isPub) {
     99 	assert.object(pk, 'key');
    100 	if (!isPub)
    101 		utils.assertCompatible(pk, PrivateKey, [1, 3], 'key');
    102 	utils.assertCompatible(pk, Key, [1, 4], 'key');
    103 
    104 	if (pk.type !== this._algo) {
    105 		throw (new Error('A ' + pk.type + ' key cannot be used in ' +
    106 		    this._algo + ' Diffie-Hellman'));
    107 	}
    108 
    109 	if (pk.curve !== this._curve) {
    110 		throw (new Error('A key from the ' + pk.curve + ' curve ' +
    111 		    'cannot be used with a ' + this._curve +
    112 		    ' Diffie-Hellman'));
    113 	}
    114 
    115 	if (pk.type === 'dsa') {
    116 		assert.deepEqual(pk.part.p, this._p,
    117 		    'DSA key prime does not match');
    118 		assert.deepEqual(pk.part.g, this._g,
    119 		    'DSA key generator does not match');
    120 	}
    121 };
    122 
    123 DiffieHellman.prototype.setKey = function (pk) {
    124 	this._keyCheck(pk);
    125 
    126 	if (pk.type === 'dsa') {
    127 		this._dh.setPrivateKey(pk.part.x.data);
    128 		this._dh.setPublicKey(pk.part.y.data);
    129 
    130 	} else if (pk.type === 'ecdsa') {
    131 		if (CRYPTO_HAVE_ECDH) {
    132 			this._dh.setPrivateKey(pk.part.d.data);
    133 			this._dh.setPublicKey(pk.part.Q.data);
    134 		} else {
    135 			this._priv = new ECPrivate(
    136 			    this._ecParams, pk.part.d.data);
    137 		}
    138 
    139 	} else if (pk.type === 'curve25519') {
    140 		var k = pk.part.k;
    141 		if (!pk.part.k)
    142 			k = pk.part.r;
    143 		this._priv = k.data;
    144 		if (this._priv[0] === 0x00)
    145 			this._priv = this._priv.slice(1);
    146 		this._priv = this._priv.slice(0, 32);
    147 	}
    148 	this._key = pk;
    149 	this._isPriv = true;
    150 };
    151 DiffieHellman.prototype.setPrivateKey = DiffieHellman.prototype.setKey;
    152 
    153 DiffieHellman.prototype.computeSecret = function (otherpk) {
    154 	this._keyCheck(otherpk, true);
    155 	if (!this._isPriv)
    156 		throw (new Error('DH exchange has not been initialized with ' +
    157 		    'a private key yet'));
    158 
    159 	var pub;
    160 	if (this._algo === 'dsa') {
    161 		return (this._dh.computeSecret(
    162 		    otherpk.part.y.data));
    163 
    164 	} else if (this._algo === 'ecdsa') {
    165 		if (CRYPTO_HAVE_ECDH) {
    166 			return (this._dh.computeSecret(
    167 			    otherpk.part.Q.data));
    168 		} else {
    169 			pub = new ECPublic(
    170 			    this._ecParams, otherpk.part.Q.data);
    171 			return (this._priv.deriveSharedSecret(pub));
    172 		}
    173 
    174 	} else if (this._algo === 'curve25519') {
    175 		pub = otherpk.part.A.data;
    176 		while (pub[0] === 0x00 && pub.length > 32)
    177 			pub = pub.slice(1);
    178 		var priv = this._priv;
    179 		assert.strictEqual(pub.length, 32);
    180 		assert.strictEqual(priv.length, 32);
    181 
    182 		var secret = nacl.box.before(new Uint8Array(pub),
    183 		    new Uint8Array(priv));
    184 
    185 		return (Buffer.from(secret));
    186 	}
    187 
    188 	throw (new Error('Invalid algorithm: ' + this._algo));
    189 };
    190 
    191 DiffieHellman.prototype.generateKey = function () {
    192 	var parts = [];
    193 	var priv, pub;
    194 	if (this._algo === 'dsa') {
    195 		this._dh.generateKeys();
    196 
    197 		parts.push({name: 'p', data: this._p.data});
    198 		parts.push({name: 'q', data: this._key.part.q.data});
    199 		parts.push({name: 'g', data: this._g.data});
    200 		parts.push({name: 'y', data: this._dh.getPublicKey()});
    201 		parts.push({name: 'x', data: this._dh.getPrivateKey()});
    202 		this._key = new PrivateKey({
    203 			type: 'dsa',
    204 			parts: parts
    205 		});
    206 		this._isPriv = true;
    207 		return (this._key);
    208 
    209 	} else if (this._algo === 'ecdsa') {
    210 		if (CRYPTO_HAVE_ECDH) {
    211 			this._dh.generateKeys();
    212 
    213 			parts.push({name: 'curve',
    214 			    data: Buffer.from(this._curve)});
    215 			parts.push({name: 'Q', data: this._dh.getPublicKey()});
    216 			parts.push({name: 'd', data: this._dh.getPrivateKey()});
    217 			this._key = new PrivateKey({
    218 				type: 'ecdsa',
    219 				curve: this._curve,
    220 				parts: parts
    221 			});
    222 			this._isPriv = true;
    223 			return (this._key);
    224 
    225 		} else {
    226 			var n = this._ecParams.getN();
    227 			var r = new jsbn(crypto.randomBytes(n.bitLength()));
    228 			var n1 = n.subtract(jsbn.ONE);
    229 			priv = r.mod(n1).add(jsbn.ONE);
    230 			pub = this._ecParams.getG().multiply(priv);
    231 
    232 			priv = Buffer.from(priv.toByteArray());
    233 			pub = Buffer.from(this._ecParams.getCurve().
    234 			    encodePointHex(pub), 'hex');
    235 
    236 			this._priv = new ECPrivate(this._ecParams, priv);
    237 
    238 			parts.push({name: 'curve',
    239 			    data: Buffer.from(this._curve)});
    240 			parts.push({name: 'Q', data: pub});
    241 			parts.push({name: 'd', data: priv});
    242 
    243 			this._key = new PrivateKey({
    244 				type: 'ecdsa',
    245 				curve: this._curve,
    246 				parts: parts
    247 			});
    248 			this._isPriv = true;
    249 			return (this._key);
    250 		}
    251 
    252 	} else if (this._algo === 'curve25519') {
    253 		var pair = nacl.box.keyPair();
    254 		priv = Buffer.from(pair.secretKey);
    255 		pub = Buffer.from(pair.publicKey);
    256 		priv = Buffer.concat([priv, pub]);
    257 		assert.strictEqual(priv.length, 64);
    258 		assert.strictEqual(pub.length, 32);
    259 
    260 		parts.push({name: 'A', data: pub});
    261 		parts.push({name: 'k', data: priv});
    262 		this._key = new PrivateKey({
    263 			type: 'curve25519',
    264 			parts: parts
    265 		});
    266 		this._isPriv = true;
    267 		return (this._key);
    268 	}
    269 
    270 	throw (new Error('Invalid algorithm: ' + this._algo));
    271 };
    272 DiffieHellman.prototype.generateKeys = DiffieHellman.prototype.generateKey;
    273 
    274 /* These are helpers for using ecc-jsbn (for node 0.10 compatibility). */
    275 
    276 function X9ECParameters(name) {
    277 	var params = algs.curves[name];
    278 	assert.object(params);
    279 
    280 	var p = new jsbn(params.p);
    281 	var a = new jsbn(params.a);
    282 	var b = new jsbn(params.b);
    283 	var n = new jsbn(params.n);
    284 	var h = jsbn.ONE;
    285 	var curve = new ec.ECCurveFp(p, a, b);
    286 	var G = curve.decodePointHex(params.G.toString('hex'));
    287 
    288 	this.curve = curve;
    289 	this.g = G;
    290 	this.n = n;
    291 	this.h = h;
    292 }
    293 X9ECParameters.prototype.getCurve = function () { return (this.curve); };
    294 X9ECParameters.prototype.getG = function () { return (this.g); };
    295 X9ECParameters.prototype.getN = function () { return (this.n); };
    296 X9ECParameters.prototype.getH = function () { return (this.h); };
    297 
    298 function ECPublic(params, buffer) {
    299 	this._params = params;
    300 	if (buffer[0] === 0x00)
    301 		buffer = buffer.slice(1);
    302 	this._pub = params.getCurve().decodePointHex(buffer.toString('hex'));
    303 }
    304 
    305 function ECPrivate(params, buffer) {
    306 	this._params = params;
    307 	this._priv = new jsbn(utils.mpNormalize(buffer));
    308 }
    309 ECPrivate.prototype.deriveSharedSecret = function (pubKey) {
    310 	assert.ok(pubKey instanceof ECPublic);
    311 	var S = pubKey._pub.multiply(this._priv);
    312 	return (Buffer.from(S.getX().toBigInteger().toByteArray()));
    313 };
    314 
    315 function generateED25519() {
    316 	var pair = nacl.sign.keyPair();
    317 	var priv = Buffer.from(pair.secretKey);
    318 	var pub = Buffer.from(pair.publicKey);
    319 	assert.strictEqual(priv.length, 64);
    320 	assert.strictEqual(pub.length, 32);
    321 
    322 	var parts = [];
    323 	parts.push({name: 'A', data: pub});
    324 	parts.push({name: 'k', data: priv.slice(0, 32)});
    325 	var key = new PrivateKey({
    326 		type: 'ed25519',
    327 		parts: parts
    328 	});
    329 	return (key);
    330 }
    331 
    332 /* Generates a new ECDSA private key on a given curve. */
    333 function generateECDSA(curve) {
    334 	var parts = [];
    335 	var key;
    336 
    337 	if (CRYPTO_HAVE_ECDH) {
    338 		/*
    339 		 * Node crypto doesn't expose key generation directly, but the
    340 		 * ECDH instances can generate keys. It turns out this just
    341 		 * calls into the OpenSSL generic key generator, and we can
    342 		 * read its output happily without doing an actual DH. So we
    343 		 * use that here.
    344 		 */
    345 		var osCurve = {
    346 			'nistp256': 'prime256v1',
    347 			'nistp384': 'secp384r1',
    348 			'nistp521': 'secp521r1'
    349 		}[curve];
    350 
    351 		var dh = crypto.createECDH(osCurve);
    352 		dh.generateKeys();
    353 
    354 		parts.push({name: 'curve',
    355 		    data: Buffer.from(curve)});
    356 		parts.push({name: 'Q', data: dh.getPublicKey()});
    357 		parts.push({name: 'd', data: dh.getPrivateKey()});
    358 
    359 		key = new PrivateKey({
    360 			type: 'ecdsa',
    361 			curve: curve,
    362 			parts: parts
    363 		});
    364 		return (key);
    365 	} else {
    366 
    367 		var ecParams = new X9ECParameters(curve);
    368 
    369 		/* This algorithm taken from FIPS PUB 186-4 (section B.4.1) */
    370 		var n = ecParams.getN();
    371 		/*
    372 		 * The crypto.randomBytes() function can only give us whole
    373 		 * bytes, so taking a nod from X9.62, we round up.
    374 		 */
    375 		var cByteLen = Math.ceil((n.bitLength() + 64) / 8);
    376 		var c = new jsbn(crypto.randomBytes(cByteLen));
    377 
    378 		var n1 = n.subtract(jsbn.ONE);
    379 		var priv = c.mod(n1).add(jsbn.ONE);
    380 		var pub = ecParams.getG().multiply(priv);
    381 
    382 		priv = Buffer.from(priv.toByteArray());
    383 		pub = Buffer.from(ecParams.getCurve().
    384 		    encodePointHex(pub), 'hex');
    385 
    386 		parts.push({name: 'curve', data: Buffer.from(curve)});
    387 		parts.push({name: 'Q', data: pub});
    388 		parts.push({name: 'd', data: priv});
    389 
    390 		key = new PrivateKey({
    391 			type: 'ecdsa',
    392 			curve: curve,
    393 			parts: parts
    394 		});
    395 		return (key);
    396 	}
    397 }