dhe.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. // Copyright 2015 Joyent, Inc.
  2. module.exports = DiffieHellman;
  3. var assert = require('assert-plus');
  4. var crypto = require('crypto');
  5. var algs = require('./algs');
  6. var utils = require('./utils');
  7. var ed;
  8. var Key = require('./key');
  9. var PrivateKey = require('./private-key');
  10. var CRYPTO_HAVE_ECDH = (crypto.createECDH !== undefined);
  11. var ecdh, ec, jsbn;
  12. function DiffieHellman(key) {
  13. utils.assertCompatible(key, Key, [1, 4], 'key');
  14. this._isPriv = PrivateKey.isPrivateKey(key, [1, 3]);
  15. this._algo = key.type;
  16. this._curve = key.curve;
  17. this._key = key;
  18. if (key.type === 'dsa') {
  19. if (!CRYPTO_HAVE_ECDH) {
  20. throw (new Error('Due to bugs in the node 0.10 ' +
  21. 'crypto API, node 0.12.x or later is required ' +
  22. 'to use DH'));
  23. }
  24. this._dh = crypto.createDiffieHellman(
  25. key.part.p.data, undefined,
  26. key.part.g.data, undefined);
  27. this._p = key.part.p;
  28. this._g = key.part.g;
  29. if (this._isPriv)
  30. this._dh.setPrivateKey(key.part.x.data);
  31. this._dh.setPublicKey(key.part.y.data);
  32. } else if (key.type === 'ecdsa') {
  33. if (!CRYPTO_HAVE_ECDH) {
  34. if (ecdh === undefined)
  35. ecdh = require('ecc-jsbn');
  36. if (ec === undefined)
  37. ec = require('ecc-jsbn/lib/ec');
  38. if (jsbn === undefined)
  39. jsbn = require('jsbn').BigInteger;
  40. this._ecParams = new X9ECParameters(this._curve);
  41. if (this._isPriv) {
  42. this._priv = new ECPrivate(
  43. this._ecParams, key.part.d.data);
  44. }
  45. return;
  46. }
  47. var curve = {
  48. 'nistp256': 'prime256v1',
  49. 'nistp384': 'secp384r1',
  50. 'nistp521': 'secp521r1'
  51. }[key.curve];
  52. this._dh = crypto.createECDH(curve);
  53. if (typeof (this._dh) !== 'object' ||
  54. typeof (this._dh.setPrivateKey) !== 'function') {
  55. CRYPTO_HAVE_ECDH = false;
  56. DiffieHellman.call(this, key);
  57. return;
  58. }
  59. if (this._isPriv)
  60. this._dh.setPrivateKey(key.part.d.data);
  61. this._dh.setPublicKey(key.part.Q.data);
  62. } else if (key.type === 'curve25519') {
  63. if (ed === undefined)
  64. ed = require('jodid25519');
  65. if (this._isPriv) {
  66. this._priv = key.part.r.data;
  67. if (this._priv[0] === 0x00)
  68. this._priv = this._priv.slice(1);
  69. this._priv = this._priv.slice(0, 32);
  70. }
  71. } else {
  72. throw (new Error('DH not supported for ' + key.type + ' keys'));
  73. }
  74. }
  75. DiffieHellman.prototype.getPublicKey = function () {
  76. if (this._isPriv)
  77. return (this._key.toPublic());
  78. return (this._key);
  79. };
  80. DiffieHellman.prototype.getPrivateKey = function () {
  81. if (this._isPriv)
  82. return (this._key);
  83. else
  84. return (undefined);
  85. };
  86. DiffieHellman.prototype.getKey = DiffieHellman.prototype.getPrivateKey;
  87. DiffieHellman.prototype._keyCheck = function (pk, isPub) {
  88. assert.object(pk, 'key');
  89. if (!isPub)
  90. utils.assertCompatible(pk, PrivateKey, [1, 3], 'key');
  91. utils.assertCompatible(pk, Key, [1, 4], 'key');
  92. if (pk.type !== this._algo) {
  93. throw (new Error('A ' + pk.type + ' key cannot be used in ' +
  94. this._algo + ' Diffie-Hellman'));
  95. }
  96. if (pk.curve !== this._curve) {
  97. throw (new Error('A key from the ' + pk.curve + ' curve ' +
  98. 'cannot be used with a ' + this._curve +
  99. ' Diffie-Hellman'));
  100. }
  101. if (pk.type === 'dsa') {
  102. assert.deepEqual(pk.part.p, this._p,
  103. 'DSA key prime does not match');
  104. assert.deepEqual(pk.part.g, this._g,
  105. 'DSA key generator does not match');
  106. }
  107. };
  108. DiffieHellman.prototype.setKey = function (pk) {
  109. this._keyCheck(pk);
  110. if (pk.type === 'dsa') {
  111. this._dh.setPrivateKey(pk.part.x.data);
  112. this._dh.setPublicKey(pk.part.y.data);
  113. } else if (pk.type === 'ecdsa') {
  114. if (CRYPTO_HAVE_ECDH) {
  115. this._dh.setPrivateKey(pk.part.d.data);
  116. this._dh.setPublicKey(pk.part.Q.data);
  117. } else {
  118. this._priv = new ECPrivate(
  119. this._ecParams, pk.part.d.data);
  120. }
  121. } else if (pk.type === 'curve25519') {
  122. this._priv = pk.part.r.data;
  123. if (this._priv[0] === 0x00)
  124. this._priv = this._priv.slice(1);
  125. this._priv = this._priv.slice(0, 32);
  126. }
  127. this._key = pk;
  128. this._isPriv = true;
  129. };
  130. DiffieHellman.prototype.setPrivateKey = DiffieHellman.prototype.setKey;
  131. DiffieHellman.prototype.computeSecret = function (otherpk) {
  132. this._keyCheck(otherpk, true);
  133. if (!this._isPriv)
  134. throw (new Error('DH exchange has not been initialized with ' +
  135. 'a private key yet'));
  136. var pub;
  137. if (this._algo === 'dsa') {
  138. return (this._dh.computeSecret(
  139. otherpk.part.y.data));
  140. } else if (this._algo === 'ecdsa') {
  141. if (CRYPTO_HAVE_ECDH) {
  142. return (this._dh.computeSecret(
  143. otherpk.part.Q.data));
  144. } else {
  145. pub = new ECPublic(
  146. this._ecParams, otherpk.part.Q.data);
  147. return (this._priv.deriveSharedSecret(pub));
  148. }
  149. } else if (this._algo === 'curve25519') {
  150. pub = otherpk.part.R.data;
  151. if (pub[0] === 0x00)
  152. pub = pub.slice(1);
  153. var secret = ed.dh.computeKey(
  154. this._priv.toString('binary'),
  155. pub.toString('binary'));
  156. return (new Buffer(secret, 'binary'));
  157. }
  158. throw (new Error('Invalid algorithm: ' + this._algo));
  159. };
  160. DiffieHellman.prototype.generateKey = function () {
  161. var parts = [];
  162. var priv, pub;
  163. if (this._algo === 'dsa') {
  164. this._dh.generateKeys();
  165. parts.push({name: 'p', data: this._p.data});
  166. parts.push({name: 'q', data: this._key.part.q.data});
  167. parts.push({name: 'g', data: this._g.data});
  168. parts.push({name: 'y', data: this._dh.getPublicKey()});
  169. parts.push({name: 'x', data: this._dh.getPrivateKey()});
  170. this._key = new PrivateKey({
  171. type: 'dsa',
  172. parts: parts
  173. });
  174. this._isPriv = true;
  175. return (this._key);
  176. } else if (this._algo === 'ecdsa') {
  177. if (CRYPTO_HAVE_ECDH) {
  178. this._dh.generateKeys();
  179. parts.push({name: 'curve',
  180. data: new Buffer(this._curve)});
  181. parts.push({name: 'Q', data: this._dh.getPublicKey()});
  182. parts.push({name: 'd', data: this._dh.getPrivateKey()});
  183. this._key = new PrivateKey({
  184. type: 'ecdsa',
  185. curve: this._curve,
  186. parts: parts
  187. });
  188. this._isPriv = true;
  189. return (this._key);
  190. } else {
  191. var n = this._ecParams.getN();
  192. var r = new jsbn(crypto.randomBytes(n.bitLength()));
  193. var n1 = n.subtract(jsbn.ONE);
  194. priv = r.mod(n1).add(jsbn.ONE);
  195. pub = this._ecParams.getG().multiply(priv);
  196. priv = new Buffer(priv.toByteArray());
  197. pub = new Buffer(this._ecParams.getCurve().
  198. encodePointHex(pub), 'hex');
  199. this._priv = new ECPrivate(this._ecParams, priv);
  200. parts.push({name: 'curve',
  201. data: new Buffer(this._curve)});
  202. parts.push({name: 'Q', data: pub});
  203. parts.push({name: 'd', data: priv});
  204. this._key = new PrivateKey({
  205. type: 'ecdsa',
  206. curve: this._curve,
  207. parts: parts
  208. });
  209. this._isPriv = true;
  210. return (this._key);
  211. }
  212. } else if (this._algo === 'curve25519') {
  213. priv = ed.dh.generateKey();
  214. pub = ed.dh.publicKey(priv);
  215. this._priv = priv = new Buffer(priv, 'binary');
  216. pub = new Buffer(pub, 'binary');
  217. parts.push({name: 'R', data: pub});
  218. parts.push({name: 'r', data: Buffer.concat([priv, pub])});
  219. this._key = new PrivateKey({
  220. type: 'curve25519',
  221. parts: parts
  222. });
  223. this._isPriv = true;
  224. return (this._key);
  225. }
  226. throw (new Error('Invalid algorithm: ' + this._algo));
  227. };
  228. DiffieHellman.prototype.generateKeys = DiffieHellman.prototype.generateKey;
  229. /* These are helpers for using ecc-jsbn (for node 0.10 compatibility). */
  230. function X9ECParameters(name) {
  231. var params = algs.curves[name];
  232. assert.object(params);
  233. var p = new jsbn(params.p);
  234. var a = new jsbn(params.a);
  235. var b = new jsbn(params.b);
  236. var n = new jsbn(params.n);
  237. var h = jsbn.ONE;
  238. var curve = new ec.ECCurveFp(p, a, b);
  239. var G = curve.decodePointHex(params.G.toString('hex'));
  240. this.curve = curve;
  241. this.g = G;
  242. this.n = n;
  243. this.h = h;
  244. }
  245. X9ECParameters.prototype.getCurve = function () { return (this.curve); };
  246. X9ECParameters.prototype.getG = function () { return (this.g); };
  247. X9ECParameters.prototype.getN = function () { return (this.n); };
  248. X9ECParameters.prototype.getH = function () { return (this.h); };
  249. function ECPublic(params, buffer) {
  250. this._params = params;
  251. if (buffer[0] === 0x00)
  252. buffer = buffer.slice(1);
  253. this._pub = params.getCurve().decodePointHex(buffer.toString('hex'));
  254. }
  255. function ECPrivate(params, buffer) {
  256. this._params = params;
  257. this._priv = new jsbn(utils.mpNormalize(buffer));
  258. }
  259. ECPrivate.prototype.deriveSharedSecret = function (pubKey) {
  260. assert.ok(pubKey instanceof ECPublic);
  261. var S = pubKey._pub.multiply(this._priv);
  262. return (new Buffer(S.getX().toBigInteger().toByteArray()));
  263. };