UNPKG

@bsv/sdk

Version:

BSV Blockchain Software Development Kit

161 lines (145 loc) 4.71 kB
import ReductionContext from './ReductionContext.js' import BigNumber from './BigNumber.js' /** * Represents a Montgomery reduction context, which is a mathematical method * for performing modular multiplication without division. * * Montgomery reduction is an algorithm used mainly in cryptography which can * help to speed up calculations in contexts where there are many repeated * computations. * * This class extends the `ReductionContext` class. * * @class MontgomoryMethod * @extends {ReductionContext} * * @property shift - The number of bits in the modulus. * @property r - The 2^shift, shifted left by the bit length of modulus `m`. * @property r2 - The square of `r` modulo `m`. * @property rinv - The modular multiplicative inverse of `r` mod `m`. * @property minv - The modular multiplicative inverse of `m` mod `r`. */ export default class MontgomoryMethod extends ReductionContext { shift: number r: BigNumber r2: BigNumber rinv: BigNumber minv: BigNumber /** * @constructor * @param m - The modulus to be used for the Montgomery method reductions. */ constructor (m: BigNumber | 'k256') { super(m) this.shift = this.m.bitLength() if (this.shift % 26 !== 0) { this.shift += 26 - (this.shift % 26) } this.r = new BigNumber(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) } /** * Converts a number into the Montgomery domain. * * @method convertTo * @param num - The number to be converted into the Montgomery domain. * @returns The result of the conversion into the Montgomery domain. * * @example * const montMethod = new MontgomoryMethod(m); * const convertedNum = montMethod.convertTo(num); */ convertTo (num: BigNumber): BigNumber { return this.imod(num.ushln(this.shift)) } /** * Converts a number from the Montgomery domain back to the original domain. * * @method convertFrom * @param num - The number to be converted from the Montgomery domain. * @returns The result of the conversion from the Montgomery domain. * * @example * const montMethod = new MontgomoryMethod(m); * const convertedNum = montMethod.convertFrom(num); */ convertFrom (num: BigNumber): BigNumber { const r = this.imod(num.mul(this.rinv)) r.red = null return r } /** * Performs an in-place multiplication of two numbers in the Montgomery domain. * * @method imul * @param a - The first number to multiply. * @param b - The second number to multiply. * @returns The result of the in-place multiplication. * * @example * const montMethod = new MontgomoryMethod(m); * const product = montMethod.imul(a, b); */ imul (a: BigNumber, b: BigNumber): BigNumber { if (a.isZero() || b.isZero()) { a.words[0] = 0; (a as any).length = 1 return a } const t = a.imul(b) const c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m) const u = t.isub(c).iushrn(this.shift) let 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) } /** * Performs the multiplication of two numbers in the Montgomery domain. * * @method mul * @param a - The first number to multiply. * @param b - The second number to multiply. * @returns The result of the multiplication. * * @example * const montMethod = new MontgomoryMethod(m); * const product = montMethod.mul(a, b); */ mul (a: BigNumber, b: BigNumber): BigNumber { if (a.isZero() || b.isZero()) return new BigNumber(0).forceRed(this) const t = a.mul(b) const c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m) const u = t.isub(c).iushrn(this.shift) let 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) } /** * Calculates the modular multiplicative inverse of a number in the Montgomery domain. * * @method invm * @param a - The number to compute the modular multiplicative inverse of. * @returns The modular multiplicative inverse of 'a'. * * @example * const montMethod = new MontgomoryMethod(m); * const inverse = montMethod.invm(a); */ invm (a: BigNumber): BigNumber { // (AR)^-1 * R^2 = (A^-1 * R^-1) * R^2 = A^-1 * R const res = this.imod(a._invmp(this.m).mul(this.r2)) return res.forceRed(this) } }