UNPKG

@okxweb3/crypto-lib

Version:

A base package for @okxweb3/coin-*

203 lines 6.95 kB
'use strict'; var BN = require('bn.js/lib/bn'); 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); if (typeof options === 'string') { assert(Object.prototype.hasOwnProperty.call(curves, options), 'Unknown curve ' + options); options = curves[options]; } 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; this.g = options.curve.g; this.g.precompute(options.curve.n.bitLength() + 1); 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 = {}; 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)); for (;;) { var priv = new BN(drbg.generate(bytes)); if (priv.cmp(ns2) > 0) continue; priv.iaddn(1); return this.keyFromPrivate(priv); } }; EC.prototype._truncateToN = function _truncateToN(msg, truncOnly, bitLength) { var byteLength; if (BN.isBN(msg) || typeof msg === 'number') { msg = new BN(msg, 16); byteLength = msg.byteLength(); } else if (typeof msg === 'object') { byteLength = msg.length; msg = new BN(msg, 16); } else { var str = msg.toString(); byteLength = (str.length + 1) >>> 1; msg = new BN(str, 16); } if (typeof bitLength !== 'number') { bitLength = byteLength * 8; } var delta = bitLength - 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 = {}; if (typeof msg !== 'string' && typeof msg !== 'number' && !BN.isBN(msg)) { assert(typeof msg === 'object' && msg && typeof msg.length === 'number', 'Expected message to be an array-like, a hex string, or a BN instance'); assert((msg.length >>> 0) === msg.length); for (var i = 0; i < msg.length; i++) assert((msg[i] & 255) === msg[i]); } key = this.keyFromPrivate(key, enc); msg = this._truncateToN(msg, false, options.msgBitLength); assert(!msg.isNeg(), 'Can not sign a negative message'); var bytes = this.n.byteLength(); var bkey = key.getPrivate().toArray('be', bytes); var nonce = msg.toArray('be', bytes); assert((new BN(nonce)).eq(msg), 'Can not sign message'); var drbg = new HmacDRBG({ hash: this.hash, entropy: bkey, nonce: nonce, pers: options.pers, persEnc: options.persEnc || 'utf8', }); var ns1 = this.n.sub(new BN(1)); for (var iter = 0;; 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); if (options.canonical && s.cmp(this.nh) > 0) { s = this.n.sub(s); recoveryParam ^= 1; } return new Signature(this.curve, { r: r, s: s, recoveryParam: recoveryParam }); } }; EC.prototype.verify = function verify(msg, signature, key, enc, options) { if (!options) options = {}; msg = this._truncateToN(msg, false, options.msgBitLength); key = this.keyFromPublic(key, enc); signature = new Signature(this.curve, signature, 'hex'); 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; var sinv = s.invm(this.n); var u1 = sinv.mul(msg).umod(this.n); var u2 = sinv.mul(r).umod(this.n); var p; if (!this.curve._maxwellTrick) { p = this.g.mulAdd(u1, key.getPublic(), u2); if (p.isInfinity()) return false; return p.getX().umod(this.n).cmp(r) === 0; } p = this.g.jmulAdd(u1, key.getPublic(), u2); if (p.isInfinity()) return false; 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(this.curve, signature, enc); var n = this.n; var e = new BN(msg); var r = signature.r; var s = signature.s; 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'); 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); return this.g.mulAdd(s1, r, s2); }; EC.prototype.getKeyRecoveryParam = function (e, signature, Q, enc) { signature = new Signature(this.curve, 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'); }; //# sourceMappingURL=index.js.map