45c5414ad6bcebf3aa5e4180a1d498f33da33d0b938042c4d7949a934c337c355239046117b8fe10eb444490fdb1d4e3a4a0976990f5eccc9027cf7b5b4ae2 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. 'use strict';
  2. var BN = require('bn.js');
  3. var HmacDRBG = require('hmac-drbg');
  4. var utils = require('../utils');
  5. var curves = require('../curves');
  6. var rand = require('brorand');
  7. var assert = utils.assert;
  8. var KeyPair = require('./key');
  9. var Signature = require('./signature');
  10. function EC(options) {
  11. if (!(this instanceof EC))
  12. return new EC(options);
  13. // Shortcut `elliptic.ec(curve-name)`
  14. if (typeof options === 'string') {
  15. assert(Object.prototype.hasOwnProperty.call(curves, options),
  16. 'Unknown curve ' + options);
  17. options = curves[options];
  18. }
  19. // Shortcut for `elliptic.ec(elliptic.curves.curveName)`
  20. if (options instanceof curves.PresetCurve)
  21. options = { curve: options };
  22. this.curve = options.curve.curve;
  23. this.n = this.curve.n;
  24. this.nh = this.n.ushrn(1);
  25. this.g = this.curve.g;
  26. // Point on curve
  27. this.g = options.curve.g;
  28. this.g.precompute(options.curve.n.bitLength() + 1);
  29. // Hash for function for DRBG
  30. this.hash = options.hash || options.curve.hash;
  31. }
  32. module.exports = EC;
  33. EC.prototype.keyPair = function keyPair(options) {
  34. return new KeyPair(this, options);
  35. };
  36. EC.prototype.keyFromPrivate = function keyFromPrivate(priv, enc) {
  37. return KeyPair.fromPrivate(this, priv, enc);
  38. };
  39. EC.prototype.keyFromPublic = function keyFromPublic(pub, enc) {
  40. return KeyPair.fromPublic(this, pub, enc);
  41. };
  42. EC.prototype.genKeyPair = function genKeyPair(options) {
  43. if (!options)
  44. options = {};
  45. // Instantiate Hmac_DRBG
  46. var drbg = new HmacDRBG({
  47. hash: this.hash,
  48. pers: options.pers,
  49. persEnc: options.persEnc || 'utf8',
  50. entropy: options.entropy || rand(this.hash.hmacStrength),
  51. entropyEnc: options.entropy && options.entropyEnc || 'utf8',
  52. nonce: this.n.toArray(),
  53. });
  54. var bytes = this.n.byteLength();
  55. var ns2 = this.n.sub(new BN(2));
  56. for (;;) {
  57. var priv = new BN(drbg.generate(bytes));
  58. if (priv.cmp(ns2) > 0)
  59. continue;
  60. priv.iaddn(1);
  61. return this.keyFromPrivate(priv);
  62. }
  63. };
  64. EC.prototype._truncateToN = function _truncateToN(msg, truncOnly, bitLength) {
  65. var byteLength;
  66. if (BN.isBN(msg) || typeof msg === 'number') {
  67. msg = new BN(msg, 16);
  68. byteLength = msg.byteLength();
  69. } else if (typeof msg === 'object') {
  70. // BN assumes an array-like input and asserts length
  71. byteLength = msg.length;
  72. msg = new BN(msg, 16);
  73. } else {
  74. // BN converts the value to string
  75. var str = msg.toString();
  76. // HEX encoding
  77. byteLength = (str.length + 1) >>> 1;
  78. msg = new BN(str, 16);
  79. }
  80. // Allow overriding
  81. if (typeof bitLength !== 'number') {
  82. bitLength = byteLength * 8;
  83. }
  84. var delta = bitLength - this.n.bitLength();
  85. if (delta > 0)
  86. msg = msg.ushrn(delta);
  87. if (!truncOnly && msg.cmp(this.n) >= 0)
  88. return msg.sub(this.n);
  89. else
  90. return msg;
  91. };
  92. EC.prototype.sign = function sign(msg, key, enc, options) {
  93. if (typeof enc === 'object') {
  94. options = enc;
  95. enc = null;
  96. }
  97. if (!options)
  98. options = {};
  99. if (typeof msg !== 'string' && typeof msg !== 'number' && !BN.isBN(msg)) {
  100. assert(typeof msg === 'object' && msg && typeof msg.length === 'number',
  101. 'Expected message to be an array-like, a hex string, or a BN instance');
  102. assert((msg.length >>> 0) === msg.length); // non-negative 32-bit integer
  103. for (var i = 0; i < msg.length; i++) assert((msg[i] & 255) === msg[i]);
  104. }
  105. key = this.keyFromPrivate(key, enc);
  106. msg = this._truncateToN(msg, false, options.msgBitLength);
  107. // Would fail further checks, but let's make the error message clear
  108. assert(!msg.isNeg(), 'Can not sign a negative message');
  109. // Zero-extend key to provide enough entropy
  110. var bytes = this.n.byteLength();
  111. var bkey = key.getPrivate().toArray('be', bytes);
  112. // Zero-extend nonce to have the same byte size as N
  113. var nonce = msg.toArray('be', bytes);
  114. // Recheck nonce to be bijective to msg
  115. assert((new BN(nonce)).eq(msg), 'Can not sign message');
  116. // Instantiate Hmac_DRBG
  117. var drbg = new HmacDRBG({
  118. hash: this.hash,
  119. entropy: bkey,
  120. nonce: nonce,
  121. pers: options.pers,
  122. persEnc: options.persEnc || 'utf8',
  123. });
  124. // Number of bytes to generate
  125. var ns1 = this.n.sub(new BN(1));
  126. for (var iter = 0; ; iter++) {
  127. var k = options.k ?
  128. options.k(iter) :
  129. new BN(drbg.generate(this.n.byteLength()));
  130. k = this._truncateToN(k, true);
  131. if (k.cmpn(1) <= 0 || k.cmp(ns1) >= 0)
  132. continue;
  133. var kp = this.g.mul(k);
  134. if (kp.isInfinity())
  135. continue;
  136. var kpX = kp.getX();
  137. var r = kpX.umod(this.n);
  138. if (r.cmpn(0) === 0)
  139. continue;
  140. var s = k.invm(this.n).mul(r.mul(key.getPrivate()).iadd(msg));
  141. s = s.umod(this.n);
  142. if (s.cmpn(0) === 0)
  143. continue;
  144. var recoveryParam = (kp.getY().isOdd() ? 1 : 0) |
  145. (kpX.cmp(r) !== 0 ? 2 : 0);
  146. // Use complement of `s`, if it is > `n / 2`
  147. if (options.canonical && s.cmp(this.nh) > 0) {
  148. s = this.n.sub(s);
  149. recoveryParam ^= 1;
  150. }
  151. return new Signature({ r: r, s: s, recoveryParam: recoveryParam });
  152. }
  153. };
  154. EC.prototype.verify = function verify(msg, signature, key, enc, options) {
  155. if (!options)
  156. options = {};
  157. msg = this._truncateToN(msg, false, options.msgBitLength);
  158. key = this.keyFromPublic(key, enc);
  159. signature = new Signature(signature, 'hex');
  160. // Perform primitive values validation
  161. var r = signature.r;
  162. var s = signature.s;
  163. if (r.cmpn(1) < 0 || r.cmp(this.n) >= 0)
  164. return false;
  165. if (s.cmpn(1) < 0 || s.cmp(this.n) >= 0)
  166. return false;
  167. // Validate signature
  168. var sinv = s.invm(this.n);
  169. var u1 = sinv.mul(msg).umod(this.n);
  170. var u2 = sinv.mul(r).umod(this.n);
  171. var p;
  172. if (!this.curve._maxwellTrick) {
  173. p = this.g.mulAdd(u1, key.getPublic(), u2);
  174. if (p.isInfinity())
  175. return false;
  176. return p.getX().umod(this.n).cmp(r) === 0;
  177. }
  178. // NOTE: Greg Maxwell's trick, inspired by:
  179. // https://git.io/vad3K
  180. p = this.g.jmulAdd(u1, key.getPublic(), u2);
  181. if (p.isInfinity())
  182. return false;
  183. // Compare `p.x` of Jacobian point with `r`,
  184. // this will do `p.x == r * p.z^2` instead of multiplying `p.x` by the
  185. // inverse of `p.z^2`
  186. return p.eqXToP(r);
  187. };
  188. EC.prototype.recoverPubKey = function(msg, signature, j, enc) {
  189. assert((3 & j) === j, 'The recovery param is more than two bits');
  190. signature = new Signature(signature, enc);
  191. var n = this.n;
  192. var e = new BN(msg);
  193. var r = signature.r;
  194. var s = signature.s;
  195. // A set LSB signifies that the y-coordinate is odd
  196. var isYOdd = j & 1;
  197. var isSecondKey = j >> 1;
  198. if (r.cmp(this.curve.p.umod(this.curve.n)) >= 0 && isSecondKey)
  199. throw new Error('Unable to find sencond key candinate');
  200. // 1.1. Let x = r + jn.
  201. if (isSecondKey)
  202. r = this.curve.pointFromX(r.add(this.curve.n), isYOdd);
  203. else
  204. r = this.curve.pointFromX(r, isYOdd);
  205. var rInv = signature.r.invm(n);
  206. var s1 = n.sub(e).mul(rInv).umod(n);
  207. var s2 = s.mul(rInv).umod(n);
  208. // 1.6.1 Compute Q = r^-1 (sR - eG)
  209. // Q = r^-1 (sR + -eG)
  210. return this.g.mulAdd(s1, r, s2);
  211. };
  212. EC.prototype.getKeyRecoveryParam = function(e, signature, Q, enc) {
  213. signature = new Signature(signature, enc);
  214. if (signature.recoveryParam !== null)
  215. return signature.recoveryParam;
  216. for (var i = 0; i < 4; i++) {
  217. var Qprime;
  218. try {
  219. Qprime = this.recoverPubKey(e, signature, i);
  220. } catch (e) {
  221. continue;
  222. }
  223. if (Qprime.eq(Q))
  224. return i;
  225. }
  226. throw new Error('Unable to find valid recovery factor');
  227. };