@noble/curves 2.0.1 → 2.2.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 +214 -122
- package/abstract/bls.d.ts +299 -16
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/bls.js +82 -22
- package/abstract/bls.js.map +1 -1
- package/abstract/curve.d.ts +274 -27
- package/abstract/curve.d.ts.map +1 -1
- package/abstract/curve.js +177 -23
- package/abstract/curve.js.map +1 -1
- package/abstract/edwards.d.ts +166 -30
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +221 -86
- package/abstract/edwards.js.map +1 -1
- package/abstract/fft.d.ts +322 -10
- package/abstract/fft.d.ts.map +1 -1
- package/abstract/fft.js +154 -12
- package/abstract/fft.js.map +1 -1
- package/abstract/frost.d.ts +293 -0
- package/abstract/frost.d.ts.map +1 -0
- package/abstract/frost.js +704 -0
- package/abstract/frost.js.map +1 -0
- package/abstract/hash-to-curve.d.ts +173 -24
- package/abstract/hash-to-curve.d.ts.map +1 -1
- package/abstract/hash-to-curve.js +170 -31
- package/abstract/hash-to-curve.js.map +1 -1
- package/abstract/modular.d.ts +429 -37
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +414 -119
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts +83 -12
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +32 -7
- package/abstract/montgomery.js.map +1 -1
- package/abstract/oprf.d.ts +164 -91
- package/abstract/oprf.d.ts.map +1 -1
- package/abstract/oprf.js +88 -29
- package/abstract/oprf.js.map +1 -1
- package/abstract/poseidon.d.ts +138 -7
- package/abstract/poseidon.d.ts.map +1 -1
- package/abstract/poseidon.js +178 -15
- package/abstract/poseidon.js.map +1 -1
- package/abstract/tower.d.ts +122 -3
- package/abstract/tower.d.ts.map +1 -1
- package/abstract/tower.js +323 -139
- package/abstract/tower.js.map +1 -1
- package/abstract/weierstrass.d.ts +339 -76
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +395 -205
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts +16 -2
- package/bls12-381.d.ts.map +1 -1
- package/bls12-381.js +199 -209
- package/bls12-381.js.map +1 -1
- package/bn254.d.ts +11 -2
- package/bn254.d.ts.map +1 -1
- package/bn254.js +93 -38
- package/bn254.js.map +1 -1
- package/ed25519.d.ts +125 -14
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +202 -40
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +108 -14
- package/ed448.d.ts.map +1 -1
- package/ed448.js +194 -42
- package/ed448.js.map +1 -1
- package/index.js +7 -1
- package/index.js.map +1 -1
- package/misc.d.ts +106 -7
- package/misc.d.ts.map +1 -1
- package/misc.js +141 -32
- package/misc.js.map +1 -1
- package/nist.d.ts +112 -11
- package/nist.d.ts.map +1 -1
- package/nist.js +139 -17
- package/nist.js.map +1 -1
- package/package.json +11 -6
- package/secp256k1.d.ts +92 -15
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +211 -28
- package/secp256k1.js.map +1 -1
- package/src/abstract/bls.ts +350 -67
- package/src/abstract/curve.ts +327 -44
- package/src/abstract/edwards.ts +367 -143
- package/src/abstract/fft.ts +369 -36
- package/src/abstract/frost.ts +1092 -0
- package/src/abstract/hash-to-curve.ts +255 -56
- package/src/abstract/modular.ts +591 -144
- package/src/abstract/montgomery.ts +114 -30
- package/src/abstract/oprf.ts +383 -194
- package/src/abstract/poseidon.ts +235 -35
- package/src/abstract/tower.ts +428 -159
- package/src/abstract/weierstrass.ts +710 -312
- package/src/bls12-381.ts +239 -236
- package/src/bn254.ts +107 -46
- package/src/ed25519.ts +227 -55
- package/src/ed448.ts +227 -57
- package/src/index.ts +7 -1
- package/src/misc.ts +154 -35
- package/src/nist.ts +143 -20
- package/src/secp256k1.ts +284 -41
- package/src/utils.ts +583 -81
- package/src/webcrypto.ts +302 -73
- package/utils.d.ts +457 -24
- package/utils.d.ts.map +1 -1
- package/utils.js +410 -53
- package/utils.js.map +1 -1
- package/webcrypto.d.ts +167 -25
- package/webcrypto.d.ts.map +1 -1
- package/webcrypto.js +165 -58
- package/webcrypto.js.map +1 -1
package/src/abstract/tower.ts
CHANGED
|
@@ -10,80 +10,161 @@
|
|
|
10
10
|
* @module
|
|
11
11
|
*/
|
|
12
12
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
abytes,
|
|
15
|
+
aInRange,
|
|
16
|
+
asafenumber,
|
|
17
|
+
bitGet,
|
|
18
|
+
bitLen,
|
|
19
|
+
concatBytes,
|
|
20
|
+
notImplemented,
|
|
21
|
+
validateObject,
|
|
22
|
+
type TArg,
|
|
23
|
+
type TRet,
|
|
24
|
+
} from '../utils.ts';
|
|
14
25
|
import * as mod from './modular.ts';
|
|
15
26
|
import type { WeierstrassPoint, WeierstrassPointCons } from './weierstrass.ts';
|
|
16
27
|
|
|
17
28
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
|
18
29
|
// prettier-ignore
|
|
19
|
-
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
|
|
30
|
+
const _0n = /* @__PURE__ */ BigInt(0), _1n = /* @__PURE__ */ BigInt(1), _2n = /* @__PURE__ */ BigInt(2), _3n = /* @__PURE__ */ BigInt(3), _6n = /* @__PURE__ */ BigInt(6), _12n = /* @__PURE__ */ BigInt(12);
|
|
20
31
|
|
|
21
32
|
// Fp₂ over complex plane
|
|
33
|
+
/** Pair of bigints used for quadratic-extension tuples. */
|
|
22
34
|
export type BigintTuple = [bigint, bigint];
|
|
35
|
+
/** Prime-field element. */
|
|
23
36
|
export type Fp = bigint;
|
|
24
37
|
// Finite extension field over irreducible polynominal.
|
|
25
38
|
// Fp(u) / (u² - β) where β = -1
|
|
26
|
-
|
|
39
|
+
/** Quadratic-extension field element `c0 + c1 * u`. */
|
|
40
|
+
export type Fp2 = {
|
|
41
|
+
/** Real component. */
|
|
42
|
+
c0: bigint;
|
|
43
|
+
/** Imaginary component. */
|
|
44
|
+
c1: bigint;
|
|
45
|
+
};
|
|
46
|
+
/** Six bigints used for sextic-extension tuples. */
|
|
27
47
|
export type BigintSix = [bigint, bigint, bigint, bigint, bigint, bigint];
|
|
28
|
-
|
|
29
|
-
export type
|
|
48
|
+
/** Sextic-extension field element `c0 + c1 * v + c2 * v^2`. */
|
|
49
|
+
export type Fp6 = {
|
|
50
|
+
/** Constant coefficient. */
|
|
51
|
+
c0: Fp2;
|
|
52
|
+
/** Linear coefficient. */
|
|
53
|
+
c1: Fp2;
|
|
54
|
+
/** Quadratic coefficient. */
|
|
55
|
+
c2: Fp2;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Degree-12 extension field element `c0 + c1 * w`.
|
|
59
|
+
* Fp₁₂ = Fp₆² over Fp₂³, with Fp₆(w) / (w² - γ) where γ = v.
|
|
60
|
+
*/
|
|
61
|
+
export type Fp12 = {
|
|
62
|
+
/** Constant coefficient. */
|
|
63
|
+
c0: Fp6;
|
|
64
|
+
/** Linear coefficient. */
|
|
65
|
+
c1: Fp6;
|
|
66
|
+
};
|
|
30
67
|
// prettier-ignore
|
|
68
|
+
/** Twelve bigints used for degree-12 extension tuples. */
|
|
31
69
|
export type BigintTwelve = [
|
|
32
70
|
bigint, bigint, bigint, bigint, bigint, bigint,
|
|
33
71
|
bigint, bigint, bigint, bigint, bigint, bigint
|
|
34
72
|
];
|
|
35
73
|
|
|
74
|
+
const isObj = (value: unknown): value is Record<string, unknown> =>
|
|
75
|
+
!!value && typeof value === 'object';
|
|
76
|
+
|
|
77
|
+
/** BLS-friendly helpers on top of the quadratic extension field. */
|
|
36
78
|
export type Fp2Bls = mod.IField<Fp2> & {
|
|
79
|
+
/** Underlying prime field. */
|
|
37
80
|
Fp: mod.IField<Fp>;
|
|
81
|
+
/** Apply one Frobenius map. */
|
|
38
82
|
frobeniusMap(num: Fp2, power: number): Fp2;
|
|
83
|
+
/** Build one field element from a raw bigint tuple. */
|
|
39
84
|
fromBigTuple(num: BigintTuple): Fp2;
|
|
85
|
+
/** Multiply by the curve `b` constant. */
|
|
40
86
|
mulByB: (num: Fp2) => Fp2;
|
|
87
|
+
/** Multiply by the quadratic non-residue. */
|
|
41
88
|
mulByNonresidue: (num: Fp2) => Fp2;
|
|
89
|
+
/** Split one quadratic element into real and imaginary components. */
|
|
42
90
|
reim: (num: Fp2) => { re: Fp; im: Fp };
|
|
91
|
+
/** Specialized helper used by sextic squaring formulas. */
|
|
43
92
|
Fp4Square: (a: Fp2, b: Fp2) => { first: Fp2; second: Fp2 };
|
|
93
|
+
/** Quadratic non-residue used by the extension. */
|
|
44
94
|
NONRESIDUE: Fp2;
|
|
45
95
|
};
|
|
46
96
|
|
|
97
|
+
/** BLS-friendly helpers on top of the sextic extension field. */
|
|
47
98
|
export type Fp6Bls = mod.IField<Fp6> & {
|
|
99
|
+
/** Underlying quadratic extension field. */
|
|
48
100
|
Fp2: Fp2Bls;
|
|
101
|
+
/** Apply one Frobenius map. */
|
|
49
102
|
frobeniusMap(num: Fp6, power: number): Fp6;
|
|
103
|
+
/** Build one field element from a raw six-bigint tuple. */
|
|
50
104
|
fromBigSix: (tuple: BigintSix) => Fp6;
|
|
105
|
+
/** Multiply by a sparse `(0, b1, 0)` sextic element. */
|
|
51
106
|
mul1(num: Fp6, b1: Fp2): Fp6;
|
|
107
|
+
/** Multiply by a sparse `(b0, b1, 0)` sextic element. */
|
|
52
108
|
mul01(num: Fp6, b0: Fp2, b1: Fp2): Fp6;
|
|
109
|
+
/** Multiply by one quadratic-extension element. */
|
|
53
110
|
mulByFp2(lhs: Fp6, rhs: Fp2): Fp6;
|
|
111
|
+
/** Multiply by the sextic non-residue. */
|
|
54
112
|
mulByNonresidue: (num: Fp6) => Fp6;
|
|
55
113
|
};
|
|
56
114
|
|
|
115
|
+
/** BLS-friendly helpers on top of the degree-12 extension field. */
|
|
57
116
|
export type Fp12Bls = mod.IField<Fp12> & {
|
|
117
|
+
/** Underlying sextic extension field. */
|
|
58
118
|
Fp6: Fp6Bls;
|
|
119
|
+
/** Apply one Frobenius map. */
|
|
59
120
|
frobeniusMap(num: Fp12, power: number): Fp12;
|
|
121
|
+
/** Build one field element from a raw twelve-bigint tuple. */
|
|
60
122
|
fromBigTwelve: (t: BigintTwelve) => Fp12;
|
|
123
|
+
/** Multiply by a sparse `(o0, o1, 0, 0, o4, 0)` element. */
|
|
61
124
|
mul014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
|
|
125
|
+
/** Multiply by a sparse `(o0, 0, 0, o3, o4, 0)` element. */
|
|
62
126
|
mul034(num: Fp12, o0: Fp2, o3: Fp2, o4: Fp2): Fp12;
|
|
127
|
+
/** Multiply by one quadratic-extension element. */
|
|
63
128
|
mulByFp2(lhs: Fp12, rhs: Fp2): Fp12;
|
|
129
|
+
/** Conjugate one degree-12 element. */
|
|
64
130
|
conjugate(num: Fp12): Fp12;
|
|
131
|
+
/** Apply the final exponentiation from pairing arithmetic. */
|
|
65
132
|
finalExponentiate(num: Fp12): Fp12;
|
|
133
|
+
/** Apply one cyclotomic square. */
|
|
66
134
|
_cyclotomicSquare(num: Fp12): Fp12;
|
|
135
|
+
/** Apply one cyclotomic exponentiation. */
|
|
67
136
|
_cyclotomicExp(num: Fp12, n: bigint): Fp12;
|
|
68
137
|
};
|
|
69
138
|
|
|
70
139
|
function calcFrobeniusCoefficients<T>(
|
|
71
|
-
Fp: mod.IField<T
|
|
140
|
+
Fp: TArg<mod.IField<T>>,
|
|
72
141
|
nonResidue: T,
|
|
73
142
|
modulus: bigint,
|
|
74
143
|
degree: number,
|
|
75
144
|
num: number = 1,
|
|
76
145
|
divisor?: number
|
|
77
|
-
) {
|
|
146
|
+
): T[][] {
|
|
147
|
+
asafenumber(num, 'num');
|
|
148
|
+
const F = Fp as mod.IField<T>;
|
|
149
|
+
// Generic callers can hit empty / fractional row counts through `__TEST`; fail closed instead of
|
|
150
|
+
// silently returning `[]` or deriving extra Frobenius rows from a truncated loop bound.
|
|
151
|
+
if (num <= 0)
|
|
152
|
+
throw new Error('calcFrobeniusCoefficients: expected positive row count, got ' + num);
|
|
78
153
|
const _divisor = BigInt(divisor === undefined ? degree : divisor);
|
|
79
154
|
const towerModulus: any = modulus ** BigInt(degree);
|
|
80
155
|
const res: T[][] = [];
|
|
156
|
+
// Derive tower-basis multipliers for the `p^k` Frobenius action. The
|
|
157
|
+
// divisions below are expected to be exact for the chosen tower parameters.
|
|
81
158
|
for (let i = 0; i < num; i++) {
|
|
82
159
|
const a = BigInt(i + 1);
|
|
83
160
|
const powers: T[] = [];
|
|
84
161
|
for (let j = 0, qPower = _1n; j < degree; j++) {
|
|
85
|
-
const
|
|
86
|
-
|
|
162
|
+
const numer = a * qPower - a;
|
|
163
|
+
// Shipped towers divide cleanly here, but generic callers can pick bad
|
|
164
|
+
// params. Bigint division would floor and derive the wrong Frobenius table.
|
|
165
|
+
if (numer % _divisor) throw new Error('calcFrobeniusCoefficients: inexact tower exponent');
|
|
166
|
+
const power = (numer / _divisor) % towerModulus;
|
|
167
|
+
powers.push(F.pow(nonResidue, power));
|
|
87
168
|
qPower *= modulus;
|
|
88
169
|
}
|
|
89
170
|
res.push(powers);
|
|
@@ -91,11 +172,35 @@ function calcFrobeniusCoefficients<T>(
|
|
|
91
172
|
return res;
|
|
92
173
|
}
|
|
93
174
|
|
|
175
|
+
export const __TEST: { calcFrobeniusCoefficients: typeof calcFrobeniusCoefficients } =
|
|
176
|
+
/* @__PURE__ */ Object.freeze({
|
|
177
|
+
calcFrobeniusCoefficients,
|
|
178
|
+
});
|
|
179
|
+
|
|
94
180
|
// This works same at least for bls12-381, bn254 and bls12-377
|
|
181
|
+
/**
|
|
182
|
+
* @param Fp - Base field implementation.
|
|
183
|
+
* @param Fp2 - Quadratic extension field.
|
|
184
|
+
* @param base - Twist-specific Frobenius base whose powers yield the `c1` / `c2` constants.
|
|
185
|
+
* BLS12-381 uses `1 / NONRESIDUE`; BN254 uses `NONRESIDUE`.
|
|
186
|
+
* @returns Frobenius endomorphism helpers.
|
|
187
|
+
* @throws If the derived Frobenius constants are inconsistent for the tower. {@link Error}
|
|
188
|
+
* @example
|
|
189
|
+
* Build Frobenius endomorphism helpers for a BLS extension tower.
|
|
190
|
+
*
|
|
191
|
+
* ```ts
|
|
192
|
+
* import { psiFrobenius } from '@noble/curves/abstract/tower.js';
|
|
193
|
+
* import { bls12_381 } from '@noble/curves/bls12-381.js';
|
|
194
|
+
* const Fp = bls12_381.fields.Fp;
|
|
195
|
+
* const Fp2 = bls12_381.fields.Fp2;
|
|
196
|
+
* const frob = psiFrobenius(Fp, Fp2, Fp2.div(Fp2.ONE, Fp2.NONRESIDUE));
|
|
197
|
+
* const point = frob.G2psi(bls12_381.G2.Point, bls12_381.G2.Point.BASE);
|
|
198
|
+
* ```
|
|
199
|
+
*/
|
|
95
200
|
export function psiFrobenius(
|
|
96
|
-
Fp: mod.IField<Fp
|
|
97
|
-
Fp2: Fp2Bls
|
|
98
|
-
base: Fp2
|
|
201
|
+
Fp: TArg<mod.IField<Fp>>,
|
|
202
|
+
Fp2: TArg<Fp2Bls>,
|
|
203
|
+
base: TArg<Fp2>
|
|
99
204
|
): {
|
|
100
205
|
psi: (x: Fp2, y: Fp2) => [Fp2, Fp2];
|
|
101
206
|
psi2: (x: Fp2, y: Fp2) => [Fp2, Fp2];
|
|
@@ -117,9 +222,9 @@ export function psiFrobenius(
|
|
|
117
222
|
}
|
|
118
223
|
// Ψ²(P) endomorphism (psi2(x) = psi(psi(x)))
|
|
119
224
|
const PSI2_X = Fp2.pow(base, (Fp.ORDER ** _2n - _1n) / _3n); // u^((p^2 - 1)/3)
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
const PSI2_Y = Fp2.pow(base, (Fp.ORDER ** _2n - _1n) / _2n); // u^((p^2 - 1)/
|
|
225
|
+
// Current towers rely on this landing on `-1`, which lets psi2 map `y` with
|
|
226
|
+
// one negation instead of carrying a separate Frobenius multiplier.
|
|
227
|
+
const PSI2_Y = Fp2.pow(base, (Fp.ORDER ** _2n - _1n) / _2n); // u^((p^2 - 1)/2)
|
|
123
228
|
if (!Fp2.eql(PSI2_Y, Fp2.neg(Fp2.ONE))) throw new Error('psiFrobenius: PSI2_Y!==-1');
|
|
124
229
|
function psi2(x: Fp2, y: Fp2): [Fp2, Fp2] {
|
|
125
230
|
return [Fp2.mul(x, PSI2_X), Fp2.neg(y)];
|
|
@@ -137,22 +242,35 @@ export function psiFrobenius(
|
|
|
137
242
|
return { psi, psi2, G2psi, G2psi2, PSI_X, PSI_Y, PSI2_X, PSI2_Y };
|
|
138
243
|
}
|
|
139
244
|
|
|
245
|
+
/** Construction options for the BLS-style degree-12 tower. */
|
|
140
246
|
export type Tower12Opts = {
|
|
247
|
+
/** Prime-field order. */
|
|
141
248
|
ORDER: bigint;
|
|
249
|
+
/** Bit length of the BLS parameter `x`. */
|
|
142
250
|
X_LEN: number;
|
|
251
|
+
/** Prime-field non-residue used by the quadratic extension. */
|
|
143
252
|
NONRESIDUE?: Fp;
|
|
253
|
+
/** Quadratic-extension non-residue used by the sextic tower. */
|
|
144
254
|
FP2_NONRESIDUE: BigintTuple;
|
|
255
|
+
/**
|
|
256
|
+
* Optional custom quadratic square-root helper.
|
|
257
|
+
* Receives one quadratic-extension element and returns one square root.
|
|
258
|
+
*/
|
|
145
259
|
Fp2sqrt?: (num: Fp2) => Fp2;
|
|
260
|
+
/**
|
|
261
|
+
* Multiply one quadratic element by the curve `b` constant.
|
|
262
|
+
* @param num - Quadratic-extension element to scale.
|
|
263
|
+
* @returns Product by the curve `b` constant.
|
|
264
|
+
*/
|
|
146
265
|
Fp2mulByB: (num: Fp2) => Fp2;
|
|
266
|
+
/**
|
|
267
|
+
* Final exponentiation used by pairing arithmetic.
|
|
268
|
+
* @param num - Degree-12 field element to exponentiate.
|
|
269
|
+
* @returns Pairing result after final exponentiation.
|
|
270
|
+
*/
|
|
147
271
|
Fp12finalExponentiate: (num: Fp12) => Fp12;
|
|
148
272
|
};
|
|
149
273
|
|
|
150
|
-
const Fp2fromBigTuple = (Fp: mod.IField<bigint>, tuple: BigintTuple | bigint[]) => {
|
|
151
|
-
if (tuple.length !== 2) throw new Error('invalid tuple');
|
|
152
|
-
const fps = tuple.map((n) => Fp.create(n)) as BigintTuple;
|
|
153
|
-
return { c0: fps[0], c1: fps[1] };
|
|
154
|
-
};
|
|
155
|
-
|
|
156
274
|
class _Field2 implements mod.IField<Fp2> {
|
|
157
275
|
readonly ORDER: bigint;
|
|
158
276
|
readonly BITS: number;
|
|
@@ -167,7 +285,7 @@ class _Field2 implements mod.IField<Fp2> {
|
|
|
167
285
|
readonly mulByB: Tower12Opts['Fp2mulByB'];
|
|
168
286
|
readonly Fp_NONRESIDUE: bigint;
|
|
169
287
|
readonly Fp_div2: bigint;
|
|
170
|
-
readonly FROBENIUS_COEFFICIENTS: Fp[];
|
|
288
|
+
readonly FROBENIUS_COEFFICIENTS: readonly Fp[];
|
|
171
289
|
|
|
172
290
|
constructor(
|
|
173
291
|
Fp: mod.IField<bigint>,
|
|
@@ -177,6 +295,7 @@ class _Field2 implements mod.IField<Fp2> {
|
|
|
177
295
|
Fp2mulByB: Tower12Opts['Fp2mulByB'];
|
|
178
296
|
}> = {}
|
|
179
297
|
) {
|
|
298
|
+
const { NONRESIDUE = BigInt(-1), FP2_NONRESIDUE, Fp2mulByB } = opts;
|
|
180
299
|
const ORDER = Fp.ORDER;
|
|
181
300
|
const FP2_ORDER = ORDER * ORDER;
|
|
182
301
|
this.Fp = Fp;
|
|
@@ -184,40 +303,67 @@ class _Field2 implements mod.IField<Fp2> {
|
|
|
184
303
|
this.BITS = bitLen(FP2_ORDER);
|
|
185
304
|
this.BYTES = Math.ceil(bitLen(FP2_ORDER) / 8);
|
|
186
305
|
this.isLE = Fp.isLE;
|
|
187
|
-
this.ZERO = { c0: Fp.ZERO, c1: Fp.ZERO };
|
|
188
|
-
this.ONE = { c0: Fp.ONE, c1: Fp.ZERO };
|
|
306
|
+
this.ZERO = this.create({ c0: Fp.ZERO, c1: Fp.ZERO });
|
|
307
|
+
this.ONE = this.create({ c0: Fp.ONE, c1: Fp.ZERO });
|
|
189
308
|
|
|
190
|
-
|
|
309
|
+
// These knobs only swap constants for the shipped quadratic tower shape:
|
|
310
|
+
// arithmetic below assumes `u^2 = -1`, and bytes are handled as two adjacent
|
|
311
|
+
// `Fp` limbs (`fromBytes` / `toBytes` expect the shipped `2 * Fp.BYTES` layout).
|
|
312
|
+
this.Fp_NONRESIDUE = Fp.create(NONRESIDUE);
|
|
191
313
|
this.Fp_div2 = Fp.div(Fp.ONE, _2n); // 1/2
|
|
192
|
-
this.NONRESIDUE =
|
|
193
|
-
// const Fp2Nonresidue =
|
|
194
|
-
this.FROBENIUS_COEFFICIENTS =
|
|
195
|
-
|
|
196
|
-
|
|
314
|
+
this.NONRESIDUE = this.create({ c0: FP2_NONRESIDUE![0], c1: FP2_NONRESIDUE![1] });
|
|
315
|
+
// const Fp2Nonresidue = this.create({ c0: FP2_NONRESIDUE![0], c1: FP2_NONRESIDUE![1] });
|
|
316
|
+
this.FROBENIUS_COEFFICIENTS = Object.freeze(
|
|
317
|
+
calcFrobeniusCoefficients(Fp, this.Fp_NONRESIDUE, Fp.ORDER, 2)[0]
|
|
318
|
+
);
|
|
319
|
+
this.mulByB = (num) => {
|
|
320
|
+
// This config hook is trusted to return a canonical Fp2 value already.
|
|
321
|
+
// Copy+freeze it to keep the tower immutability invariant without mutating caller objects.
|
|
322
|
+
const { c0, c1 } = Fp2mulByB!(num);
|
|
323
|
+
return Object.freeze({ c0, c1 });
|
|
324
|
+
};
|
|
325
|
+
Object.freeze(this);
|
|
197
326
|
}
|
|
198
327
|
fromBigTuple(tuple: BigintTuple) {
|
|
199
|
-
|
|
328
|
+
if (!Array.isArray(tuple) || tuple.length !== 2) throw new Error('invalid Fp2.fromBigTuple');
|
|
329
|
+
const [c0, c1] = tuple;
|
|
330
|
+
if (typeof c0 !== 'bigint' || typeof c1 !== 'bigint')
|
|
331
|
+
throw new Error('invalid Fp2.fromBigTuple');
|
|
332
|
+
return this.create({ c0, c1 });
|
|
200
333
|
}
|
|
201
334
|
create(num: Fp2) {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
}
|
|
208
|
-
|
|
335
|
+
const { Fp } = this;
|
|
336
|
+
const c0 = Fp.create(num.c0);
|
|
337
|
+
const c1 = Fp.create(num.c1);
|
|
338
|
+
// Bigint field elements are immutable values, and higher-level code relies on
|
|
339
|
+
// that invariant. Copy+freeze tower values too without mutating caller-owned objects.
|
|
340
|
+
return Object.freeze({ c0, c1 });
|
|
341
|
+
}
|
|
342
|
+
isValid(num: Fp2) {
|
|
343
|
+
if (!isObj(num))
|
|
344
|
+
throw new TypeError('invalid field element: expected object, got ' + typeof num);
|
|
345
|
+
const { c0, c1 } = num;
|
|
346
|
+
const { Fp } = this;
|
|
347
|
+
// Match base-field `isValid(...)`: malformed coordinate types are errors, not a `false`
|
|
348
|
+
// predicate result.
|
|
349
|
+
return Fp.isValid(c0) && Fp.isValid(c1);
|
|
209
350
|
}
|
|
210
|
-
is0(
|
|
211
|
-
|
|
351
|
+
is0(num: Fp2) {
|
|
352
|
+
if (!isObj(num)) return false;
|
|
353
|
+
const { c0, c1 } = num;
|
|
354
|
+
const { Fp } = this;
|
|
355
|
+
return Fp.is0(c0) && Fp.is0(c1);
|
|
212
356
|
}
|
|
213
357
|
isValidNot0(num: Fp2) {
|
|
214
358
|
return !this.is0(num) && this.isValid(num);
|
|
215
359
|
}
|
|
216
360
|
eql({ c0, c1 }: Fp2, { c0: r0, c1: r1 }: Fp2) {
|
|
217
|
-
|
|
361
|
+
const { Fp } = this;
|
|
362
|
+
return Fp.eql(c0, r0) && Fp.eql(c1, r1);
|
|
218
363
|
}
|
|
219
364
|
neg({ c0, c1 }: Fp2) {
|
|
220
|
-
|
|
365
|
+
const { Fp } = this;
|
|
366
|
+
return Object.freeze({ c0: Fp.neg(c0), c1: Fp.neg(c1) });
|
|
221
367
|
}
|
|
222
368
|
pow(num: Fp2, power: bigint): Fp2 {
|
|
223
369
|
return mod.FpPow(this, num, power);
|
|
@@ -227,22 +373,24 @@ class _Field2 implements mod.IField<Fp2> {
|
|
|
227
373
|
}
|
|
228
374
|
// Normalized
|
|
229
375
|
add(f1: Fp2, f2: Fp2): Fp2 {
|
|
376
|
+
const { Fp } = this;
|
|
230
377
|
const { c0, c1 } = f1;
|
|
231
378
|
const { c0: r0, c1: r1 } = f2;
|
|
232
|
-
return {
|
|
233
|
-
c0:
|
|
234
|
-
c1:
|
|
235
|
-
};
|
|
379
|
+
return Object.freeze({
|
|
380
|
+
c0: Fp.add(c0, r0),
|
|
381
|
+
c1: Fp.add(c1, r1),
|
|
382
|
+
});
|
|
236
383
|
}
|
|
237
384
|
sub({ c0, c1 }: Fp2, { c0: r0, c1: r1 }: Fp2) {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
385
|
+
const { Fp } = this;
|
|
386
|
+
return Object.freeze({
|
|
387
|
+
c0: Fp.sub(c0, r0),
|
|
388
|
+
c1: Fp.sub(c1, r1),
|
|
389
|
+
});
|
|
242
390
|
}
|
|
243
391
|
mul({ c0, c1 }: Fp2, rhs: Fp2) {
|
|
244
392
|
const { Fp } = this;
|
|
245
|
-
if (typeof rhs === 'bigint') return { c0: Fp.mul(c0, rhs), c1: Fp.mul(c1, rhs) };
|
|
393
|
+
if (typeof rhs === 'bigint') return Object.freeze({ c0: Fp.mul(c0, rhs), c1: Fp.mul(c1, rhs) });
|
|
246
394
|
// (a+bi)(c+di) = (ac−bd) + (ad+bc)i
|
|
247
395
|
const { c0: r0, c1: r1 } = rhs;
|
|
248
396
|
let t1 = Fp.mul(c0, r0); // c0 * o0
|
|
@@ -250,14 +398,14 @@ class _Field2 implements mod.IField<Fp2> {
|
|
|
250
398
|
// (T1 - T2) + ((c0 + c1) * (r0 + r1) - (T1 + T2))*i
|
|
251
399
|
const o0 = Fp.sub(t1, t2);
|
|
252
400
|
const o1 = Fp.sub(Fp.mul(Fp.add(c0, c1), Fp.add(r0, r1)), Fp.add(t1, t2));
|
|
253
|
-
return { c0: o0, c1: o1 };
|
|
401
|
+
return Object.freeze({ c0: o0, c1: o1 });
|
|
254
402
|
}
|
|
255
403
|
sqr({ c0, c1 }: Fp2) {
|
|
256
404
|
const { Fp } = this;
|
|
257
405
|
const a = Fp.add(c0, c1);
|
|
258
406
|
const b = Fp.sub(c0, c1);
|
|
259
407
|
const c = Fp.add(c0, c0);
|
|
260
|
-
return { c0: Fp.mul(a, b), c1: Fp.mul(c, c1) };
|
|
408
|
+
return Object.freeze({ c0: Fp.mul(a, b), c1: Fp.mul(c, c1) });
|
|
261
409
|
}
|
|
262
410
|
// NonNormalized stuff
|
|
263
411
|
addN(a: Fp2, b: Fp2): Fp2 {
|
|
@@ -294,7 +442,7 @@ class _Field2 implements mod.IField<Fp2> {
|
|
|
294
442
|
// only a single inversion in Fp.
|
|
295
443
|
const { Fp } = this;
|
|
296
444
|
const factor = Fp.inv(Fp.create(a * a + b * b));
|
|
297
|
-
return { c0: Fp.mul(factor, Fp.create(a)), c1: Fp.mul(factor, Fp.create(-b)) };
|
|
445
|
+
return Object.freeze({ c0: Fp.mul(factor, Fp.create(a)), c1: Fp.mul(factor, Fp.create(-b)) });
|
|
298
446
|
}
|
|
299
447
|
sqrt(num: Fp2) {
|
|
300
448
|
// This is generic for all quadratic extensions (Fp2)
|
|
@@ -333,17 +481,22 @@ class _Field2 implements mod.IField<Fp2> {
|
|
|
333
481
|
// Bytes util
|
|
334
482
|
fromBytes(b: Uint8Array): Fp2 {
|
|
335
483
|
const { Fp } = this;
|
|
484
|
+
abytes(b);
|
|
336
485
|
if (b.length !== this.BYTES) throw new Error('fromBytes invalid length=' + b.length);
|
|
337
|
-
return
|
|
486
|
+
return this.create({
|
|
487
|
+
c0: Fp.fromBytes(b.subarray(0, Fp.BYTES)),
|
|
488
|
+
c1: Fp.fromBytes(b.subarray(Fp.BYTES)),
|
|
489
|
+
});
|
|
338
490
|
}
|
|
339
|
-
toBytes({ c0, c1 }: Fp2) {
|
|
491
|
+
toBytes({ c0, c1 }: Fp2): Uint8Array {
|
|
340
492
|
return concatBytes(this.Fp.toBytes(c0), this.Fp.toBytes(c1));
|
|
341
493
|
}
|
|
342
494
|
cmov({ c0, c1 }: Fp2, { c0: r0, c1: r1 }: Fp2, c: boolean) {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
495
|
+
const { Fp } = this;
|
|
496
|
+
return this.create({
|
|
497
|
+
c0: Fp.cmov(c0, r0, c),
|
|
498
|
+
c1: Fp.cmov(c1, r1, c),
|
|
499
|
+
});
|
|
347
500
|
}
|
|
348
501
|
reim({ c0, c1 }: Fp2) {
|
|
349
502
|
return { re: c0, im: c1 };
|
|
@@ -362,10 +515,10 @@ class _Field2 implements mod.IField<Fp2> {
|
|
|
362
515
|
return this.mul({ c0, c1 }, this.NONRESIDUE);
|
|
363
516
|
}
|
|
364
517
|
frobeniusMap({ c0, c1 }: Fp2, power: number): Fp2 {
|
|
365
|
-
return {
|
|
518
|
+
return Object.freeze({
|
|
366
519
|
c0,
|
|
367
520
|
c1: this.Fp.mul(c1, this.FROBENIUS_COEFFICIENTS[power % 2]),
|
|
368
|
-
};
|
|
521
|
+
});
|
|
369
522
|
}
|
|
370
523
|
}
|
|
371
524
|
|
|
@@ -378,53 +531,67 @@ class _Field6 implements Fp6Bls {
|
|
|
378
531
|
readonly ZERO: Fp6;
|
|
379
532
|
readonly ONE: Fp6;
|
|
380
533
|
readonly Fp2: Fp2Bls;
|
|
381
|
-
readonly FROBENIUS_COEFFICIENTS_1: Fp2[];
|
|
382
|
-
readonly FROBENIUS_COEFFICIENTS_2: Fp2[];
|
|
383
534
|
|
|
384
535
|
constructor(Fp2: Fp2Bls) {
|
|
385
536
|
this.Fp2 = Fp2;
|
|
386
|
-
|
|
537
|
+
// `IField.ORDER` is the field cardinality `q`; for sextic extensions that is `p^6`.
|
|
538
|
+
// Generic helpers like Frobenius-style `x^q = x` checks rely on the literal field size here.
|
|
539
|
+
this.ORDER = Fp2.Fp.ORDER ** _6n;
|
|
387
540
|
this.BITS = 3 * Fp2.BITS;
|
|
388
541
|
this.BYTES = 3 * Fp2.BYTES;
|
|
389
542
|
this.isLE = Fp2.isLE;
|
|
390
|
-
this.ZERO = { c0: Fp2.ZERO, c1: Fp2.ZERO, c2: Fp2.ZERO };
|
|
391
|
-
this.ONE = { c0: Fp2.ONE, c1: Fp2.ZERO, c2: Fp2.ZERO };
|
|
543
|
+
this.ZERO = this.create({ c0: Fp2.ZERO, c1: Fp2.ZERO, c2: Fp2.ZERO });
|
|
544
|
+
this.ONE = this.create({ c0: Fp2.ONE, c1: Fp2.ZERO, c2: Fp2.ZERO });
|
|
545
|
+
Object.freeze(this);
|
|
546
|
+
}
|
|
547
|
+
// Most callers never touch Frobenius maps, so keep the sextic tables lazy:
|
|
548
|
+
// eagerly deriving them dominates `bls12-381.js` / `bn254.js` import time.
|
|
549
|
+
get FROBENIUS_COEFFICIENTS_1(): readonly Fp2[] {
|
|
550
|
+
const frob = _FROBENIUS_COEFFICIENTS_6.get(this);
|
|
551
|
+
if (frob) return frob[0];
|
|
552
|
+
const { Fp2 } = this;
|
|
392
553
|
const { Fp } = Fp2;
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
this
|
|
396
|
-
|
|
554
|
+
const rows = calcFrobeniusCoefficients(Fp2, Fp2.NONRESIDUE, Fp.ORDER, 6, 2, 3);
|
|
555
|
+
const cache = [Object.freeze(rows[0]), Object.freeze(rows[1])] as const;
|
|
556
|
+
_FROBENIUS_COEFFICIENTS_6.set(this, cache);
|
|
557
|
+
return cache[0];
|
|
558
|
+
}
|
|
559
|
+
get FROBENIUS_COEFFICIENTS_2(): readonly Fp2[] {
|
|
560
|
+
const frob = _FROBENIUS_COEFFICIENTS_6.get(this);
|
|
561
|
+
if (frob) return frob[1];
|
|
562
|
+
void this.FROBENIUS_COEFFICIENTS_1;
|
|
563
|
+
return _FROBENIUS_COEFFICIENTS_6.get(this)![1];
|
|
397
564
|
}
|
|
398
565
|
add({ c0, c1, c2 }: Fp6, { c0: r0, c1: r1, c2: r2 }: Fp6) {
|
|
399
566
|
const { Fp2 } = this;
|
|
400
|
-
return {
|
|
567
|
+
return Object.freeze({
|
|
401
568
|
c0: Fp2.add(c0, r0),
|
|
402
569
|
c1: Fp2.add(c1, r1),
|
|
403
570
|
c2: Fp2.add(c2, r2),
|
|
404
|
-
};
|
|
571
|
+
});
|
|
405
572
|
}
|
|
406
573
|
sub({ c0, c1, c2 }: Fp6, { c0: r0, c1: r1, c2: r2 }: Fp6) {
|
|
407
574
|
const { Fp2 } = this;
|
|
408
|
-
return {
|
|
575
|
+
return Object.freeze({
|
|
409
576
|
c0: Fp2.sub(c0, r0),
|
|
410
577
|
c1: Fp2.sub(c1, r1),
|
|
411
578
|
c2: Fp2.sub(c2, r2),
|
|
412
|
-
};
|
|
579
|
+
});
|
|
413
580
|
}
|
|
414
581
|
mul({ c0, c1, c2 }: Fp6, rhs: Fp6 | bigint) {
|
|
415
582
|
const { Fp2 } = this;
|
|
416
583
|
if (typeof rhs === 'bigint') {
|
|
417
|
-
return {
|
|
584
|
+
return Object.freeze({
|
|
418
585
|
c0: Fp2.mul(c0, rhs),
|
|
419
586
|
c1: Fp2.mul(c1, rhs),
|
|
420
587
|
c2: Fp2.mul(c2, rhs),
|
|
421
|
-
};
|
|
588
|
+
});
|
|
422
589
|
}
|
|
423
590
|
const { c0: r0, c1: r1, c2: r2 } = rhs;
|
|
424
591
|
const t0 = Fp2.mul(c0, r0); // c0 * o0
|
|
425
592
|
const t1 = Fp2.mul(c1, r1); // c1 * o1
|
|
426
593
|
const t2 = Fp2.mul(c2, r2); // c2 * o2
|
|
427
|
-
return {
|
|
594
|
+
return Object.freeze({
|
|
428
595
|
// t0 + (c1 + c2) * (r1 * r2) - (T1 + T2) * (u + 1)
|
|
429
596
|
c0: Fp2.add(
|
|
430
597
|
t0,
|
|
@@ -437,7 +604,7 @@ class _Field6 implements Fp6Bls {
|
|
|
437
604
|
),
|
|
438
605
|
// T1 + (c0 + c2) * (r0 + r2) - T0 + T2
|
|
439
606
|
c2: Fp2.sub(Fp2.add(t1, Fp2.mul(Fp2.add(c0, c2), Fp2.add(r0, r2))), Fp2.add(t0, t2)),
|
|
440
|
-
};
|
|
607
|
+
});
|
|
441
608
|
}
|
|
442
609
|
sqr({ c0, c1, c2 }: Fp6) {
|
|
443
610
|
const { Fp2 } = this;
|
|
@@ -445,12 +612,12 @@ class _Field6 implements Fp6Bls {
|
|
|
445
612
|
let t1 = Fp2.mul(Fp2.mul(c0, c1), _2n); // 2 * c0 * c1
|
|
446
613
|
let t3 = Fp2.mul(Fp2.mul(c1, c2), _2n); // 2 * c1 * c2
|
|
447
614
|
let t4 = Fp2.sqr(c2); // c2²
|
|
448
|
-
return {
|
|
615
|
+
return Object.freeze({
|
|
449
616
|
c0: Fp2.add(Fp2.mulByNonresidue(t3), t0), // T3 * (u + 1) + T0
|
|
450
617
|
c1: Fp2.add(Fp2.mulByNonresidue(t4), t1), // T4 * (u + 1) + T1
|
|
451
618
|
// T1 + (c0 - c1 + c2)² + T3 - T0 - T4
|
|
452
619
|
c2: Fp2.sub(Fp2.sub(Fp2.add(Fp2.add(t1, Fp2.sqr(Fp2.add(Fp2.sub(c0, c1), c2))), t3), t0), t4),
|
|
453
|
-
};
|
|
620
|
+
});
|
|
454
621
|
}
|
|
455
622
|
addN(a: Fp6, b: Fp6): Fp6 {
|
|
456
623
|
return this.add(a, b);
|
|
@@ -466,14 +633,23 @@ class _Field6 implements Fp6Bls {
|
|
|
466
633
|
}
|
|
467
634
|
|
|
468
635
|
create(num: Fp6) {
|
|
469
|
-
|
|
636
|
+
const { Fp2 } = this;
|
|
637
|
+
const c0 = Fp2.create(num.c0);
|
|
638
|
+
const c1 = Fp2.create(num.c1);
|
|
639
|
+
const c2 = Fp2.create(num.c2);
|
|
640
|
+
return Object.freeze({ c0, c1, c2 });
|
|
470
641
|
}
|
|
471
642
|
|
|
472
|
-
isValid(
|
|
643
|
+
isValid(num: Fp6) {
|
|
644
|
+
if (!isObj(num))
|
|
645
|
+
throw new TypeError('invalid field element: expected object, got ' + typeof num);
|
|
646
|
+
const { c0, c1, c2 } = num;
|
|
473
647
|
const { Fp2 } = this;
|
|
474
648
|
return Fp2.isValid(c0) && Fp2.isValid(c1) && Fp2.isValid(c2);
|
|
475
649
|
}
|
|
476
|
-
is0(
|
|
650
|
+
is0(num: Fp6) {
|
|
651
|
+
if (!isObj(num)) return false;
|
|
652
|
+
const { c0, c1, c2 } = num;
|
|
477
653
|
const { Fp2 } = this;
|
|
478
654
|
return Fp2.is0(c0) && Fp2.is0(c1) && Fp2.is0(c2);
|
|
479
655
|
}
|
|
@@ -482,13 +658,16 @@ class _Field6 implements Fp6Bls {
|
|
|
482
658
|
}
|
|
483
659
|
neg({ c0, c1, c2 }: Fp6) {
|
|
484
660
|
const { Fp2 } = this;
|
|
485
|
-
return { c0: Fp2.neg(c0), c1: Fp2.neg(c1), c2: Fp2.neg(c2) };
|
|
661
|
+
return Object.freeze({ c0: Fp2.neg(c0), c1: Fp2.neg(c1), c2: Fp2.neg(c2) });
|
|
486
662
|
}
|
|
487
663
|
eql({ c0, c1, c2 }: Fp6, { c0: r0, c1: r1, c2: r2 }: Fp6) {
|
|
488
664
|
const { Fp2 } = this;
|
|
489
665
|
return Fp2.eql(c0, r0) && Fp2.eql(c1, r1) && Fp2.eql(c2, r2);
|
|
490
666
|
}
|
|
491
667
|
sqrt(_: Fp6) {
|
|
668
|
+
// Sextic extensions can use generic odd-field Tonelli-Shanks, but the helper must work
|
|
669
|
+
// over `IField<T>` with a quadratic non-residue from Fp6 itself. The current
|
|
670
|
+
// `mod.tonelliShanks(P)` precomputation only searches integer residues in the base field.
|
|
492
671
|
return notImplemented();
|
|
493
672
|
}
|
|
494
673
|
// Do we need division by bigint at all? Should be done via order:
|
|
@@ -513,18 +692,19 @@ class _Field6 implements Fp6Bls {
|
|
|
513
692
|
let t4 = Fp2.inv(
|
|
514
693
|
Fp2.add(Fp2.mulByNonresidue(Fp2.add(Fp2.mul(c2, t1), Fp2.mul(c1, t2))), Fp2.mul(c0, t0))
|
|
515
694
|
);
|
|
516
|
-
return { c0: Fp2.mul(t4, t0), c1: Fp2.mul(t4, t1), c2: Fp2.mul(t4, t2) };
|
|
695
|
+
return Object.freeze({ c0: Fp2.mul(t4, t0), c1: Fp2.mul(t4, t1), c2: Fp2.mul(t4, t2) });
|
|
517
696
|
}
|
|
518
697
|
// Bytes utils
|
|
519
698
|
fromBytes(b: Uint8Array): Fp6 {
|
|
520
699
|
const { Fp2 } = this;
|
|
700
|
+
abytes(b);
|
|
521
701
|
if (b.length !== this.BYTES) throw new Error('fromBytes invalid length=' + b.length);
|
|
522
702
|
const B2 = Fp2.BYTES;
|
|
523
|
-
return {
|
|
703
|
+
return this.create({
|
|
524
704
|
c0: Fp2.fromBytes(b.subarray(0, B2)),
|
|
525
705
|
c1: Fp2.fromBytes(b.subarray(B2, B2 * 2)),
|
|
526
706
|
c2: Fp2.fromBytes(b.subarray(2 * B2)),
|
|
527
|
-
};
|
|
707
|
+
});
|
|
528
708
|
}
|
|
529
709
|
toBytes({ c0, c1, c2 }: Fp6): Uint8Array {
|
|
530
710
|
const { Fp2 } = this;
|
|
@@ -532,66 +712,73 @@ class _Field6 implements Fp6Bls {
|
|
|
532
712
|
}
|
|
533
713
|
cmov({ c0, c1, c2 }: Fp6, { c0: r0, c1: r1, c2: r2 }: Fp6, c: boolean) {
|
|
534
714
|
const { Fp2 } = this;
|
|
535
|
-
return {
|
|
715
|
+
return this.create({
|
|
536
716
|
c0: Fp2.cmov(c0, r0, c),
|
|
537
717
|
c1: Fp2.cmov(c1, r1, c),
|
|
538
718
|
c2: Fp2.cmov(c2, r2, c),
|
|
539
|
-
};
|
|
719
|
+
});
|
|
540
720
|
}
|
|
541
|
-
fromBigSix(
|
|
721
|
+
fromBigSix(tuple: BigintSix): Fp6 {
|
|
542
722
|
const { Fp2 } = this;
|
|
543
|
-
if (!Array.isArray(
|
|
544
|
-
|
|
723
|
+
if (!Array.isArray(tuple) || tuple.length !== 6) throw new Error('invalid Fp6.fromBigSix');
|
|
724
|
+
for (let i = 0; i < 6; i++)
|
|
725
|
+
if (typeof tuple[i] !== 'bigint') throw new Error('invalid Fp6.fromBigSix');
|
|
726
|
+
const t = tuple;
|
|
727
|
+
return this.create({
|
|
545
728
|
c0: Fp2.fromBigTuple(t.slice(0, 2) as BigintTuple),
|
|
546
729
|
c1: Fp2.fromBigTuple(t.slice(2, 4) as BigintTuple),
|
|
547
730
|
c2: Fp2.fromBigTuple(t.slice(4, 6) as BigintTuple),
|
|
548
|
-
};
|
|
731
|
+
});
|
|
549
732
|
}
|
|
550
733
|
frobeniusMap({ c0, c1, c2 }: Fp6, power: number) {
|
|
551
734
|
const { Fp2 } = this;
|
|
552
|
-
return {
|
|
735
|
+
return Object.freeze({
|
|
553
736
|
c0: Fp2.frobeniusMap(c0, power),
|
|
554
737
|
c1: Fp2.mul(Fp2.frobeniusMap(c1, power), this.FROBENIUS_COEFFICIENTS_1[power % 6]),
|
|
555
738
|
c2: Fp2.mul(Fp2.frobeniusMap(c2, power), this.FROBENIUS_COEFFICIENTS_2[power % 6]),
|
|
556
|
-
};
|
|
739
|
+
});
|
|
557
740
|
}
|
|
558
741
|
mulByFp2({ c0, c1, c2 }: Fp6, rhs: Fp2): Fp6 {
|
|
559
742
|
const { Fp2 } = this;
|
|
560
|
-
return {
|
|
743
|
+
return Object.freeze({
|
|
561
744
|
c0: Fp2.mul(c0, rhs),
|
|
562
745
|
c1: Fp2.mul(c1, rhs),
|
|
563
746
|
c2: Fp2.mul(c2, rhs),
|
|
564
|
-
};
|
|
747
|
+
});
|
|
565
748
|
}
|
|
566
749
|
mulByNonresidue({ c0, c1, c2 }: Fp6) {
|
|
567
750
|
const { Fp2 } = this;
|
|
568
|
-
return { c0: Fp2.mulByNonresidue(c2), c1: c0, c2: c1 };
|
|
751
|
+
return Object.freeze({ c0: Fp2.mulByNonresidue(c2), c1: c0, c2: c1 });
|
|
569
752
|
}
|
|
570
753
|
// Sparse multiplication
|
|
571
754
|
mul1({ c0, c1, c2 }: Fp6, b1: Fp2): Fp6 {
|
|
572
755
|
const { Fp2 } = this;
|
|
573
|
-
return {
|
|
756
|
+
return Object.freeze({
|
|
574
757
|
c0: Fp2.mulByNonresidue(Fp2.mul(c2, b1)),
|
|
575
758
|
c1: Fp2.mul(c0, b1),
|
|
576
759
|
c2: Fp2.mul(c1, b1),
|
|
577
|
-
};
|
|
760
|
+
});
|
|
578
761
|
}
|
|
579
762
|
// Sparse multiplication
|
|
580
763
|
mul01({ c0, c1, c2 }: Fp6, b0: Fp2, b1: Fp2): Fp6 {
|
|
581
764
|
const { Fp2 } = this;
|
|
582
765
|
let t0 = Fp2.mul(c0, b0); // c0 * b0
|
|
583
766
|
let t1 = Fp2.mul(c1, b1); // c1 * b1
|
|
584
|
-
return {
|
|
767
|
+
return Object.freeze({
|
|
585
768
|
// ((c1 + c2) * b1 - T1) * (u + 1) + T0
|
|
586
769
|
c0: Fp2.add(Fp2.mulByNonresidue(Fp2.sub(Fp2.mul(Fp2.add(c1, c2), b1), t1)), t0),
|
|
587
770
|
// (b0 + b1) * (c0 + c1) - T0 - T1
|
|
588
771
|
c1: Fp2.sub(Fp2.sub(Fp2.mul(Fp2.add(b0, b1), Fp2.add(c0, c1)), t0), t1),
|
|
589
772
|
// (c0 + c2) * b0 - T0 + T1
|
|
590
773
|
c2: Fp2.add(Fp2.sub(Fp2.mul(Fp2.add(c0, c2), b0), t0), t1),
|
|
591
|
-
};
|
|
774
|
+
});
|
|
592
775
|
}
|
|
593
776
|
}
|
|
594
777
|
|
|
778
|
+
// Keep lazy tower caches off-object: field instances stay frozen, and debugger output
|
|
779
|
+
// stays readable without JS private slots while second/subsequent lookups still hit cache.
|
|
780
|
+
const _FROBENIUS_COEFFICIENTS_6 = new WeakMap<_Field6, readonly [readonly Fp2[], readonly Fp2[]]>();
|
|
781
|
+
|
|
595
782
|
class _Field12 implements Fp12Bls {
|
|
596
783
|
readonly ORDER: bigint;
|
|
597
784
|
readonly BITS: number;
|
|
@@ -602,41 +789,66 @@ class _Field12 implements Fp12Bls {
|
|
|
602
789
|
readonly ONE: Fp12;
|
|
603
790
|
|
|
604
791
|
readonly Fp6: Fp6Bls;
|
|
605
|
-
readonly FROBENIUS_COEFFICIENTS: Fp2[];
|
|
606
792
|
readonly X_LEN: number;
|
|
607
793
|
readonly finalExponentiate: Tower12Opts['Fp12finalExponentiate'];
|
|
608
794
|
|
|
609
795
|
constructor(Fp6: Fp6Bls, opts: Tower12Opts) {
|
|
796
|
+
const { X_LEN, Fp12finalExponentiate } = opts;
|
|
610
797
|
const { Fp2 } = Fp6;
|
|
611
798
|
const { Fp } = Fp2;
|
|
612
799
|
this.Fp6 = Fp6;
|
|
613
800
|
|
|
614
|
-
|
|
801
|
+
// `IField.ORDER` is the field cardinality `q`; for degree-12 extensions that is `p^12`.
|
|
802
|
+
// Keeping `p^2` here breaks generic field identities like `x^q = x` on Fp12.
|
|
803
|
+
this.ORDER = Fp.ORDER ** _12n;
|
|
615
804
|
this.BITS = 2 * Fp6.BITS;
|
|
616
805
|
this.BYTES = 2 * Fp6.BYTES;
|
|
617
806
|
this.isLE = Fp6.isLE;
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
this.
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
807
|
+
// Returned tower values are frozen, so larger constants can safely reuse
|
|
808
|
+
// already-frozen child coefficients instead of cloning them.
|
|
809
|
+
this.ZERO = this.create({ c0: Fp6.ZERO, c1: Fp6.ZERO });
|
|
810
|
+
this.ONE = this.create({ c0: Fp6.ONE, c1: Fp6.ZERO });
|
|
811
|
+
this.X_LEN = X_LEN;
|
|
812
|
+
this.finalExponentiate = (num) => {
|
|
813
|
+
const copy2 = ({ c0, c1 }: Fp2): Fp2 => Object.freeze({ c0, c1 });
|
|
814
|
+
const copy6 = ({ c0, c1, c2 }: Fp6): Fp6 =>
|
|
815
|
+
Object.freeze({ c0: copy2(c0), c1: copy2(c1), c2: copy2(c2) });
|
|
816
|
+
// This config hook is trusted to return a canonical Fp12 value already.
|
|
817
|
+
// Copy+freeze it to keep the tower immutability invariant without mutating caller objects.
|
|
818
|
+
const res = Fp12finalExponentiate(num);
|
|
819
|
+
return Object.freeze({ c0: copy6(res.c0), c1: copy6(res.c1) });
|
|
820
|
+
};
|
|
821
|
+
Object.freeze(this);
|
|
822
|
+
}
|
|
823
|
+
// Keep the degree-12 Frobenius row lazy too; after the first lookup the cached
|
|
824
|
+
// array is reused exactly like the old eager table.
|
|
825
|
+
get FROBENIUS_COEFFICIENTS(): readonly Fp2[] {
|
|
826
|
+
const frob = _FROBENIUS_COEFFICIENTS_12.get(this);
|
|
827
|
+
if (frob) return frob;
|
|
828
|
+
const { Fp2 } = this.Fp6;
|
|
829
|
+
const { Fp } = Fp2;
|
|
830
|
+
const cache = Object.freeze(
|
|
831
|
+
calcFrobeniusCoefficients(Fp2, Fp2.NONRESIDUE, Fp.ORDER, 12, 1, 6)[0]
|
|
832
|
+
);
|
|
833
|
+
_FROBENIUS_COEFFICIENTS_12.set(this, cache);
|
|
834
|
+
return cache;
|
|
631
835
|
}
|
|
632
836
|
create(num: Fp12) {
|
|
633
|
-
|
|
837
|
+
const { Fp6 } = this;
|
|
838
|
+
const c0 = Fp6.create(num.c0);
|
|
839
|
+
const c1 = Fp6.create(num.c1);
|
|
840
|
+
return Object.freeze({ c0, c1 });
|
|
634
841
|
}
|
|
635
|
-
isValid(
|
|
842
|
+
isValid(num: Fp12) {
|
|
843
|
+
if (!isObj(num))
|
|
844
|
+
throw new TypeError('invalid field element: expected object, got ' + typeof num);
|
|
845
|
+
const { c0, c1 } = num;
|
|
636
846
|
const { Fp6 } = this;
|
|
637
847
|
return Fp6.isValid(c0) && Fp6.isValid(c1);
|
|
638
848
|
}
|
|
639
|
-
is0(
|
|
849
|
+
is0(num: Fp12) {
|
|
850
|
+
if (!isObj(num)) return false;
|
|
851
|
+
const { c0, c1 } = num;
|
|
640
852
|
const { Fp6 } = this;
|
|
641
853
|
return Fp6.is0(c0) && Fp6.is0(c1);
|
|
642
854
|
}
|
|
@@ -645,19 +857,23 @@ class _Field12 implements Fp12Bls {
|
|
|
645
857
|
}
|
|
646
858
|
neg({ c0, c1 }: Fp12) {
|
|
647
859
|
const { Fp6 } = this;
|
|
648
|
-
return { c0: Fp6.neg(c0), c1: Fp6.neg(c1) };
|
|
860
|
+
return Object.freeze({ c0: Fp6.neg(c0), c1: Fp6.neg(c1) });
|
|
649
861
|
}
|
|
650
862
|
eql({ c0, c1 }: Fp12, { c0: r0, c1: r1 }: Fp12) {
|
|
651
863
|
const { Fp6 } = this;
|
|
652
864
|
return Fp6.eql(c0, r0) && Fp6.eql(c1, r1);
|
|
653
865
|
}
|
|
654
|
-
sqrt(_:
|
|
655
|
-
|
|
866
|
+
sqrt(_: Fp12): Fp12 {
|
|
867
|
+
// Fp12 is quadratic over Fp6, so a dedicated quadratic-extension sqrt is possible here
|
|
868
|
+
// once Fp6.sqrt() exists. Without that lower-level sqrt, only a field-generic
|
|
869
|
+
// Tonelli-Shanks path over Fp12 itself would work.
|
|
870
|
+
return notImplemented();
|
|
656
871
|
}
|
|
657
872
|
inv({ c0, c1 }: Fp12) {
|
|
658
873
|
const { Fp6 } = this;
|
|
659
874
|
let t = Fp6.inv(Fp6.sub(Fp6.sqr(c0), Fp6.mulByNonresidue(Fp6.sqr(c1)))); // 1 / (c0² - c1² * v)
|
|
660
|
-
|
|
875
|
+
// ((C0 * T) * T) + (-C1 * T) * w
|
|
876
|
+
return Object.freeze({ c0: Fp6.mul(c0, t), c1: Fp6.neg(Fp6.mul(c1, t)) });
|
|
661
877
|
}
|
|
662
878
|
div(lhs: Fp12, rhs: Fp12) {
|
|
663
879
|
const { Fp6 } = this;
|
|
@@ -675,41 +891,42 @@ class _Field12 implements Fp12Bls {
|
|
|
675
891
|
// Normalized
|
|
676
892
|
add({ c0, c1 }: Fp12, { c0: r0, c1: r1 }: Fp12) {
|
|
677
893
|
const { Fp6 } = this;
|
|
678
|
-
return {
|
|
894
|
+
return Object.freeze({
|
|
679
895
|
c0: Fp6.add(c0, r0),
|
|
680
896
|
c1: Fp6.add(c1, r1),
|
|
681
|
-
};
|
|
897
|
+
});
|
|
682
898
|
}
|
|
683
899
|
sub({ c0, c1 }: Fp12, { c0: r0, c1: r1 }: Fp12) {
|
|
684
900
|
const { Fp6 } = this;
|
|
685
|
-
return {
|
|
901
|
+
return Object.freeze({
|
|
686
902
|
c0: Fp6.sub(c0, r0),
|
|
687
903
|
c1: Fp6.sub(c1, r1),
|
|
688
|
-
};
|
|
904
|
+
});
|
|
689
905
|
}
|
|
690
906
|
mul({ c0, c1 }: Fp12, rhs: Fp12 | bigint) {
|
|
691
907
|
const { Fp6 } = this;
|
|
692
|
-
if (typeof rhs === 'bigint')
|
|
908
|
+
if (typeof rhs === 'bigint')
|
|
909
|
+
return Object.freeze({ c0: Fp6.mul(c0, rhs), c1: Fp6.mul(c1, rhs) });
|
|
693
910
|
let { c0: r0, c1: r1 } = rhs;
|
|
694
911
|
let t1 = Fp6.mul(c0, r0); // c0 * r0
|
|
695
912
|
let t2 = Fp6.mul(c1, r1); // c1 * r1
|
|
696
|
-
return {
|
|
913
|
+
return Object.freeze({
|
|
697
914
|
c0: Fp6.add(t1, Fp6.mulByNonresidue(t2)), // T1 + T2 * v
|
|
698
915
|
// (c0 + c1) * (r0 + r1) - (T1 + T2)
|
|
699
916
|
c1: Fp6.sub(Fp6.mul(Fp6.add(c0, c1), Fp6.add(r0, r1)), Fp6.add(t1, t2)),
|
|
700
|
-
};
|
|
917
|
+
});
|
|
701
918
|
}
|
|
702
919
|
sqr({ c0, c1 }: Fp12) {
|
|
703
920
|
const { Fp6 } = this;
|
|
704
921
|
let ab = Fp6.mul(c0, c1); // c0 * c1
|
|
705
|
-
return {
|
|
922
|
+
return Object.freeze({
|
|
706
923
|
// (c1 * v + c0) * (c0 + c1) - AB - AB * v
|
|
707
924
|
c0: Fp6.sub(
|
|
708
925
|
Fp6.sub(Fp6.mul(Fp6.add(Fp6.mulByNonresidue(c1), c0), Fp6.add(c0, c1)), ab),
|
|
709
926
|
Fp6.mulByNonresidue(ab)
|
|
710
927
|
),
|
|
711
928
|
c1: Fp6.add(ab, ab),
|
|
712
|
-
}; // AB + AB
|
|
929
|
+
}); // AB + AB
|
|
713
930
|
}
|
|
714
931
|
// NonNormalized stuff
|
|
715
932
|
addN(a: Fp12, b: Fp12): Fp12 {
|
|
@@ -728,11 +945,12 @@ class _Field12 implements Fp12Bls {
|
|
|
728
945
|
// Bytes utils
|
|
729
946
|
fromBytes(b: Uint8Array): Fp12 {
|
|
730
947
|
const { Fp6 } = this;
|
|
948
|
+
abytes(b);
|
|
731
949
|
if (b.length !== this.BYTES) throw new Error('fromBytes invalid length=' + b.length);
|
|
732
|
-
return {
|
|
950
|
+
return this.create({
|
|
733
951
|
c0: Fp6.fromBytes(b.subarray(0, Fp6.BYTES)),
|
|
734
952
|
c1: Fp6.fromBytes(b.subarray(Fp6.BYTES)),
|
|
735
|
-
};
|
|
953
|
+
});
|
|
736
954
|
}
|
|
737
955
|
toBytes({ c0, c1 }: Fp12): Uint8Array {
|
|
738
956
|
const { Fp6 } = this;
|
|
@@ -740,10 +958,10 @@ class _Field12 implements Fp12Bls {
|
|
|
740
958
|
}
|
|
741
959
|
cmov({ c0, c1 }: Fp12, { c0: r0, c1: r1 }: Fp12, c: boolean) {
|
|
742
960
|
const { Fp6 } = this;
|
|
743
|
-
return {
|
|
961
|
+
return this.create({
|
|
744
962
|
c0: Fp6.cmov(c0, r0, c),
|
|
745
963
|
c1: Fp6.cmov(c1, r1, c),
|
|
746
|
-
};
|
|
964
|
+
});
|
|
747
965
|
}
|
|
748
966
|
// Utils
|
|
749
967
|
// toString() {
|
|
@@ -752,12 +970,16 @@ class _Field12 implements Fp12Bls {
|
|
|
752
970
|
// fromTuple(c: [Fp6, Fp6]) {
|
|
753
971
|
// return new Fp12(...c);
|
|
754
972
|
// }
|
|
755
|
-
fromBigTwelve(
|
|
973
|
+
fromBigTwelve(tuple: BigintTwelve): Fp12 {
|
|
756
974
|
const { Fp6 } = this;
|
|
757
|
-
|
|
975
|
+
if (!Array.isArray(tuple) || tuple.length !== 12) throw new Error('invalid Fp12.fromBigTwelve');
|
|
976
|
+
for (let i = 0; i < 12; i++)
|
|
977
|
+
if (typeof tuple[i] !== 'bigint') throw new Error('invalid Fp12.fromBigTwelve');
|
|
978
|
+
const t = tuple;
|
|
979
|
+
return this.create({
|
|
758
980
|
c0: Fp6.fromBigSix(t.slice(0, 6) as BigintSix),
|
|
759
981
|
c1: Fp6.fromBigSix(t.slice(6, 12) as BigintSix),
|
|
760
|
-
};
|
|
982
|
+
});
|
|
761
983
|
}
|
|
762
984
|
// Raises to q**i -th power
|
|
763
985
|
frobeniusMap(lhs: Fp12, power: number) {
|
|
@@ -765,24 +987,25 @@ class _Field12 implements Fp12Bls {
|
|
|
765
987
|
const { Fp2 } = Fp6;
|
|
766
988
|
const { c0, c1, c2 } = Fp6.frobeniusMap(lhs.c1, power);
|
|
767
989
|
const coeff = this.FROBENIUS_COEFFICIENTS[power % 12];
|
|
768
|
-
return {
|
|
990
|
+
return Object.freeze({
|
|
769
991
|
c0: Fp6.frobeniusMap(lhs.c0, power),
|
|
770
|
-
c1:
|
|
992
|
+
c1: Object.freeze({
|
|
771
993
|
c0: Fp2.mul(c0, coeff),
|
|
772
994
|
c1: Fp2.mul(c1, coeff),
|
|
773
995
|
c2: Fp2.mul(c2, coeff),
|
|
774
996
|
}),
|
|
775
|
-
};
|
|
997
|
+
});
|
|
776
998
|
}
|
|
777
999
|
mulByFp2({ c0, c1 }: Fp12, rhs: Fp2): Fp12 {
|
|
778
1000
|
const { Fp6 } = this;
|
|
779
|
-
return {
|
|
1001
|
+
return Object.freeze({
|
|
780
1002
|
c0: Fp6.mulByFp2(c0, rhs),
|
|
781
1003
|
c1: Fp6.mulByFp2(c1, rhs),
|
|
782
|
-
};
|
|
1004
|
+
});
|
|
783
1005
|
}
|
|
784
1006
|
conjugate({ c0, c1 }: Fp12): Fp12 {
|
|
785
|
-
|
|
1007
|
+
// Reuse `c0` by reference and only negate the `w` coefficient.
|
|
1008
|
+
return Object.freeze({ c0, c1: this.Fp6.neg(c1) });
|
|
786
1009
|
}
|
|
787
1010
|
// Sparse multiplication
|
|
788
1011
|
mul014({ c0, c1 }: Fp12, o0: Fp2, o1: Fp2, o4: Fp2) {
|
|
@@ -790,26 +1013,26 @@ class _Field12 implements Fp12Bls {
|
|
|
790
1013
|
const { Fp2 } = Fp6;
|
|
791
1014
|
let t0 = Fp6.mul01(c0, o0, o1);
|
|
792
1015
|
let t1 = Fp6.mul1(c1, o4);
|
|
793
|
-
return {
|
|
1016
|
+
return Object.freeze({
|
|
794
1017
|
c0: Fp6.add(Fp6.mulByNonresidue(t1), t0), // T1 * v + T0
|
|
795
1018
|
// (c1 + c0) * [o0, o1+o4] - T0 - T1
|
|
796
1019
|
c1: Fp6.sub(Fp6.sub(Fp6.mul01(Fp6.add(c1, c0), o0, Fp2.add(o1, o4)), t0), t1),
|
|
797
|
-
};
|
|
1020
|
+
});
|
|
798
1021
|
}
|
|
799
1022
|
mul034({ c0, c1 }: Fp12, o0: Fp2, o3: Fp2, o4: Fp2) {
|
|
800
1023
|
const { Fp6 } = this;
|
|
801
1024
|
const { Fp2 } = Fp6;
|
|
802
|
-
const a =
|
|
1025
|
+
const a = Object.freeze({
|
|
803
1026
|
c0: Fp2.mul(c0.c0, o0),
|
|
804
1027
|
c1: Fp2.mul(c0.c1, o0),
|
|
805
1028
|
c2: Fp2.mul(c0.c2, o0),
|
|
806
1029
|
});
|
|
807
1030
|
const b = Fp6.mul01(c1, o3, o4);
|
|
808
1031
|
const e = Fp6.mul01(Fp6.add(c0, c1), Fp2.add(o0, o3), o4);
|
|
809
|
-
return {
|
|
1032
|
+
return Object.freeze({
|
|
810
1033
|
c0: Fp6.add(Fp6.mulByNonresidue(b), a),
|
|
811
1034
|
c1: Fp6.sub(e, Fp6.add(a, b)),
|
|
812
|
-
};
|
|
1035
|
+
});
|
|
813
1036
|
}
|
|
814
1037
|
|
|
815
1038
|
// A cyclotomic group is a subgroup of Fp^n defined by
|
|
@@ -826,21 +1049,24 @@ class _Field12 implements Fp12Bls {
|
|
|
826
1049
|
const { first: t5, second: t6 } = Fp2.Fp4Square(c1c0, c0c2);
|
|
827
1050
|
const { first: t7, second: t8 } = Fp2.Fp4Square(c0c1, c1c2);
|
|
828
1051
|
const t9 = Fp2.mulByNonresidue(t8); // T8 * (u + 1)
|
|
829
|
-
return {
|
|
830
|
-
c0:
|
|
1052
|
+
return Object.freeze({
|
|
1053
|
+
c0: Object.freeze({
|
|
831
1054
|
c0: Fp2.add(Fp2.mul(Fp2.sub(t3, c0c0), _2n), t3), // 2 * (T3 - c0c0) + T3
|
|
832
1055
|
c1: Fp2.add(Fp2.mul(Fp2.sub(t5, c0c1), _2n), t5), // 2 * (T5 - c0c1) + T5
|
|
833
1056
|
c2: Fp2.add(Fp2.mul(Fp2.sub(t7, c0c2), _2n), t7),
|
|
834
1057
|
}), // 2 * (T7 - c0c2) + T7
|
|
835
|
-
c1:
|
|
1058
|
+
c1: Object.freeze({
|
|
836
1059
|
c0: Fp2.add(Fp2.mul(Fp2.add(t9, c1c0), _2n), t9), // 2 * (T9 + c1c0) + T9
|
|
837
1060
|
c1: Fp2.add(Fp2.mul(Fp2.add(t4, c1c1), _2n), t4), // 2 * (T4 + c1c1) + T4
|
|
838
1061
|
c2: Fp2.add(Fp2.mul(Fp2.add(t6, c1c2), _2n), t6),
|
|
839
1062
|
}),
|
|
840
|
-
}; // 2 * (T6 + c1c2) + T6
|
|
1063
|
+
}); // 2 * (T6 + c1c2) + T6
|
|
841
1064
|
}
|
|
842
1065
|
// https://eprint.iacr.org/2009/565.pdf
|
|
843
1066
|
_cyclotomicExp(num: Fp12, n: bigint): Fp12 {
|
|
1067
|
+
// The loop only consumes `X_LEN` bits, so out-of-range exponents would otherwise get silently
|
|
1068
|
+
// truncated (or sign-extended for negatives) instead of matching the caller's requested power.
|
|
1069
|
+
aInRange('cyclotomic exponent', n, _0n, _1n << BigInt(this.X_LEN));
|
|
844
1070
|
let z = this.ONE;
|
|
845
1071
|
for (let i = this.X_LEN - 1; i >= 0; i--) {
|
|
846
1072
|
z = this._cyclotomicSquare(z);
|
|
@@ -850,15 +1076,58 @@ class _Field12 implements Fp12Bls {
|
|
|
850
1076
|
}
|
|
851
1077
|
}
|
|
852
1078
|
|
|
853
|
-
|
|
1079
|
+
const _FROBENIUS_COEFFICIENTS_12 = new WeakMap<_Field12, readonly Fp2[]>();
|
|
1080
|
+
|
|
1081
|
+
/**
|
|
1082
|
+
* @param opts - Tower construction options. See {@link Tower12Opts}.
|
|
1083
|
+
* @returns BLS tower fields.
|
|
1084
|
+
* @throws If the tower options or derived Frobenius helpers are invalid. {@link Error}
|
|
1085
|
+
* @example
|
|
1086
|
+
* Construct the Fp2/Fp6/Fp12 tower used by a pairing-friendly curve.
|
|
1087
|
+
*
|
|
1088
|
+
* ```ts
|
|
1089
|
+
* const fields = tower12({
|
|
1090
|
+
* ORDER: 17n,
|
|
1091
|
+
* X_LEN: 4,
|
|
1092
|
+
* FP2_NONRESIDUE: [1n, 1n],
|
|
1093
|
+
* Fp2mulByB: (num) => num,
|
|
1094
|
+
* Fp12finalExponentiate: (num) => num,
|
|
1095
|
+
* });
|
|
1096
|
+
* const fp12 = fields.Fp12.ONE;
|
|
1097
|
+
* ```
|
|
1098
|
+
*/
|
|
1099
|
+
export function tower12(opts: TArg<Tower12Opts>): TRet<{
|
|
854
1100
|
Fp: Readonly<mod.IField<bigint> & Required<Pick<mod.IField<bigint>, 'isOdd'>>>;
|
|
855
1101
|
Fp2: Fp2Bls;
|
|
856
1102
|
Fp6: Fp6Bls;
|
|
857
1103
|
Fp12: Fp12Bls;
|
|
858
|
-
} {
|
|
1104
|
+
}> {
|
|
1105
|
+
validateObject(
|
|
1106
|
+
opts,
|
|
1107
|
+
{
|
|
1108
|
+
ORDER: 'bigint',
|
|
1109
|
+
X_LEN: 'number',
|
|
1110
|
+
FP2_NONRESIDUE: 'object',
|
|
1111
|
+
Fp2mulByB: 'function',
|
|
1112
|
+
Fp12finalExponentiate: 'function',
|
|
1113
|
+
},
|
|
1114
|
+
{ NONRESIDUE: 'bigint' }
|
|
1115
|
+
);
|
|
1116
|
+
asafenumber(opts.X_LEN, 'X_LEN');
|
|
1117
|
+
if (opts.X_LEN < 1) throw new Error('invalid X_LEN');
|
|
1118
|
+
const nonresidue = opts.FP2_NONRESIDUE as bigint[];
|
|
1119
|
+
if (!Array.isArray(nonresidue) || nonresidue.length !== 2)
|
|
1120
|
+
throw new Error('invalid FP2_NONRESIDUE');
|
|
1121
|
+
if (typeof nonresidue[0] !== 'bigint' || typeof nonresidue[1] !== 'bigint')
|
|
1122
|
+
throw new Error('invalid FP2_NONRESIDUE');
|
|
859
1123
|
const Fp = mod.Field(opts.ORDER);
|
|
860
1124
|
const Fp2 = new _Field2(Fp, opts);
|
|
861
1125
|
const Fp6 = new _Field6(Fp2);
|
|
862
1126
|
const Fp12 = new _Field12(Fp6, opts);
|
|
863
|
-
return { Fp, Fp2, Fp6, Fp12 }
|
|
1127
|
+
return { Fp, Fp2, Fp6, Fp12 } as TRet<{
|
|
1128
|
+
Fp: Readonly<mod.IField<bigint> & Required<Pick<mod.IField<bigint>, 'isOdd'>>>;
|
|
1129
|
+
Fp2: Fp2Bls;
|
|
1130
|
+
Fp6: Fp6Bls;
|
|
1131
|
+
Fp12: Fp12Bls;
|
|
1132
|
+
}>;
|
|
864
1133
|
}
|