index.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  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(curves.hasOwnProperty(options), 'Unknown curve ' + options);
  16. options = curves[options];
  17. }
  18. // Shortcut for `elliptic.ec(elliptic.curves.curveName)`
  19. if (options instanceof curves.PresetCurve)
  20. options = { curve: options };
  21. this.curve = options.curve.curve;
  22. this.n = this.curve.n;
  23. this.nh = this.n.ushrn(1);
  24. this.g = this.curve.g;
  25. // Point on curve
  26. this.g = options.curve.g;
  27. this.g.precompute(options.curve.n.bitLength() + 1);
  28. // Hash for function for DRBG
  29. this.hash = options.hash || options.curve.hash;
  30. }
  31. module.exports = EC;
  32. EC.prototype.keyPair = function keyPair(options) {
  33. return new KeyPair(this, options);
  34. };
  35. EC.prototype.keyFromPrivate = function keyFromPrivate(priv, enc) {
  36. return KeyPair.fromPrivate(this, priv, enc);
  37. };
  38. EC.prototype.keyFromPublic = function keyFromPublic(pub, enc) {
  39. return KeyPair.fromPublic(this, pub, enc);
  40. };
  41. EC.prototype.genKeyPair = function genKeyPair(options) {
  42. if (!options)
  43. options = {};
  44. // Instantiate Hmac_DRBG
  45. var drbg = new HmacDRBG({
  46. hash: this.hash,
  47. pers: options.pers,
  48. persEnc: options.persEnc || 'utf8',
  49. entropy: options.entropy || rand(this.hash.hmacStrength),
  50. entropyEnc: options.entropy && options.entropyEnc || 'utf8',
  51. nonce: this.n.toArray()
  52. });
  53. var bytes = this.n.byteLength();
  54. var ns2 = this.n.sub(new BN(2));
  55. do {
  56. var priv = new BN(drbg.generate(bytes));
  57. if (priv.cmp(ns2) > 0)
  58. continue;
  59. priv.iaddn(1);
  60. return this.keyFromPrivate(priv);
  61. } while (true);
  62. };
  63. EC.prototype._truncateToN = function truncateToN(msg, truncOnly) {
  64. var delta = msg.byteLength() * 8 - this.n.bitLength();
  65. if (delta > 0)
  66. msg = msg.ushrn(delta);
  67. if (!truncOnly && msg.cmp(this.n) >= 0)
  68. return msg.sub(this.n);
  69. else
  70. return msg;
  71. };
  72. EC.prototype.sign = function sign(msg, key, enc, options) {
  73. if (typeof enc === 'object') {
  74. options = enc;
  75. enc = null;
  76. }
  77. if (!options)
  78. options = {};
  79. key = this.keyFromPrivate(key, enc);
  80. msg = this._truncateToN(new BN(msg, 16));
  81. // Zero-extend key to provide enough entropy
  82. var bytes = this.n.byteLength();
  83. var bkey = key.getPrivate().toArray('be', bytes);
  84. // Zero-extend nonce to have the same byte size as N
  85. var nonce = msg.toArray('be', bytes);
  86. // Instantiate Hmac_DRBG
  87. var drbg = new HmacDRBG({
  88. hash: this.hash,
  89. entropy: bkey,
  90. nonce: nonce,
  91. pers: options.pers,
  92. persEnc: options.persEnc || 'utf8'
  93. });
  94. // Number of bytes to generate
  95. var ns1 = this.n.sub(new BN(1));
  96. for (var iter = 0; true; iter++) {
  97. var k = options.k ?
  98. options.k(iter) :
  99. new BN(drbg.generate(this.n.byteLength()));
  100. k = this._truncateToN(k, true);
  101. if (k.cmpn(1) <= 0 || k.cmp(ns1) >= 0)
  102. continue;
  103. var kp = this.g.mul(k);
  104. if (kp.isInfinity())
  105. continue;
  106. var kpX = kp.getX();
  107. var r = kpX.umod(this.n);
  108. if (r.cmpn(0) === 0)
  109. continue;
  110. var s = k.invm(this.n).mul(r.mul(key.getPrivate()).iadd(msg));
  111. s = s.umod(this.n);
  112. if (s.cmpn(0) === 0)
  113. continue;
  114. var recoveryParam = (kp.getY().isOdd() ? 1 : 0) |
  115. (kpX.cmp(r) !== 0 ? 2 : 0);
  116. // Use complement of `s`, if it is > `n / 2`
  117. if (options.canonical && s.cmp(this.nh) > 0) {
  118. s = this.n.sub(s);
  119. recoveryParam ^= 1;
  120. }
  121. return new Signature({ r: r, s: s, recoveryParam: recoveryParam });
  122. }
  123. };
  124. EC.prototype.verify = function verify(msg, signature, key, enc) {
  125. msg = this._truncateToN(new BN(msg, 16));
  126. key = this.keyFromPublic(key, enc);
  127. signature = new Signature(signature, 'hex');
  128. // Perform primitive values validation
  129. var r = signature.r;
  130. var s = signature.s;
  131. if (r.cmpn(1) < 0 || r.cmp(this.n) >= 0)
  132. return false;
  133. if (s.cmpn(1) < 0 || s.cmp(this.n) >= 0)
  134. return false;
  135. // Validate signature
  136. var sinv = s.invm(this.n);
  137. var u1 = sinv.mul(msg).umod(this.n);
  138. var u2 = sinv.mul(r).umod(this.n);
  139. if (!this.curve._maxwellTrick) {
  140. var p = this.g.mulAdd(u1, key.getPublic(), u2);
  141. if (p.isInfinity())
  142. return false;
  143. return p.getX().umod(this.n).cmp(r) === 0;
  144. }
  145. // NOTE: Greg Maxwell's trick, inspired by:
  146. // https://git.io/vad3K
  147. var p = this.g.jmulAdd(u1, key.getPublic(), u2);
  148. if (p.isInfinity())
  149. return false;
  150. // Compare `p.x` of Jacobian point with `r`,
  151. // this will do `p.x == r * p.z^2` instead of multiplying `p.x` by the
  152. // inverse of `p.z^2`
  153. return p.eqXToP(r);
  154. };
  155. EC.prototype.recoverPubKey = function(msg, signature, j, enc) {
  156. assert((3 & j) === j, 'The recovery param is more than two bits');
  157. signature = new Signature(signature, enc);
  158. var n = this.n;
  159. var e = new BN(msg);
  160. var r = signature.r;
  161. var s = signature.s;
  162. // A set LSB signifies that the y-coordinate is odd
  163. var isYOdd = j & 1;
  164. var isSecondKey = j >> 1;
  165. if (r.cmp(this.curve.p.umod(this.curve.n)) >= 0 && isSecondKey)
  166. throw new Error('Unable to find sencond key candinate');
  167. // 1.1. Let x = r + jn.
  168. if (isSecondKey)
  169. r = this.curve.pointFromX(r.add(this.curve.n), isYOdd);
  170. else
  171. r = this.curve.pointFromX(r, isYOdd);
  172. var rInv = signature.r.invm(n);
  173. var s1 = n.sub(e).mul(rInv).umod(n);
  174. var s2 = s.mul(rInv).umod(n);
  175. // 1.6.1 Compute Q = r^-1 (sR - eG)
  176. // Q = r^-1 (sR + -eG)
  177. return this.g.mulAdd(s1, r, s2);
  178. };
  179. EC.prototype.getKeyRecoveryParam = function(e, signature, Q, enc) {
  180. signature = new Signature(signature, enc);
  181. if (signature.recoveryParam !== null)
  182. return signature.recoveryParam;
  183. for (var i = 0; i < 4; i++) {
  184. var Qprime;
  185. try {
  186. Qprime = this.recoverPubKey(e, signature, i);
  187. } catch (e) {
  188. continue;
  189. }
  190. if (Qprime.eq(Q))
  191. return i;
  192. }
  193. throw new Error('Unable to find valid recovery factor');
  194. };