| 'use strict'; |
| |
| var BN = require('bn.js'); |
| var HmacDRBG = require('hmac-drbg'); |
| var utils = require('../utils'); |
| var curves = require('../curves'); |
| var rand = require('brorand'); |
| var assert = utils.assert; |
| |
| var KeyPair = require('./key'); |
| var Signature = require('./signature'); |
| |
| function EC(options) { |
| if (!(this instanceof EC)) |
| return new EC(options); |
| |
| // Shortcut `elliptic.ec(curve-name)` |
| if (typeof options === 'string') { |
| assert(curves.hasOwnProperty(options), 'Unknown curve ' + options); |
| |
| options = curves[options]; |
| } |
| |
| // Shortcut for `elliptic.ec(elliptic.curves.curveName)` |
| if (options instanceof curves.PresetCurve) |
| options = { curve: options }; |
| |
| this.curve = options.curve.curve; |
| this.n = this.curve.n; |
| this.nh = this.n.ushrn(1); |
| this.g = this.curve.g; |
| |
| // Point on curve |
| this.g = options.curve.g; |
| this.g.precompute(options.curve.n.bitLength() + 1); |
| |
| // Hash for function for DRBG |
| this.hash = options.hash || options.curve.hash; |
| } |
| module.exports = EC; |
| |
| EC.prototype.keyPair = function keyPair(options) { |
| return new KeyPair(this, options); |
| }; |
| |
| EC.prototype.keyFromPrivate = function keyFromPrivate(priv, enc) { |
| return KeyPair.fromPrivate(this, priv, enc); |
| }; |
| |
| EC.prototype.keyFromPublic = function keyFromPublic(pub, enc) { |
| return KeyPair.fromPublic(this, pub, enc); |
| }; |
| |
| EC.prototype.genKeyPair = function genKeyPair(options) { |
| if (!options) |
| options = {}; |
| |
| // Instantiate Hmac_DRBG |
| var drbg = new HmacDRBG({ |
| hash: this.hash, |
| pers: options.pers, |
| persEnc: options.persEnc || 'utf8', |
| entropy: options.entropy || rand(this.hash.hmacStrength), |
| entropyEnc: options.entropy && options.entropyEnc || 'utf8', |
| nonce: this.n.toArray() |
| }); |
| |
| var bytes = this.n.byteLength(); |
| var ns2 = this.n.sub(new BN(2)); |
| do { |
| var priv = new BN(drbg.generate(bytes)); |
| if (priv.cmp(ns2) > 0) |
| continue; |
| |
| priv.iaddn(1); |
| return this.keyFromPrivate(priv); |
| } while (true); |
| }; |
| |
| EC.prototype._truncateToN = function truncateToN(msg, truncOnly) { |
| var delta = msg.byteLength() * 8 - this.n.bitLength(); |
| if (delta > 0) |
| msg = msg.ushrn(delta); |
| if (!truncOnly && msg.cmp(this.n) >= 0) |
| return msg.sub(this.n); |
| else |
| return msg; |
| }; |
| |
| EC.prototype.sign = function sign(msg, key, enc, options) { |
| if (typeof enc === 'object') { |
| options = enc; |
| enc = null; |
| } |
| if (!options) |
| options = {}; |
| |
| key = this.keyFromPrivate(key, enc); |
| msg = this._truncateToN(new BN(msg, 16)); |
| |
| // Zero-extend key to provide enough entropy |
| var bytes = this.n.byteLength(); |
| var bkey = key.getPrivate().toArray('be', bytes); |
| |
| // Zero-extend nonce to have the same byte size as N |
| var nonce = msg.toArray('be', bytes); |
| |
| // Instantiate Hmac_DRBG |
| var drbg = new HmacDRBG({ |
| hash: this.hash, |
| entropy: bkey, |
| nonce: nonce, |
| pers: options.pers, |
| persEnc: options.persEnc || 'utf8' |
| }); |
| |
| // Number of bytes to generate |
| var ns1 = this.n.sub(new BN(1)); |
| |
| for (var iter = 0; true; iter++) { |
| var k = options.k ? |
| options.k(iter) : |
| new BN(drbg.generate(this.n.byteLength())); |
| k = this._truncateToN(k, true); |
| if (k.cmpn(1) <= 0 || k.cmp(ns1) >= 0) |
| continue; |
| |
| var kp = this.g.mul(k); |
| if (kp.isInfinity()) |
| continue; |
| |
| var kpX = kp.getX(); |
| var r = kpX.umod(this.n); |
| if (r.cmpn(0) === 0) |
| continue; |
| |
| var s = k.invm(this.n).mul(r.mul(key.getPrivate()).iadd(msg)); |
| s = s.umod(this.n); |
| if (s.cmpn(0) === 0) |
| continue; |
| |
| var recoveryParam = (kp.getY().isOdd() ? 1 : 0) | |
| (kpX.cmp(r) !== 0 ? 2 : 0); |
| |
| // Use complement of `s`, if it is > `n / 2` |
| if (options.canonical && s.cmp(this.nh) > 0) { |
| s = this.n.sub(s); |
| recoveryParam ^= 1; |
| } |
| |
| return new Signature({ r: r, s: s, recoveryParam: recoveryParam }); |
| } |
| }; |
| |
| EC.prototype.verify = function verify(msg, signature, key, enc) { |
| msg = this._truncateToN(new BN(msg, 16)); |
| key = this.keyFromPublic(key, enc); |
| signature = new Signature(signature, 'hex'); |
| |
| // Perform primitive values validation |
| var r = signature.r; |
| var s = signature.s; |
| if (r.cmpn(1) < 0 || r.cmp(this.n) >= 0) |
| return false; |
| if (s.cmpn(1) < 0 || s.cmp(this.n) >= 0) |
| return false; |
| |
| // Validate signature |
| var sinv = s.invm(this.n); |
| var u1 = sinv.mul(msg).umod(this.n); |
| var u2 = sinv.mul(r).umod(this.n); |
| |
| if (!this.curve._maxwellTrick) { |
| var p = this.g.mulAdd(u1, key.getPublic(), u2); |
| if (p.isInfinity()) |
| return false; |
| |
| return p.getX().umod(this.n).cmp(r) === 0; |
| } |
| |
| // NOTE: Greg Maxwell's trick, inspired by: |
| // https://git.io/vad3K |
| |
| var p = this.g.jmulAdd(u1, key.getPublic(), u2); |
| if (p.isInfinity()) |
| return false; |
| |
| // Compare `p.x` of Jacobian point with `r`, |
| // this will do `p.x == r * p.z^2` instead of multiplying `p.x` by the |
| // inverse of `p.z^2` |
| return p.eqXToP(r); |
| }; |
| |
| EC.prototype.recoverPubKey = function(msg, signature, j, enc) { |
| assert((3 & j) === j, 'The recovery param is more than two bits'); |
| signature = new Signature(signature, enc); |
| |
| var n = this.n; |
| var e = new BN(msg); |
| var r = signature.r; |
| var s = signature.s; |
| |
| // A set LSB signifies that the y-coordinate is odd |
| var isYOdd = j & 1; |
| var isSecondKey = j >> 1; |
| if (r.cmp(this.curve.p.umod(this.curve.n)) >= 0 && isSecondKey) |
| throw new Error('Unable to find sencond key candinate'); |
| |
| // 1.1. Let x = r + jn. |
| if (isSecondKey) |
| r = this.curve.pointFromX(r.add(this.curve.n), isYOdd); |
| else |
| r = this.curve.pointFromX(r, isYOdd); |
| |
| var rInv = signature.r.invm(n); |
| var s1 = n.sub(e).mul(rInv).umod(n); |
| var s2 = s.mul(rInv).umod(n); |
| |
| // 1.6.1 Compute Q = r^-1 (sR - eG) |
| // Q = r^-1 (sR + -eG) |
| return this.g.mulAdd(s1, r, s2); |
| }; |
| |
| EC.prototype.getKeyRecoveryParam = function(e, signature, Q, enc) { |
| signature = new Signature(signature, enc); |
| if (signature.recoveryParam !== null) |
| return signature.recoveryParam; |
| |
| for (var i = 0; i < 4; i++) { |
| var Qprime; |
| try { |
| Qprime = this.recoverPubKey(e, signature, i); |
| } catch (e) { |
| continue; |
| } |
| |
| if (Qprime.eq(Q)) |
| return i; |
| } |
| throw new Error('Unable to find valid recovery factor'); |
| }; |