dhe.js 11 KB

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