dhe.js 11 KB

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