dhe.js 11 KB

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