UNPKG

@exodus/bn.js

Version:

Big number implementation in pure javascript

1,366 lines (1,105 loc) 32 kB
(function (module, exports) { "use strict"; // Utils function assert(val, msg) { if (!val) throw new Error(msg || "Assertion failed"); } // Could use `inherits` module, but don't want to move from single file // architecture yet. function inherits(ctor, superCtor) { ctor.super_ = superCtor; var TempCtor = function () {}; TempCtor.prototype = superCtor.prototype; ctor.prototype = new TempCtor(); ctor.prototype.constructor = ctor; } function BN(number, base, endian) { if (BN.isBN(number)) { return number; }else if(typeof number === 'bigint'){ this.red = null; // Reduction context this.num = number } this.red = null; // Reduction context if (number !== null) { if (base === "le" || base === "be") { endian = base; base = 10; } this._init(number || 0, base || 10, endian || "be"); } else { this._init(number || 0, base || 10, endian || "be"); } } if (typeof module === "object") { module.exports = BN; } else { exports.BN = BN; } BN.BN = BN; Object.defineProperty(BN.prototype, "negative", { get: function () { return this.num < 0n ? 1 : 0; }, }); var Buffer; try { if (typeof window !== "undefined" && typeof window.Buffer !== "undefined") { Buffer = window.Buffer; } else { Buffer = require("buffer").Buffer; } } catch (e) {} BN.isBN = function isBN(num) { if (num instanceof BN) { return true; } return ( num !== null && typeof num === "object" && typeof num.num === "bigint" ); }; BN.max = function max(left, right) { if (left.cmp(right) > 0) return left; return right; }; BN.min = function min(left, right) { if (left.cmp(right) < 0) return left; return right; }; BN.prototype._validBase = function _validBase(input, base) { switch (base) { case 10: return /^[0-9]+$/.test(input); case "hex": case 16: return /^[0-9a-fA-F]+$/.test(input); case 8: return /^[0-7]+$/.test(input); case 2: return /^[01]+$/.test(input); case 36: return /^[0-9a-zA-Z]+$/.test(input); // this handles base 36 default: throw new Error(`Base ${base} is not supported`); } }; BN.prototype._init = function init(number, base, endian) { if (Array.isArray(number) || Buffer.isBuffer(number)) { if (number.length === 0) { this.num = BigInt(0); return; } const byteArray = [...number]; // Copy the array if (endian === "le") { byteArray.reverse(); } const hexStr = byteArray .map((byte) => byte.toString(16).padStart(2, "0")) .join(""); this.num = BigInt("0x" + hexStr); return; } let isNegative = false; const type = typeof number; number = number.toString().replace(/\s+/g, ""); if (number.startsWith("-")) { isNegative = true; number = number.slice(1); } if (!this._validBase(number.toString(), base)) { throw new Error("Invalid character"); } if (endian === "le" && type === "number") { const byteArray = this._toByteArray(BigInt(number)); byteArray.reverse(); // Reverse the byte order const hexStr = byteArray .map((byte) => byte.toString(16).padStart(2, "0")) .join(""); this.num = BigInt("0x" + hexStr); } else { if (type === "number") { assert(number < 0x20000000000000); } let prefix = ""; switch (base) { case 2: prefix = "0b"; break; case 8: prefix = "0o"; break; case "hex": case 16: prefix = "0x"; if (endian === "le") { number = number.length % 2 === 0 ? number : "0" + number; number = number .match(/.{1,2}/g) .reverse() .join(""); } break; case 36: this.num = this._base36ToBigInt(number.toLowerCase()); break; case 10: default: this.num = BigInt(number); } if (!this.num) { number = number.startsWith(prefix) ? number : prefix + number; this.num = BigInt(number); } } if (isNegative) { this.num = -this.num; } }; BN.prototype._toByteArray = function _toByteArray(number) { let hexStr = number.toString(16); if (hexStr.length % 2) hexStr = "0" + hexStr; // pad if needed const byteArray = []; for (let i = 0; i < hexStr.length; i += 2) { byteArray.push(parseInt(hexStr.slice(i, i + 2), 16)); } return byteArray; }; BN.prototype._base36ToBigInt = function _base36ToBigInt(str) { const chars = "0123456789abcdefghijklmnopqrstuvwxyz"; let result = BigInt(0); for (let char of str) { result = result * BigInt(36) + BigInt(chars.indexOf(char)); } return result; }; BN.prototype.copy = function (dest) { dest.num = this.num; dest.red = this.red; }; BN.prototype._move = function (dest) { this.copy(dest); }; function move(dest, src) { dest.num = src.num; dest.red = src.red; } BN.prototype.clone = function () { const clone = new BN(); this.copy(clone); return clone; }; BN.prototype.toString = function toString(base, padding) { base = base || 10; if (base === "hex") base = 16; padding = padding | 0 || 1; let out = this.num.toString(base); if (this.negative !== 0) { out = out.substring(1); } while (out.length % padding !== 0) { out = "0" + out; } if (this.negative !== 0) { out = "-" + out; } return out; }; BN.prototype.toNumber = function toNumber() { // Ensure the BigInt value can be safely represented as a JS number const maxSafe = BigInt(Number.MAX_SAFE_INTEGER); const minSafe = BigInt(Number.MIN_SAFE_INTEGER); if (this.num > maxSafe || this.num < minSafe) { throw new Error("Number can only safely store up to 53 bits"); } return Number(this.num); }; BN.prototype.toJSON = function toJSON() { return this.toString(16, 2); }; if (Buffer) { BN.prototype.toBuffer = function toBuffer(endian, length) { return Buffer.from(this.toArrayLike(endian, length)); }; } BN.prototype.toArray = function toArray(endian, length) { return this.toArrayLike(endian, length); }; BN.prototype.toArrayLike = function toArrayLike(endian, length) { let hex = this.num.toString(16); // Padding the hex string if (hex.length % 2 !== 0) { hex = "0" + hex; } let bytes = []; for (let i = 0; i < hex.length; i += 2) { bytes.push(parseInt(hex.slice(i, i + 2), 16)); } while (length && bytes.length < length) { bytes.unshift(0); // Pad with zeros in the front } // Check if natural length exceeds desired length if (length && bytes.length > length) { throw new Error("byte array longer than desired length"); } // Reverse the bytes for little endian if (endian === "le") { bytes = bytes.reverse(); } return bytes; }; BN.prototype.bitLength = function bitLength() { if (this.num === 0n) { return 0; } return this.toString(2).length; }; BN.prototype.zeroBits = function () { if (this.num === 0n) return 0; // Updated this line let z = 0; let temp = this.num; // Updated this line while ((temp & 1n) === 0n) { temp >>= 1n; z++; } return z; }; // Compute byte length BN.prototype.byteLength = function () { return Math.ceil(Number(this.bitLength()) / 8); }; // Convert to two's complement BN.prototype.toTwos = function (width) { if (this.num < 0n) { return (this.abs().num ^ ((1n << BigInt(width)) - 1n)) + 1n; } return this; }; // Convert from two's complement BN.prototype.fromTwos = function (width) { if ((this.num & (1n << BigInt(width - 1))) !== 0n) { this.num = this.num - (1n << BigInt(width)); } return this; }; // Check if negative BN.prototype.isNeg = function () { return this.num < 0n; }; BN.prototype.neg = function () { return new BN(-this.num); }; BN.prototype.ineg = function () { this.num = -this.num; return this; }; BN.prototype.iuor = function iuor(num) { this.num = this.num | num.num; return this; }; BN.prototype.ior = function ior(num) { return this.iuor(num); }; BN.prototype.or = function or(num) { return new BN(this.num | num.num); }; BN.prototype.uor = function uor(num) { return this.or(num); }; BN.prototype.iuand = function iuand(num) { this.num = this.num & num.num; return this; }; BN.prototype.iand = function iand(num) { assert((this.negative | num.negative) === 0); return this.iuand(num); }; BN.prototype.and = function and(num) { return this.clone().iand(num); }; BN.prototype.uand = function uand(num) { return this.clone().iuand(num); }; BN.prototype.iuxor = function iuxor(num) { this.num = this.num ^ num.num; return this; }; BN.prototype.ixor = function ixor(num) { assert((this.negative | num.negative) === 0); return this.iuxor(num); }; // Bitwise XOR with num, returning a new BN BN.prototype.xor = function xor(num) { return new BN(this.num ^ num.num); }; BN.prototype.uxor = function uxor(num) { return this.xor(num); }; // In-place bitwise NOT of width bits BN.prototype.inotn = function inotn(width) { assert(typeof width === "number" && width >= 0); let mask = (1n << BigInt(width)) - 1n; // Create a mask of width bits set to 1 this.num = ~this.num & mask; return this; }; BN.prototype.notn = function notn(width) { return this.clone().inotn(width); }; // Set or unset a bit at a particular position BN.prototype.setn = function setn(bit, val) { if (typeof bit !== "number" || bit < 0) { throw new Error("Bit must be a non-negative number."); } let mask = 1n << BigInt(bit); if (val) { this.num |= mask; // set bit } else { this.num &= ~mask; // unset bit } return this; }; BN.prototype.iadd = function iadd(b) { this.num += b.num; return this; }; BN.prototype.add = function add(b) { return this.clone().iadd(b); }; BN.prototype.isub = function isub(b) { this.num -= b.num; return this; }; BN.prototype.sub = function sub(b) { return this.clone().isub(b); }; BN.prototype.mulTo = function mulTo(b) { return this.mul(b); }; BN.prototype.mulf = function mulf(num) { return this.mul(num); }; BN.prototype.mul = function mul(num) { return this.clone().imul(num); }; BN.prototype.imul = function imul(num) { this.num *= num.num; return this; }; BN.prototype.imuln = function imuln(n) { assert(typeof n === "number"); assert(n < 0x4000000); this.num *= BigInt(n); return this; }; BN.prototype.muln = function muln(n) { return this.clone().imuln(n); }; BN.prototype.sqr = function sqr() { return this.mul(this); }; BN.prototype.isqr = function isqr() { return this.imul(this.clone()); }; BN.prototype.pow = function pow(b) { return new BN(this.num ** b.num); }; BN.prototype.iushln = function iushln(bits) { this.num <<= BigInt(bits); return this; }; BN.prototype.ishln = function ishln(bits) { assert(!this.isNeg()); return this.iushln(bits); }; BN.prototype.iushrn = function iushrn(bits) { this.num >>= BigInt(bits); return this; }; BN.prototype.ishrn = function ishrn(bits) { // Assuming the number is non-negative return this.iushrn(bits); }; // Shift-left BN.prototype.shln = function shln(bits) { return this.clone().ishln(bits); }; BN.prototype.ushln = function ushln(bits) { return this.clone().iushln(bits); }; // Shift-right BN.prototype.shrn = function shrn(bits) { return this.clone().ishrn(bits); }; BN.prototype.ushrn = function ushrn(bits) { return this.clone().iushrn(bits); }; // Test if n bit is set BN.prototype.testn = function testn(bit) { assert(typeof bit === "number" && bit >= 0); return !!(this.num & (1n << BigInt(bit))); }; // Return only lower bits of number (in-place) BN.prototype.imaskn = function imaskn(bits) { assert(typeof bits === "number" && bits >= 0); assert(this.num >= 0n, "imaskn works only with positive numbers"); const mask = (1n << BigInt(bits)) - 1n; this.num = this.num & mask; return this; }; // Return only lowers bits of number BN.prototype.maskn = function maskn(bits) { return this.clone().imaskn(bits); }; // Add plain number `num` to `this` BN.prototype.iaddn = function iaddn(num) { assert(typeof num === "number"); assert(num < 0x4000000); return this._iaddn(num); }; BN.prototype._iaddn = function _iaddn(num) { this.num += BigInt(num); return this; }; // Subtract plain number `num` from `this` BN.prototype.isubn = function isubn(num) { assert(typeof num === "number"); assert(num < 0x4000000); this.num -= BigInt(num); return this; }; BN.prototype.addn = function addn(num) { return this.clone().iaddn(num); }; BN.prototype.subn = function subn(num) { return this.clone().isubn(num); }; BN.prototype.iabs = function iabs() { this.num = this.num < 0n ? -this.num : this.num; return this; }; BN.prototype.abs = function abs() { return this.clone().iabs(); }; BN.prototype.div = function div(num) { assert(!num.isZero()); // Ensure we're not dividing by zero. if (this.isZero()) return new BN(0); return new BN(this.num / num.num); }; BN.prototype.mod = function mod(num) { return new BN(this.num % num.num); }; BN.prototype.umod = function umod(num) { const result = this.num % num.num; if (result >= 0n) { return new BN(result); } return new BN(result + (num.num < 0n ? -num.num : num.num)); }; BN.prototype.divRound = function divRound(num) { const quotient = this.num / num.num; const remainder = this.num % num.num; // Check if the remainder is more than half of `num` if (2n * remainder >= num.num) { return quotient + 1n; } return quotient; }; BN.prototype.modrn = function modrn(num) { const isNegNum = num < 0; const absNum = BigInt(isNegNum ? -num : num); assert(absNum <= 0x3ffffffn); const p = (1n << 26n) % absNum; let acc = 0n; acc = (p * acc + this.num) % absNum; return isNegNum ? new BN(-acc) : new BN(acc); }; // WARNING: DEPRECATED BN.prototype.modn = function modn(num) { return this.modrn(num); }; BN.prototype.idivn = function idivn(num) { this.num = this.num / BigInt(num); return this; }; BN.prototype.divn = function divn(num) { return this.clone().idivn(num); }; BN.prototype.egcd = function egcd(b) { assert(b.negative === 0); assert(!b.isZero()); let a = this.abs().num; b = BigInt(b.abs().num); let [x, y, u, v] = [1n, 0n, 0n, 1n]; while (b !== 0n) { let q = a / b; [a, b] = [b, a % b]; [x, u] = [u, x - u * q]; [y, v] = [v, y - v * q]; } return { gcd: new BN(a), a: new BN(x), b: new BN(y), }; }; BN.prototype.gcd = function gcd(num) { let a = this.abs().num; let b = BigInt(num.abs().num); while (b !== 0n) { let temp = b; b = a % b; a = temp; } return new BN(a); }; // Invert number in the field F(num) BN.prototype.invm = function invm(num) { return this.egcd(num).a.umod(num); }; BN.prototype._invmp = function _invmp(p) { if (p.num <= 0n) throw new Error("Invalid modulus"); let a = this.num; let mod = p.num; if (a < 0n) a = ((a % mod) + mod) % mod; let [x1, x2] = [1n, 0n]; let [y1, y2] = [0n, 1n]; while (a > 0n) { let [q, r] = [mod / a, mod % a]; [x1, x2] = [x2, x1 - q * x2]; [y1, y2] = [y2, y1 - q * y2]; mod = a; a = r; } if (mod === 1n) { return new BN(y1 < 0n ? y1 + p.num : y1); } throw new Error("Inverse does not exist"); }; BN.prototype.isEven = function isEven() { return (this.num & 1n) === 0n; }; BN.prototype.isOdd = function isOdd() { return (this.num & 1n) === 1n; }; BN.prototype.andln = function andln(num) { return Number(this.num & BigInt(num)); }; BN.prototype.bincn = function bincn(bit) { assert(typeof bit === "number"); // Construct a BigInt with only the specified bit set to 1 let increment = 1n << BigInt(bit); // Increment the BN's value by the constructed BigInt this.num += increment; return this; }; BN.prototype.isZero = function isZero() { return this.num === 0n; }; BN.prototype.cmpn = function cmpn(n) { assert(n <= 0x3ffffff, "Number is too big"); const other = BigInt(n); if (this.num > other) return 1; if (this.num < other) return -1; return 0; }; BN.prototype.gtn = function gtn(num) { return this.cmpn(num) === 1; }; BN.prototype.gt = function gt(num) { return this.cmp(num) === 1; }; BN.prototype.gten = function gten(num) { return this.cmpn(num) >= 0; }; BN.prototype.gte = function gte(num) { return this.cmp(num) >= 0; }; BN.prototype.ltn = function ltn(num) { return this.cmpn(num) === -1; }; BN.prototype.lt = function lt(num) { return this.cmp(num) === -1; }; BN.prototype.lten = function lten(num) { return this.cmpn(num) <= 0; }; BN.prototype.lte = function lte(num) { return this.cmp(num) <= 0; }; BN.prototype.eqn = function eqn(num) { return this.cmpn(num) === 0; }; BN.prototype.eq = function eq(num) { return this.cmp(num) === 0; }; BN.prototype.cmp = function cmp(other) { if (this.num > other.num) return 1; if (this.num < other.num) return -1; return 0; }; BN.prototype.ucmp = function (other) { if (this.num < 0n && other.num >= 0n) return -1; if (this.num >= 0n && other.num < 0n) return 1; return this.cmp(other); }; BN.red = function red(num) { return new Red(num); }; BN.prototype.toRed = function toRed(ctx) { assert(!this.red, "Already a number in reduction context"); assert(this.negative === 0, "red works only with positives"); return ctx.convertTo(this)._forceRed(ctx); }; BN.prototype.fromRed = function fromRed() { assert(this.red, "fromRed works only with numbers in reduction context"); return this.red.convertFrom(this); }; BN.prototype._forceRed = function _forceRed(ctx) { this.red = ctx; return this; }; BN.prototype.forceRed = function forceRed(ctx) { assert(!this.red, "Already a number in reduction context"); return this._forceRed(ctx); }; BN.prototype.redAdd = function redAdd(num) { assert(this.red, "redAdd works only with red numbers"); return this.red.add(this, num); }; BN.prototype.redIAdd = function redIAdd(num) { assert(this.red, "redIAdd works only with red numbers"); return this.red.iadd(this, num); }; BN.prototype.redSub = function redSub(num) { assert(this.red, "redSub works only with red numbers"); return this.red.sub(this, num); }; BN.prototype.redISub = function redISub(num) { assert(this.red, "redISub works only with red numbers"); return this.red.isub(this, num); }; BN.prototype.redShl = function redShl(num) { assert(this.red, "redShl works only with red numbers"); return this.red.shl(this, num); }; BN.prototype.redMul = function redMul(num) { assert(this.red, "redMul works only with red numbers"); this.red._verify2(this, num); return this.red.mul(this, num); }; BN.prototype.redIMul = function redIMul(num) { assert(this.red, "redMul works only with red numbers"); this.red._verify2(this, num); return this.red.imul(this, num); }; BN.prototype.redSqr = function redSqr() { assert(this.red, "redSqr works only with red numbers"); this.red._verify1(this); return this.red.sqr(this); }; BN.prototype.redISqr = function redISqr() { assert(this.red, "redISqr works only with red numbers"); this.red._verify1(this); return this.red.isqr(this); }; // Square root over p BN.prototype.redSqrt = function redSqrt() { assert(this.red, "redSqrt works only with red numbers"); this.red._verify1(this); return this.red.sqrt(this); }; BN.prototype.redInvm = function redInvm() { assert(this.red, "redInvm works only with red numbers"); this.red._verify1(this); return this.red.invm(this); }; // Return negative clone of `this` % `red modulo` BN.prototype.redNeg = function redNeg() { assert(this.red, "redNeg works only with red numbers"); this.red._verify1(this); return this.red.neg(this); }; BN.prototype.redPow = function redPow(num) { assert(this.red && !num.red, "redPow(normalNum)"); this.red._verify1(this); return this.red.pow(this, num); }; var primes = { k256: null, p224: null, p192: null, p25519: null, }; // Pseudo-Mersenne prime function MPrime(name, p) { this.name = name; this.p = new BN(p, 16); this.n = this.p.bitLength(); this.k = new BN(1).ishln(this.n).isub(this.p); } MPrime.prototype.ireduce = function (num) { let r = num; let tmp = new BN(0); do { this.split(r, tmp); r = this.imulK(r); r = r.iadd(tmp); } while (r.bitLength() > this.n); let cmp = new BN(r.bitLength()).cmp(this.n) < 0 ? -1 : r.ucmp(this.p); if (cmp === 0) { r = new BN(0); } else if (cmp > 0) { r = r.isub(this.p); } // strip related operations are ignored return r; }; MPrime.prototype.split = function (num, output) { let mask = new BN(1).ishln(this.n).isub(new BN(1)); let lo = num.iand(mask); let hi = num.ishrn(this.n); output.num = lo.num; num.num = hi.num; }; MPrime.prototype.imulK = function (num) { return num.imul(this.k); }; function K256() { MPrime.call( this, "k256", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f" ); } // Assumes you have a utility function for inheritance inherits(K256, MPrime); K256.prototype.split = function split(input, output) { // 256 = 9 * 26 + 22 const mask = 0x3fffffn; // BigInt literal if (input.bitLength() <= 9 * 26) { output.num = input.num; input.num = 0n; return; } // Extract the bottom 9 * 26 bits (lower part) let lower = input.num & ((1n << (9n * 26n)) - 1n); output.num = lower; // Shift by 9 * 26 bits (upper part) let upper = input.num >> (9n * 26n); // Extract the first 22 bits of the upper part let first22Bits = upper & mask; output.num |= first22Bits << (9n * 26n); upper >>= 22n; input.num = upper; }; K256.prototype.imulK = function imulK(num) { const MASK = 0x3ffffffn; const K = 0x3d1n; let w = num.num; let lo = w * K; let hi = (w << 6n) + (lo >> 26n); let result = (hi << 26n) | (lo & MASK); return new BN(result); }; function P224() { MPrime.call( this, "p224", "ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001" ); } inherits(P224, MPrime); function P192() { MPrime.call( this, "p192", "ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff" ); } inherits(P192, MPrime); function P25519() { // 2 ^ 255 - 19 MPrime.call( this, "25519", "7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed" ); } inherits(P25519, MPrime); P25519.prototype.imulK = function imulK(num) { // K = 0x13 let carry = 0n; let val = num.num; let res = 0n; let base = 1n; while (val > 0n) { let digit = val % 0x4000000n; // Get the last 26 bits val /= 0x4000000n; // Shift right by 26 bits let hi = digit * 0x13n + carry; let lo = hi & 0x3ffffffn; // Masking to get the last 26 bits hi >>= 26n; // Equivalent to hi >>>= 26 res += lo * base; base *= 0x4000000n; // Update the base for the next iteration carry = hi; } if (carry !== 0n) { res += carry * base; } num.num = res; return num; }; BN._prime = function prime(name) { // Cached version of prime if (primes[name]) return primes[name]; var prime; if (name === "k256") { prime = new K256(); } else if (name === "p224") { prime = new P224(); } else if (name === "p192") { prime = new P192(); } else if (name === "p25519") { prime = new P25519(); } else { throw new Error("Unknown prime " + name); } primes[name] = prime; return prime; }; function Red(m) { if (typeof m === "string") { var prime = BN._prime(m); this.m = prime.p; this.prime = prime; } else { assert(m.gtn(1), "modulus must be greater than 1"); this.m = m; this.prime = null; } } Red.prototype._verify1 = function _verify1(a) { assert(a.negative === 0, "red works only with positives"); assert(a.red, "red works only with red numbers"); }; Red.prototype._verify2 = function _verify2(a, b) { assert((a.negative | b.negative) === 0, "red works only with positives"); assert(a.red && a.red === b.red, "red works only with red numbers"); }; Red.prototype.imod = function imod(a) { if (this.prime) return this.prime.ireduce(a)._forceRed(this); move(a, a.umod(this.m)._forceRed(this)); return a; }; Red.prototype.neg = function neg(a) { if (a.isZero()) { return a.clone(); } return this.m.sub(a)._forceRed(this); }; Red.prototype.add = function add(a, b) { this._verify2(a, b); var res = a.add(b); if (res.cmp(this.m) >= 0) { res.isub(this.m); } return res._forceRed(this); }; Red.prototype.iadd = function iadd(a, b) { this._verify2(a, b); var res = a.iadd(b); if (res.cmp(this.m) >= 0) { res.isub(this.m); } return res; }; Red.prototype.sub = function sub(a, b) { this._verify2(a, b); var res = a.sub(b); if (res.cmpn(0) < 0) { res.iadd(this.m); } return res._forceRed(this); }; Red.prototype.isub = function isub(a, b) { this._verify2(a, b); var res = a.isub(b); if (res.cmpn(0) < 0) { res.iadd(this.m); } return res; }; Red.prototype.shl = function shl(a, num) { this._verify1(a); return this.imod(a.ushln(num)); }; Red.prototype.imul = function imul(a, b) { this._verify2(a, b); return this.imod(a.imul(b)); }; Red.prototype.mul = function mul(a, b) { this._verify2(a, b); return this.imod(a.mul(b)); }; Red.prototype.isqr = function isqr(a) { return this.imul(a, a.clone()); }; Red.prototype.sqr = function sqr(a) { return this.mul(a, a); }; Red.prototype.sqrt = function sqrt(a) { if (a.isZero()) return a.clone(); var mod3 = this.m.andln(3); assert(mod3 % 2 === 1); // Fast case if (mod3 === 3) { var pow = this.m.add(new BN(1)).iushrn(2); return this.pow(a, pow); } // Tonelli-Shanks algorithm (Totally unoptimized and slow) // // Find Q and S, that Q * 2 ^ S = (P - 1) var q = this.m.subn(1); var s = 0; while (!q.isZero() && q.andln(1) === 0) { s++; q.iushrn(1); } assert(!q.isZero()); var one = new BN(1).toRed(this); var nOne = one.redNeg(); // Find quadratic non-residue // NOTE: Max is such because of generalized Riemann hypothesis. var lpow = this.m.subn(1).iushrn(1); var z = this.m.bitLength(); z = new BN(2 * z * z).toRed(this); while (this.pow(z, lpow).cmp(nOne) !== 0) { z.redIAdd(nOne); } var c = this.pow(z, q); var r = this.pow(a, q.addn(1).iushrn(1)); var t = this.pow(a, q); var m = s; while (t.cmp(one) !== 0) { var tmp = t; for (var i = 0; tmp.cmp(one) !== 0; i++) { tmp = tmp.redSqr(); } assert(i < m); var b = this.pow(c, new BN(1).iushln(m - i - 1)); r = r.redMul(b); c = b.redSqr(); t = t.redMul(c); m = i; } return r; }; Red.prototype.pow = function pow(a, num) { if (num.isZero()) return new BN(1).toRed(this); if (num.cmpn(1) === 0) return a.clone(); var windowSize = 4; var wnd = new Array(1 << windowSize); wnd[0] = new BN(1).toRed(this); wnd[1] = a; for (let i = 2; i < wnd.length; i++) { wnd[i] = this.mul(wnd[i - 1], a); } let res = wnd[0]; let current = 0n; let currentLen = 0; let numBinary = num.toString(2); for (let i = 0; i < numBinary.length; i++) { let bit = BigInt(numBinary[i]); if (!res.eq(wnd[0])) { res = this.sqr(res); } if (bit === 0n && current === 0n) { currentLen = 0; continue; } current <<= 1n; current |= bit; currentLen++; if (currentLen !== windowSize && i !== numBinary.length - 1) continue; res = this.mul(res, wnd[Number(current)]); currentLen = 0; current = 0n; } return res; }; Red.prototype.invm = function invm(a) { var inv = a._invmp(this.m); if (inv.negative !== 0) { return this.imod(inv).redNeg(); } else { return this.imod(inv); } }; Red.prototype.convertTo = function convertTo(num) { var r = num.umod(this.m); return r === num ? r.clone() : r; }; Red.prototype.convertFrom = function convertFrom(num) { var res = num.clone(); res.red = null; return res; }; // // Montgomery method engine // BN.mont = function mont(num) { return new Mont(num); }; function Mont(m) { Red.call(this, m); this.shift = this.m.bitLength(); if (this.shift % 26 !== 0) { this.shift += 26 - (this.shift % 26); } this.r = new BN(1).iushln(this.shift); this.r2 = this.imod(this.r.sqr()); this.rinv = this.r._invmp(this.m); this.minv = this.rinv.mul(this.r).isubn(1).div(this.m); this.minv = this.minv.umod(this.r); this.minv = this.r.sub(this.minv); } inherits(Mont, Red); Mont.prototype.convertTo = function convertTo(num) { return this.imod(num.ushln(this.shift)); }; Mont.prototype.convertFrom = function convertFrom(num) { var r = this.imod(num.mul(this.rinv)); r.red = null; return r; }; Mont.prototype.imul = function imul(a, b) { if (a.isZero() || b.isZero()) { return a; } var t = a.imul(b); var c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m); var u = t.isub(c).iushrn(this.shift); var res = u; if (u.cmp(this.m) >= 0) { res = u.isub(this.m); } else if (u.cmpn(0) < 0) { res = u.iadd(this.m); } return res._forceRed(this); }; Mont.prototype.mul = function mul(a, b) { if (a.isZero() || b.isZero()) return new BN(0)._forceRed(this); var t = a.mul(b); var c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m); var u = t.isub(c).iushrn(this.shift); var res = u; if (u.cmp(this.m) >= 0) { res = u.isub(this.m); } else if (u.cmpn(0) < 0) { res = u.iadd(this.m); } return res._forceRed(this); }; Mont.prototype.invm = function invm(a) { // (AR)^-1 * R^2 = (A^-1 * R^-1) * R^2 = A^-1 * R var res = this.imod(a._invmp(this.m).mul(this.r2)); return res._forceRed(this); }; })(typeof module === "undefined" || module, this);