@noble/bls12-381
Advanced tools
Comparing version 1.2.0 to 1.3.0
@@ -1,2 +0,2 @@ | ||
/*! noble-bls12-381 - MIT License (c) Paul Miller (paulmillr.com) */ | ||
/*! noble-bls12-381 - MIT License (c) 2019 Paul Miller (paulmillr.com) */ | ||
import nodeCrypto from 'crypto'; | ||
@@ -26,2 +26,11 @@ import { Fp, Fr, Fp2, Fp12, CURVE, ProjectivePoint, map_to_curve_simple_swu_9mod16, isogenyMapG2, millerLoop, psi, psi2, calcPairingPrecomputes, mod } from './math.js'; | ||
hashToField: hash_to_field, | ||
hashToPrivateKey: (hash) => { | ||
hash = ensureBytes(hash); | ||
if (hash.length < 40 || hash.length > 1024) | ||
throw new Error('Expected 40-1024 bytes of private key as per FIPS 186'); | ||
const num = mod(bytesToNumberBE(hash), CURVE.r); | ||
if (num === 0n || num === 1n) | ||
throw new Error('Invalid private key'); | ||
return numberTo32BytesBE(num); | ||
}, | ||
bytesToHex, | ||
@@ -41,10 +50,3 @@ randomBytes: (bytesLength = 32) => { | ||
randomPrivateKey: () => { | ||
let i = 8; | ||
while (i--) { | ||
const b32 = utils.randomBytes(32); | ||
const num = bytesToNumberBE(b32); | ||
if (isWithinCurveOrder(num) && num !== 1n) | ||
return b32; | ||
} | ||
throw new Error('Valid private key was not found in 8 iterations. PRNG is broken'); | ||
return utils.hashToPrivateKey(utils.randomBytes(40)); | ||
}, | ||
@@ -74,8 +76,6 @@ sha256: async (message) => { | ||
}; | ||
function bytesToNumberBE(bytes) { | ||
let value = 0n; | ||
for (let i = bytes.length - 1, j = 0; i >= 0; i--, j++) { | ||
value += (BigInt(bytes[i]) & 255n) << (8n * BigInt(j)); | ||
} | ||
return value; | ||
function bytesToNumberBE(uint8a) { | ||
if (!(uint8a instanceof Uint8Array)) | ||
throw new Error('Expected Uint8Array'); | ||
return BigInt('0x' + bytesToHex(Uint8Array.from(uint8a))); | ||
} | ||
@@ -103,3 +103,3 @@ const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0')); | ||
const byte = Number.parseInt(hexByte, 16); | ||
if (Number.isNaN(byte)) | ||
if (Number.isNaN(byte) || byte < 0) | ||
throw new Error('Invalid byte sequence'); | ||
@@ -110,5 +110,10 @@ array[i] = byte; | ||
} | ||
function numberTo32BytesBE(num) { | ||
const length = 32; | ||
const hex = num.toString(16).padStart(length * 2, '0'); | ||
return hexToBytes(hex); | ||
} | ||
function toPaddedHex(num, padding) { | ||
if (num < 0n) | ||
throw new Error('Expected valid number'); | ||
if (typeof num !== 'bigint' || num < 0n) | ||
throw new Error('Expected valid bigint'); | ||
if (typeof padding !== 'number') | ||
@@ -119,7 +124,3 @@ throw new TypeError('Expected valid padding'); | ||
function ensureBytes(hex) { | ||
if (hex instanceof Uint8Array) | ||
return hex; | ||
if (typeof hex === 'string') | ||
return hexToBytes(hex); | ||
throw new TypeError('Expected hex string or Uint8Array'); | ||
return hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes(hex); | ||
} | ||
@@ -243,5 +244,5 @@ function concatBytes(...arrays) { | ||
bytes = ensureBytes(bytes); | ||
const { P } = CURVE; | ||
let point; | ||
if (bytes.length === 48) { | ||
const { P } = CURVE; | ||
const compressedValue = bytesToNumberBE(bytes); | ||
@@ -283,4 +284,4 @@ const bflag = mod(compressedValue, POW_2_383) / POW_2_382; | ||
this.assertValidity(); | ||
const { P } = CURVE; | ||
if (isCompressed) { | ||
const { P } = CURVE; | ||
let hex; | ||
@@ -383,10 +384,10 @@ if (this.isZero()) { | ||
return this.ZERO; | ||
const x1 = z1 % POW_2_381; | ||
const x2 = z2; | ||
const x = new Fp2([x2, x1]); | ||
const y2 = x.pow(3n).add(new Fp2(CURVE.b2)); | ||
const x1 = new Fp(z1 % POW_2_381); | ||
const x2 = new Fp(z2); | ||
const x = new Fp2(x2, x1); | ||
const y2 = x.pow(3n).add(Fp2.fromBigTuple(CURVE.b2)); | ||
let y = y2.sqrt(); | ||
if (!y) | ||
throw new Error('Failed to find a square root'); | ||
const [y0, y1] = y.values; | ||
const { re: y0, im: y1 } = y.reim(); | ||
const aflag1 = (z1 % POW_2_382) / POW_2_381; | ||
@@ -403,7 +404,32 @@ const isGreater = y1 > 0n && (y1 * 2n) / P !== aflag1; | ||
bytes = ensureBytes(bytes); | ||
const m_byte = bytes[0] & 0xe0; | ||
if (m_byte === 0x20 || m_byte === 0x60 || m_byte === 0xe0) { | ||
throw new Error('Invalid encoding flag: ' + m_byte); | ||
} | ||
const bitC = m_byte & 0x80; | ||
const bitI = m_byte & 0x40; | ||
const bitS = m_byte & 0x20; | ||
let point; | ||
if (bytes.length === 96) { | ||
throw new Error('Compressed format not supported yet.'); | ||
if (bytes.length === 96 && bitC) { | ||
const { P, b2 } = CURVE; | ||
const b = Fp2.fromBigTuple(b2); | ||
bytes[0] = bytes[0] & 0x1f; | ||
if (bitI) { | ||
if (bytes.reduce((p, c) => (p !== 0 ? c + 1 : c), 0) > 0) { | ||
throw new Error('Invalid compressed G2 point'); | ||
} | ||
return PointG2.ZERO; | ||
} | ||
const x_1 = bytesToNumberBE(bytes.slice(0, PUBLIC_KEY_LENGTH)); | ||
const x_0 = bytesToNumberBE(bytes.slice(PUBLIC_KEY_LENGTH)); | ||
const x = new Fp2(new Fp(x_0), new Fp(x_1)); | ||
const right = x.pow(3n).add(b); | ||
let y = right.sqrt(); | ||
if (!y) | ||
throw new Error('Invalid compressed G2 point'); | ||
const Y_bit = y.c1.value === 0n ? (y.c0.value * 2n) / P : (y.c1.value * 2n) / P ? 1n : 0n; | ||
y = bitS > 0 && Y_bit > 0 ? y : y.negate(); | ||
return new PointG2(x, y); | ||
} | ||
else if (bytes.length === 192) { | ||
else if (bytes.length === 192 && !bitC) { | ||
if ((bytes[0] & (1 << 6)) !== 0) { | ||
@@ -416,6 +442,6 @@ return PointG2.ZERO; | ||
const y0 = bytesToNumberBE(bytes.slice(3 * PUBLIC_KEY_LENGTH)); | ||
point = new PointG2(new Fp2([x0, x1]), new Fp2([y0, y1])); | ||
point = new PointG2(Fp2.fromBigTuple([x0, x1]), Fp2.fromBigTuple([y0, y1])); | ||
} | ||
else { | ||
throw new Error('Invalid uncompressed point G2, expected 192 bytes'); | ||
throw new Error('Invalid point G2, expected 96/192 bytes'); | ||
} | ||
@@ -434,3 +460,3 @@ point.assertValidity(); | ||
} | ||
const [[x0, x1], [y0, y1]] = this.toAffine().map((a) => a.values); | ||
const [{ re: x0, im: x1 }, { re: y0, im: y1 }] = this.toAffine().map((a) => a.reim()); | ||
const tmp = y1 > 0n ? y1 * 2n : y0 * 2n; | ||
@@ -448,3 +474,15 @@ const aflag1 = tmp / CURVE.P; | ||
if (isCompressed) { | ||
throw new Error('Point compression has not yet been implemented'); | ||
const { P } = CURVE; | ||
let x_1 = 0n; | ||
let x_0 = 0n; | ||
if (this.isZero()) { | ||
x_1 = POW_2_383 + POW_2_382; | ||
} | ||
else { | ||
const [x, y] = this.toAffine(); | ||
const flag = y.c1.value === 0n ? (y.c0.value * 2n) / P : (y.c1.value * 2n) / P ? 1n : 0n; | ||
x_1 = x.c1.value + flag * POW_2_381 + POW_2_383; | ||
x_0 = x.c0.value; | ||
} | ||
return toPaddedHex(x_1, PUBLIC_KEY_LENGTH) + toPaddedHex(x_0, PUBLIC_KEY_LENGTH); | ||
} | ||
@@ -455,3 +493,3 @@ else { | ||
} | ||
const [[x0, x1], [y0, y1]] = this.toAffine().map((a) => a.values); | ||
const [{ re: x0, im: x1 }, { re: y0, im: y1 }] = this.toAffine().map((a) => a.reim()); | ||
return (toPaddedHex(x1, PUBLIC_KEY_LENGTH) + | ||
@@ -496,3 +534,3 @@ toPaddedHex(x0, PUBLIC_KEY_LENGTH) + | ||
isOnCurve() { | ||
const b = new Fp2(CURVE.b2); | ||
const b = Fp2.fromBigTuple(CURVE.b2); | ||
const { x, y, z } = this; | ||
@@ -520,3 +558,3 @@ const left = y.pow(2n).multiply(z).subtract(x.pow(3n)); | ||
} | ||
PointG2.BASE = new PointG2(new Fp2(CURVE.G2x), new Fp2(CURVE.G2y), Fp2.ONE); | ||
PointG2.BASE = new PointG2(Fp2.fromBigTuple(CURVE.G2x), Fp2.fromBigTuple(CURVE.G2y), Fp2.ONE); | ||
PointG2.ZERO = new PointG2(Fp2.ONE, Fp2.ONE, Fp2.ZERO); | ||
@@ -523,0 +561,0 @@ export function pairing(P, Q, withFinalExponent = true) { |
@@ -29,8 +29,12 @@ export const CURVE = { | ||
} | ||
export function powMod(a, power, modulo) { | ||
export function powMod(num, power, modulo) { | ||
if (modulo <= 0n || power < 0n) | ||
throw new Error('Expected power/modulo > 0'); | ||
if (modulo === 1n) | ||
return 0n; | ||
let res = 1n; | ||
while (power > 0n) { | ||
if (power & 1n) | ||
res = (res * a) % modulo; | ||
a = (a * a) % modulo; | ||
res = (res * num) % modulo; | ||
num = (num * num) % modulo; | ||
power >>= 1n; | ||
@@ -41,20 +45,17 @@ } | ||
function genInvertBatch(cls, nums) { | ||
const len = nums.length; | ||
const scratch = new Array(len); | ||
let acc = cls.ONE; | ||
for (let i = 0; i < len; i++) { | ||
if (nums[i].isZero()) | ||
continue; | ||
scratch[i] = acc; | ||
acc = acc.multiply(nums[i]); | ||
} | ||
acc = acc.invert(); | ||
for (let i = len - 1; i >= 0; i--) { | ||
if (nums[i].isZero()) | ||
continue; | ||
let tmp = acc.multiply(nums[i]); | ||
nums[i] = acc.multiply(scratch[i]); | ||
acc = tmp; | ||
} | ||
return nums; | ||
const tmp = new Array(nums.length); | ||
const lastMultiplied = nums.reduce((acc, num, i) => { | ||
if (num.isZero()) | ||
return acc; | ||
tmp[i] = acc; | ||
return acc.multiply(num); | ||
}, cls.ONE); | ||
const inverted = lastMultiplied.invert(); | ||
nums.reduceRight((acc, num, i) => { | ||
if (num.isZero()) | ||
return acc; | ||
tmp[i] = acc.multiply(tmp[i]); | ||
return acc.multiply(num); | ||
}, inverted); | ||
return tmp; | ||
} | ||
@@ -71,3 +72,5 @@ function bitLen(n) { | ||
function invert(number, modulo = CURVE.P) { | ||
if (number === 0n || modulo <= 0n) { | ||
const _0n = 0n; | ||
const _1n = 1n; | ||
if (number === _0n || modulo <= _0n) { | ||
throw new Error(`invert: expected positive integers, got n=${number} mod=${modulo}`); | ||
@@ -77,4 +80,4 @@ } | ||
let b = modulo; | ||
let [x, y, u, v] = [0n, 1n, 1n, 0n]; | ||
while (a !== 0n) { | ||
let x = _0n, y = _1n, u = _1n, v = _0n; | ||
while (a !== _0n) { | ||
const q = b / a; | ||
@@ -84,8 +87,6 @@ const r = b % a; | ||
const n = y - v * q; | ||
[b, a] = [a, r]; | ||
[x, y] = [u, v]; | ||
[u, v] = [m, n]; | ||
b = a, a = r, x = u, y = v, u = m, v = n; | ||
} | ||
const gcd = b; | ||
if (gcd !== 1n) | ||
if (gcd !== _1n) | ||
throw new Error('invert: does not exist'); | ||
@@ -229,60 +230,74 @@ return mod(x, modulo); | ||
Fr.ONE = new Fr(1n); | ||
class FQP { | ||
zip(rhs, mapper) { | ||
const c0 = this.c; | ||
const c1 = rhs.c; | ||
const res = []; | ||
for (let i = 0; i < c0.length; i++) { | ||
res.push(mapper(c0[i], c1[i])); | ||
} | ||
return res; | ||
function powMod_FQP(fqp, fqpOne, n) { | ||
const elm = fqp; | ||
if (n === 0n) | ||
return fqpOne; | ||
if (n === 1n) | ||
return elm; | ||
let p = fqpOne; | ||
let d = elm; | ||
while (n > 0n) { | ||
if (n & 1n) | ||
p = p.multiply(d); | ||
n >>= 1n; | ||
d = d.square(); | ||
} | ||
map(callbackfn) { | ||
return this.c.map(callbackfn); | ||
return p; | ||
} | ||
export class Fp2 { | ||
constructor(c0, c1) { | ||
this.c0 = c0; | ||
this.c1 = c1; | ||
if (typeof c0 === 'bigint') | ||
throw new Error('c0: Expected Fp'); | ||
if (typeof c1 === 'bigint') | ||
throw new Error('c1: Expected Fp'); | ||
} | ||
static fromBigTuple(tuple) { | ||
const fps = tuple.map(n => new Fp(n)); | ||
return new Fp2(...fps); | ||
} | ||
one() { | ||
return Fp2.ONE; | ||
} | ||
isZero() { | ||
return this.c.every((c) => c.isZero()); | ||
return this.c0.isZero() && this.c1.isZero(); | ||
} | ||
equals(rhs) { | ||
return this.zip(rhs, (left, right) => left.equals(right)).every((r) => r); | ||
toString() { | ||
return `Fp2(${this.c0} + ${this.c1}×i)`; | ||
} | ||
reim() { | ||
return { re: this.c0.value, im: this.c1.value }; | ||
} | ||
negate() { | ||
return this.init(this.map((c) => c.negate())); | ||
const { c0, c1 } = this; | ||
return new Fp2(c0.negate(), c1.negate()); | ||
} | ||
equals(rhs) { | ||
const { c0, c1 } = this; | ||
const { c0: r0, c1: r1 } = rhs; | ||
return c0.equals(r0) && c1.equals(r1); | ||
} | ||
add(rhs) { | ||
return this.init(this.zip(rhs, (left, right) => left.add(right))); | ||
const { c0, c1 } = this; | ||
const { c0: r0, c1: r1 } = rhs; | ||
return new Fp2(c0.add(r0), c1.add(r1)); | ||
} | ||
subtract(rhs) { | ||
return this.init(this.zip(rhs, (left, right) => left.subtract(right))); | ||
const { c0, c1 } = this; | ||
const { c0: r0, c1: r1 } = rhs; | ||
return new Fp2(c0.subtract(r0), c1.subtract(r1)); | ||
} | ||
conjugate() { | ||
return this.init([this.c[0], this.c[1].negate()]); | ||
multiply(rhs) { | ||
const { c0, c1 } = this; | ||
if (typeof rhs === 'bigint') { | ||
return new Fp2(c0.multiply(rhs), c1.multiply(rhs)); | ||
} | ||
const { c0: r0, c1: r1 } = rhs; | ||
let t1 = c0.multiply(r0); | ||
let t2 = c1.multiply(r1); | ||
return new Fp2(t1.subtract(t2), c0.add(c1).multiply(r0.add(r1)).subtract(t1.add(t2))); | ||
} | ||
one() { | ||
const el = this; | ||
let one; | ||
if (el instanceof Fp2) | ||
one = Fp2.ONE; | ||
if (el instanceof Fp6) | ||
one = Fp6.ONE; | ||
if (el instanceof Fp12) | ||
one = Fp12.ONE; | ||
return one; | ||
} | ||
pow(n) { | ||
const elm = this; | ||
const one = this.one(); | ||
if (n === 0n) | ||
return one; | ||
if (n === 1n) | ||
return elm; | ||
let p = one; | ||
let d = elm; | ||
while (n > 0n) { | ||
if (n & 1n) | ||
p = p.multiply(d); | ||
n >>= 1n; | ||
d = d.square(); | ||
} | ||
return p; | ||
return powMod_FQP(this, Fp2.ONE, n); | ||
} | ||
@@ -293,44 +308,14 @@ div(rhs) { | ||
} | ||
} | ||
export class Fp2 extends FQP { | ||
constructor(coeffs) { | ||
super(); | ||
if (coeffs.length !== 2) | ||
throw new Error(`Expected array with 2 elements`); | ||
coeffs.forEach((c, i) => { | ||
if (typeof c === 'bigint') | ||
coeffs[i] = new Fp(c); | ||
}); | ||
this.c = coeffs; | ||
} | ||
init(tuple) { | ||
return new Fp2(tuple); | ||
} | ||
toString() { | ||
return `Fp2(${this.c[0]} + ${this.c[1]}×i)`; | ||
} | ||
get values() { | ||
return this.c.map((c) => c.value); | ||
} | ||
multiply(rhs) { | ||
if (typeof rhs === 'bigint') | ||
return new Fp2(this.map((c) => c.multiply(rhs))); | ||
const [c0, c1] = this.c; | ||
const [r0, r1] = rhs.c; | ||
let t1 = c0.multiply(r0); | ||
let t2 = c1.multiply(r1); | ||
return new Fp2([t1.subtract(t2), c0.add(c1).multiply(r0.add(r1)).subtract(t1.add(t2))]); | ||
} | ||
mulByNonresidue() { | ||
const c0 = this.c[0]; | ||
const c1 = this.c[1]; | ||
return new Fp2([c0.subtract(c1), c0.add(c1)]); | ||
const c0 = this.c0; | ||
const c1 = this.c1; | ||
return new Fp2(c0.subtract(c1), c0.add(c1)); | ||
} | ||
square() { | ||
const c0 = this.c[0]; | ||
const c1 = this.c[1]; | ||
const c0 = this.c0; | ||
const c1 = this.c1; | ||
const a = c0.add(c1); | ||
const b = c0.subtract(c1); | ||
const c = c0.add(c0); | ||
return new Fp2([a.multiply(b), c.multiply(c1)]); | ||
return new Fp2(a.multiply(b), c.multiply(c1)); | ||
} | ||
@@ -350,4 +335,4 @@ sqrt() { | ||
const x2 = x1.negate(); | ||
const [re1, im1] = x1.values; | ||
const [re2, im2] = x2.values; | ||
const { re: re1, im: im1 } = x1.reim(); | ||
const { re: re2, im: im2 } = x2.reim(); | ||
if (im1 > im2 || (im1 === im2 && re1 > re2)) | ||
@@ -358,14 +343,15 @@ return x1; | ||
invert() { | ||
const [a, b] = this.values; | ||
const { re: a, im: b } = this.reim(); | ||
const factor = new Fp(a * a + b * b).invert(); | ||
return new Fp2([factor.multiply(new Fp(a)), factor.multiply(new Fp(-b))]); | ||
return new Fp2(factor.multiply(new Fp(a)), factor.multiply(new Fp(-b))); | ||
} | ||
frobeniusMap(power) { | ||
return new Fp2([this.c[0], this.c[1].multiply(FP2_FROBENIUS_COEFFICIENTS[power % 2])]); | ||
return new Fp2(this.c0, this.c1.multiply(FP2_FROBENIUS_COEFFICIENTS[power % 2])); | ||
} | ||
multiplyByB() { | ||
let [c0, c1] = this.c; | ||
let c0 = this.c0; | ||
let c1 = this.c1; | ||
let t0 = c0.multiply(4n); | ||
let t1 = c1.multiply(4n); | ||
return new Fp2([t0.subtract(t1), t0.add(t1)]); | ||
return new Fp2(t0.subtract(t1), t0.add(t1)); | ||
} | ||
@@ -375,64 +361,83 @@ } | ||
Fp2.MAX_BITS = bitLen(CURVE.P2); | ||
Fp2.ZERO = new Fp2([0n, 0n]); | ||
Fp2.ONE = new Fp2([1n, 0n]); | ||
export class Fp6 extends FQP { | ||
constructor(c) { | ||
super(); | ||
this.c = c; | ||
if (c.length !== 3) | ||
throw new Error(`Expected array with 3 elements`); | ||
Fp2.ZERO = new Fp2(Fp.ZERO, Fp.ZERO); | ||
Fp2.ONE = new Fp2(Fp.ONE, Fp.ZERO); | ||
export class Fp6 { | ||
constructor(c0, c1, c2) { | ||
this.c0 = c0; | ||
this.c1 = c1; | ||
this.c2 = c2; | ||
} | ||
static fromTuple(t) { | ||
static fromBigSix(t) { | ||
if (!Array.isArray(t) || t.length !== 6) | ||
throw new Error('Invalid Fp6 usage'); | ||
return new Fp6([new Fp2(t.slice(0, 2)), new Fp2(t.slice(2, 4)), new Fp2(t.slice(4, 6))]); | ||
const c = [t.slice(0, 2), t.slice(2, 4), t.slice(4, 6)].map(t => Fp2.fromBigTuple(t)); | ||
return new Fp6(...c); | ||
} | ||
init(triple) { | ||
return new Fp6(triple); | ||
fromTriple(triple) { | ||
return new Fp6(...triple); | ||
} | ||
one() { | ||
return Fp6.ONE; | ||
} | ||
isZero() { | ||
return this.c0.isZero() && this.c1.isZero() && this.c2.isZero(); | ||
} | ||
negate() { | ||
const { c0, c1, c2 } = this; | ||
return new Fp6(c0.negate(), c1.negate(), c2.negate()); | ||
} | ||
toString() { | ||
return `Fp6(${this.c[0]} + ${this.c[1]} * v, ${this.c[2]} * v^2)`; | ||
return `Fp6(${this.c0} + ${this.c1} * v, ${this.c2} * v^2)`; | ||
} | ||
conjugate() { | ||
throw new TypeError('No conjugate on Fp6'); | ||
equals(rhs) { | ||
const { c0, c1, c2 } = this; | ||
const { c0: r0, c1: r1, c2: r2 } = rhs; | ||
return c0.equals(r0) && c1.equals(r1) && c2.equals(r2); | ||
} | ||
add(rhs) { | ||
const { c0, c1, c2 } = this; | ||
const { c0: r0, c1: r1, c2: r2 } = rhs; | ||
return new Fp6(c0.add(r0), c1.add(r1), c2.add(r2)); | ||
} | ||
subtract(rhs) { | ||
const { c0, c1, c2 } = this; | ||
const { c0: r0, c1: r1, c2: r2 } = rhs; | ||
return new Fp6(c0.subtract(r0), c1.subtract(r1), c2.subtract(r2)); | ||
} | ||
multiply(rhs) { | ||
if (typeof rhs === 'bigint') | ||
return new Fp6([this.c[0].multiply(rhs), this.c[1].multiply(rhs), this.c[2].multiply(rhs)]); | ||
let [c0, c1, c2] = this.c; | ||
const [r0, r1, r2] = rhs.c; | ||
if (typeof rhs === 'bigint') { | ||
return new Fp6(this.c0.multiply(rhs), this.c1.multiply(rhs), this.c2.multiply(rhs)); | ||
} | ||
let { c0, c1, c2 } = this; | ||
let { c0: r0, c1: r1, c2: r2 } = rhs; | ||
let t0 = c0.multiply(r0); | ||
let t1 = c1.multiply(r1); | ||
let t2 = c2.multiply(r2); | ||
return new Fp6([ | ||
t0.add(c1.add(c2).multiply(r1.add(r2)).subtract(t1.add(t2)).mulByNonresidue()), | ||
c0.add(c1).multiply(r0.add(r1)).subtract(t0.add(t1)).add(t2.mulByNonresidue()), | ||
t1.add(c0.add(c2).multiply(r0.add(r2)).subtract(t0.add(t2))), | ||
]); | ||
return new Fp6(t0.add(c1.add(c2).multiply(r1.add(r2)).subtract(t1.add(t2)).mulByNonresidue()), c0.add(c1).multiply(r0.add(r1)).subtract(t0.add(t1)).add(t2.mulByNonresidue()), t1.add(c0.add(c2).multiply(r0.add(r2)).subtract(t0.add(t2)))); | ||
} | ||
pow(n) { | ||
return powMod_FQP(this, Fp6.ONE, n); | ||
} | ||
div(rhs) { | ||
const inv = typeof rhs === 'bigint' ? new Fp(rhs).invert().value : rhs.invert(); | ||
return this.multiply(inv); | ||
} | ||
mulByNonresidue() { | ||
return new Fp6([this.c[2].mulByNonresidue(), this.c[0], this.c[1]]); | ||
return new Fp6(this.c2.mulByNonresidue(), this.c0, this.c1); | ||
} | ||
multiplyBy1(b1) { | ||
return new Fp6([ | ||
this.c[2].multiply(b1).mulByNonresidue(), | ||
this.c[0].multiply(b1), | ||
this.c[1].multiply(b1), | ||
]); | ||
return new Fp6(this.c2.multiply(b1).mulByNonresidue(), this.c0.multiply(b1), this.c1.multiply(b1)); | ||
} | ||
multiplyBy01(b0, b1) { | ||
let [c0, c1, c2] = this.c; | ||
let { c0, c1, c2 } = this; | ||
let t0 = c0.multiply(b0); | ||
let t1 = c1.multiply(b1); | ||
return new Fp6([ | ||
c1.add(c2).multiply(b1).subtract(t1).mulByNonresidue().add(t0), | ||
b0.add(b1).multiply(c0.add(c1)).subtract(t0).subtract(t1), | ||
c0.add(c2).multiply(b0).subtract(t0).add(t1), | ||
]); | ||
return new Fp6(c1.add(c2).multiply(b1).subtract(t1).mulByNonresidue().add(t0), b0.add(b1).multiply(c0.add(c1)).subtract(t0).subtract(t1), c0.add(c2).multiply(b0).subtract(t0).add(t1)); | ||
} | ||
multiplyByFp2(rhs) { | ||
return new Fp6(this.map((c) => c.multiply(rhs))); | ||
let { c0, c1, c2 } = this; | ||
return new Fp6(c0.multiply(rhs), c1.multiply(rhs), c2.multiply(rhs)); | ||
} | ||
square() { | ||
let [c0, c1, c2] = this.c; | ||
let { c0, c1, c2 } = this; | ||
let t0 = c0.square(); | ||
@@ -442,10 +447,6 @@ let t1 = c0.multiply(c1).multiply(2n); | ||
let t4 = c2.square(); | ||
return new Fp6([ | ||
t3.mulByNonresidue().add(t0), | ||
t4.mulByNonresidue().add(t1), | ||
t1.add(c0.subtract(c1).add(c2).square()).add(t3).subtract(t0).subtract(t4), | ||
]); | ||
return new Fp6(t3.mulByNonresidue().add(t0), t4.mulByNonresidue().add(t1), t1.add(c0.subtract(c1).add(c2).square()).add(t3).subtract(t0).subtract(t4)); | ||
} | ||
invert() { | ||
let [c0, c1, c2] = this.c; | ||
let { c0, c1, c2 } = this; | ||
let t0 = c0.square().subtract(c2.multiply(c1).mulByNonresidue()); | ||
@@ -455,106 +456,109 @@ let t1 = c2.square().mulByNonresidue().subtract(c0.multiply(c1)); | ||
let t4 = c2.multiply(t1).add(c1.multiply(t2)).mulByNonresidue().add(c0.multiply(t0)).invert(); | ||
return new Fp6([t4.multiply(t0), t4.multiply(t1), t4.multiply(t2)]); | ||
return new Fp6(t4.multiply(t0), t4.multiply(t1), t4.multiply(t2)); | ||
} | ||
frobeniusMap(power) { | ||
return new Fp6([ | ||
this.c[0].frobeniusMap(power), | ||
this.c[1].frobeniusMap(power).multiply(FP6_FROBENIUS_COEFFICIENTS_1[power % 6]), | ||
this.c[2].frobeniusMap(power).multiply(FP6_FROBENIUS_COEFFICIENTS_2[power % 6]), | ||
]); | ||
return new Fp6(this.c0.frobeniusMap(power), this.c1.frobeniusMap(power).multiply(FP6_FROBENIUS_COEFFICIENTS_1[power % 6]), this.c2.frobeniusMap(power).multiply(FP6_FROBENIUS_COEFFICIENTS_2[power % 6])); | ||
} | ||
} | ||
Fp6.ZERO = new Fp6([Fp2.ZERO, Fp2.ZERO, Fp2.ZERO]); | ||
Fp6.ONE = new Fp6([Fp2.ONE, Fp2.ZERO, Fp2.ZERO]); | ||
export class Fp12 extends FQP { | ||
constructor(c) { | ||
super(); | ||
this.c = c; | ||
if (c.length !== 2) | ||
throw new Error(`Expected array with 2 elements`); | ||
Fp6.ZERO = new Fp6(Fp2.ZERO, Fp2.ZERO, Fp2.ZERO); | ||
Fp6.ONE = new Fp6(Fp2.ONE, Fp2.ZERO, Fp2.ZERO); | ||
export class Fp12 { | ||
constructor(c0, c1) { | ||
this.c0 = c0; | ||
this.c1 = c1; | ||
} | ||
static fromTuple(t) { | ||
return new Fp12([ | ||
Fp6.fromTuple(t.slice(0, 6)), | ||
Fp6.fromTuple(t.slice(6, 12)), | ||
]); | ||
static fromBigTwelve(t) { | ||
return new Fp12(Fp6.fromBigSix(t.slice(0, 6)), Fp6.fromBigSix(t.slice(6, 12))); | ||
} | ||
init(c) { | ||
return new Fp12(c); | ||
fromTuple(c) { | ||
return new Fp12(...c); | ||
} | ||
one() { | ||
return Fp12.ONE; | ||
} | ||
isZero() { | ||
return this.c0.isZero() && this.c1.isZero(); | ||
} | ||
toString() { | ||
return `Fp12(${this.c[0]} + ${this.c[1]} * w)`; | ||
return `Fp12(${this.c0} + ${this.c1} * w)`; | ||
} | ||
negate() { | ||
const { c0, c1 } = this; | ||
return new Fp12(c0.negate(), c1.negate()); | ||
} | ||
equals(rhs) { | ||
const { c0, c1 } = this; | ||
const { c0: r0, c1: r1 } = rhs; | ||
return c0.equals(r0) && c1.equals(r1); | ||
} | ||
add(rhs) { | ||
const { c0, c1 } = this; | ||
const { c0: r0, c1: r1 } = rhs; | ||
return new Fp12(c0.add(r0), c1.add(r1)); | ||
} | ||
subtract(rhs) { | ||
const { c0, c1 } = this; | ||
const { c0: r0, c1: r1 } = rhs; | ||
return new Fp12(c0.subtract(r0), c1.subtract(r1)); | ||
} | ||
multiply(rhs) { | ||
if (typeof rhs === 'bigint') | ||
return new Fp12([this.c[0].multiply(rhs), this.c[1].multiply(rhs)]); | ||
let [c0, c1] = this.c; | ||
const [r0, r1] = rhs.c; | ||
return new Fp12(this.c0.multiply(rhs), this.c1.multiply(rhs)); | ||
let { c0, c1 } = this; | ||
let { c0: r0, c1: r1 } = rhs; | ||
let t1 = c0.multiply(r0); | ||
let t2 = c1.multiply(r1); | ||
return new Fp12([ | ||
t1.add(t2.mulByNonresidue()), | ||
c0.add(c1).multiply(r0.add(r1)).subtract(t1.add(t2)), | ||
]); | ||
return new Fp12(t1.add(t2.mulByNonresidue()), c0.add(c1).multiply(r0.add(r1)).subtract(t1.add(t2))); | ||
} | ||
pow(n) { | ||
return powMod_FQP(this, Fp12.ONE, n); | ||
} | ||
div(rhs) { | ||
const inv = typeof rhs === 'bigint' ? new Fp(rhs).invert().value : rhs.invert(); | ||
return this.multiply(inv); | ||
} | ||
multiplyBy014(o0, o1, o4) { | ||
let [c0, c1] = this.c; | ||
let [t0, t1] = [c0.multiplyBy01(o0, o1), c1.multiplyBy1(o4)]; | ||
return new Fp12([ | ||
t1.mulByNonresidue().add(t0), | ||
c1.add(c0).multiplyBy01(o0, o1.add(o4)).subtract(t0).subtract(t1), | ||
]); | ||
let { c0, c1 } = this; | ||
let t0 = c0.multiplyBy01(o0, o1); | ||
let t1 = c1.multiplyBy1(o4); | ||
return new Fp12(t1.mulByNonresidue().add(t0), c1.add(c0).multiplyBy01(o0, o1.add(o4)).subtract(t0).subtract(t1)); | ||
} | ||
multiplyByFp2(rhs) { | ||
return this.init(this.map((c) => c.multiplyByFp2(rhs))); | ||
return new Fp12(this.c0.multiplyByFp2(rhs), this.c1.multiplyByFp2(rhs)); | ||
} | ||
square() { | ||
let [c0, c1] = this.c; | ||
let { c0, c1 } = this; | ||
let ab = c0.multiply(c1); | ||
return new Fp12([ | ||
c1.mulByNonresidue().add(c0).multiply(c0.add(c1)).subtract(ab).subtract(ab.mulByNonresidue()), | ||
ab.add(ab), | ||
]); | ||
return new Fp12(c1.mulByNonresidue().add(c0).multiply(c0.add(c1)).subtract(ab).subtract(ab.mulByNonresidue()), ab.add(ab)); | ||
} | ||
invert() { | ||
let [c0, c1] = this.c; | ||
let { c0, c1 } = this; | ||
let t = c0.square().subtract(c1.square().mulByNonresidue()).invert(); | ||
return new Fp12([c0.multiply(t), c1.multiply(t).negate()]); | ||
return new Fp12(c0.multiply(t), c1.multiply(t).negate()); | ||
} | ||
conjugate() { | ||
return new Fp12(this.c0, this.c1.negate()); | ||
} | ||
frobeniusMap(power) { | ||
const [c0, c1] = this.c; | ||
let r0 = c0.frobeniusMap(power); | ||
let [c1_0, c1_1, c1_2] = c1.frobeniusMap(power).c; | ||
const r0 = this.c0.frobeniusMap(power); | ||
const { c0, c1, c2 } = this.c1.frobeniusMap(power); | ||
const coeff = FP12_FROBENIUS_COEFFICIENTS[power % 12]; | ||
return new Fp12([ | ||
r0, | ||
new Fp6([c1_0.multiply(coeff), c1_1.multiply(coeff), c1_2.multiply(coeff)]), | ||
]); | ||
return new Fp12(r0, new Fp6(c0.multiply(coeff), c1.multiply(coeff), c2.multiply(coeff))); | ||
} | ||
Fp4Square(a, b) { | ||
const a2 = a.square(), b2 = b.square(); | ||
return [ | ||
b2.mulByNonresidue().add(a2), | ||
a.add(b).square().subtract(a2).subtract(b2), | ||
]; | ||
const a2 = a.square(); | ||
const b2 = b.square(); | ||
return { | ||
first: b2.mulByNonresidue().add(a2), | ||
second: a.add(b).square().subtract(a2).subtract(b2), | ||
}; | ||
} | ||
cyclotomicSquare() { | ||
const [c0, c1] = this.c; | ||
const [c0c0, c0c1, c0c2] = c0.c; | ||
const [c1c0, c1c1, c1c2] = c1.c; | ||
let [t3, t4] = this.Fp4Square(c0c0, c1c1); | ||
let [t5, t6] = this.Fp4Square(c1c0, c0c2); | ||
let [t7, t8] = this.Fp4Square(c0c1, c1c2); | ||
const { c0: c0c0, c1: c0c1, c2: c0c2 } = this.c0; | ||
const { c0: c1c0, c1: c1c1, c2: c1c2 } = this.c1; | ||
const { first: t3, second: t4 } = this.Fp4Square(c0c0, c1c1); | ||
const { first: t5, second: t6 } = this.Fp4Square(c1c0, c0c2); | ||
const { first: t7, second: t8 } = this.Fp4Square(c0c1, c1c2); | ||
let t9 = t8.mulByNonresidue(); | ||
return new Fp12([ | ||
new Fp6([ | ||
t3.subtract(c0c0).multiply(2n).add(t3), | ||
t5.subtract(c0c1).multiply(2n).add(t5), | ||
t7.subtract(c0c2).multiply(2n).add(t7), | ||
]), | ||
new Fp6([ | ||
t9.add(c1c0).multiply(2n).add(t9), | ||
t4.add(c1c1).multiply(2n).add(t4), | ||
t6.add(c1c2).multiply(2n).add(t6), | ||
]), | ||
]); | ||
return new Fp12(new Fp6(t3.subtract(c0c0).multiply(2n).add(t3), t5.subtract(c0c1).multiply(2n).add(t5), t7.subtract(c0c2).multiply(2n).add(t7)), new Fp6(t9.add(c1c0).multiply(2n).add(t9), t4.add(c1c1).multiply(2n).add(t4), t6.add(c1c2).multiply(2n).add(t6))); | ||
} | ||
@@ -587,4 +591,4 @@ cyclotomicExp(n) { | ||
} | ||
Fp12.ZERO = new Fp12([Fp6.ZERO, Fp6.ZERO]); | ||
Fp12.ONE = new Fp12([Fp6.ONE, Fp6.ZERO]); | ||
Fp12.ZERO = new Fp12(Fp6.ZERO, Fp6.ZERO); | ||
Fp12.ONE = new Fp12(Fp6.ONE, Fp6.ZERO); | ||
export class ProjectivePoint { | ||
@@ -619,2 +623,5 @@ constructor(x, y, z, C) { | ||
toString(isAffine = true) { | ||
if (this.isZero()) { | ||
return `Point<Zero>`; | ||
} | ||
if (!isAffine) { | ||
@@ -630,2 +637,4 @@ return `Point<x=${this.x}, y=${this.y}, z=${this.z}>`; | ||
toAffine(invZ = this.z.invert()) { | ||
if (invZ.isZero()) | ||
throw new Error('Invalid inverted z'); | ||
return [this.x.multiply(invZ), this.y.multiply(invZ)]; | ||
@@ -769,3 +778,4 @@ } | ||
} | ||
let [p, f] = [this.getZero(), this.getZero()]; | ||
let p = this.getZero(); | ||
let f = this.getZero(); | ||
const windows = Math.ceil(this.maxBits() / W); | ||
@@ -799,3 +809,3 @@ const windowSize = 2 ** (W - 1); | ||
function sgn0(x) { | ||
const [x0, x1] = x.values; | ||
const { re: x0, im: x1 } = x.reim(); | ||
const sign_0 = x0 % 2n; | ||
@@ -815,3 +825,3 @@ const zero_0 = x0 === 0n; | ||
const positiveRootsOfUnity = FP2_ROOTS_OF_UNITY.slice(0, 4); | ||
for (const root of positiveRootsOfUnity) { | ||
positiveRootsOfUnity.forEach((root) => { | ||
const candidate = root.multiply(gamma); | ||
@@ -822,11 +832,11 @@ if (candidate.pow(2n).multiply(v).subtract(u).isZero() && !success) { | ||
} | ||
} | ||
return [success, result]; | ||
}); | ||
return { success, sqrtCandidateOrGamma: result }; | ||
} | ||
export function map_to_curve_simple_swu_9mod16(t) { | ||
const iso_3_a = new Fp2([0n, 240n]); | ||
const iso_3_b = new Fp2([1012n, 1012n]); | ||
const iso_3_z = new Fp2([-2n, -1n]); | ||
const iso_3_a = new Fp2(new Fp(0n), new Fp(240n)); | ||
const iso_3_b = new Fp2(new Fp(1012n), new Fp(1012n)); | ||
const iso_3_z = new Fp2(new Fp(-2n), new Fp(-1n)); | ||
if (Array.isArray(t)) | ||
t = new Fp2(t); | ||
t = Fp2.fromBigTuple(t); | ||
const t2 = t.pow(2n); | ||
@@ -844,3 +854,3 @@ const iso_3_z_t2 = iso_3_z.multiply(t2); | ||
.add(iso_3_b.multiply(v)); | ||
const [success, sqrtCandidateOrGamma] = sqrt_div_fp2(u, v); | ||
const { success, sqrtCandidateOrGamma } = sqrt_div_fp2(u, v); | ||
let y; | ||
@@ -852,3 +862,3 @@ if (success) | ||
let success2 = false; | ||
for (const eta of FP2_ETAs) { | ||
FP2_ETAs.forEach((eta) => { | ||
const etaSqrtCandidate = eta.multiply(sqrtCandidateX1); | ||
@@ -860,3 +870,3 @@ const temp = etaSqrtCandidate.pow(2n).multiply(v).subtract(u); | ||
} | ||
} | ||
}); | ||
if (!success && !success2) | ||
@@ -873,3 +883,3 @@ throw new Error('Hash to Curve - Optimized SWU failure'); | ||
export function isogenyMapG2(xyz) { | ||
const [x, y, z] = xyz; | ||
const x = xyz[0], y = xyz[1], z = xyz[2]; | ||
const zz = z.multiply(z); | ||
@@ -896,4 +906,4 @@ const zzz = zz.multiply(z); | ||
export function calcPairingPrecomputes(x, y) { | ||
const [Qx, Qy, Qz] = [x, y, Fp2.ONE]; | ||
let [Rx, Ry, Rz] = [Qx, Qy, Qz]; | ||
const Qx = x, Qy = y, Qz = Fp2.ONE; | ||
let Rx = Qx, Ry = Qy, Rz = Qz; | ||
let ell_coeff = []; | ||
@@ -934,10 +944,12 @@ for (let i = BLS_X_LEN - 2; i >= 0; i--) { | ||
export function millerLoop(ell, g1) { | ||
const Px = g1[0].value; | ||
const Py = g1[1].value; | ||
let f12 = Fp12.ONE; | ||
const [x, y] = g1; | ||
const [Px, Py] = [x, y]; | ||
for (let j = 0, i = BLS_X_LEN - 2; i >= 0; i--, j++) { | ||
f12 = f12.multiplyBy014(ell[j][0], ell[j][1].multiply(Px.value), ell[j][2].multiply(Py.value)); | ||
const E = ell[j]; | ||
f12 = f12.multiplyBy014(E[0], E[1].multiply(Px), E[2].multiply(Py)); | ||
if (bitGet(CURVE.x, i)) { | ||
j += 1; | ||
f12 = f12.multiplyBy014(ell[j][0], ell[j][1].multiply(Px.value), ell[j][2].multiply(Py.value)); | ||
const F = ell[j]; | ||
f12 = f12.multiplyBy014(F[0], F[1].multiply(Px), F[2].multiply(Py)); | ||
} | ||
@@ -949,10 +961,9 @@ if (i !== 0) | ||
} | ||
const ut_root = new Fp6([Fp2.ZERO, Fp2.ONE, Fp2.ZERO]); | ||
const wsq = new Fp12([ut_root, Fp6.ZERO]); | ||
const wsq_inv = wsq.invert(); | ||
const wcu = new Fp12([Fp6.ZERO, ut_root]); | ||
const wcu_inv = wcu.invert(); | ||
const ut_root = new Fp6(Fp2.ZERO, Fp2.ONE, Fp2.ZERO); | ||
const wsq = new Fp12(ut_root, Fp6.ZERO); | ||
const wcu = new Fp12(Fp6.ZERO, ut_root); | ||
const [wsq_inv, wcu_inv] = genInvertBatch(Fp12, [wsq, wcu]); | ||
export function psi(x, y) { | ||
const x2 = wsq_inv.multiplyByFp2(x).frobeniusMap(1).multiply(wsq).c[0].c[0]; | ||
const y2 = wcu_inv.multiplyByFp2(y).frobeniusMap(1).multiply(wcu).c[0].c[0]; | ||
const x2 = wsq_inv.multiplyByFp2(x).frobeniusMap(1).multiply(wsq).c0.c0; | ||
const y2 = wcu_inv.multiplyByFp2(y).frobeniusMap(1).multiply(wcu).c0.c0; | ||
return [x2, y2]; | ||
@@ -982,3 +993,3 @@ } | ||
[-rv1, -rv1], | ||
].map((pair) => new Fp2(pair)); | ||
].map((pair) => Fp2.fromBigTuple(pair)); | ||
const FP2_ETAs = [ | ||
@@ -989,3 +1000,3 @@ [ev1, ev2], | ||
[-ev4, ev3], | ||
].map((pair) => new Fp2(pair)); | ||
].map((pair) => Fp2.fromBigTuple(pair)); | ||
const FP6_FROBENIUS_COEFFICIENTS_1 = [ | ||
@@ -1010,3 +1021,3 @@ [0x1n, 0x0n], | ||
], | ||
].map((pair) => new Fp2(pair)); | ||
].map((pair) => Fp2.fromBigTuple(pair)); | ||
const FP6_FROBENIUS_COEFFICIENTS_2 = [ | ||
@@ -1034,3 +1045,3 @@ [0x1n, 0x0n], | ||
], | ||
].map((pair) => new Fp2(pair)); | ||
].map((pair) => Fp2.fromBigTuple(pair)); | ||
const FP12_FROBENIUS_COEFFICIENTS = [ | ||
@@ -1082,3 +1093,3 @@ [0x1n, 0x0n], | ||
], | ||
].map((pair) => new Fp2(pair)); | ||
].map(n => Fp2.fromBigTuple(n)); | ||
const xnum = [ | ||
@@ -1101,3 +1112,3 @@ [ | ||
], | ||
].map((pair) => new Fp2(pair)); | ||
].map((pair) => Fp2.fromBigTuple(pair)); | ||
const xden = [ | ||
@@ -1114,3 +1125,3 @@ [ | ||
[0x0n, 0x0n], | ||
].map((pair) => new Fp2(pair)); | ||
].map((pair) => Fp2.fromBigTuple(pair)); | ||
const ynum = [ | ||
@@ -1133,3 +1144,3 @@ [ | ||
], | ||
].map((pair) => new Fp2(pair)); | ||
].map((pair) => Fp2.fromBigTuple(pair)); | ||
const yden = [ | ||
@@ -1149,3 +1160,3 @@ [ | ||
[0x1n, 0x0n], | ||
].map((pair) => new Fp2(pair)); | ||
].map((pair) => Fp2.fromBigTuple(pair)); | ||
const ISOGENY_COEFFICIENTS = [xnum, xden, ynum, yden]; |
@@ -1,8 +0,9 @@ | ||
/*! noble-bls12-381 - MIT License (c) Paul Miller (paulmillr.com) */ | ||
/*! noble-bls12-381 - MIT License (c) 2019 Paul Miller (paulmillr.com) */ | ||
import { Fp, Fr, Fp2, Fp12, CURVE, ProjectivePoint, mod } from './math.js'; | ||
export { Fp, Fr, Fp2, Fp12, CURVE }; | ||
declare type Bytes = Uint8Array | string; | ||
declare type PrivateKey = Bytes | bigint | number; | ||
declare type Hex = Uint8Array | string; | ||
declare type PrivateKey = Hex | bigint | number; | ||
export declare const utils: { | ||
hashToField: typeof hash_to_field; | ||
hashToPrivateKey: (hash: Hex) => Uint8Array; | ||
bytesToHex: typeof bytesToHex; | ||
@@ -22,3 +23,3 @@ randomBytes: (bytesLength?: number) => Uint8Array; | ||
constructor(x: Fp, y: Fp, z?: Fp); | ||
static fromHex(bytes: Bytes): PointG1; | ||
static fromHex(bytes: Hex): PointG1; | ||
static fromPrivateKey(privateKey: PrivateKey): PointG1; | ||
@@ -42,5 +43,5 @@ toRawBytes(isCompressed?: boolean): Uint8Array; | ||
constructor(x: Fp2, y: Fp2, z?: Fp2); | ||
static hashToCurve(msg: Bytes): Promise<PointG2>; | ||
static fromSignature(hex: Bytes): PointG2; | ||
static fromHex(bytes: Bytes): PointG2; | ||
static hashToCurve(msg: Hex): Promise<PointG2>; | ||
static fromSignature(hex: Hex): PointG2; | ||
static fromHex(bytes: Hex): PointG2; | ||
static fromPrivateKey(privateKey: PrivateKey): PointG2; | ||
@@ -61,12 +62,12 @@ toSignature(): Uint8Array; | ||
export declare function pairing(P: PointG1, Q: PointG2, withFinalExponent?: boolean): Fp12; | ||
declare type G1Hex = Bytes | PointG1; | ||
declare type G2Hex = Bytes | PointG2; | ||
declare type G1Hex = Hex | PointG1; | ||
declare type G2Hex = Hex | PointG2; | ||
export declare function getPublicKey(privateKey: PrivateKey): Uint8Array; | ||
export declare function sign(message: Bytes, privateKey: PrivateKey): Promise<Uint8Array>; | ||
export declare function sign(message: Hex, privateKey: PrivateKey): Promise<Uint8Array>; | ||
export declare function sign(message: PointG2, privateKey: PrivateKey): Promise<PointG2>; | ||
export declare function verify(signature: G2Hex, message: G2Hex, publicKey: G1Hex): Promise<boolean>; | ||
export declare function aggregatePublicKeys(publicKeys: Bytes[]): Uint8Array; | ||
export declare function aggregatePublicKeys(publicKeys: Hex[]): Uint8Array; | ||
export declare function aggregatePublicKeys(publicKeys: PointG1[]): PointG1; | ||
export declare function aggregateSignatures(signatures: Bytes[]): Uint8Array; | ||
export declare function aggregateSignatures(signatures: Hex[]): Uint8Array; | ||
export declare function aggregateSignatures(signatures: PointG2[]): PointG2; | ||
export declare function verifyBatch(signature: G2Hex, messages: G2Hex[], publicKeys: G1Hex[]): Promise<boolean>; |
118
lib/index.js
"use strict"; | ||
/*! noble-bls12-381 - MIT License (c) Paul Miller (paulmillr.com) */ | ||
/*! noble-bls12-381 - MIT License (c) 2019 Paul Miller (paulmillr.com) */ | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
@@ -36,2 +36,11 @@ return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
hashToField: hash_to_field, | ||
hashToPrivateKey: (hash) => { | ||
hash = ensureBytes(hash); | ||
if (hash.length < 40 || hash.length > 1024) | ||
throw new Error('Expected 40-1024 bytes of private key as per FIPS 186'); | ||
const num = (0, math_js_1.mod)(bytesToNumberBE(hash), math_js_1.CURVE.r); | ||
if (num === 0n || num === 1n) | ||
throw new Error('Invalid private key'); | ||
return numberTo32BytesBE(num); | ||
}, | ||
bytesToHex, | ||
@@ -51,10 +60,3 @@ randomBytes: (bytesLength = 32) => { | ||
randomPrivateKey: () => { | ||
let i = 8; | ||
while (i--) { | ||
const b32 = exports.utils.randomBytes(32); | ||
const num = bytesToNumberBE(b32); | ||
if (isWithinCurveOrder(num) && num !== 1n) | ||
return b32; | ||
} | ||
throw new Error('Valid private key was not found in 8 iterations. PRNG is broken'); | ||
return exports.utils.hashToPrivateKey(exports.utils.randomBytes(40)); | ||
}, | ||
@@ -84,8 +86,6 @@ sha256: async (message) => { | ||
}; | ||
function bytesToNumberBE(bytes) { | ||
let value = 0n; | ||
for (let i = bytes.length - 1, j = 0; i >= 0; i--, j++) { | ||
value += (BigInt(bytes[i]) & 255n) << (8n * BigInt(j)); | ||
} | ||
return value; | ||
function bytesToNumberBE(uint8a) { | ||
if (!(uint8a instanceof Uint8Array)) | ||
throw new Error('Expected Uint8Array'); | ||
return BigInt('0x' + bytesToHex(Uint8Array.from(uint8a))); | ||
} | ||
@@ -113,3 +113,3 @@ const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0')); | ||
const byte = Number.parseInt(hexByte, 16); | ||
if (Number.isNaN(byte)) | ||
if (Number.isNaN(byte) || byte < 0) | ||
throw new Error('Invalid byte sequence'); | ||
@@ -120,5 +120,10 @@ array[i] = byte; | ||
} | ||
function numberTo32BytesBE(num) { | ||
const length = 32; | ||
const hex = num.toString(16).padStart(length * 2, '0'); | ||
return hexToBytes(hex); | ||
} | ||
function toPaddedHex(num, padding) { | ||
if (num < 0n) | ||
throw new Error('Expected valid number'); | ||
if (typeof num !== 'bigint' || num < 0n) | ||
throw new Error('Expected valid bigint'); | ||
if (typeof padding !== 'number') | ||
@@ -129,7 +134,3 @@ throw new TypeError('Expected valid padding'); | ||
function ensureBytes(hex) { | ||
if (hex instanceof Uint8Array) | ||
return hex; | ||
if (typeof hex === 'string') | ||
return hexToBytes(hex); | ||
throw new TypeError('Expected hex string or Uint8Array'); | ||
return hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes(hex); | ||
} | ||
@@ -253,5 +254,5 @@ function concatBytes(...arrays) { | ||
bytes = ensureBytes(bytes); | ||
const { P } = math_js_1.CURVE; | ||
let point; | ||
if (bytes.length === 48) { | ||
const { P } = math_js_1.CURVE; | ||
const compressedValue = bytesToNumberBE(bytes); | ||
@@ -293,4 +294,4 @@ const bflag = (0, math_js_1.mod)(compressedValue, POW_2_383) / POW_2_382; | ||
this.assertValidity(); | ||
const { P } = math_js_1.CURVE; | ||
if (isCompressed) { | ||
const { P } = math_js_1.CURVE; | ||
let hex; | ||
@@ -394,10 +395,10 @@ if (this.isZero()) { | ||
return this.ZERO; | ||
const x1 = z1 % POW_2_381; | ||
const x2 = z2; | ||
const x = new math_js_1.Fp2([x2, x1]); | ||
const y2 = x.pow(3n).add(new math_js_1.Fp2(math_js_1.CURVE.b2)); | ||
const x1 = new math_js_1.Fp(z1 % POW_2_381); | ||
const x2 = new math_js_1.Fp(z2); | ||
const x = new math_js_1.Fp2(x2, x1); | ||
const y2 = x.pow(3n).add(math_js_1.Fp2.fromBigTuple(math_js_1.CURVE.b2)); | ||
let y = y2.sqrt(); | ||
if (!y) | ||
throw new Error('Failed to find a square root'); | ||
const [y0, y1] = y.values; | ||
const { re: y0, im: y1 } = y.reim(); | ||
const aflag1 = (z1 % POW_2_382) / POW_2_381; | ||
@@ -414,7 +415,32 @@ const isGreater = y1 > 0n && (y1 * 2n) / P !== aflag1; | ||
bytes = ensureBytes(bytes); | ||
const m_byte = bytes[0] & 0xe0; | ||
if (m_byte === 0x20 || m_byte === 0x60 || m_byte === 0xe0) { | ||
throw new Error('Invalid encoding flag: ' + m_byte); | ||
} | ||
const bitC = m_byte & 0x80; | ||
const bitI = m_byte & 0x40; | ||
const bitS = m_byte & 0x20; | ||
let point; | ||
if (bytes.length === 96) { | ||
throw new Error('Compressed format not supported yet.'); | ||
if (bytes.length === 96 && bitC) { | ||
const { P, b2 } = math_js_1.CURVE; | ||
const b = math_js_1.Fp2.fromBigTuple(b2); | ||
bytes[0] = bytes[0] & 0x1f; | ||
if (bitI) { | ||
if (bytes.reduce((p, c) => (p !== 0 ? c + 1 : c), 0) > 0) { | ||
throw new Error('Invalid compressed G2 point'); | ||
} | ||
return PointG2.ZERO; | ||
} | ||
const x_1 = bytesToNumberBE(bytes.slice(0, PUBLIC_KEY_LENGTH)); | ||
const x_0 = bytesToNumberBE(bytes.slice(PUBLIC_KEY_LENGTH)); | ||
const x = new math_js_1.Fp2(new math_js_1.Fp(x_0), new math_js_1.Fp(x_1)); | ||
const right = x.pow(3n).add(b); | ||
let y = right.sqrt(); | ||
if (!y) | ||
throw new Error('Invalid compressed G2 point'); | ||
const Y_bit = y.c1.value === 0n ? (y.c0.value * 2n) / P : (y.c1.value * 2n) / P ? 1n : 0n; | ||
y = bitS > 0 && Y_bit > 0 ? y : y.negate(); | ||
return new PointG2(x, y); | ||
} | ||
else if (bytes.length === 192) { | ||
else if (bytes.length === 192 && !bitC) { | ||
if ((bytes[0] & (1 << 6)) !== 0) { | ||
@@ -427,6 +453,6 @@ return PointG2.ZERO; | ||
const y0 = bytesToNumberBE(bytes.slice(3 * PUBLIC_KEY_LENGTH)); | ||
point = new PointG2(new math_js_1.Fp2([x0, x1]), new math_js_1.Fp2([y0, y1])); | ||
point = new PointG2(math_js_1.Fp2.fromBigTuple([x0, x1]), math_js_1.Fp2.fromBigTuple([y0, y1])); | ||
} | ||
else { | ||
throw new Error('Invalid uncompressed point G2, expected 192 bytes'); | ||
throw new Error('Invalid point G2, expected 96/192 bytes'); | ||
} | ||
@@ -445,3 +471,3 @@ point.assertValidity(); | ||
} | ||
const [[x0, x1], [y0, y1]] = this.toAffine().map((a) => a.values); | ||
const [{ re: x0, im: x1 }, { re: y0, im: y1 }] = this.toAffine().map((a) => a.reim()); | ||
const tmp = y1 > 0n ? y1 * 2n : y0 * 2n; | ||
@@ -459,3 +485,15 @@ const aflag1 = tmp / math_js_1.CURVE.P; | ||
if (isCompressed) { | ||
throw new Error('Point compression has not yet been implemented'); | ||
const { P } = math_js_1.CURVE; | ||
let x_1 = 0n; | ||
let x_0 = 0n; | ||
if (this.isZero()) { | ||
x_1 = POW_2_383 + POW_2_382; | ||
} | ||
else { | ||
const [x, y] = this.toAffine(); | ||
const flag = y.c1.value === 0n ? (y.c0.value * 2n) / P : (y.c1.value * 2n) / P ? 1n : 0n; | ||
x_1 = x.c1.value + flag * POW_2_381 + POW_2_383; | ||
x_0 = x.c0.value; | ||
} | ||
return toPaddedHex(x_1, PUBLIC_KEY_LENGTH) + toPaddedHex(x_0, PUBLIC_KEY_LENGTH); | ||
} | ||
@@ -466,3 +504,3 @@ else { | ||
} | ||
const [[x0, x1], [y0, y1]] = this.toAffine().map((a) => a.values); | ||
const [{ re: x0, im: x1 }, { re: y0, im: y1 }] = this.toAffine().map((a) => a.reim()); | ||
return (toPaddedHex(x1, PUBLIC_KEY_LENGTH) + | ||
@@ -507,3 +545,3 @@ toPaddedHex(x0, PUBLIC_KEY_LENGTH) + | ||
isOnCurve() { | ||
const b = new math_js_1.Fp2(math_js_1.CURVE.b2); | ||
const b = math_js_1.Fp2.fromBigTuple(math_js_1.CURVE.b2); | ||
const { x, y, z } = this; | ||
@@ -532,3 +570,3 @@ const left = y.pow(2n).multiply(z).subtract(x.pow(3n)); | ||
exports.PointG2 = PointG2; | ||
PointG2.BASE = new PointG2(new math_js_1.Fp2(math_js_1.CURVE.G2x), new math_js_1.Fp2(math_js_1.CURVE.G2y), math_js_1.Fp2.ONE); | ||
PointG2.BASE = new PointG2(math_js_1.Fp2.fromBigTuple(math_js_1.CURVE.G2x), math_js_1.Fp2.fromBigTuple(math_js_1.CURVE.G2y), math_js_1.Fp2.ONE); | ||
PointG2.ZERO = new PointG2(math_js_1.Fp2.ONE, math_js_1.Fp2.ONE, math_js_1.Fp2.ZERO); | ||
@@ -535,0 +573,0 @@ function pairing(P, Q, withFinalExponent = true) { |
@@ -49,3 +49,3 @@ export declare const CURVE: { | ||
export declare function mod(a: bigint, b: bigint): bigint; | ||
export declare function powMod(a: bigint, power: bigint, modulo: bigint): bigint; | ||
export declare function powMod(num: bigint, power: bigint, modulo: bigint): bigint; | ||
export declare class Fp implements Field<Fp> { | ||
@@ -92,23 +92,5 @@ static readonly ORDER: bigint; | ||
} | ||
declare abstract class FQP<TT extends { | ||
c: TTT; | ||
} & Field<TT>, CT extends Field<CT>, TTT extends CT[]> implements Field<TT> { | ||
abstract readonly c: CT[]; | ||
abstract init(c: TTT): TT; | ||
abstract multiply(rhs: TT | bigint): TT; | ||
abstract invert(): TT; | ||
abstract square(): TT; | ||
zip<T, RT extends T[]>(rhs: TT, mapper: (left: CT, right: CT) => T): RT; | ||
map<T, RT extends T[]>(callbackfn: (value: CT) => T): RT; | ||
isZero(): boolean; | ||
equals(rhs: TT): boolean; | ||
negate(): TT; | ||
add(rhs: TT): TT; | ||
subtract(rhs: TT): TT; | ||
conjugate(): TT; | ||
private one; | ||
pow(n: bigint): TT; | ||
div(rhs: TT | bigint): TT; | ||
} | ||
export declare class Fp2 extends FQP<Fp2, Fp, [Fp, Fp]> { | ||
export declare class Fp2 implements Field<Fp2> { | ||
readonly c0: Fp; | ||
readonly c1: Fp; | ||
static readonly ORDER: bigint; | ||
@@ -118,8 +100,18 @@ static readonly MAX_BITS: number; | ||
static readonly ONE: Fp2; | ||
readonly c: [Fp, Fp]; | ||
constructor(coeffs: [Fp, Fp] | [bigint, bigint] | bigint[]); | ||
init(tuple: [Fp, Fp]): Fp2; | ||
constructor(c0: Fp, c1: Fp); | ||
static fromBigTuple(tuple: BigintTuple | bigint[]): Fp2; | ||
one(): Fp2; | ||
isZero(): boolean; | ||
toString(): string; | ||
get values(): BigintTuple; | ||
reim(): { | ||
re: bigint; | ||
im: bigint; | ||
}; | ||
negate(): Fp2; | ||
equals(rhs: Fp2): boolean; | ||
add(rhs: Fp2): Fp2; | ||
subtract(rhs: Fp2): Fp2; | ||
multiply(rhs: Fp2 | bigint): Fp2; | ||
pow(n: bigint): Fp2; | ||
div(rhs: Fp2 | bigint): Fp2; | ||
mulByNonresidue(): Fp2; | ||
@@ -132,12 +124,21 @@ square(): Fp2; | ||
} | ||
export declare class Fp6 extends FQP<Fp6, Fp2, [Fp2, Fp2, Fp2]> { | ||
readonly c: [Fp2, Fp2, Fp2]; | ||
export declare class Fp6 implements Field<Fp6> { | ||
readonly c0: Fp2; | ||
readonly c1: Fp2; | ||
readonly c2: Fp2; | ||
static readonly ZERO: Fp6; | ||
static readonly ONE: Fp6; | ||
static fromTuple(t: BigintSix): Fp6; | ||
constructor(c: [Fp2, Fp2, Fp2]); | ||
init(triple: [Fp2, Fp2, Fp2]): Fp6; | ||
static fromBigSix(t: BigintSix): Fp6; | ||
constructor(c0: Fp2, c1: Fp2, c2: Fp2); | ||
fromTriple(triple: [Fp2, Fp2, Fp2]): Fp6; | ||
one(): Fp6; | ||
isZero(): boolean; | ||
negate(): Fp6; | ||
toString(): string; | ||
conjugate(): any; | ||
equals(rhs: Fp6): boolean; | ||
add(rhs: Fp6): Fp6; | ||
subtract(rhs: Fp6): Fp6; | ||
multiply(rhs: Fp6 | bigint): Fp6; | ||
pow(n: bigint): Fp6; | ||
div(rhs: Fp6 | bigint): Fp6; | ||
mulByNonresidue(): Fp6; | ||
@@ -151,11 +152,20 @@ multiplyBy1(b1: Fp2): Fp6; | ||
} | ||
export declare class Fp12 extends FQP<Fp12, Fp6, [Fp6, Fp6]> { | ||
readonly c: [Fp6, Fp6]; | ||
export declare class Fp12 implements Field<Fp12> { | ||
readonly c0: Fp6; | ||
readonly c1: Fp6; | ||
static readonly ZERO: Fp12; | ||
static readonly ONE: Fp12; | ||
static fromTuple(t: BigintTwelve): Fp12; | ||
constructor(c: [Fp6, Fp6]); | ||
init(c: [Fp6, Fp6]): Fp12; | ||
static fromBigTwelve(t: BigintTwelve): Fp12; | ||
constructor(c0: Fp6, c1: Fp6); | ||
fromTuple(c: [Fp6, Fp6]): Fp12; | ||
one(): Fp12; | ||
isZero(): boolean; | ||
toString(): string; | ||
negate(): Fp12; | ||
equals(rhs: Fp12): boolean; | ||
add(rhs: Fp12): Fp12; | ||
subtract(rhs: Fp12): Fp12; | ||
multiply(rhs: Fp12 | bigint): Fp12; | ||
pow(n: bigint): Fp12; | ||
div(rhs: Fp12 | bigint): Fp12; | ||
multiplyBy014(o0: Fp2, o1: Fp2, o4: Fp2): Fp12; | ||
@@ -165,2 +175,3 @@ multiplyByFp2(rhs: Fp2): Fp12; | ||
invert(): Fp12; | ||
conjugate(): Fp12; | ||
frobeniusMap(power: number): Fp12; | ||
@@ -167,0 +178,0 @@ private Fp4Square; |
551
lib/math.js
@@ -33,8 +33,12 @@ "use strict"; | ||
exports.mod = mod; | ||
function powMod(a, power, modulo) { | ||
function powMod(num, power, modulo) { | ||
if (modulo <= 0n || power < 0n) | ||
throw new Error('Expected power/modulo > 0'); | ||
if (modulo === 1n) | ||
return 0n; | ||
let res = 1n; | ||
while (power > 0n) { | ||
if (power & 1n) | ||
res = (res * a) % modulo; | ||
a = (a * a) % modulo; | ||
res = (res * num) % modulo; | ||
num = (num * num) % modulo; | ||
power >>= 1n; | ||
@@ -46,20 +50,17 @@ } | ||
function genInvertBatch(cls, nums) { | ||
const len = nums.length; | ||
const scratch = new Array(len); | ||
let acc = cls.ONE; | ||
for (let i = 0; i < len; i++) { | ||
if (nums[i].isZero()) | ||
continue; | ||
scratch[i] = acc; | ||
acc = acc.multiply(nums[i]); | ||
} | ||
acc = acc.invert(); | ||
for (let i = len - 1; i >= 0; i--) { | ||
if (nums[i].isZero()) | ||
continue; | ||
let tmp = acc.multiply(nums[i]); | ||
nums[i] = acc.multiply(scratch[i]); | ||
acc = tmp; | ||
} | ||
return nums; | ||
const tmp = new Array(nums.length); | ||
const lastMultiplied = nums.reduce((acc, num, i) => { | ||
if (num.isZero()) | ||
return acc; | ||
tmp[i] = acc; | ||
return acc.multiply(num); | ||
}, cls.ONE); | ||
const inverted = lastMultiplied.invert(); | ||
nums.reduceRight((acc, num, i) => { | ||
if (num.isZero()) | ||
return acc; | ||
tmp[i] = acc.multiply(tmp[i]); | ||
return acc.multiply(num); | ||
}, inverted); | ||
return tmp; | ||
} | ||
@@ -76,3 +77,5 @@ function bitLen(n) { | ||
function invert(number, modulo = exports.CURVE.P) { | ||
if (number === 0n || modulo <= 0n) { | ||
const _0n = 0n; | ||
const _1n = 1n; | ||
if (number === _0n || modulo <= _0n) { | ||
throw new Error(`invert: expected positive integers, got n=${number} mod=${modulo}`); | ||
@@ -82,4 +85,4 @@ } | ||
let b = modulo; | ||
let [x, y, u, v] = [0n, 1n, 1n, 0n]; | ||
while (a !== 0n) { | ||
let x = _0n, y = _1n, u = _1n, v = _0n; | ||
while (a !== _0n) { | ||
const q = b / a; | ||
@@ -89,8 +92,6 @@ const r = b % a; | ||
const n = y - v * q; | ||
[b, a] = [a, r]; | ||
[x, y] = [u, v]; | ||
[u, v] = [m, n]; | ||
b = a, a = r, x = u, y = v, u = m, v = n; | ||
} | ||
const gcd = b; | ||
if (gcd !== 1n) | ||
if (gcd !== _1n) | ||
throw new Error('invert: does not exist'); | ||
@@ -236,60 +237,74 @@ return mod(x, modulo); | ||
Fr.ONE = new Fr(1n); | ||
class FQP { | ||
zip(rhs, mapper) { | ||
const c0 = this.c; | ||
const c1 = rhs.c; | ||
const res = []; | ||
for (let i = 0; i < c0.length; i++) { | ||
res.push(mapper(c0[i], c1[i])); | ||
} | ||
return res; | ||
function powMod_FQP(fqp, fqpOne, n) { | ||
const elm = fqp; | ||
if (n === 0n) | ||
return fqpOne; | ||
if (n === 1n) | ||
return elm; | ||
let p = fqpOne; | ||
let d = elm; | ||
while (n > 0n) { | ||
if (n & 1n) | ||
p = p.multiply(d); | ||
n >>= 1n; | ||
d = d.square(); | ||
} | ||
map(callbackfn) { | ||
return this.c.map(callbackfn); | ||
return p; | ||
} | ||
class Fp2 { | ||
constructor(c0, c1) { | ||
this.c0 = c0; | ||
this.c1 = c1; | ||
if (typeof c0 === 'bigint') | ||
throw new Error('c0: Expected Fp'); | ||
if (typeof c1 === 'bigint') | ||
throw new Error('c1: Expected Fp'); | ||
} | ||
static fromBigTuple(tuple) { | ||
const fps = tuple.map(n => new Fp(n)); | ||
return new Fp2(...fps); | ||
} | ||
one() { | ||
return Fp2.ONE; | ||
} | ||
isZero() { | ||
return this.c.every((c) => c.isZero()); | ||
return this.c0.isZero() && this.c1.isZero(); | ||
} | ||
equals(rhs) { | ||
return this.zip(rhs, (left, right) => left.equals(right)).every((r) => r); | ||
toString() { | ||
return `Fp2(${this.c0} + ${this.c1}×i)`; | ||
} | ||
reim() { | ||
return { re: this.c0.value, im: this.c1.value }; | ||
} | ||
negate() { | ||
return this.init(this.map((c) => c.negate())); | ||
const { c0, c1 } = this; | ||
return new Fp2(c0.negate(), c1.negate()); | ||
} | ||
equals(rhs) { | ||
const { c0, c1 } = this; | ||
const { c0: r0, c1: r1 } = rhs; | ||
return c0.equals(r0) && c1.equals(r1); | ||
} | ||
add(rhs) { | ||
return this.init(this.zip(rhs, (left, right) => left.add(right))); | ||
const { c0, c1 } = this; | ||
const { c0: r0, c1: r1 } = rhs; | ||
return new Fp2(c0.add(r0), c1.add(r1)); | ||
} | ||
subtract(rhs) { | ||
return this.init(this.zip(rhs, (left, right) => left.subtract(right))); | ||
const { c0, c1 } = this; | ||
const { c0: r0, c1: r1 } = rhs; | ||
return new Fp2(c0.subtract(r0), c1.subtract(r1)); | ||
} | ||
conjugate() { | ||
return this.init([this.c[0], this.c[1].negate()]); | ||
multiply(rhs) { | ||
const { c0, c1 } = this; | ||
if (typeof rhs === 'bigint') { | ||
return new Fp2(c0.multiply(rhs), c1.multiply(rhs)); | ||
} | ||
const { c0: r0, c1: r1 } = rhs; | ||
let t1 = c0.multiply(r0); | ||
let t2 = c1.multiply(r1); | ||
return new Fp2(t1.subtract(t2), c0.add(c1).multiply(r0.add(r1)).subtract(t1.add(t2))); | ||
} | ||
one() { | ||
const el = this; | ||
let one; | ||
if (el instanceof Fp2) | ||
one = Fp2.ONE; | ||
if (el instanceof Fp6) | ||
one = Fp6.ONE; | ||
if (el instanceof Fp12) | ||
one = Fp12.ONE; | ||
return one; | ||
} | ||
pow(n) { | ||
const elm = this; | ||
const one = this.one(); | ||
if (n === 0n) | ||
return one; | ||
if (n === 1n) | ||
return elm; | ||
let p = one; | ||
let d = elm; | ||
while (n > 0n) { | ||
if (n & 1n) | ||
p = p.multiply(d); | ||
n >>= 1n; | ||
d = d.square(); | ||
} | ||
return p; | ||
return powMod_FQP(this, Fp2.ONE, n); | ||
} | ||
@@ -300,44 +315,14 @@ div(rhs) { | ||
} | ||
} | ||
class Fp2 extends FQP { | ||
constructor(coeffs) { | ||
super(); | ||
if (coeffs.length !== 2) | ||
throw new Error(`Expected array with 2 elements`); | ||
coeffs.forEach((c, i) => { | ||
if (typeof c === 'bigint') | ||
coeffs[i] = new Fp(c); | ||
}); | ||
this.c = coeffs; | ||
} | ||
init(tuple) { | ||
return new Fp2(tuple); | ||
} | ||
toString() { | ||
return `Fp2(${this.c[0]} + ${this.c[1]}×i)`; | ||
} | ||
get values() { | ||
return this.c.map((c) => c.value); | ||
} | ||
multiply(rhs) { | ||
if (typeof rhs === 'bigint') | ||
return new Fp2(this.map((c) => c.multiply(rhs))); | ||
const [c0, c1] = this.c; | ||
const [r0, r1] = rhs.c; | ||
let t1 = c0.multiply(r0); | ||
let t2 = c1.multiply(r1); | ||
return new Fp2([t1.subtract(t2), c0.add(c1).multiply(r0.add(r1)).subtract(t1.add(t2))]); | ||
} | ||
mulByNonresidue() { | ||
const c0 = this.c[0]; | ||
const c1 = this.c[1]; | ||
return new Fp2([c0.subtract(c1), c0.add(c1)]); | ||
const c0 = this.c0; | ||
const c1 = this.c1; | ||
return new Fp2(c0.subtract(c1), c0.add(c1)); | ||
} | ||
square() { | ||
const c0 = this.c[0]; | ||
const c1 = this.c[1]; | ||
const c0 = this.c0; | ||
const c1 = this.c1; | ||
const a = c0.add(c1); | ||
const b = c0.subtract(c1); | ||
const c = c0.add(c0); | ||
return new Fp2([a.multiply(b), c.multiply(c1)]); | ||
return new Fp2(a.multiply(b), c.multiply(c1)); | ||
} | ||
@@ -357,4 +342,4 @@ sqrt() { | ||
const x2 = x1.negate(); | ||
const [re1, im1] = x1.values; | ||
const [re2, im2] = x2.values; | ||
const { re: re1, im: im1 } = x1.reim(); | ||
const { re: re2, im: im2 } = x2.reim(); | ||
if (im1 > im2 || (im1 === im2 && re1 > re2)) | ||
@@ -365,14 +350,15 @@ return x1; | ||
invert() { | ||
const [a, b] = this.values; | ||
const { re: a, im: b } = this.reim(); | ||
const factor = new Fp(a * a + b * b).invert(); | ||
return new Fp2([factor.multiply(new Fp(a)), factor.multiply(new Fp(-b))]); | ||
return new Fp2(factor.multiply(new Fp(a)), factor.multiply(new Fp(-b))); | ||
} | ||
frobeniusMap(power) { | ||
return new Fp2([this.c[0], this.c[1].multiply(FP2_FROBENIUS_COEFFICIENTS[power % 2])]); | ||
return new Fp2(this.c0, this.c1.multiply(FP2_FROBENIUS_COEFFICIENTS[power % 2])); | ||
} | ||
multiplyByB() { | ||
let [c0, c1] = this.c; | ||
let c0 = this.c0; | ||
let c1 = this.c1; | ||
let t0 = c0.multiply(4n); | ||
let t1 = c1.multiply(4n); | ||
return new Fp2([t0.subtract(t1), t0.add(t1)]); | ||
return new Fp2(t0.subtract(t1), t0.add(t1)); | ||
} | ||
@@ -383,64 +369,83 @@ } | ||
Fp2.MAX_BITS = bitLen(exports.CURVE.P2); | ||
Fp2.ZERO = new Fp2([0n, 0n]); | ||
Fp2.ONE = new Fp2([1n, 0n]); | ||
class Fp6 extends FQP { | ||
constructor(c) { | ||
super(); | ||
this.c = c; | ||
if (c.length !== 3) | ||
throw new Error(`Expected array with 3 elements`); | ||
Fp2.ZERO = new Fp2(Fp.ZERO, Fp.ZERO); | ||
Fp2.ONE = new Fp2(Fp.ONE, Fp.ZERO); | ||
class Fp6 { | ||
constructor(c0, c1, c2) { | ||
this.c0 = c0; | ||
this.c1 = c1; | ||
this.c2 = c2; | ||
} | ||
static fromTuple(t) { | ||
static fromBigSix(t) { | ||
if (!Array.isArray(t) || t.length !== 6) | ||
throw new Error('Invalid Fp6 usage'); | ||
return new Fp6([new Fp2(t.slice(0, 2)), new Fp2(t.slice(2, 4)), new Fp2(t.slice(4, 6))]); | ||
const c = [t.slice(0, 2), t.slice(2, 4), t.slice(4, 6)].map(t => Fp2.fromBigTuple(t)); | ||
return new Fp6(...c); | ||
} | ||
init(triple) { | ||
return new Fp6(triple); | ||
fromTriple(triple) { | ||
return new Fp6(...triple); | ||
} | ||
one() { | ||
return Fp6.ONE; | ||
} | ||
isZero() { | ||
return this.c0.isZero() && this.c1.isZero() && this.c2.isZero(); | ||
} | ||
negate() { | ||
const { c0, c1, c2 } = this; | ||
return new Fp6(c0.negate(), c1.negate(), c2.negate()); | ||
} | ||
toString() { | ||
return `Fp6(${this.c[0]} + ${this.c[1]} * v, ${this.c[2]} * v^2)`; | ||
return `Fp6(${this.c0} + ${this.c1} * v, ${this.c2} * v^2)`; | ||
} | ||
conjugate() { | ||
throw new TypeError('No conjugate on Fp6'); | ||
equals(rhs) { | ||
const { c0, c1, c2 } = this; | ||
const { c0: r0, c1: r1, c2: r2 } = rhs; | ||
return c0.equals(r0) && c1.equals(r1) && c2.equals(r2); | ||
} | ||
add(rhs) { | ||
const { c0, c1, c2 } = this; | ||
const { c0: r0, c1: r1, c2: r2 } = rhs; | ||
return new Fp6(c0.add(r0), c1.add(r1), c2.add(r2)); | ||
} | ||
subtract(rhs) { | ||
const { c0, c1, c2 } = this; | ||
const { c0: r0, c1: r1, c2: r2 } = rhs; | ||
return new Fp6(c0.subtract(r0), c1.subtract(r1), c2.subtract(r2)); | ||
} | ||
multiply(rhs) { | ||
if (typeof rhs === 'bigint') | ||
return new Fp6([this.c[0].multiply(rhs), this.c[1].multiply(rhs), this.c[2].multiply(rhs)]); | ||
let [c0, c1, c2] = this.c; | ||
const [r0, r1, r2] = rhs.c; | ||
if (typeof rhs === 'bigint') { | ||
return new Fp6(this.c0.multiply(rhs), this.c1.multiply(rhs), this.c2.multiply(rhs)); | ||
} | ||
let { c0, c1, c2 } = this; | ||
let { c0: r0, c1: r1, c2: r2 } = rhs; | ||
let t0 = c0.multiply(r0); | ||
let t1 = c1.multiply(r1); | ||
let t2 = c2.multiply(r2); | ||
return new Fp6([ | ||
t0.add(c1.add(c2).multiply(r1.add(r2)).subtract(t1.add(t2)).mulByNonresidue()), | ||
c0.add(c1).multiply(r0.add(r1)).subtract(t0.add(t1)).add(t2.mulByNonresidue()), | ||
t1.add(c0.add(c2).multiply(r0.add(r2)).subtract(t0.add(t2))), | ||
]); | ||
return new Fp6(t0.add(c1.add(c2).multiply(r1.add(r2)).subtract(t1.add(t2)).mulByNonresidue()), c0.add(c1).multiply(r0.add(r1)).subtract(t0.add(t1)).add(t2.mulByNonresidue()), t1.add(c0.add(c2).multiply(r0.add(r2)).subtract(t0.add(t2)))); | ||
} | ||
pow(n) { | ||
return powMod_FQP(this, Fp6.ONE, n); | ||
} | ||
div(rhs) { | ||
const inv = typeof rhs === 'bigint' ? new Fp(rhs).invert().value : rhs.invert(); | ||
return this.multiply(inv); | ||
} | ||
mulByNonresidue() { | ||
return new Fp6([this.c[2].mulByNonresidue(), this.c[0], this.c[1]]); | ||
return new Fp6(this.c2.mulByNonresidue(), this.c0, this.c1); | ||
} | ||
multiplyBy1(b1) { | ||
return new Fp6([ | ||
this.c[2].multiply(b1).mulByNonresidue(), | ||
this.c[0].multiply(b1), | ||
this.c[1].multiply(b1), | ||
]); | ||
return new Fp6(this.c2.multiply(b1).mulByNonresidue(), this.c0.multiply(b1), this.c1.multiply(b1)); | ||
} | ||
multiplyBy01(b0, b1) { | ||
let [c0, c1, c2] = this.c; | ||
let { c0, c1, c2 } = this; | ||
let t0 = c0.multiply(b0); | ||
let t1 = c1.multiply(b1); | ||
return new Fp6([ | ||
c1.add(c2).multiply(b1).subtract(t1).mulByNonresidue().add(t0), | ||
b0.add(b1).multiply(c0.add(c1)).subtract(t0).subtract(t1), | ||
c0.add(c2).multiply(b0).subtract(t0).add(t1), | ||
]); | ||
return new Fp6(c1.add(c2).multiply(b1).subtract(t1).mulByNonresidue().add(t0), b0.add(b1).multiply(c0.add(c1)).subtract(t0).subtract(t1), c0.add(c2).multiply(b0).subtract(t0).add(t1)); | ||
} | ||
multiplyByFp2(rhs) { | ||
return new Fp6(this.map((c) => c.multiply(rhs))); | ||
let { c0, c1, c2 } = this; | ||
return new Fp6(c0.multiply(rhs), c1.multiply(rhs), c2.multiply(rhs)); | ||
} | ||
square() { | ||
let [c0, c1, c2] = this.c; | ||
let { c0, c1, c2 } = this; | ||
let t0 = c0.square(); | ||
@@ -450,10 +455,6 @@ let t1 = c0.multiply(c1).multiply(2n); | ||
let t4 = c2.square(); | ||
return new Fp6([ | ||
t3.mulByNonresidue().add(t0), | ||
t4.mulByNonresidue().add(t1), | ||
t1.add(c0.subtract(c1).add(c2).square()).add(t3).subtract(t0).subtract(t4), | ||
]); | ||
return new Fp6(t3.mulByNonresidue().add(t0), t4.mulByNonresidue().add(t1), t1.add(c0.subtract(c1).add(c2).square()).add(t3).subtract(t0).subtract(t4)); | ||
} | ||
invert() { | ||
let [c0, c1, c2] = this.c; | ||
let { c0, c1, c2 } = this; | ||
let t0 = c0.square().subtract(c2.multiply(c1).mulByNonresidue()); | ||
@@ -463,107 +464,110 @@ let t1 = c2.square().mulByNonresidue().subtract(c0.multiply(c1)); | ||
let t4 = c2.multiply(t1).add(c1.multiply(t2)).mulByNonresidue().add(c0.multiply(t0)).invert(); | ||
return new Fp6([t4.multiply(t0), t4.multiply(t1), t4.multiply(t2)]); | ||
return new Fp6(t4.multiply(t0), t4.multiply(t1), t4.multiply(t2)); | ||
} | ||
frobeniusMap(power) { | ||
return new Fp6([ | ||
this.c[0].frobeniusMap(power), | ||
this.c[1].frobeniusMap(power).multiply(FP6_FROBENIUS_COEFFICIENTS_1[power % 6]), | ||
this.c[2].frobeniusMap(power).multiply(FP6_FROBENIUS_COEFFICIENTS_2[power % 6]), | ||
]); | ||
return new Fp6(this.c0.frobeniusMap(power), this.c1.frobeniusMap(power).multiply(FP6_FROBENIUS_COEFFICIENTS_1[power % 6]), this.c2.frobeniusMap(power).multiply(FP6_FROBENIUS_COEFFICIENTS_2[power % 6])); | ||
} | ||
} | ||
exports.Fp6 = Fp6; | ||
Fp6.ZERO = new Fp6([Fp2.ZERO, Fp2.ZERO, Fp2.ZERO]); | ||
Fp6.ONE = new Fp6([Fp2.ONE, Fp2.ZERO, Fp2.ZERO]); | ||
class Fp12 extends FQP { | ||
constructor(c) { | ||
super(); | ||
this.c = c; | ||
if (c.length !== 2) | ||
throw new Error(`Expected array with 2 elements`); | ||
Fp6.ZERO = new Fp6(Fp2.ZERO, Fp2.ZERO, Fp2.ZERO); | ||
Fp6.ONE = new Fp6(Fp2.ONE, Fp2.ZERO, Fp2.ZERO); | ||
class Fp12 { | ||
constructor(c0, c1) { | ||
this.c0 = c0; | ||
this.c1 = c1; | ||
} | ||
static fromTuple(t) { | ||
return new Fp12([ | ||
Fp6.fromTuple(t.slice(0, 6)), | ||
Fp6.fromTuple(t.slice(6, 12)), | ||
]); | ||
static fromBigTwelve(t) { | ||
return new Fp12(Fp6.fromBigSix(t.slice(0, 6)), Fp6.fromBigSix(t.slice(6, 12))); | ||
} | ||
init(c) { | ||
return new Fp12(c); | ||
fromTuple(c) { | ||
return new Fp12(...c); | ||
} | ||
one() { | ||
return Fp12.ONE; | ||
} | ||
isZero() { | ||
return this.c0.isZero() && this.c1.isZero(); | ||
} | ||
toString() { | ||
return `Fp12(${this.c[0]} + ${this.c[1]} * w)`; | ||
return `Fp12(${this.c0} + ${this.c1} * w)`; | ||
} | ||
negate() { | ||
const { c0, c1 } = this; | ||
return new Fp12(c0.negate(), c1.negate()); | ||
} | ||
equals(rhs) { | ||
const { c0, c1 } = this; | ||
const { c0: r0, c1: r1 } = rhs; | ||
return c0.equals(r0) && c1.equals(r1); | ||
} | ||
add(rhs) { | ||
const { c0, c1 } = this; | ||
const { c0: r0, c1: r1 } = rhs; | ||
return new Fp12(c0.add(r0), c1.add(r1)); | ||
} | ||
subtract(rhs) { | ||
const { c0, c1 } = this; | ||
const { c0: r0, c1: r1 } = rhs; | ||
return new Fp12(c0.subtract(r0), c1.subtract(r1)); | ||
} | ||
multiply(rhs) { | ||
if (typeof rhs === 'bigint') | ||
return new Fp12([this.c[0].multiply(rhs), this.c[1].multiply(rhs)]); | ||
let [c0, c1] = this.c; | ||
const [r0, r1] = rhs.c; | ||
return new Fp12(this.c0.multiply(rhs), this.c1.multiply(rhs)); | ||
let { c0, c1 } = this; | ||
let { c0: r0, c1: r1 } = rhs; | ||
let t1 = c0.multiply(r0); | ||
let t2 = c1.multiply(r1); | ||
return new Fp12([ | ||
t1.add(t2.mulByNonresidue()), | ||
c0.add(c1).multiply(r0.add(r1)).subtract(t1.add(t2)), | ||
]); | ||
return new Fp12(t1.add(t2.mulByNonresidue()), c0.add(c1).multiply(r0.add(r1)).subtract(t1.add(t2))); | ||
} | ||
pow(n) { | ||
return powMod_FQP(this, Fp12.ONE, n); | ||
} | ||
div(rhs) { | ||
const inv = typeof rhs === 'bigint' ? new Fp(rhs).invert().value : rhs.invert(); | ||
return this.multiply(inv); | ||
} | ||
multiplyBy014(o0, o1, o4) { | ||
let [c0, c1] = this.c; | ||
let [t0, t1] = [c0.multiplyBy01(o0, o1), c1.multiplyBy1(o4)]; | ||
return new Fp12([ | ||
t1.mulByNonresidue().add(t0), | ||
c1.add(c0).multiplyBy01(o0, o1.add(o4)).subtract(t0).subtract(t1), | ||
]); | ||
let { c0, c1 } = this; | ||
let t0 = c0.multiplyBy01(o0, o1); | ||
let t1 = c1.multiplyBy1(o4); | ||
return new Fp12(t1.mulByNonresidue().add(t0), c1.add(c0).multiplyBy01(o0, o1.add(o4)).subtract(t0).subtract(t1)); | ||
} | ||
multiplyByFp2(rhs) { | ||
return this.init(this.map((c) => c.multiplyByFp2(rhs))); | ||
return new Fp12(this.c0.multiplyByFp2(rhs), this.c1.multiplyByFp2(rhs)); | ||
} | ||
square() { | ||
let [c0, c1] = this.c; | ||
let { c0, c1 } = this; | ||
let ab = c0.multiply(c1); | ||
return new Fp12([ | ||
c1.mulByNonresidue().add(c0).multiply(c0.add(c1)).subtract(ab).subtract(ab.mulByNonresidue()), | ||
ab.add(ab), | ||
]); | ||
return new Fp12(c1.mulByNonresidue().add(c0).multiply(c0.add(c1)).subtract(ab).subtract(ab.mulByNonresidue()), ab.add(ab)); | ||
} | ||
invert() { | ||
let [c0, c1] = this.c; | ||
let { c0, c1 } = this; | ||
let t = c0.square().subtract(c1.square().mulByNonresidue()).invert(); | ||
return new Fp12([c0.multiply(t), c1.multiply(t).negate()]); | ||
return new Fp12(c0.multiply(t), c1.multiply(t).negate()); | ||
} | ||
conjugate() { | ||
return new Fp12(this.c0, this.c1.negate()); | ||
} | ||
frobeniusMap(power) { | ||
const [c0, c1] = this.c; | ||
let r0 = c0.frobeniusMap(power); | ||
let [c1_0, c1_1, c1_2] = c1.frobeniusMap(power).c; | ||
const r0 = this.c0.frobeniusMap(power); | ||
const { c0, c1, c2 } = this.c1.frobeniusMap(power); | ||
const coeff = FP12_FROBENIUS_COEFFICIENTS[power % 12]; | ||
return new Fp12([ | ||
r0, | ||
new Fp6([c1_0.multiply(coeff), c1_1.multiply(coeff), c1_2.multiply(coeff)]), | ||
]); | ||
return new Fp12(r0, new Fp6(c0.multiply(coeff), c1.multiply(coeff), c2.multiply(coeff))); | ||
} | ||
Fp4Square(a, b) { | ||
const a2 = a.square(), b2 = b.square(); | ||
return [ | ||
b2.mulByNonresidue().add(a2), | ||
a.add(b).square().subtract(a2).subtract(b2), | ||
]; | ||
const a2 = a.square(); | ||
const b2 = b.square(); | ||
return { | ||
first: b2.mulByNonresidue().add(a2), | ||
second: a.add(b).square().subtract(a2).subtract(b2), | ||
}; | ||
} | ||
cyclotomicSquare() { | ||
const [c0, c1] = this.c; | ||
const [c0c0, c0c1, c0c2] = c0.c; | ||
const [c1c0, c1c1, c1c2] = c1.c; | ||
let [t3, t4] = this.Fp4Square(c0c0, c1c1); | ||
let [t5, t6] = this.Fp4Square(c1c0, c0c2); | ||
let [t7, t8] = this.Fp4Square(c0c1, c1c2); | ||
const { c0: c0c0, c1: c0c1, c2: c0c2 } = this.c0; | ||
const { c0: c1c0, c1: c1c1, c2: c1c2 } = this.c1; | ||
const { first: t3, second: t4 } = this.Fp4Square(c0c0, c1c1); | ||
const { first: t5, second: t6 } = this.Fp4Square(c1c0, c0c2); | ||
const { first: t7, second: t8 } = this.Fp4Square(c0c1, c1c2); | ||
let t9 = t8.mulByNonresidue(); | ||
return new Fp12([ | ||
new Fp6([ | ||
t3.subtract(c0c0).multiply(2n).add(t3), | ||
t5.subtract(c0c1).multiply(2n).add(t5), | ||
t7.subtract(c0c2).multiply(2n).add(t7), | ||
]), | ||
new Fp6([ | ||
t9.add(c1c0).multiply(2n).add(t9), | ||
t4.add(c1c1).multiply(2n).add(t4), | ||
t6.add(c1c2).multiply(2n).add(t6), | ||
]), | ||
]); | ||
return new Fp12(new Fp6(t3.subtract(c0c0).multiply(2n).add(t3), t5.subtract(c0c1).multiply(2n).add(t5), t7.subtract(c0c2).multiply(2n).add(t7)), new Fp6(t9.add(c1c0).multiply(2n).add(t9), t4.add(c1c1).multiply(2n).add(t4), t6.add(c1c2).multiply(2n).add(t6))); | ||
} | ||
@@ -597,4 +601,4 @@ cyclotomicExp(n) { | ||
exports.Fp12 = Fp12; | ||
Fp12.ZERO = new Fp12([Fp6.ZERO, Fp6.ZERO]); | ||
Fp12.ONE = new Fp12([Fp6.ONE, Fp6.ZERO]); | ||
Fp12.ZERO = new Fp12(Fp6.ZERO, Fp6.ZERO); | ||
Fp12.ONE = new Fp12(Fp6.ONE, Fp6.ZERO); | ||
class ProjectivePoint { | ||
@@ -629,2 +633,5 @@ constructor(x, y, z, C) { | ||
toString(isAffine = true) { | ||
if (this.isZero()) { | ||
return `Point<Zero>`; | ||
} | ||
if (!isAffine) { | ||
@@ -640,2 +647,4 @@ return `Point<x=${this.x}, y=${this.y}, z=${this.z}>`; | ||
toAffine(invZ = this.z.invert()) { | ||
if (invZ.isZero()) | ||
throw new Error('Invalid inverted z'); | ||
return [this.x.multiply(invZ), this.y.multiply(invZ)]; | ||
@@ -779,3 +788,4 @@ } | ||
} | ||
let [p, f] = [this.getZero(), this.getZero()]; | ||
let p = this.getZero(); | ||
let f = this.getZero(); | ||
const windows = Math.ceil(this.maxBits() / W); | ||
@@ -810,3 +820,3 @@ const windowSize = 2 ** (W - 1); | ||
function sgn0(x) { | ||
const [x0, x1] = x.values; | ||
const { re: x0, im: x1 } = x.reim(); | ||
const sign_0 = x0 % 2n; | ||
@@ -826,3 +836,3 @@ const zero_0 = x0 === 0n; | ||
const positiveRootsOfUnity = FP2_ROOTS_OF_UNITY.slice(0, 4); | ||
for (const root of positiveRootsOfUnity) { | ||
positiveRootsOfUnity.forEach((root) => { | ||
const candidate = root.multiply(gamma); | ||
@@ -833,11 +843,11 @@ if (candidate.pow(2n).multiply(v).subtract(u).isZero() && !success) { | ||
} | ||
} | ||
return [success, result]; | ||
}); | ||
return { success, sqrtCandidateOrGamma: result }; | ||
} | ||
function map_to_curve_simple_swu_9mod16(t) { | ||
const iso_3_a = new Fp2([0n, 240n]); | ||
const iso_3_b = new Fp2([1012n, 1012n]); | ||
const iso_3_z = new Fp2([-2n, -1n]); | ||
const iso_3_a = new Fp2(new Fp(0n), new Fp(240n)); | ||
const iso_3_b = new Fp2(new Fp(1012n), new Fp(1012n)); | ||
const iso_3_z = new Fp2(new Fp(-2n), new Fp(-1n)); | ||
if (Array.isArray(t)) | ||
t = new Fp2(t); | ||
t = Fp2.fromBigTuple(t); | ||
const t2 = t.pow(2n); | ||
@@ -855,3 +865,3 @@ const iso_3_z_t2 = iso_3_z.multiply(t2); | ||
.add(iso_3_b.multiply(v)); | ||
const [success, sqrtCandidateOrGamma] = sqrt_div_fp2(u, v); | ||
const { success, sqrtCandidateOrGamma } = sqrt_div_fp2(u, v); | ||
let y; | ||
@@ -863,3 +873,3 @@ if (success) | ||
let success2 = false; | ||
for (const eta of FP2_ETAs) { | ||
FP2_ETAs.forEach((eta) => { | ||
const etaSqrtCandidate = eta.multiply(sqrtCandidateX1); | ||
@@ -871,3 +881,3 @@ const temp = etaSqrtCandidate.pow(2n).multiply(v).subtract(u); | ||
} | ||
} | ||
}); | ||
if (!success && !success2) | ||
@@ -885,3 +895,3 @@ throw new Error('Hash to Curve - Optimized SWU failure'); | ||
function isogenyMapG2(xyz) { | ||
const [x, y, z] = xyz; | ||
const x = xyz[0], y = xyz[1], z = xyz[2]; | ||
const zz = z.multiply(z); | ||
@@ -909,4 +919,4 @@ const zzz = zz.multiply(z); | ||
function calcPairingPrecomputes(x, y) { | ||
const [Qx, Qy, Qz] = [x, y, Fp2.ONE]; | ||
let [Rx, Ry, Rz] = [Qx, Qy, Qz]; | ||
const Qx = x, Qy = y, Qz = Fp2.ONE; | ||
let Rx = Qx, Ry = Qy, Rz = Qz; | ||
let ell_coeff = []; | ||
@@ -948,10 +958,12 @@ for (let i = BLS_X_LEN - 2; i >= 0; i--) { | ||
function millerLoop(ell, g1) { | ||
const Px = g1[0].value; | ||
const Py = g1[1].value; | ||
let f12 = Fp12.ONE; | ||
const [x, y] = g1; | ||
const [Px, Py] = [x, y]; | ||
for (let j = 0, i = BLS_X_LEN - 2; i >= 0; i--, j++) { | ||
f12 = f12.multiplyBy014(ell[j][0], ell[j][1].multiply(Px.value), ell[j][2].multiply(Py.value)); | ||
const E = ell[j]; | ||
f12 = f12.multiplyBy014(E[0], E[1].multiply(Px), E[2].multiply(Py)); | ||
if (bitGet(exports.CURVE.x, i)) { | ||
j += 1; | ||
f12 = f12.multiplyBy014(ell[j][0], ell[j][1].multiply(Px.value), ell[j][2].multiply(Py.value)); | ||
const F = ell[j]; | ||
f12 = f12.multiplyBy014(F[0], F[1].multiply(Px), F[2].multiply(Py)); | ||
} | ||
@@ -964,10 +976,9 @@ if (i !== 0) | ||
exports.millerLoop = millerLoop; | ||
const ut_root = new Fp6([Fp2.ZERO, Fp2.ONE, Fp2.ZERO]); | ||
const wsq = new Fp12([ut_root, Fp6.ZERO]); | ||
const wsq_inv = wsq.invert(); | ||
const wcu = new Fp12([Fp6.ZERO, ut_root]); | ||
const wcu_inv = wcu.invert(); | ||
const ut_root = new Fp6(Fp2.ZERO, Fp2.ONE, Fp2.ZERO); | ||
const wsq = new Fp12(ut_root, Fp6.ZERO); | ||
const wcu = new Fp12(Fp6.ZERO, ut_root); | ||
const [wsq_inv, wcu_inv] = genInvertBatch(Fp12, [wsq, wcu]); | ||
function psi(x, y) { | ||
const x2 = wsq_inv.multiplyByFp2(x).frobeniusMap(1).multiply(wsq).c[0].c[0]; | ||
const y2 = wcu_inv.multiplyByFp2(y).frobeniusMap(1).multiply(wcu).c[0].c[0]; | ||
const x2 = wsq_inv.multiplyByFp2(x).frobeniusMap(1).multiply(wsq).c0.c0; | ||
const y2 = wcu_inv.multiplyByFp2(y).frobeniusMap(1).multiply(wcu).c0.c0; | ||
return [x2, y2]; | ||
@@ -999,3 +1010,3 @@ } | ||
[-rv1, -rv1], | ||
].map((pair) => new Fp2(pair)); | ||
].map((pair) => Fp2.fromBigTuple(pair)); | ||
const FP2_ETAs = [ | ||
@@ -1006,3 +1017,3 @@ [ev1, ev2], | ||
[-ev4, ev3], | ||
].map((pair) => new Fp2(pair)); | ||
].map((pair) => Fp2.fromBigTuple(pair)); | ||
const FP6_FROBENIUS_COEFFICIENTS_1 = [ | ||
@@ -1027,3 +1038,3 @@ [0x1n, 0x0n], | ||
], | ||
].map((pair) => new Fp2(pair)); | ||
].map((pair) => Fp2.fromBigTuple(pair)); | ||
const FP6_FROBENIUS_COEFFICIENTS_2 = [ | ||
@@ -1051,3 +1062,3 @@ [0x1n, 0x0n], | ||
], | ||
].map((pair) => new Fp2(pair)); | ||
].map((pair) => Fp2.fromBigTuple(pair)); | ||
const FP12_FROBENIUS_COEFFICIENTS = [ | ||
@@ -1099,3 +1110,3 @@ [0x1n, 0x0n], | ||
], | ||
].map((pair) => new Fp2(pair)); | ||
].map(n => Fp2.fromBigTuple(n)); | ||
const xnum = [ | ||
@@ -1118,3 +1129,3 @@ [ | ||
], | ||
].map((pair) => new Fp2(pair)); | ||
].map((pair) => Fp2.fromBigTuple(pair)); | ||
const xden = [ | ||
@@ -1131,3 +1142,3 @@ [ | ||
[0x0n, 0x0n], | ||
].map((pair) => new Fp2(pair)); | ||
].map((pair) => Fp2.fromBigTuple(pair)); | ||
const ynum = [ | ||
@@ -1150,3 +1161,3 @@ [ | ||
], | ||
].map((pair) => new Fp2(pair)); | ||
].map((pair) => Fp2.fromBigTuple(pair)); | ||
const yden = [ | ||
@@ -1166,3 +1177,3 @@ [ | ||
[0x1n, 0x0n], | ||
].map((pair) => new Fp2(pair)); | ||
].map((pair) => Fp2.fromBigTuple(pair)); | ||
const ISOGENY_COEFFICIENTS = [xnum, xden, ynum, yden]; |
{ | ||
"name": "@noble/bls12-381", | ||
"version": "1.2.0", | ||
"version": "1.3.0", | ||
"description": "Fastest JS implementation of BLS12-381. Auditable, secure, 0-dependency aggregated signatures & pairings", | ||
@@ -14,3 +14,3 @@ "files": [ | ||
"build": "tsc -d && tsc -p tsconfig.esm.json", | ||
"build-release": "rollup -c rollup.config.js", | ||
"build:release": "rollup -c rollup.config.js", | ||
"bench": "node test/benchmark.js", | ||
@@ -30,13 +30,13 @@ "lint": "prettier --print-width 100 --single-quote --check index.ts" | ||
"devDependencies": { | ||
"@rollup/plugin-commonjs": "^19", | ||
"@rollup/plugin-node-resolve": "^13", | ||
"@types/jest": "^27", | ||
"@types/node": "^16.9.2", | ||
"fast-check": "^2.17", | ||
"jest": "^27.0.5", | ||
"micro-bmark": "^0.1.3", | ||
"prettier": "^2.3.2", | ||
"rollup": "^2.52.2", | ||
"ts-jest": "^27", | ||
"typescript": "4.5.4" | ||
"@rollup/plugin-commonjs": "22.0.0", | ||
"@rollup/plugin-node-resolve": "13.3.0", | ||
"@types/jest": "28.1.1", | ||
"@types/node": "18.0.0", | ||
"fast-check": "3.0.0", | ||
"jest": "28.1.0", | ||
"micro-bmark": "0.1.3", | ||
"prettier": "2.6.2", | ||
"rollup": "2.75.5", | ||
"ts-jest": "28.0.4", | ||
"typescript": "4.7.3" | ||
}, | ||
@@ -61,12 +61,18 @@ "keywords": [ | ||
"./math": { | ||
"types": "./lib/math.d.ts", | ||
"import": "./lib/esm/math.js", | ||
"default": "./lib/math.js" | ||
}, | ||
"./math.d.ts": "./lib/math.d.ts", | ||
".": { | ||
"types": "./lib/index.d.ts", | ||
"import": "./lib/esm/index.js", | ||
"default": "./lib/index.js" | ||
}, | ||
"./index.d.ts": "./lib/index.d.ts" | ||
} | ||
} | ||
}, | ||
"funding": [ | ||
{ | ||
"type": "individual", | ||
"url": "https://paulmillr.com/funding/" | ||
} | ||
] | ||
} |
214
README.md
@@ -12,6 +12,5 @@ # noble-bls12-381 ![Node CI](https://github.com/paulmillr/noble-secp256k1/workflows/Node%20CI/badge.svg) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier) | ||
Compatible with Algorand, Chia, Dfinity, Ethereum, FIL, Zcash. Matches specs [pairing-curves-10](https://tools.ietf.org/html/draft-irtf-cfrg-pairing-friendly-curves-10), [bls-sigs-04](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04), [hash-to-curve-12](https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-12). | ||
Compatible with Algorand, Chia, Dfinity, Ethereum, FIL, Zcash. Matches specs [pairing-curves-10](https://tools.ietf.org/html/draft-irtf-cfrg-pairing-friendly-curves-10), [bls-sigs-04](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04), [hash-to-curve-12](https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-12). To learn more about internals, navigate to | ||
[utilities](#utilities) section. | ||
To learn more about internals, check out [BLS12-381 for the rest of us](https://hackmd.io/@benjaminion/bls12-381) & [key concepts of pairings](https://medium.com/@alonmuroch_65570/bls-signatures-part-2-key-concepts-of-pairings-27a8a9533d0c). To try it live, see [the online demo](https://paulmillr.com/ecc) & [threshold sigs demo](https://genthresh.com). | ||
### This library belongs to *noble* crypto | ||
@@ -41,33 +40,26 @@ | ||
const bls = require('@noble/bls12-381'); | ||
// if you're using single file, use global variable nobleBls12381 | ||
// If you're using single file, use global variable instead: `window.nobleBls12381` | ||
// You can use Uint8Array, or hex string for readability | ||
const privateKey = '67d53f170b908cabb9eb326c3c337762d59289a8fec79f7bc9254b584b73265c'; | ||
const privateKeys = [ | ||
'18f020b98eb798752a50ed0563b079c125b0db5dd0b1060d1c1b47d4a193e1e4', | ||
'ed69a8c50cf8c9836be3b67c7eeff416612d45ba39a5c099d48fa668bf558c9c', | ||
'16ae669f3be7a2121e17d0c68c05a8f3d6bef21ec0f2315f1d7aec12484e4cf5' | ||
]; | ||
const message = '64726e3da8'; | ||
const messages = ['d2', '0d98', '05caf3']; | ||
(async () => { | ||
// keys, messages & other inputs can be Uint8Arrays or hex strings | ||
const privateKey = '67d53f170b908cabb9eb326c3c337762d59289a8fec79f7bc9254b584b73265c'; | ||
const message = '64726e3da8'; | ||
const publicKey = bls.getPublicKey(privateKey); | ||
const publicKeys = privateKeys.map(bls.getPublicKey); | ||
const signature = await bls.sign(message, privateKey); | ||
const isCorrect = await bls.verify(signature, message, publicKey); | ||
console.log('key', publicKey); | ||
console.log('signature', signature); | ||
console.log('is correct:', isCorrect); | ||
const isValid = await bls.verify(signature, message, publicKey); | ||
console.log({ publicKey, signature, isValid }); | ||
// Sign 1 msg with 3 keys | ||
const privateKeys = [ | ||
'18f020b98eb798752a50ed0563b079c125b0db5dd0b1060d1c1b47d4a193e1e4', | ||
'ed69a8c50cf8c9836be3b67c7eeff416612d45ba39a5c099d48fa668bf558c9c', | ||
'16ae669f3be7a2121e17d0c68c05a8f3d6bef21ec0f2315f1d7aec12484e4cf5' | ||
]; | ||
const messages = ['d2', '0d98', '05caf3']; | ||
const publicKeys = privateKeys.map(bls.getPublicKey); | ||
const signatures2 = await Promise.all(privateKeys.map(p => bls.sign(message, p))); | ||
const aggPubKey2 = bls.aggregatePublicKeys(publicKeys); | ||
const aggSignature2 = bls.aggregateSignatures(signatures2); | ||
const isCorrect2 = await bls.verify(aggSignature2, message, aggPubKey2); | ||
console.log(); | ||
console.log('signatures are', signatures2); | ||
console.log('merged to one signature', aggSignature2); | ||
console.log('is correct:', isCorrect2); | ||
const isValid2 = await bls.verify(aggSignature2, message, aggPubKey2); | ||
console.log({ signatures2, aggSignature2, isValid2 }); | ||
@@ -77,8 +69,4 @@ // Sign 3 msgs with 3 keys | ||
const aggSignature3 = bls.aggregateSignatures(signatures3); | ||
const isCorrect3 = await bls.verifyBatch(aggSignature3, messages, publicKeys); | ||
console.log(); | ||
console.log('keys', publicKeys); | ||
console.log('signatures', signatures3); | ||
console.log('merged to one signature', aggSignature3); | ||
console.log('is correct:', isCorrect3); | ||
const isValid3 = await bls.verifyBatch(aggSignature3, messages, publicKeys); | ||
console.log({ publicKeys, signatures3, aggSignature3, isValid3 }); | ||
})(); | ||
@@ -91,21 +79,5 @@ ``` | ||
- `deno run --import-map=imports.json app.ts` | ||
- app.ts: `import * as bls from "https://deno.land/x/bls12_381/mod.ts";` | ||
- imports.json: `{"imports": {"crypto": "https://deno.land/std@0.119.0/node/crypto.ts"}}` | ||
- `app.ts` | ||
```typescript | ||
import * as bls from "https://deno.land/x/bls12_381/mod.ts"; | ||
const publicKey = bls.getPublicKey(bls.utils.randomPrivateKey()); | ||
console.log(publicKey); | ||
``` | ||
- `imports.json` | ||
```json | ||
{ | ||
"imports": { | ||
"crypto": "https://deno.land/std@0.119.0/node/crypto.ts", | ||
"./math": "./math.ts" | ||
} | ||
} | ||
``` | ||
## API | ||
@@ -120,2 +92,3 @@ | ||
- [`pairing(G1Point, G2Point)`](#pairingg1point-g2point) | ||
- [Utilities](#utilities) | ||
@@ -131,3 +104,3 @@ ##### `getPublicKey(privateKey)` | ||
**Note:** if you need spec-based `KeyGen`, use [paulmillr/bls12-381-keygen](https://github.com/paulmillr/bls12-381-keygen). It should work properly with ETH2 and FIL keys. | ||
**Note:** if you need EIP2333-compliant `KeyGen` (eth2/fil), use [paulmillr/bls12-381-keygen](https://github.com/paulmillr/bls12-381-keygen). | ||
@@ -143,3 +116,3 @@ ##### `sign(message, privateKey)` | ||
Check out **Internals** section on instructions about domain separation tag (DST). | ||
Check out [Utilities](#utilities) section on instructions about domain separation tag (DST). | ||
@@ -201,43 +174,14 @@ ##### `verify(signature, message, publicKey)` | ||
##### Helpers | ||
### Utilities | ||
Use `utils.bytesToHex(str)` to convert byte output to hexademical string. | ||
Resources that help to understand bls12-381: | ||
```typescript | ||
// characteristic; z + (z⁴ - z² + 1)(z - 1)²/3 | ||
bls.CURVE.P // 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab | ||
- [BLS12-381 for the rest of us](https://hackmd.io/@benjaminion/bls12-381) | ||
- [Key concepts of pairings](https://medium.com/@alonmuroch_65570/bls-signatures-part-2-key-concepts-of-pairings-27a8a9533d0c) | ||
- Pairing over bls12-381: [part 1](https://research.nccgroup.com/2020/07/06/pairing-over-bls12-381-part-1-fields/), | ||
[part 2](https://research.nccgroup.com/2020/07/13/pairing-over-bls12-381-part-2-curves/), | ||
[part 3](https://research.nccgroup.com/2020/08/13/pairing-over-bls12-381-part-3-pairing/) | ||
- [Estimating the bit security of pairing-friendly curves](https://research.nccgroup.com/2022/02/03/estimating-the-bit-security-of-pairing-friendly-curves/) | ||
- Check out [the online demo](https://paulmillr.com/ecc) and [threshold sigs demo](https://genthresh.com) | ||
// curve order; z⁴ − z² + 1 | ||
bls.CURVE.r // 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 | ||
// cofactor; (z - 1)²/3 | ||
bls.curve.h // 0x396c8c005555e1568c00aaab0000aaab | ||
// G1 base point coordinates (x, y) | ||
bls.CURVE.Gx | ||
// x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507 | ||
bls.CURVE.Gy | ||
// y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569 | ||
// G2 base point coordinates (x₁, x₂+i), (y₁, y₂+i) | ||
bls.CURVE.G2x | ||
// x = | ||
// 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758, | ||
// 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160 | ||
bls.CURVE.G2y | ||
// y = | ||
// 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582, | ||
// 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905 | ||
// Classes | ||
bls.Fp // field over Fp | ||
bls.Fp2 // field over Fp₂ | ||
bls.Fp12 // finite extension field over irreducible polynominal | ||
bls.PointG1 // projective point (xyz) at G1 | ||
bls.PointG2 // projective point (xyz) at G2 | ||
``` | ||
## Internals | ||
The library uses G1 for public keys and G2 for signatures. Adding support for G1 signatures is planned. | ||
@@ -269,2 +213,58 @@ | ||
```typescript | ||
// Exports `CURVE`, `utils`, `PointG1`, `PointG2`, `Fp`, `Fp2`, `Fp12` helpers | ||
const utils: { | ||
hashToField(msg: Uint8Array, count: number, options = {}): Promise<bigint[][]>; | ||
bytesToHex: (bytes: Uint8Array): string; | ||
randomBytes: (bytesLength?: number) => Uint8Array; | ||
randomPrivateKey: () => Uint8Array; | ||
sha256: (message: Uint8Array) => Promise<Uint8Array>; | ||
mod: (a: bigint, b = CURVE.P): bigint; | ||
getDSTLabel(): string; | ||
setDSTLabel(newLabel: string): void; | ||
}; | ||
// characteristic; z + (z⁴ - z² + 1)(z - 1)²/3 | ||
CURVE.P // 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab | ||
CURVE.r // curve order; z⁴ − z² + 1, 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 | ||
curve.h // cofactor; (z - 1)²/3, 0x396c8c005555e1568c00aaab0000aaab | ||
CURVE.Gx, CURVE.Gy // G1 base point coordinates (x, y) | ||
CURVE.G2x, CURVE.G2y // G2 base point coordinates (x₁, x₂+i), (y₁, y₂+i) | ||
// Classes | ||
bls.Fp // field over Fp | ||
bls.Fp2 // field over Fp₂ | ||
bls.Fp12 // finite extension field over irreducible polynominal | ||
// projective point (xyz) at G1 | ||
class PointG1 extends ProjectivePoint<Fp> { | ||
constructor(x: Fp, y: Fp, z?: Fp); | ||
static BASE: PointG1; | ||
static ZERO: PointG1; | ||
static fromHex(bytes: Bytes): PointG1; | ||
static fromPrivateKey(privateKey: PrivateKey): PointG1; | ||
toRawBytes(isCompressed?: boolean): Uint8Array; | ||
toHex(isCompressed?: boolean): string; | ||
assertValidity(): this; | ||
millerLoop(P: PointG2): Fp12; | ||
clearCofactor(): PointG1; | ||
} | ||
// projective point (xyz) at G2 | ||
class PointG2 extends ProjectivePoint<Fp2> { | ||
constructor(x: Fp2, y: Fp2, z?: Fp2); | ||
static BASE: PointG2; | ||
static ZERO: PointG2; | ||
static hashToCurve(msg: Bytes): Promise<PointG2>; | ||
static fromSignature(hex: Bytes): PointG2; | ||
static fromHex(bytes: Bytes): PointG2; | ||
static fromPrivateKey(privateKey: PrivateKey): PointG2; | ||
toSignature(): Uint8Array; | ||
toRawBytes(isCompressed?: boolean): Uint8Array; | ||
toHex(isCompressed?: boolean): string; | ||
assertValidity(): this; | ||
clearCofactor(): PointG2; | ||
} | ||
``` | ||
## Speed | ||
@@ -281,20 +281,20 @@ | ||
``` | ||
getPublicKey x 737 ops/sec @ 1ms/op | ||
sign x 37 ops/sec @ 26ms/op | ||
verify x 28 ops/sec @ 34ms/op | ||
pairing x 70 ops/sec @ 14ms/op | ||
aggregatePublicKeys/8 x 102 ops/sec @ 9ms/op | ||
aggregateSignatures/8 x 39 ops/sec @ 25ms/op | ||
getPublicKey x 742 ops/sec @ 1ms/op | ||
sign x 39 ops/sec @ 25ms/op | ||
verify x 31 ops/sec @ 32ms/op | ||
pairing x 77 ops/sec @ 12ms/op | ||
aggregatePublicKeys/8 x 105 ops/sec @ 9ms/op | ||
aggregateSignatures/8 x 41 ops/sec @ 24ms/op | ||
with compression / decompression disabled: | ||
sign/nc x 52 ops/sec @ 18ms/op | ||
verify/nc x 47 ops/sec @ 21ms/op | ||
aggregatePublicKeys/32 x 1,014 ops/sec @ 986μs/op | ||
aggregatePublicKeys/128 x 662 ops/sec @ 1ms/op | ||
aggregatePublicKeys/512 x 278 ops/sec @ 3ms/op | ||
aggregatePublicKeys/2048 x 84 ops/sec @ 11ms/op | ||
aggregateSignatures/32 x 444 ops/sec @ 2ms/op | ||
aggregateSignatures/128 x 236 ops/sec @ 4ms/op | ||
aggregateSignatures/512 x 81 ops/sec @ 12ms/op | ||
aggregateSignatures/2048 x 22 ops/sec @ 44ms/op | ||
sign/nc x 55 ops/sec @ 17ms/op | ||
verify/nc x 52 ops/sec @ 19ms/op | ||
aggregatePublicKeys/32 x 1,045 ops/sec @ 956μs/op | ||
aggregatePublicKeys/128 x 681 ops/sec @ 1ms/op | ||
aggregatePublicKeys/512 x 285 ops/sec @ 3ms/op | ||
aggregatePublicKeys/2048 x 85 ops/sec @ 11ms/op | ||
aggregateSignatures/32 x 471 ops/sec @ 2ms/op | ||
aggregateSignatures/128 x 247 ops/sec @ 4ms/op | ||
aggregateSignatures/512 x 85 ops/sec @ 11ms/op | ||
aggregateSignatures/2048 x 23 ops/sec @ 41ms/op | ||
``` | ||
@@ -314,3 +314,3 @@ | ||
We however consider infrastructure attacks like rogue NPM modules very important; that's why it's crucial to minimize the amount of 3rd-party dependencies & native bindings. If your app uses 500 dependencies, any dep could get hacked and you'll be downloading rootkits with every `npm install`. Our goal is to minimize this attack vector. | ||
We however consider infrastructure attacks like rogue NPM modules very important; that's why it's crucial to minimize the amount of 3rd-party dependencies & native bindings. If your app uses 500 dependencies, any dep could get hacked and you'll be downloading malware with every `npm install`. Our goal is to minimize this attack vector. | ||
@@ -328,2 +328,2 @@ ## Contributing | ||
MIT (c) Paul Miller [(https://paulmillr.com)](https://paulmillr.com), see LICENSE file. | ||
MIT (c) 2019 Paul Miller [(https://paulmillr.com)](https://paulmillr.com), see LICENSE file. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
154871
3823