@noble/curves 1.4.2 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +159 -128
- package/_shortw_utils.d.ts.map +1 -1
- package/abstract/bls.d.ts +37 -34
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/bls.js +167 -115
- package/abstract/bls.js.map +1 -1
- package/abstract/curve.d.ts +14 -1
- package/abstract/curve.d.ts.map +1 -1
- package/abstract/curve.js +77 -7
- package/abstract/curve.js.map +1 -1
- package/abstract/edwards.d.ts +12 -0
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +84 -75
- package/abstract/edwards.js.map +1 -1
- package/abstract/hash-to-curve.d.ts.map +1 -1
- package/abstract/hash-to-curve.js +4 -2
- package/abstract/hash-to-curve.js.map +1 -1
- package/abstract/modular.d.ts +4 -0
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +13 -2
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +4 -9
- package/abstract/montgomery.js.map +1 -1
- package/abstract/tower.d.ts +107 -0
- package/abstract/tower.d.ts.map +1 -0
- package/abstract/tower.js +498 -0
- package/abstract/tower.js.map +1 -0
- package/abstract/utils.d.ts +17 -0
- package/abstract/utils.d.ts.map +1 -1
- package/abstract/utils.js +50 -1
- package/abstract/utils.js.map +1 -1
- package/abstract/weierstrass.d.ts +25 -3
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +189 -113
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts +1 -65
- package/bls12-381.d.ts.map +1 -1
- package/bls12-381.js +48 -575
- package/bls12-381.js.map +1 -1
- package/bn254.d.ts +10 -6
- package/bn254.d.ts.map +1 -1
- package/bn254.js +207 -10
- package/bn254.js.map +1 -1
- package/ed25519.d.ts +7 -4
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +3 -0
- package/ed25519.js.map +1 -1
- package/esm/_shortw_utils.d.ts.map +1 -1
- package/esm/abstract/bls.d.ts +37 -34
- package/esm/abstract/bls.d.ts.map +1 -1
- package/esm/abstract/bls.js +168 -116
- package/esm/abstract/bls.js.map +1 -1
- package/esm/abstract/curve.d.ts +14 -1
- package/esm/abstract/curve.d.ts.map +1 -1
- package/esm/abstract/curve.js +77 -8
- package/esm/abstract/curve.js.map +1 -1
- package/esm/abstract/edwards.d.ts +12 -0
- package/esm/abstract/edwards.d.ts.map +1 -1
- package/esm/abstract/edwards.js +87 -78
- package/esm/abstract/edwards.js.map +1 -1
- package/esm/abstract/hash-to-curve.d.ts.map +1 -1
- package/esm/abstract/hash-to-curve.js +4 -2
- package/esm/abstract/hash-to-curve.js.map +1 -1
- package/esm/abstract/modular.d.ts +4 -0
- package/esm/abstract/modular.d.ts.map +1 -1
- package/esm/abstract/modular.js +12 -2
- package/esm/abstract/modular.js.map +1 -1
- package/esm/abstract/montgomery.d.ts.map +1 -1
- package/esm/abstract/montgomery.js +5 -10
- package/esm/abstract/montgomery.js.map +1 -1
- package/esm/abstract/tower.d.ts +107 -0
- package/esm/abstract/tower.d.ts.map +1 -0
- package/esm/abstract/tower.js +494 -0
- package/esm/abstract/tower.js.map +1 -0
- package/esm/abstract/utils.d.ts +17 -0
- package/esm/abstract/utils.d.ts.map +1 -1
- package/esm/abstract/utils.js +44 -0
- package/esm/abstract/utils.js.map +1 -1
- package/esm/abstract/weierstrass.d.ts +25 -3
- package/esm/abstract/weierstrass.d.ts.map +1 -1
- package/esm/abstract/weierstrass.js +191 -115
- package/esm/abstract/weierstrass.js.map +1 -1
- package/esm/bls12-381.d.ts +1 -65
- package/esm/bls12-381.d.ts.map +1 -1
- package/esm/bls12-381.js +50 -577
- package/esm/bls12-381.js.map +1 -1
- package/esm/bn254.d.ts +10 -6
- package/esm/bn254.d.ts.map +1 -1
- package/esm/bn254.js +206 -9
- package/esm/bn254.js.map +1 -1
- package/esm/ed25519.d.ts +7 -4
- package/esm/ed25519.d.ts.map +1 -1
- package/esm/ed25519.js +3 -0
- package/esm/ed25519.js.map +1 -1
- package/esm/jubjub.d.ts.map +1 -1
- package/esm/jubjub.js +8 -2
- package/esm/jubjub.js.map +1 -1
- package/esm/p256.d.ts.map +1 -1
- package/esm/p384.d.ts.map +1 -1
- package/esm/p521.d.ts.map +1 -1
- package/esm/secp256k1.d.ts +6 -0
- package/esm/secp256k1.d.ts.map +1 -1
- package/esm/secp256k1.js +17 -13
- package/esm/secp256k1.js.map +1 -1
- package/jubjub.d.ts.map +1 -1
- package/jubjub.js +8 -2
- package/jubjub.js.map +1 -1
- package/p256.d.ts.map +1 -1
- package/p384.d.ts.map +1 -1
- package/p521.d.ts.map +1 -1
- package/package.json +27 -19
- package/secp256k1.d.ts +6 -0
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +16 -12
- package/secp256k1.js.map +1 -1
- package/src/abstract/bls.ts +222 -168
- package/src/abstract/curve.ts +80 -8
- package/src/abstract/edwards.ts +97 -70
- package/src/abstract/hash-to-curve.ts +3 -1
- package/src/abstract/modular.ts +13 -3
- package/src/abstract/montgomery.ts +11 -10
- package/src/abstract/tower.ts +605 -0
- package/src/abstract/utils.ts +49 -0
- package/src/abstract/weierstrass.ts +179 -104
- package/src/bls12-381.ts +53 -707
- package/src/bn254.ts +224 -9
- package/src/ed25519.ts +5 -2
- package/src/jubjub.ts +7 -2
- package/src/secp256k1.ts +24 -12
|
@@ -0,0 +1,605 @@
|
|
|
1
|
+
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2
|
+
import * as mod from './modular.js';
|
|
3
|
+
import { bitLen, bitMask, concatBytes, notImplemented } from './utils.js';
|
|
4
|
+
import type { ProjConstructor, ProjPointType } from './weierstrass.js';
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
Towered extension fields
|
|
8
|
+
|
|
9
|
+
Rather than implementing a massive 12th-degree extension directly, it is more efficient
|
|
10
|
+
to build it up from smaller extensions: a tower of extensions.
|
|
11
|
+
|
|
12
|
+
For BLS12-381, the Fp12 field is implemented as a quadratic (degree two) extension,
|
|
13
|
+
on top of a cubic (degree three) extension, on top of a quadratic extension of Fp.
|
|
14
|
+
|
|
15
|
+
For more info: "Pairings for beginners" by Costello, section 7.3.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
|
19
|
+
// prettier-ignore
|
|
20
|
+
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
|
|
21
|
+
|
|
22
|
+
// Fp₂ over complex plane
|
|
23
|
+
export type BigintTuple = [bigint, bigint];
|
|
24
|
+
export type Fp = bigint;
|
|
25
|
+
// Finite extension field over irreducible polynominal.
|
|
26
|
+
// Fp(u) / (u² - β) where β = -1
|
|
27
|
+
export type Fp2 = { c0: bigint; c1: bigint };
|
|
28
|
+
export type BigintSix = [bigint, bigint, bigint, bigint, bigint, bigint];
|
|
29
|
+
export type Fp6 = { c0: Fp2; c1: Fp2; c2: Fp2 };
|
|
30
|
+
export type Fp12 = { c0: Fp6; c1: Fp6 }; // Fp₁₂ = Fp₆² => Fp₂³, Fp₆(w) / (w² - γ) where γ = v
|
|
31
|
+
// prettier-ignore
|
|
32
|
+
export type BigintTwelve = [
|
|
33
|
+
bigint, bigint, bigint, bigint, bigint, bigint,
|
|
34
|
+
bigint, bigint, bigint, bigint, bigint, bigint
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
export type Fp2Bls = mod.IField<Fp2> & {
|
|
38
|
+
reim: (num: Fp2) => { re: Fp; im: Fp };
|
|
39
|
+
mulByB: (num: Fp2) => Fp2;
|
|
40
|
+
frobeniusMap(num: Fp2, power: number): Fp2;
|
|
41
|
+
fromBigTuple(num: [bigint, bigint]): Fp2;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export type Fp12Bls = mod.IField<Fp12> & {
|
|
45
|
+
frobeniusMap(num: Fp12, power: number): Fp12;
|
|
46
|
+
mul014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
|
|
47
|
+
mul034(num: Fp12, o0: Fp2, o3: Fp2, o4: Fp2): Fp12;
|
|
48
|
+
conjugate(num: Fp12): Fp12;
|
|
49
|
+
finalExponentiate(num: Fp12): Fp12;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
function calcFrobeniusCoefficients<T>(
|
|
53
|
+
Fp: mod.IField<T>,
|
|
54
|
+
nonResidue: T,
|
|
55
|
+
modulus: bigint,
|
|
56
|
+
degree: number,
|
|
57
|
+
num: number = 1,
|
|
58
|
+
divisor?: number
|
|
59
|
+
) {
|
|
60
|
+
const _divisor = BigInt(divisor === undefined ? degree : divisor);
|
|
61
|
+
const towerModulus: any = modulus ** BigInt(degree);
|
|
62
|
+
const res: T[][] = [];
|
|
63
|
+
for (let i = 0; i < num; i++) {
|
|
64
|
+
const a = BigInt(i + 1);
|
|
65
|
+
const powers: T[] = [];
|
|
66
|
+
for (let j = 0, qPower = _1n; j < degree; j++) {
|
|
67
|
+
const power = ((a * qPower - a) / _divisor) % towerModulus;
|
|
68
|
+
powers.push(Fp.pow(nonResidue, power));
|
|
69
|
+
qPower *= modulus;
|
|
70
|
+
}
|
|
71
|
+
res.push(powers);
|
|
72
|
+
}
|
|
73
|
+
return res;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// This works same at least for bls12-381, bn254 and bls12-377
|
|
77
|
+
export function psiFrobenius(Fp: mod.IField<Fp>, Fp2: Fp2Bls, base: Fp2) {
|
|
78
|
+
// Ψ endomorphism
|
|
79
|
+
const PSI_X = Fp2.pow(base, (Fp.ORDER - _1n) / _3n); // u^((p-1)/3)
|
|
80
|
+
const PSI_Y = Fp2.pow(base, (Fp.ORDER - _1n) / _2n); // u^((p-1)/2)
|
|
81
|
+
function psi(x: Fp2, y: Fp2): [Fp2, Fp2] {
|
|
82
|
+
// This x10 faster than previous version in bls12-381
|
|
83
|
+
const x2 = Fp2.mul(Fp2.frobeniusMap(x, 1), PSI_X);
|
|
84
|
+
const y2 = Fp2.mul(Fp2.frobeniusMap(y, 1), PSI_Y);
|
|
85
|
+
return [x2, y2];
|
|
86
|
+
}
|
|
87
|
+
// Ψ²(P) endomorphism (psi2(x) = psi(psi(x)))
|
|
88
|
+
const PSI2_X = Fp2.pow(base, (Fp.ORDER ** _2n - _1n) / _3n); // u^((p^2 - 1)/3)
|
|
89
|
+
// This equals -1, which causes y to be Fp2.neg(y).
|
|
90
|
+
// But not sure if there are case when this is not true?
|
|
91
|
+
const PSI2_Y = Fp2.pow(base, (Fp.ORDER ** _2n - _1n) / _2n); // u^((p^2 - 1)/3)
|
|
92
|
+
if (!Fp2.eql(PSI2_Y, Fp2.neg(Fp2.ONE))) throw new Error('psiFrobenius: PSI2_Y!==-1');
|
|
93
|
+
function psi2(x: Fp2, y: Fp2): [Fp2, Fp2] {
|
|
94
|
+
return [Fp2.mul(x, PSI2_X), Fp2.neg(y)];
|
|
95
|
+
}
|
|
96
|
+
// Map points
|
|
97
|
+
const mapAffine =
|
|
98
|
+
<T>(fn: (x: T, y: T) => [T, T]) =>
|
|
99
|
+
(c: ProjConstructor<T>, P: ProjPointType<T>) => {
|
|
100
|
+
const affine = P.toAffine();
|
|
101
|
+
const p = fn(affine.x, affine.y);
|
|
102
|
+
return c.fromAffine({ x: p[0], y: p[1] });
|
|
103
|
+
};
|
|
104
|
+
const G2psi = mapAffine(psi);
|
|
105
|
+
const G2psi2 = mapAffine(psi2);
|
|
106
|
+
return { psi, psi2, G2psi, G2psi2, PSI_X, PSI_Y, PSI2_X, PSI2_Y };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export type Tower12Opts = {
|
|
110
|
+
ORDER: bigint;
|
|
111
|
+
NONRESIDUE?: Fp;
|
|
112
|
+
// Fp2
|
|
113
|
+
FP2_NONRESIDUE: BigintTuple;
|
|
114
|
+
Fp2sqrt?: (num: Fp2) => Fp2;
|
|
115
|
+
Fp2mulByB: (num: Fp2) => Fp2;
|
|
116
|
+
// Fp12
|
|
117
|
+
Fp12cyclotomicSquare: (num: Fp12) => Fp12;
|
|
118
|
+
Fp12cyclotomicExp: (num: Fp12, n: bigint) => Fp12;
|
|
119
|
+
Fp12finalExponentiate: (num: Fp12) => Fp12;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export function tower12(opts: Tower12Opts) {
|
|
123
|
+
const { ORDER } = opts;
|
|
124
|
+
// Fp
|
|
125
|
+
const Fp = mod.Field(ORDER);
|
|
126
|
+
const FpNONRESIDUE = Fp.create(opts.NONRESIDUE || BigInt(-1));
|
|
127
|
+
const FpLegendre = mod.FpLegendre(ORDER);
|
|
128
|
+
const Fpdiv2 = Fp.div(Fp.ONE, _2n); // 1/2
|
|
129
|
+
|
|
130
|
+
// Fp2
|
|
131
|
+
const FP2_FROBENIUS_COEFFICIENTS = calcFrobeniusCoefficients(Fp, FpNONRESIDUE, Fp.ORDER, 2)[0];
|
|
132
|
+
const Fp2Add = ({ c0, c1 }: Fp2, { c0: r0, c1: r1 }: Fp2) => ({
|
|
133
|
+
c0: Fp.add(c0, r0),
|
|
134
|
+
c1: Fp.add(c1, r1),
|
|
135
|
+
});
|
|
136
|
+
const Fp2Subtract = ({ c0, c1 }: Fp2, { c0: r0, c1: r1 }: Fp2) => ({
|
|
137
|
+
c0: Fp.sub(c0, r0),
|
|
138
|
+
c1: Fp.sub(c1, r1),
|
|
139
|
+
});
|
|
140
|
+
const Fp2Multiply = ({ c0, c1 }: Fp2, rhs: Fp2) => {
|
|
141
|
+
if (typeof rhs === 'bigint') return { c0: Fp.mul(c0, rhs), c1: Fp.mul(c1, rhs) };
|
|
142
|
+
// (a+bi)(c+di) = (ac−bd) + (ad+bc)i
|
|
143
|
+
const { c0: r0, c1: r1 } = rhs;
|
|
144
|
+
let t1 = Fp.mul(c0, r0); // c0 * o0
|
|
145
|
+
let t2 = Fp.mul(c1, r1); // c1 * o1
|
|
146
|
+
// (T1 - T2) + ((c0 + c1) * (r0 + r1) - (T1 + T2))*i
|
|
147
|
+
const o0 = Fp.sub(t1, t2);
|
|
148
|
+
const o1 = Fp.sub(Fp.mul(Fp.add(c0, c1), Fp.add(r0, r1)), Fp.add(t1, t2));
|
|
149
|
+
return { c0: o0, c1: o1 };
|
|
150
|
+
};
|
|
151
|
+
const Fp2Square = ({ c0, c1 }: Fp2) => {
|
|
152
|
+
const a = Fp.add(c0, c1);
|
|
153
|
+
const b = Fp.sub(c0, c1);
|
|
154
|
+
const c = Fp.add(c0, c0);
|
|
155
|
+
return { c0: Fp.mul(a, b), c1: Fp.mul(c, c1) };
|
|
156
|
+
};
|
|
157
|
+
type Fp2Utils = {
|
|
158
|
+
NONRESIDUE: Fp2;
|
|
159
|
+
fromBigTuple: (tuple: BigintTuple | bigint[]) => Fp2;
|
|
160
|
+
reim: (num: Fp2) => { re: bigint; im: bigint };
|
|
161
|
+
mulByNonresidue: (num: Fp2) => Fp2;
|
|
162
|
+
mulByB: (num: Fp2) => Fp2;
|
|
163
|
+
frobeniusMap(num: Fp2, power: number): Fp2;
|
|
164
|
+
};
|
|
165
|
+
const Fp2fromBigTuple = (tuple: BigintTuple | bigint[]) => {
|
|
166
|
+
if (tuple.length !== 2) throw new Error('Invalid tuple');
|
|
167
|
+
const fps = tuple.map((n) => Fp.create(n)) as [Fp, Fp];
|
|
168
|
+
return { c0: fps[0], c1: fps[1] };
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const FP2_ORDER = ORDER * ORDER;
|
|
172
|
+
const Fp2Nonresidue = Fp2fromBigTuple(opts.FP2_NONRESIDUE);
|
|
173
|
+
const Fp2: mod.IField<Fp2> & Fp2Utils = {
|
|
174
|
+
ORDER: FP2_ORDER,
|
|
175
|
+
NONRESIDUE: Fp2Nonresidue,
|
|
176
|
+
BITS: bitLen(FP2_ORDER),
|
|
177
|
+
BYTES: Math.ceil(bitLen(FP2_ORDER) / 8),
|
|
178
|
+
MASK: bitMask(bitLen(FP2_ORDER)),
|
|
179
|
+
ZERO: { c0: Fp.ZERO, c1: Fp.ZERO },
|
|
180
|
+
ONE: { c0: Fp.ONE, c1: Fp.ZERO },
|
|
181
|
+
create: (num) => num,
|
|
182
|
+
isValid: ({ c0, c1 }) => typeof c0 === 'bigint' && typeof c1 === 'bigint',
|
|
183
|
+
is0: ({ c0, c1 }) => Fp.is0(c0) && Fp.is0(c1),
|
|
184
|
+
eql: ({ c0, c1 }: Fp2, { c0: r0, c1: r1 }: Fp2) => Fp.eql(c0, r0) && Fp.eql(c1, r1),
|
|
185
|
+
neg: ({ c0, c1 }) => ({ c0: Fp.neg(c0), c1: Fp.neg(c1) }),
|
|
186
|
+
pow: (num, power) => mod.FpPow(Fp2, num, power),
|
|
187
|
+
invertBatch: (nums) => mod.FpInvertBatch(Fp2, nums),
|
|
188
|
+
// Normalized
|
|
189
|
+
add: Fp2Add,
|
|
190
|
+
sub: Fp2Subtract,
|
|
191
|
+
mul: Fp2Multiply,
|
|
192
|
+
sqr: Fp2Square,
|
|
193
|
+
// NonNormalized stuff
|
|
194
|
+
addN: Fp2Add,
|
|
195
|
+
subN: Fp2Subtract,
|
|
196
|
+
mulN: Fp2Multiply,
|
|
197
|
+
sqrN: Fp2Square,
|
|
198
|
+
// Why inversion for bigint inside Fp instead of Fp2? it is even used in that context?
|
|
199
|
+
div: (lhs, rhs) =>
|
|
200
|
+
Fp2.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : Fp2.inv(rhs)),
|
|
201
|
+
inv: ({ c0: a, c1: b }) => {
|
|
202
|
+
// We wish to find the multiplicative inverse of a nonzero
|
|
203
|
+
// element a + bu in Fp2. We leverage an identity
|
|
204
|
+
//
|
|
205
|
+
// (a + bu)(a - bu) = a² + b²
|
|
206
|
+
//
|
|
207
|
+
// which holds because u² = -1. This can be rewritten as
|
|
208
|
+
//
|
|
209
|
+
// (a + bu)(a - bu)/(a² + b²) = 1
|
|
210
|
+
//
|
|
211
|
+
// because a² + b² = 0 has no nonzero solutions for (a, b).
|
|
212
|
+
// This gives that (a - bu)/(a² + b²) is the inverse
|
|
213
|
+
// of (a + bu). Importantly, this can be computing using
|
|
214
|
+
// only a single inversion in Fp.
|
|
215
|
+
const factor = Fp.inv(Fp.create(a * a + b * b));
|
|
216
|
+
return { c0: Fp.mul(factor, Fp.create(a)), c1: Fp.mul(factor, Fp.create(-b)) };
|
|
217
|
+
},
|
|
218
|
+
sqrt: (num) => {
|
|
219
|
+
if (opts.Fp2sqrt) return opts.Fp2sqrt(num);
|
|
220
|
+
// This is generic for all quadratic extensions (Fp2)
|
|
221
|
+
const { c0, c1 } = num;
|
|
222
|
+
if (Fp.is0(c1)) {
|
|
223
|
+
// if c0 is quadratic residue
|
|
224
|
+
if (Fp.eql(FpLegendre(Fp, c0), Fp.ONE)) return Fp2.create({ c0: Fp.sqrt(c0), c1: Fp.ZERO });
|
|
225
|
+
else return Fp2.create({ c0: Fp.ZERO, c1: Fp.sqrt(Fp.div(c0, FpNONRESIDUE)) });
|
|
226
|
+
}
|
|
227
|
+
const a = Fp.sqrt(Fp.sub(Fp.sqr(c0), Fp.mul(Fp.sqr(c1), FpNONRESIDUE)));
|
|
228
|
+
let d = Fp.mul(Fp.add(a, c0), Fpdiv2);
|
|
229
|
+
const legendre = FpLegendre(Fp, d);
|
|
230
|
+
// -1, Quadratic non residue
|
|
231
|
+
if (!Fp.is0(legendre) && !Fp.eql(legendre, Fp.ONE)) d = Fp.sub(d, a);
|
|
232
|
+
const a0 = Fp.sqrt(d);
|
|
233
|
+
const candidateSqrt = Fp2.create({ c0: a0, c1: Fp.div(Fp.mul(c1, Fpdiv2), a0) });
|
|
234
|
+
if (!Fp2.eql(Fp2.sqr(candidateSqrt), num)) throw new Error('Cannot find square root');
|
|
235
|
+
// Normalize root: at this point candidateSqrt ** 2 = num, but also -candidateSqrt ** 2 = num
|
|
236
|
+
const x1 = candidateSqrt;
|
|
237
|
+
const x2 = Fp2.neg(x1);
|
|
238
|
+
const { re: re1, im: im1 } = Fp2.reim(x1);
|
|
239
|
+
const { re: re2, im: im2 } = Fp2.reim(x2);
|
|
240
|
+
if (im1 > im2 || (im1 === im2 && re1 > re2)) return x1;
|
|
241
|
+
return x2;
|
|
242
|
+
},
|
|
243
|
+
// Same as sgn0_m_eq_2 in RFC 9380
|
|
244
|
+
isOdd: (x: Fp2) => {
|
|
245
|
+
const { re: x0, im: x1 } = Fp2.reim(x);
|
|
246
|
+
const sign_0 = x0 % _2n;
|
|
247
|
+
const zero_0 = x0 === _0n;
|
|
248
|
+
const sign_1 = x1 % _2n;
|
|
249
|
+
return BigInt(sign_0 || (zero_0 && sign_1)) == _1n;
|
|
250
|
+
},
|
|
251
|
+
// Bytes util
|
|
252
|
+
fromBytes(b: Uint8Array): Fp2 {
|
|
253
|
+
if (b.length !== Fp2.BYTES) throw new Error(`fromBytes wrong length=${b.length}`);
|
|
254
|
+
return { c0: Fp.fromBytes(b.subarray(0, Fp.BYTES)), c1: Fp.fromBytes(b.subarray(Fp.BYTES)) };
|
|
255
|
+
},
|
|
256
|
+
toBytes: ({ c0, c1 }) => concatBytes(Fp.toBytes(c0), Fp.toBytes(c1)),
|
|
257
|
+
cmov: ({ c0, c1 }, { c0: r0, c1: r1 }, c) => ({
|
|
258
|
+
c0: Fp.cmov(c0, r0, c),
|
|
259
|
+
c1: Fp.cmov(c1, r1, c),
|
|
260
|
+
}),
|
|
261
|
+
reim: ({ c0, c1 }) => ({ re: c0, im: c1 }),
|
|
262
|
+
// multiply by u + 1
|
|
263
|
+
mulByNonresidue: ({ c0, c1 }) => Fp2.mul({ c0, c1 }, Fp2Nonresidue),
|
|
264
|
+
mulByB: opts.Fp2mulByB,
|
|
265
|
+
fromBigTuple: Fp2fromBigTuple,
|
|
266
|
+
frobeniusMap: ({ c0, c1 }, power: number): Fp2 => ({
|
|
267
|
+
c0,
|
|
268
|
+
c1: Fp.mul(c1, FP2_FROBENIUS_COEFFICIENTS[power % 2]),
|
|
269
|
+
}),
|
|
270
|
+
};
|
|
271
|
+
// Fp6
|
|
272
|
+
const Fp6Add = ({ c0, c1, c2 }: Fp6, { c0: r0, c1: r1, c2: r2 }: Fp6) => ({
|
|
273
|
+
c0: Fp2.add(c0, r0),
|
|
274
|
+
c1: Fp2.add(c1, r1),
|
|
275
|
+
c2: Fp2.add(c2, r2),
|
|
276
|
+
});
|
|
277
|
+
const Fp6Subtract = ({ c0, c1, c2 }: Fp6, { c0: r0, c1: r1, c2: r2 }: Fp6) => ({
|
|
278
|
+
c0: Fp2.sub(c0, r0),
|
|
279
|
+
c1: Fp2.sub(c1, r1),
|
|
280
|
+
c2: Fp2.sub(c2, r2),
|
|
281
|
+
});
|
|
282
|
+
const Fp6Multiply = ({ c0, c1, c2 }: Fp6, rhs: Fp6 | bigint) => {
|
|
283
|
+
if (typeof rhs === 'bigint') {
|
|
284
|
+
return {
|
|
285
|
+
c0: Fp2.mul(c0, rhs),
|
|
286
|
+
c1: Fp2.mul(c1, rhs),
|
|
287
|
+
c2: Fp2.mul(c2, rhs),
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
const { c0: r0, c1: r1, c2: r2 } = rhs;
|
|
291
|
+
const t0 = Fp2.mul(c0, r0); // c0 * o0
|
|
292
|
+
const t1 = Fp2.mul(c1, r1); // c1 * o1
|
|
293
|
+
const t2 = Fp2.mul(c2, r2); // c2 * o2
|
|
294
|
+
return {
|
|
295
|
+
// t0 + (c1 + c2) * (r1 * r2) - (T1 + T2) * (u + 1)
|
|
296
|
+
c0: Fp2.add(
|
|
297
|
+
t0,
|
|
298
|
+
Fp2.mulByNonresidue(Fp2.sub(Fp2.mul(Fp2.add(c1, c2), Fp2.add(r1, r2)), Fp2.add(t1, t2)))
|
|
299
|
+
),
|
|
300
|
+
// (c0 + c1) * (r0 + r1) - (T0 + T1) + T2 * (u + 1)
|
|
301
|
+
c1: Fp2.add(
|
|
302
|
+
Fp2.sub(Fp2.mul(Fp2.add(c0, c1), Fp2.add(r0, r1)), Fp2.add(t0, t1)),
|
|
303
|
+
Fp2.mulByNonresidue(t2)
|
|
304
|
+
),
|
|
305
|
+
// T1 + (c0 + c2) * (r0 + r2) - T0 + T2
|
|
306
|
+
c2: Fp2.sub(Fp2.add(t1, Fp2.mul(Fp2.add(c0, c2), Fp2.add(r0, r2))), Fp2.add(t0, t2)),
|
|
307
|
+
};
|
|
308
|
+
};
|
|
309
|
+
const Fp6Square = ({ c0, c1, c2 }: Fp6) => {
|
|
310
|
+
let t0 = Fp2.sqr(c0); // c0²
|
|
311
|
+
let t1 = Fp2.mul(Fp2.mul(c0, c1), _2n); // 2 * c0 * c1
|
|
312
|
+
let t3 = Fp2.mul(Fp2.mul(c1, c2), _2n); // 2 * c1 * c2
|
|
313
|
+
let t4 = Fp2.sqr(c2); // c2²
|
|
314
|
+
return {
|
|
315
|
+
c0: Fp2.add(Fp2.mulByNonresidue(t3), t0), // T3 * (u + 1) + T0
|
|
316
|
+
c1: Fp2.add(Fp2.mulByNonresidue(t4), t1), // T4 * (u + 1) + T1
|
|
317
|
+
// T1 + (c0 - c1 + c2)² + T3 - T0 - T4
|
|
318
|
+
c2: Fp2.sub(Fp2.sub(Fp2.add(Fp2.add(t1, Fp2.sqr(Fp2.add(Fp2.sub(c0, c1), c2))), t3), t0), t4),
|
|
319
|
+
};
|
|
320
|
+
};
|
|
321
|
+
type Fp6Utils = {
|
|
322
|
+
fromBigSix: (tuple: BigintSix) => Fp6;
|
|
323
|
+
mulByNonresidue: (num: Fp6) => Fp6;
|
|
324
|
+
frobeniusMap(num: Fp6, power: number): Fp6;
|
|
325
|
+
mul1(num: Fp6, b1: Fp2): Fp6;
|
|
326
|
+
mul01(num: Fp6, b0: Fp2, b1: Fp2): Fp6;
|
|
327
|
+
mulByFp2(lhs: Fp6, rhs: Fp2): Fp6;
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
const [FP6_FROBENIUS_COEFFICIENTS_1, FP6_FROBENIUS_COEFFICIENTS_2] = calcFrobeniusCoefficients(
|
|
331
|
+
Fp2,
|
|
332
|
+
Fp2Nonresidue,
|
|
333
|
+
Fp.ORDER,
|
|
334
|
+
6,
|
|
335
|
+
2,
|
|
336
|
+
3
|
|
337
|
+
);
|
|
338
|
+
|
|
339
|
+
const Fp6: mod.IField<Fp6> & Fp6Utils = {
|
|
340
|
+
ORDER: Fp2.ORDER, // TODO: unused, but need to verify
|
|
341
|
+
BITS: 3 * Fp2.BITS,
|
|
342
|
+
BYTES: 3 * Fp2.BYTES,
|
|
343
|
+
MASK: bitMask(3 * Fp2.BITS),
|
|
344
|
+
ZERO: { c0: Fp2.ZERO, c1: Fp2.ZERO, c2: Fp2.ZERO },
|
|
345
|
+
ONE: { c0: Fp2.ONE, c1: Fp2.ZERO, c2: Fp2.ZERO },
|
|
346
|
+
create: (num) => num,
|
|
347
|
+
isValid: ({ c0, c1, c2 }) => Fp2.isValid(c0) && Fp2.isValid(c1) && Fp2.isValid(c2),
|
|
348
|
+
is0: ({ c0, c1, c2 }) => Fp2.is0(c0) && Fp2.is0(c1) && Fp2.is0(c2),
|
|
349
|
+
neg: ({ c0, c1, c2 }) => ({ c0: Fp2.neg(c0), c1: Fp2.neg(c1), c2: Fp2.neg(c2) }),
|
|
350
|
+
eql: ({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) =>
|
|
351
|
+
Fp2.eql(c0, r0) && Fp2.eql(c1, r1) && Fp2.eql(c2, r2),
|
|
352
|
+
sqrt: notImplemented,
|
|
353
|
+
// Do we need division by bigint at all? Should be done via order:
|
|
354
|
+
div: (lhs, rhs) =>
|
|
355
|
+
Fp6.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : Fp6.inv(rhs)),
|
|
356
|
+
pow: (num, power) => mod.FpPow(Fp6, num, power),
|
|
357
|
+
invertBatch: (nums) => mod.FpInvertBatch(Fp6, nums),
|
|
358
|
+
// Normalized
|
|
359
|
+
add: Fp6Add,
|
|
360
|
+
sub: Fp6Subtract,
|
|
361
|
+
mul: Fp6Multiply,
|
|
362
|
+
sqr: Fp6Square,
|
|
363
|
+
// NonNormalized stuff
|
|
364
|
+
addN: Fp6Add,
|
|
365
|
+
subN: Fp6Subtract,
|
|
366
|
+
mulN: Fp6Multiply,
|
|
367
|
+
sqrN: Fp6Square,
|
|
368
|
+
|
|
369
|
+
inv: ({ c0, c1, c2 }) => {
|
|
370
|
+
let t0 = Fp2.sub(Fp2.sqr(c0), Fp2.mulByNonresidue(Fp2.mul(c2, c1))); // c0² - c2 * c1 * (u + 1)
|
|
371
|
+
let t1 = Fp2.sub(Fp2.mulByNonresidue(Fp2.sqr(c2)), Fp2.mul(c0, c1)); // c2² * (u + 1) - c0 * c1
|
|
372
|
+
let t2 = Fp2.sub(Fp2.sqr(c1), Fp2.mul(c0, c2)); // c1² - c0 * c2
|
|
373
|
+
// 1/(((c2 * T1 + c1 * T2) * v) + c0 * T0)
|
|
374
|
+
let t4 = Fp2.inv(
|
|
375
|
+
Fp2.add(Fp2.mulByNonresidue(Fp2.add(Fp2.mul(c2, t1), Fp2.mul(c1, t2))), Fp2.mul(c0, t0))
|
|
376
|
+
);
|
|
377
|
+
return { c0: Fp2.mul(t4, t0), c1: Fp2.mul(t4, t1), c2: Fp2.mul(t4, t2) };
|
|
378
|
+
},
|
|
379
|
+
// Bytes utils
|
|
380
|
+
fromBytes: (b: Uint8Array): Fp6 => {
|
|
381
|
+
if (b.length !== Fp6.BYTES) throw new Error(`fromBytes wrong length=${b.length}`);
|
|
382
|
+
return {
|
|
383
|
+
c0: Fp2.fromBytes(b.subarray(0, Fp2.BYTES)),
|
|
384
|
+
c1: Fp2.fromBytes(b.subarray(Fp2.BYTES, 2 * Fp2.BYTES)),
|
|
385
|
+
c2: Fp2.fromBytes(b.subarray(2 * Fp2.BYTES)),
|
|
386
|
+
};
|
|
387
|
+
},
|
|
388
|
+
toBytes: ({ c0, c1, c2 }): Uint8Array =>
|
|
389
|
+
concatBytes(Fp2.toBytes(c0), Fp2.toBytes(c1), Fp2.toBytes(c2)),
|
|
390
|
+
cmov: ({ c0, c1, c2 }: Fp6, { c0: r0, c1: r1, c2: r2 }: Fp6, c) => ({
|
|
391
|
+
c0: Fp2.cmov(c0, r0, c),
|
|
392
|
+
c1: Fp2.cmov(c1, r1, c),
|
|
393
|
+
c2: Fp2.cmov(c2, r2, c),
|
|
394
|
+
}),
|
|
395
|
+
fromBigSix: (t: BigintSix): Fp6 => {
|
|
396
|
+
if (!Array.isArray(t) || t.length !== 6) throw new Error('Invalid Fp6 usage');
|
|
397
|
+
return {
|
|
398
|
+
c0: Fp2.fromBigTuple(t.slice(0, 2)),
|
|
399
|
+
c1: Fp2.fromBigTuple(t.slice(2, 4)),
|
|
400
|
+
c2: Fp2.fromBigTuple(t.slice(4, 6)),
|
|
401
|
+
};
|
|
402
|
+
},
|
|
403
|
+
frobeniusMap: ({ c0, c1, c2 }, power: number) => ({
|
|
404
|
+
c0: Fp2.frobeniusMap(c0, power),
|
|
405
|
+
c1: Fp2.mul(Fp2.frobeniusMap(c1, power), FP6_FROBENIUS_COEFFICIENTS_1[power % 6]),
|
|
406
|
+
c2: Fp2.mul(Fp2.frobeniusMap(c2, power), FP6_FROBENIUS_COEFFICIENTS_2[power % 6]),
|
|
407
|
+
}),
|
|
408
|
+
mulByFp2: ({ c0, c1, c2 }, rhs: Fp2): Fp6 => ({
|
|
409
|
+
c0: Fp2.mul(c0, rhs),
|
|
410
|
+
c1: Fp2.mul(c1, rhs),
|
|
411
|
+
c2: Fp2.mul(c2, rhs),
|
|
412
|
+
}),
|
|
413
|
+
mulByNonresidue: ({ c0, c1, c2 }) => ({ c0: Fp2.mulByNonresidue(c2), c1: c0, c2: c1 }),
|
|
414
|
+
// Sparse multiplication
|
|
415
|
+
mul1: ({ c0, c1, c2 }, b1: Fp2): Fp6 => ({
|
|
416
|
+
c0: Fp2.mulByNonresidue(Fp2.mul(c2, b1)),
|
|
417
|
+
c1: Fp2.mul(c0, b1),
|
|
418
|
+
c2: Fp2.mul(c1, b1),
|
|
419
|
+
}),
|
|
420
|
+
// Sparse multiplication
|
|
421
|
+
mul01({ c0, c1, c2 }, b0: Fp2, b1: Fp2): Fp6 {
|
|
422
|
+
let t0 = Fp2.mul(c0, b0); // c0 * b0
|
|
423
|
+
let t1 = Fp2.mul(c1, b1); // c1 * b1
|
|
424
|
+
return {
|
|
425
|
+
// ((c1 + c2) * b1 - T1) * (u + 1) + T0
|
|
426
|
+
c0: Fp2.add(Fp2.mulByNonresidue(Fp2.sub(Fp2.mul(Fp2.add(c1, c2), b1), t1)), t0),
|
|
427
|
+
// (b0 + b1) * (c0 + c1) - T0 - T1
|
|
428
|
+
c1: Fp2.sub(Fp2.sub(Fp2.mul(Fp2.add(b0, b1), Fp2.add(c0, c1)), t0), t1),
|
|
429
|
+
// (c0 + c2) * b0 - T0 + T1
|
|
430
|
+
c2: Fp2.add(Fp2.sub(Fp2.mul(Fp2.add(c0, c2), b0), t0), t1),
|
|
431
|
+
};
|
|
432
|
+
},
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
// Fp12
|
|
436
|
+
const FP12_FROBENIUS_COEFFICIENTS = calcFrobeniusCoefficients(
|
|
437
|
+
Fp2,
|
|
438
|
+
Fp2Nonresidue,
|
|
439
|
+
Fp.ORDER,
|
|
440
|
+
12,
|
|
441
|
+
1,
|
|
442
|
+
6
|
|
443
|
+
)[0];
|
|
444
|
+
|
|
445
|
+
const Fp12Add = ({ c0, c1 }: Fp12, { c0: r0, c1: r1 }: Fp12) => ({
|
|
446
|
+
c0: Fp6.add(c0, r0),
|
|
447
|
+
c1: Fp6.add(c1, r1),
|
|
448
|
+
});
|
|
449
|
+
const Fp12Subtract = ({ c0, c1 }: Fp12, { c0: r0, c1: r1 }: Fp12) => ({
|
|
450
|
+
c0: Fp6.sub(c0, r0),
|
|
451
|
+
c1: Fp6.sub(c1, r1),
|
|
452
|
+
});
|
|
453
|
+
const Fp12Multiply = ({ c0, c1 }: Fp12, rhs: Fp12 | bigint) => {
|
|
454
|
+
if (typeof rhs === 'bigint') return { c0: Fp6.mul(c0, rhs), c1: Fp6.mul(c1, rhs) };
|
|
455
|
+
let { c0: r0, c1: r1 } = rhs;
|
|
456
|
+
let t1 = Fp6.mul(c0, r0); // c0 * r0
|
|
457
|
+
let t2 = Fp6.mul(c1, r1); // c1 * r1
|
|
458
|
+
return {
|
|
459
|
+
c0: Fp6.add(t1, Fp6.mulByNonresidue(t2)), // T1 + T2 * v
|
|
460
|
+
// (c0 + c1) * (r0 + r1) - (T1 + T2)
|
|
461
|
+
c1: Fp6.sub(Fp6.mul(Fp6.add(c0, c1), Fp6.add(r0, r1)), Fp6.add(t1, t2)),
|
|
462
|
+
};
|
|
463
|
+
};
|
|
464
|
+
const Fp12Square = ({ c0, c1 }: Fp12) => {
|
|
465
|
+
let ab = Fp6.mul(c0, c1); // c0 * c1
|
|
466
|
+
return {
|
|
467
|
+
// (c1 * v + c0) * (c0 + c1) - AB - AB * v
|
|
468
|
+
c0: Fp6.sub(
|
|
469
|
+
Fp6.sub(Fp6.mul(Fp6.add(Fp6.mulByNonresidue(c1), c0), Fp6.add(c0, c1)), ab),
|
|
470
|
+
Fp6.mulByNonresidue(ab)
|
|
471
|
+
),
|
|
472
|
+
c1: Fp6.add(ab, ab),
|
|
473
|
+
}; // AB + AB
|
|
474
|
+
};
|
|
475
|
+
function Fp4Square(a: Fp2, b: Fp2): { first: Fp2; second: Fp2 } {
|
|
476
|
+
const a2 = Fp2.sqr(a);
|
|
477
|
+
const b2 = Fp2.sqr(b);
|
|
478
|
+
return {
|
|
479
|
+
first: Fp2.add(Fp2.mulByNonresidue(b2), a2), // b² * Nonresidue + a²
|
|
480
|
+
second: Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(a, b)), a2), b2), // (a + b)² - a² - b²
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
type Fp12Utils = {
|
|
484
|
+
fromBigTwelve: (t: BigintTwelve) => Fp12;
|
|
485
|
+
frobeniusMap(num: Fp12, power: number): Fp12;
|
|
486
|
+
mul014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
|
|
487
|
+
mul034(num: Fp12, o0: Fp2, o3: Fp2, o4: Fp2): Fp12;
|
|
488
|
+
mulByFp2(lhs: Fp12, rhs: Fp2): Fp12;
|
|
489
|
+
conjugate(num: Fp12): Fp12;
|
|
490
|
+
finalExponentiate(num: Fp12): Fp12;
|
|
491
|
+
_cyclotomicSquare(num: Fp12): Fp12;
|
|
492
|
+
_cyclotomicExp(num: Fp12, n: bigint): Fp12;
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
const Fp12: mod.IField<Fp12> & Fp12Utils = {
|
|
496
|
+
ORDER: Fp2.ORDER, // TODO: unused, but need to verify
|
|
497
|
+
BITS: 2 * Fp2.BITS,
|
|
498
|
+
BYTES: 2 * Fp2.BYTES,
|
|
499
|
+
MASK: bitMask(2 * Fp2.BITS),
|
|
500
|
+
ZERO: { c0: Fp6.ZERO, c1: Fp6.ZERO },
|
|
501
|
+
ONE: { c0: Fp6.ONE, c1: Fp6.ZERO },
|
|
502
|
+
create: (num) => num,
|
|
503
|
+
isValid: ({ c0, c1 }) => Fp6.isValid(c0) && Fp6.isValid(c1),
|
|
504
|
+
is0: ({ c0, c1 }) => Fp6.is0(c0) && Fp6.is0(c1),
|
|
505
|
+
neg: ({ c0, c1 }) => ({ c0: Fp6.neg(c0), c1: Fp6.neg(c1) }),
|
|
506
|
+
eql: ({ c0, c1 }, { c0: r0, c1: r1 }) => Fp6.eql(c0, r0) && Fp6.eql(c1, r1),
|
|
507
|
+
sqrt: notImplemented,
|
|
508
|
+
inv: ({ c0, c1 }) => {
|
|
509
|
+
let t = Fp6.inv(Fp6.sub(Fp6.sqr(c0), Fp6.mulByNonresidue(Fp6.sqr(c1)))); // 1 / (c0² - c1² * v)
|
|
510
|
+
return { c0: Fp6.mul(c0, t), c1: Fp6.neg(Fp6.mul(c1, t)) }; // ((C0 * T) * T) + (-C1 * T) * w
|
|
511
|
+
},
|
|
512
|
+
div: (lhs, rhs) =>
|
|
513
|
+
Fp12.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : Fp12.inv(rhs)),
|
|
514
|
+
pow: (num, power) => mod.FpPow(Fp12, num, power),
|
|
515
|
+
invertBatch: (nums) => mod.FpInvertBatch(Fp12, nums),
|
|
516
|
+
// Normalized
|
|
517
|
+
add: Fp12Add,
|
|
518
|
+
sub: Fp12Subtract,
|
|
519
|
+
mul: Fp12Multiply,
|
|
520
|
+
sqr: Fp12Square,
|
|
521
|
+
// NonNormalized stuff
|
|
522
|
+
addN: Fp12Add,
|
|
523
|
+
subN: Fp12Subtract,
|
|
524
|
+
mulN: Fp12Multiply,
|
|
525
|
+
sqrN: Fp12Square,
|
|
526
|
+
|
|
527
|
+
// Bytes utils
|
|
528
|
+
fromBytes: (b: Uint8Array): Fp12 => {
|
|
529
|
+
if (b.length !== Fp12.BYTES) throw new Error(`fromBytes wrong length=${b.length}`);
|
|
530
|
+
return {
|
|
531
|
+
c0: Fp6.fromBytes(b.subarray(0, Fp6.BYTES)),
|
|
532
|
+
c1: Fp6.fromBytes(b.subarray(Fp6.BYTES)),
|
|
533
|
+
};
|
|
534
|
+
},
|
|
535
|
+
toBytes: ({ c0, c1 }): Uint8Array => concatBytes(Fp6.toBytes(c0), Fp6.toBytes(c1)),
|
|
536
|
+
cmov: ({ c0, c1 }, { c0: r0, c1: r1 }, c) => ({
|
|
537
|
+
c0: Fp6.cmov(c0, r0, c),
|
|
538
|
+
c1: Fp6.cmov(c1, r1, c),
|
|
539
|
+
}),
|
|
540
|
+
// Utils
|
|
541
|
+
// toString() {
|
|
542
|
+
// return `Fp12(${this.c0} + ${this.c1} * w)`;
|
|
543
|
+
// },
|
|
544
|
+
// fromTuple(c: [Fp6, Fp6]) {
|
|
545
|
+
// return new Fp12(...c);
|
|
546
|
+
// }
|
|
547
|
+
fromBigTwelve: (t: BigintTwelve): Fp12 => ({
|
|
548
|
+
c0: Fp6.fromBigSix(t.slice(0, 6) as BigintSix),
|
|
549
|
+
c1: Fp6.fromBigSix(t.slice(6, 12) as BigintSix),
|
|
550
|
+
}),
|
|
551
|
+
// Raises to q**i -th power
|
|
552
|
+
frobeniusMap(lhs, power: number) {
|
|
553
|
+
const { c0, c1, c2 } = Fp6.frobeniusMap(lhs.c1, power);
|
|
554
|
+
const coeff = FP12_FROBENIUS_COEFFICIENTS[power % 12];
|
|
555
|
+
return {
|
|
556
|
+
c0: Fp6.frobeniusMap(lhs.c0, power),
|
|
557
|
+
c1: Fp6.create({
|
|
558
|
+
c0: Fp2.mul(c0, coeff),
|
|
559
|
+
c1: Fp2.mul(c1, coeff),
|
|
560
|
+
c2: Fp2.mul(c2, coeff),
|
|
561
|
+
}),
|
|
562
|
+
};
|
|
563
|
+
},
|
|
564
|
+
mulByFp2: ({ c0, c1 }, rhs: Fp2): Fp12 => ({
|
|
565
|
+
c0: Fp6.mulByFp2(c0, rhs),
|
|
566
|
+
c1: Fp6.mulByFp2(c1, rhs),
|
|
567
|
+
}),
|
|
568
|
+
conjugate: ({ c0, c1 }): Fp12 => ({ c0, c1: Fp6.neg(c1) }),
|
|
569
|
+
// Sparse multiplication
|
|
570
|
+
mul014: ({ c0, c1 }, o0: Fp2, o1: Fp2, o4: Fp2) => {
|
|
571
|
+
let t0 = Fp6.mul01(c0, o0, o1);
|
|
572
|
+
let t1 = Fp6.mul1(c1, o4);
|
|
573
|
+
return {
|
|
574
|
+
c0: Fp6.add(Fp6.mulByNonresidue(t1), t0), // T1 * v + T0
|
|
575
|
+
// (c1 + c0) * [o0, o1+o4] - T0 - T1
|
|
576
|
+
c1: Fp6.sub(Fp6.sub(Fp6.mul01(Fp6.add(c1, c0), o0, Fp2.add(o1, o4)), t0), t1),
|
|
577
|
+
};
|
|
578
|
+
},
|
|
579
|
+
mul034: ({ c0, c1 }, o0: Fp2, o3: Fp2, o4: Fp2) => {
|
|
580
|
+
const a = Fp6.create({
|
|
581
|
+
c0: Fp2.mul(c0.c0, o0),
|
|
582
|
+
c1: Fp2.mul(c0.c1, o0),
|
|
583
|
+
c2: Fp2.mul(c0.c2, o0),
|
|
584
|
+
});
|
|
585
|
+
const b = Fp6.mul01(c1, o3, o4);
|
|
586
|
+
const e = Fp6.mul01(Fp6.add(c0, c1), Fp2.add(o0, o3), o4);
|
|
587
|
+
return {
|
|
588
|
+
c0: Fp6.add(Fp6.mulByNonresidue(b), a),
|
|
589
|
+
c1: Fp6.sub(e, Fp6.add(a, b)),
|
|
590
|
+
};
|
|
591
|
+
},
|
|
592
|
+
|
|
593
|
+
// A cyclotomic group is a subgroup of Fp^n defined by
|
|
594
|
+
// GΦₙ(p) = {α ∈ Fpⁿ : α^Φₙ(p) = 1}
|
|
595
|
+
// The result of any pairing is in a cyclotomic subgroup
|
|
596
|
+
// https://eprint.iacr.org/2009/565.pdf
|
|
597
|
+
_cyclotomicSquare: opts.Fp12cyclotomicSquare,
|
|
598
|
+
_cyclotomicExp: opts.Fp12cyclotomicExp,
|
|
599
|
+
// https://eprint.iacr.org/2010/354.pdf
|
|
600
|
+
// https://eprint.iacr.org/2009/565.pdf
|
|
601
|
+
finalExponentiate: opts.Fp12finalExponentiate,
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
return { Fp, Fp2, Fp6, Fp4Square, Fp12 };
|
|
605
|
+
}
|
package/src/abstract/utils.ts
CHANGED
|
@@ -27,6 +27,11 @@ export function abytes(item: unknown): void {
|
|
|
27
27
|
if (!isBytes(item)) throw new Error('Uint8Array expected');
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
export function abool(title: string, value: boolean): void {
|
|
31
|
+
if (typeof value !== 'boolean')
|
|
32
|
+
throw new Error(`${title} must be valid boolean, got "${value}".`);
|
|
33
|
+
}
|
|
34
|
+
|
|
30
35
|
// Array where index 0xf0 (240) is mapped to string 'f0'
|
|
31
36
|
const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) =>
|
|
32
37
|
i.toString(16).padStart(2, '0')
|
|
@@ -174,6 +179,28 @@ export function utf8ToBytes(str: string): Uint8Array {
|
|
|
174
179
|
return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809
|
|
175
180
|
}
|
|
176
181
|
|
|
182
|
+
// Is positive bigint
|
|
183
|
+
const isPosBig = (n: bigint) => typeof n === 'bigint' && _0n <= n;
|
|
184
|
+
|
|
185
|
+
export function inRange(n: bigint, min: bigint, max: bigint) {
|
|
186
|
+
return isPosBig(n) && isPosBig(min) && isPosBig(max) && min <= n && n < max;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Asserts min <= n < max. NOTE: It's < max and not <= max.
|
|
191
|
+
* @example
|
|
192
|
+
* aInRange('x', x, 1n, 256n); // would assume x is in (1n..255n)
|
|
193
|
+
*/
|
|
194
|
+
export function aInRange(title: string, n: bigint, min: bigint, max: bigint) {
|
|
195
|
+
// Why min <= n < max and not a (min < n < max) OR b (min <= n <= max)?
|
|
196
|
+
// consider P=256n, min=0n, max=P
|
|
197
|
+
// - a for min=0 would require -1: `inRange('x', x, -1n, P)`
|
|
198
|
+
// - b would commonly require subtraction: `inRange('x', x, 0n, P - 1n)`
|
|
199
|
+
// - our way is the cleanest: `inRange('x', x, 0n, P)
|
|
200
|
+
if (!inRange(n, min, max))
|
|
201
|
+
throw new Error(`expected valid ${title}: ${min} <= n < ${max}, got ${typeof n} ${n}`);
|
|
202
|
+
}
|
|
203
|
+
|
|
177
204
|
// Bit operations
|
|
178
205
|
|
|
179
206
|
/**
|
|
@@ -317,3 +344,25 @@ export function validateObject<T extends Record<string, any>>(
|
|
|
317
344
|
// const z2 = validateObject(o, { a: 'isSafeInteger' }, { c: 'zz' });
|
|
318
345
|
// const z3 = validateObject(o, { test: 'boolean', z: 'bug' });
|
|
319
346
|
// const z4 = validateObject(o, { a: 'boolean', z: 'bug' });
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* throws not implemented error
|
|
350
|
+
*/
|
|
351
|
+
export const notImplemented = () => {
|
|
352
|
+
throw new Error('not implemented');
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Memoizes (caches) computation result.
|
|
357
|
+
* Uses WeakMap: the value is going auto-cleaned by GC after last reference is removed.
|
|
358
|
+
*/
|
|
359
|
+
export function memoized<T extends object, R, O extends any[]>(fn: (arg: T, ...args: O) => R) {
|
|
360
|
+
const map = new WeakMap<T, R>();
|
|
361
|
+
return (arg: T, ...args: O): R => {
|
|
362
|
+
const val = map.get(arg);
|
|
363
|
+
if (val !== undefined) return val;
|
|
364
|
+
const computed = fn(arg, ...args);
|
|
365
|
+
map.set(arg, computed);
|
|
366
|
+
return computed;
|
|
367
|
+
};
|
|
368
|
+
}
|