@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/abstract/tower.js
CHANGED
|
@@ -10,28 +10,65 @@
|
|
|
10
10
|
* @module
|
|
11
11
|
*/
|
|
12
12
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
13
|
-
import { bitGet, bitLen, concatBytes, notImplemented } from "../utils.js";
|
|
13
|
+
import { abytes, aInRange, asafenumber, bitGet, bitLen, concatBytes, notImplemented, validateObject, } from "../utils.js";
|
|
14
14
|
import * as mod from "./modular.js";
|
|
15
15
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
|
16
16
|
// prettier-ignore
|
|
17
|
-
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
|
|
17
|
+
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);
|
|
18
|
+
const isObj = (value) => !!value && typeof value === 'object';
|
|
18
19
|
function calcFrobeniusCoefficients(Fp, nonResidue, modulus, degree, num = 1, divisor) {
|
|
20
|
+
asafenumber(num, 'num');
|
|
21
|
+
const F = Fp;
|
|
22
|
+
// Generic callers can hit empty / fractional row counts through `__TEST`; fail closed instead of
|
|
23
|
+
// silently returning `[]` or deriving extra Frobenius rows from a truncated loop bound.
|
|
24
|
+
if (num <= 0)
|
|
25
|
+
throw new Error('calcFrobeniusCoefficients: expected positive row count, got ' + num);
|
|
19
26
|
const _divisor = BigInt(divisor === undefined ? degree : divisor);
|
|
20
27
|
const towerModulus = modulus ** BigInt(degree);
|
|
21
28
|
const res = [];
|
|
29
|
+
// Derive tower-basis multipliers for the `p^k` Frobenius action. The
|
|
30
|
+
// divisions below are expected to be exact for the chosen tower parameters.
|
|
22
31
|
for (let i = 0; i < num; i++) {
|
|
23
32
|
const a = BigInt(i + 1);
|
|
24
33
|
const powers = [];
|
|
25
34
|
for (let j = 0, qPower = _1n; j < degree; j++) {
|
|
26
|
-
const
|
|
27
|
-
|
|
35
|
+
const numer = a * qPower - a;
|
|
36
|
+
// Shipped towers divide cleanly here, but generic callers can pick bad
|
|
37
|
+
// params. Bigint division would floor and derive the wrong Frobenius table.
|
|
38
|
+
if (numer % _divisor)
|
|
39
|
+
throw new Error('calcFrobeniusCoefficients: inexact tower exponent');
|
|
40
|
+
const power = (numer / _divisor) % towerModulus;
|
|
41
|
+
powers.push(F.pow(nonResidue, power));
|
|
28
42
|
qPower *= modulus;
|
|
29
43
|
}
|
|
30
44
|
res.push(powers);
|
|
31
45
|
}
|
|
32
46
|
return res;
|
|
33
47
|
}
|
|
48
|
+
export const __TEST =
|
|
49
|
+
/* @__PURE__ */ Object.freeze({
|
|
50
|
+
calcFrobeniusCoefficients,
|
|
51
|
+
});
|
|
34
52
|
// This works same at least for bls12-381, bn254 and bls12-377
|
|
53
|
+
/**
|
|
54
|
+
* @param Fp - Base field implementation.
|
|
55
|
+
* @param Fp2 - Quadratic extension field.
|
|
56
|
+
* @param base - Twist-specific Frobenius base whose powers yield the `c1` / `c2` constants.
|
|
57
|
+
* BLS12-381 uses `1 / NONRESIDUE`; BN254 uses `NONRESIDUE`.
|
|
58
|
+
* @returns Frobenius endomorphism helpers.
|
|
59
|
+
* @throws If the derived Frobenius constants are inconsistent for the tower. {@link Error}
|
|
60
|
+
* @example
|
|
61
|
+
* Build Frobenius endomorphism helpers for a BLS extension tower.
|
|
62
|
+
*
|
|
63
|
+
* ```ts
|
|
64
|
+
* import { psiFrobenius } from '@noble/curves/abstract/tower.js';
|
|
65
|
+
* import { bls12_381 } from '@noble/curves/bls12-381.js';
|
|
66
|
+
* const Fp = bls12_381.fields.Fp;
|
|
67
|
+
* const Fp2 = bls12_381.fields.Fp2;
|
|
68
|
+
* const frob = psiFrobenius(Fp, Fp2, Fp2.div(Fp2.ONE, Fp2.NONRESIDUE));
|
|
69
|
+
* const point = frob.G2psi(bls12_381.G2.Point, bls12_381.G2.Point.BASE);
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
35
72
|
export function psiFrobenius(Fp, Fp2, base) {
|
|
36
73
|
// GLV endomorphism Ψ(P)
|
|
37
74
|
const PSI_X = Fp2.pow(base, (Fp.ORDER - _1n) / _3n); // u^((p-1)/3)
|
|
@@ -44,9 +81,9 @@ export function psiFrobenius(Fp, Fp2, base) {
|
|
|
44
81
|
}
|
|
45
82
|
// Ψ²(P) endomorphism (psi2(x) = psi(psi(x)))
|
|
46
83
|
const PSI2_X = Fp2.pow(base, (Fp.ORDER ** _2n - _1n) / _3n); // u^((p^2 - 1)/3)
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
const PSI2_Y = Fp2.pow(base, (Fp.ORDER ** _2n - _1n) / _2n); // u^((p^2 - 1)/
|
|
84
|
+
// Current towers rely on this landing on `-1`, which lets psi2 map `y` with
|
|
85
|
+
// one negation instead of carrying a separate Frobenius multiplier.
|
|
86
|
+
const PSI2_Y = Fp2.pow(base, (Fp.ORDER ** _2n - _1n) / _2n); // u^((p^2 - 1)/2)
|
|
50
87
|
if (!Fp2.eql(PSI2_Y, Fp2.neg(Fp2.ONE)))
|
|
51
88
|
throw new Error('psiFrobenius: PSI2_Y!==-1');
|
|
52
89
|
function psi2(x, y) {
|
|
@@ -62,12 +99,6 @@ export function psiFrobenius(Fp, Fp2, base) {
|
|
|
62
99
|
const G2psi2 = mapAffine(psi2);
|
|
63
100
|
return { psi, psi2, G2psi, G2psi2, PSI_X, PSI_Y, PSI2_X, PSI2_Y };
|
|
64
101
|
}
|
|
65
|
-
const Fp2fromBigTuple = (Fp, tuple) => {
|
|
66
|
-
if (tuple.length !== 2)
|
|
67
|
-
throw new Error('invalid tuple');
|
|
68
|
-
const fps = tuple.map((n) => Fp.create(n));
|
|
69
|
-
return { c0: fps[0], c1: fps[1] };
|
|
70
|
-
};
|
|
71
102
|
class _Field2 {
|
|
72
103
|
ORDER;
|
|
73
104
|
BITS;
|
|
@@ -82,6 +113,7 @@ class _Field2 {
|
|
|
82
113
|
Fp_div2;
|
|
83
114
|
FROBENIUS_COEFFICIENTS;
|
|
84
115
|
constructor(Fp, opts = {}) {
|
|
116
|
+
const { NONRESIDUE = BigInt(-1), FP2_NONRESIDUE, Fp2mulByB } = opts;
|
|
85
117
|
const ORDER = Fp.ORDER;
|
|
86
118
|
const FP2_ORDER = ORDER * ORDER;
|
|
87
119
|
this.Fp = Fp;
|
|
@@ -89,39 +121,66 @@ class _Field2 {
|
|
|
89
121
|
this.BITS = bitLen(FP2_ORDER);
|
|
90
122
|
this.BYTES = Math.ceil(bitLen(FP2_ORDER) / 8);
|
|
91
123
|
this.isLE = Fp.isLE;
|
|
92
|
-
this.ZERO = { c0: Fp.ZERO, c1: Fp.ZERO };
|
|
93
|
-
this.ONE = { c0: Fp.ONE, c1: Fp.ZERO };
|
|
94
|
-
|
|
124
|
+
this.ZERO = this.create({ c0: Fp.ZERO, c1: Fp.ZERO });
|
|
125
|
+
this.ONE = this.create({ c0: Fp.ONE, c1: Fp.ZERO });
|
|
126
|
+
// These knobs only swap constants for the shipped quadratic tower shape:
|
|
127
|
+
// arithmetic below assumes `u^2 = -1`, and bytes are handled as two adjacent
|
|
128
|
+
// `Fp` limbs (`fromBytes` / `toBytes` expect the shipped `2 * Fp.BYTES` layout).
|
|
129
|
+
this.Fp_NONRESIDUE = Fp.create(NONRESIDUE);
|
|
95
130
|
this.Fp_div2 = Fp.div(Fp.ONE, _2n); // 1/2
|
|
96
|
-
this.NONRESIDUE =
|
|
97
|
-
// const Fp2Nonresidue =
|
|
98
|
-
this.FROBENIUS_COEFFICIENTS = calcFrobeniusCoefficients(Fp, this.Fp_NONRESIDUE, Fp.ORDER, 2)[0];
|
|
99
|
-
this.mulByB =
|
|
100
|
-
|
|
131
|
+
this.NONRESIDUE = this.create({ c0: FP2_NONRESIDUE[0], c1: FP2_NONRESIDUE[1] });
|
|
132
|
+
// const Fp2Nonresidue = this.create({ c0: FP2_NONRESIDUE![0], c1: FP2_NONRESIDUE![1] });
|
|
133
|
+
this.FROBENIUS_COEFFICIENTS = Object.freeze(calcFrobeniusCoefficients(Fp, this.Fp_NONRESIDUE, Fp.ORDER, 2)[0]);
|
|
134
|
+
this.mulByB = (num) => {
|
|
135
|
+
// This config hook is trusted to return a canonical Fp2 value already.
|
|
136
|
+
// Copy+freeze it to keep the tower immutability invariant without mutating caller objects.
|
|
137
|
+
const { c0, c1 } = Fp2mulByB(num);
|
|
138
|
+
return Object.freeze({ c0, c1 });
|
|
139
|
+
};
|
|
140
|
+
Object.freeze(this);
|
|
101
141
|
}
|
|
102
142
|
fromBigTuple(tuple) {
|
|
103
|
-
|
|
143
|
+
if (!Array.isArray(tuple) || tuple.length !== 2)
|
|
144
|
+
throw new Error('invalid Fp2.fromBigTuple');
|
|
145
|
+
const [c0, c1] = tuple;
|
|
146
|
+
if (typeof c0 !== 'bigint' || typeof c1 !== 'bigint')
|
|
147
|
+
throw new Error('invalid Fp2.fromBigTuple');
|
|
148
|
+
return this.create({ c0, c1 });
|
|
104
149
|
}
|
|
105
150
|
create(num) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
|
|
151
|
+
const { Fp } = this;
|
|
152
|
+
const c0 = Fp.create(num.c0);
|
|
153
|
+
const c1 = Fp.create(num.c1);
|
|
154
|
+
// Bigint field elements are immutable values, and higher-level code relies on
|
|
155
|
+
// that invariant. Copy+freeze tower values too without mutating caller-owned objects.
|
|
156
|
+
return Object.freeze({ c0, c1 });
|
|
157
|
+
}
|
|
158
|
+
isValid(num) {
|
|
159
|
+
if (!isObj(num))
|
|
160
|
+
throw new TypeError('invalid field element: expected object, got ' + typeof num);
|
|
161
|
+
const { c0, c1 } = num;
|
|
162
|
+
const { Fp } = this;
|
|
163
|
+
// Match base-field `isValid(...)`: malformed coordinate types are errors, not a `false`
|
|
164
|
+
// predicate result.
|
|
165
|
+
return Fp.isValid(c0) && Fp.isValid(c1);
|
|
113
166
|
}
|
|
114
|
-
is0(
|
|
115
|
-
|
|
167
|
+
is0(num) {
|
|
168
|
+
if (!isObj(num))
|
|
169
|
+
return false;
|
|
170
|
+
const { c0, c1 } = num;
|
|
171
|
+
const { Fp } = this;
|
|
172
|
+
return Fp.is0(c0) && Fp.is0(c1);
|
|
116
173
|
}
|
|
117
174
|
isValidNot0(num) {
|
|
118
175
|
return !this.is0(num) && this.isValid(num);
|
|
119
176
|
}
|
|
120
177
|
eql({ c0, c1 }, { c0: r0, c1: r1 }) {
|
|
121
|
-
|
|
178
|
+
const { Fp } = this;
|
|
179
|
+
return Fp.eql(c0, r0) && Fp.eql(c1, r1);
|
|
122
180
|
}
|
|
123
181
|
neg({ c0, c1 }) {
|
|
124
|
-
|
|
182
|
+
const { Fp } = this;
|
|
183
|
+
return Object.freeze({ c0: Fp.neg(c0), c1: Fp.neg(c1) });
|
|
125
184
|
}
|
|
126
185
|
pow(num, power) {
|
|
127
186
|
return mod.FpPow(this, num, power);
|
|
@@ -131,23 +190,25 @@ class _Field2 {
|
|
|
131
190
|
}
|
|
132
191
|
// Normalized
|
|
133
192
|
add(f1, f2) {
|
|
193
|
+
const { Fp } = this;
|
|
134
194
|
const { c0, c1 } = f1;
|
|
135
195
|
const { c0: r0, c1: r1 } = f2;
|
|
136
|
-
return {
|
|
137
|
-
c0:
|
|
138
|
-
c1:
|
|
139
|
-
};
|
|
196
|
+
return Object.freeze({
|
|
197
|
+
c0: Fp.add(c0, r0),
|
|
198
|
+
c1: Fp.add(c1, r1),
|
|
199
|
+
});
|
|
140
200
|
}
|
|
141
201
|
sub({ c0, c1 }, { c0: r0, c1: r1 }) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
202
|
+
const { Fp } = this;
|
|
203
|
+
return Object.freeze({
|
|
204
|
+
c0: Fp.sub(c0, r0),
|
|
205
|
+
c1: Fp.sub(c1, r1),
|
|
206
|
+
});
|
|
146
207
|
}
|
|
147
208
|
mul({ c0, c1 }, rhs) {
|
|
148
209
|
const { Fp } = this;
|
|
149
210
|
if (typeof rhs === 'bigint')
|
|
150
|
-
return { c0: Fp.mul(c0, rhs), c1: Fp.mul(c1, rhs) };
|
|
211
|
+
return Object.freeze({ c0: Fp.mul(c0, rhs), c1: Fp.mul(c1, rhs) });
|
|
151
212
|
// (a+bi)(c+di) = (ac−bd) + (ad+bc)i
|
|
152
213
|
const { c0: r0, c1: r1 } = rhs;
|
|
153
214
|
let t1 = Fp.mul(c0, r0); // c0 * o0
|
|
@@ -155,14 +216,14 @@ class _Field2 {
|
|
|
155
216
|
// (T1 - T2) + ((c0 + c1) * (r0 + r1) - (T1 + T2))*i
|
|
156
217
|
const o0 = Fp.sub(t1, t2);
|
|
157
218
|
const o1 = Fp.sub(Fp.mul(Fp.add(c0, c1), Fp.add(r0, r1)), Fp.add(t1, t2));
|
|
158
|
-
return { c0: o0, c1: o1 };
|
|
219
|
+
return Object.freeze({ c0: o0, c1: o1 });
|
|
159
220
|
}
|
|
160
221
|
sqr({ c0, c1 }) {
|
|
161
222
|
const { Fp } = this;
|
|
162
223
|
const a = Fp.add(c0, c1);
|
|
163
224
|
const b = Fp.sub(c0, c1);
|
|
164
225
|
const c = Fp.add(c0, c0);
|
|
165
|
-
return { c0: Fp.mul(a, b), c1: Fp.mul(c, c1) };
|
|
226
|
+
return Object.freeze({ c0: Fp.mul(a, b), c1: Fp.mul(c, c1) });
|
|
166
227
|
}
|
|
167
228
|
// NonNormalized stuff
|
|
168
229
|
addN(a, b) {
|
|
@@ -199,7 +260,7 @@ class _Field2 {
|
|
|
199
260
|
// only a single inversion in Fp.
|
|
200
261
|
const { Fp } = this;
|
|
201
262
|
const factor = Fp.inv(Fp.create(a * a + b * b));
|
|
202
|
-
return { c0: Fp.mul(factor, Fp.create(a)), c1: Fp.mul(factor, Fp.create(-b)) };
|
|
263
|
+
return Object.freeze({ c0: Fp.mul(factor, Fp.create(a)), c1: Fp.mul(factor, Fp.create(-b)) });
|
|
203
264
|
}
|
|
204
265
|
sqrt(num) {
|
|
205
266
|
// This is generic for all quadratic extensions (Fp2)
|
|
@@ -243,18 +304,23 @@ class _Field2 {
|
|
|
243
304
|
// Bytes util
|
|
244
305
|
fromBytes(b) {
|
|
245
306
|
const { Fp } = this;
|
|
307
|
+
abytes(b);
|
|
246
308
|
if (b.length !== this.BYTES)
|
|
247
309
|
throw new Error('fromBytes invalid length=' + b.length);
|
|
248
|
-
return
|
|
310
|
+
return this.create({
|
|
311
|
+
c0: Fp.fromBytes(b.subarray(0, Fp.BYTES)),
|
|
312
|
+
c1: Fp.fromBytes(b.subarray(Fp.BYTES)),
|
|
313
|
+
});
|
|
249
314
|
}
|
|
250
315
|
toBytes({ c0, c1 }) {
|
|
251
316
|
return concatBytes(this.Fp.toBytes(c0), this.Fp.toBytes(c1));
|
|
252
317
|
}
|
|
253
318
|
cmov({ c0, c1 }, { c0: r0, c1: r1 }, c) {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
319
|
+
const { Fp } = this;
|
|
320
|
+
return this.create({
|
|
321
|
+
c0: Fp.cmov(c0, r0, c),
|
|
322
|
+
c1: Fp.cmov(c1, r1, c),
|
|
323
|
+
});
|
|
258
324
|
}
|
|
259
325
|
reim({ c0, c1 }) {
|
|
260
326
|
return { re: c0, im: c1 };
|
|
@@ -273,10 +339,10 @@ class _Field2 {
|
|
|
273
339
|
return this.mul({ c0, c1 }, this.NONRESIDUE);
|
|
274
340
|
}
|
|
275
341
|
frobeniusMap({ c0, c1 }, power) {
|
|
276
|
-
return {
|
|
342
|
+
return Object.freeze({
|
|
277
343
|
c0,
|
|
278
344
|
c1: this.Fp.mul(c1, this.FROBENIUS_COEFFICIENTS[power % 2]),
|
|
279
|
-
};
|
|
345
|
+
});
|
|
280
346
|
}
|
|
281
347
|
}
|
|
282
348
|
class _Field6 {
|
|
@@ -287,59 +353,75 @@ class _Field6 {
|
|
|
287
353
|
ZERO;
|
|
288
354
|
ONE;
|
|
289
355
|
Fp2;
|
|
290
|
-
FROBENIUS_COEFFICIENTS_1;
|
|
291
|
-
FROBENIUS_COEFFICIENTS_2;
|
|
292
356
|
constructor(Fp2) {
|
|
293
357
|
this.Fp2 = Fp2;
|
|
294
|
-
|
|
358
|
+
// `IField.ORDER` is the field cardinality `q`; for sextic extensions that is `p^6`.
|
|
359
|
+
// Generic helpers like Frobenius-style `x^q = x` checks rely on the literal field size here.
|
|
360
|
+
this.ORDER = Fp2.Fp.ORDER ** _6n;
|
|
295
361
|
this.BITS = 3 * Fp2.BITS;
|
|
296
362
|
this.BYTES = 3 * Fp2.BYTES;
|
|
297
363
|
this.isLE = Fp2.isLE;
|
|
298
|
-
this.ZERO = { c0: Fp2.ZERO, c1: Fp2.ZERO, c2: Fp2.ZERO };
|
|
299
|
-
this.ONE = { c0: Fp2.ONE, c1: Fp2.ZERO, c2: Fp2.ZERO };
|
|
364
|
+
this.ZERO = this.create({ c0: Fp2.ZERO, c1: Fp2.ZERO, c2: Fp2.ZERO });
|
|
365
|
+
this.ONE = this.create({ c0: Fp2.ONE, c1: Fp2.ZERO, c2: Fp2.ZERO });
|
|
366
|
+
Object.freeze(this);
|
|
367
|
+
}
|
|
368
|
+
// Most callers never touch Frobenius maps, so keep the sextic tables lazy:
|
|
369
|
+
// eagerly deriving them dominates `bls12-381.js` / `bn254.js` import time.
|
|
370
|
+
get FROBENIUS_COEFFICIENTS_1() {
|
|
371
|
+
const frob = _FROBENIUS_COEFFICIENTS_6.get(this);
|
|
372
|
+
if (frob)
|
|
373
|
+
return frob[0];
|
|
374
|
+
const { Fp2 } = this;
|
|
300
375
|
const { Fp } = Fp2;
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
this
|
|
304
|
-
|
|
376
|
+
const rows = calcFrobeniusCoefficients(Fp2, Fp2.NONRESIDUE, Fp.ORDER, 6, 2, 3);
|
|
377
|
+
const cache = [Object.freeze(rows[0]), Object.freeze(rows[1])];
|
|
378
|
+
_FROBENIUS_COEFFICIENTS_6.set(this, cache);
|
|
379
|
+
return cache[0];
|
|
380
|
+
}
|
|
381
|
+
get FROBENIUS_COEFFICIENTS_2() {
|
|
382
|
+
const frob = _FROBENIUS_COEFFICIENTS_6.get(this);
|
|
383
|
+
if (frob)
|
|
384
|
+
return frob[1];
|
|
385
|
+
void this.FROBENIUS_COEFFICIENTS_1;
|
|
386
|
+
return _FROBENIUS_COEFFICIENTS_6.get(this)[1];
|
|
305
387
|
}
|
|
306
388
|
add({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) {
|
|
307
389
|
const { Fp2 } = this;
|
|
308
|
-
return {
|
|
390
|
+
return Object.freeze({
|
|
309
391
|
c0: Fp2.add(c0, r0),
|
|
310
392
|
c1: Fp2.add(c1, r1),
|
|
311
393
|
c2: Fp2.add(c2, r2),
|
|
312
|
-
};
|
|
394
|
+
});
|
|
313
395
|
}
|
|
314
396
|
sub({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) {
|
|
315
397
|
const { Fp2 } = this;
|
|
316
|
-
return {
|
|
398
|
+
return Object.freeze({
|
|
317
399
|
c0: Fp2.sub(c0, r0),
|
|
318
400
|
c1: Fp2.sub(c1, r1),
|
|
319
401
|
c2: Fp2.sub(c2, r2),
|
|
320
|
-
};
|
|
402
|
+
});
|
|
321
403
|
}
|
|
322
404
|
mul({ c0, c1, c2 }, rhs) {
|
|
323
405
|
const { Fp2 } = this;
|
|
324
406
|
if (typeof rhs === 'bigint') {
|
|
325
|
-
return {
|
|
407
|
+
return Object.freeze({
|
|
326
408
|
c0: Fp2.mul(c0, rhs),
|
|
327
409
|
c1: Fp2.mul(c1, rhs),
|
|
328
410
|
c2: Fp2.mul(c2, rhs),
|
|
329
|
-
};
|
|
411
|
+
});
|
|
330
412
|
}
|
|
331
413
|
const { c0: r0, c1: r1, c2: r2 } = rhs;
|
|
332
414
|
const t0 = Fp2.mul(c0, r0); // c0 * o0
|
|
333
415
|
const t1 = Fp2.mul(c1, r1); // c1 * o1
|
|
334
416
|
const t2 = Fp2.mul(c2, r2); // c2 * o2
|
|
335
|
-
return {
|
|
417
|
+
return Object.freeze({
|
|
336
418
|
// t0 + (c1 + c2) * (r1 * r2) - (T1 + T2) * (u + 1)
|
|
337
419
|
c0: Fp2.add(t0, Fp2.mulByNonresidue(Fp2.sub(Fp2.mul(Fp2.add(c1, c2), Fp2.add(r1, r2)), Fp2.add(t1, t2)))),
|
|
338
420
|
// (c0 + c1) * (r0 + r1) - (T0 + T1) + T2 * (u + 1)
|
|
339
421
|
c1: Fp2.add(Fp2.sub(Fp2.mul(Fp2.add(c0, c1), Fp2.add(r0, r1)), Fp2.add(t0, t1)), Fp2.mulByNonresidue(t2)),
|
|
340
422
|
// T1 + (c0 + c2) * (r0 + r2) - T0 + T2
|
|
341
423
|
c2: Fp2.sub(Fp2.add(t1, Fp2.mul(Fp2.add(c0, c2), Fp2.add(r0, r2))), Fp2.add(t0, t2)),
|
|
342
|
-
};
|
|
424
|
+
});
|
|
343
425
|
}
|
|
344
426
|
sqr({ c0, c1, c2 }) {
|
|
345
427
|
const { Fp2 } = this;
|
|
@@ -347,12 +429,12 @@ class _Field6 {
|
|
|
347
429
|
let t1 = Fp2.mul(Fp2.mul(c0, c1), _2n); // 2 * c0 * c1
|
|
348
430
|
let t3 = Fp2.mul(Fp2.mul(c1, c2), _2n); // 2 * c1 * c2
|
|
349
431
|
let t4 = Fp2.sqr(c2); // c2²
|
|
350
|
-
return {
|
|
432
|
+
return Object.freeze({
|
|
351
433
|
c0: Fp2.add(Fp2.mulByNonresidue(t3), t0), // T3 * (u + 1) + T0
|
|
352
434
|
c1: Fp2.add(Fp2.mulByNonresidue(t4), t1), // T4 * (u + 1) + T1
|
|
353
435
|
// T1 + (c0 - c1 + c2)² + T3 - T0 - T4
|
|
354
436
|
c2: Fp2.sub(Fp2.sub(Fp2.add(Fp2.add(t1, Fp2.sqr(Fp2.add(Fp2.sub(c0, c1), c2))), t3), t0), t4),
|
|
355
|
-
};
|
|
437
|
+
});
|
|
356
438
|
}
|
|
357
439
|
addN(a, b) {
|
|
358
440
|
return this.add(a, b);
|
|
@@ -367,13 +449,23 @@ class _Field6 {
|
|
|
367
449
|
return this.sqr(a);
|
|
368
450
|
}
|
|
369
451
|
create(num) {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
452
|
+
const { Fp2 } = this;
|
|
453
|
+
const c0 = Fp2.create(num.c0);
|
|
454
|
+
const c1 = Fp2.create(num.c1);
|
|
455
|
+
const c2 = Fp2.create(num.c2);
|
|
456
|
+
return Object.freeze({ c0, c1, c2 });
|
|
457
|
+
}
|
|
458
|
+
isValid(num) {
|
|
459
|
+
if (!isObj(num))
|
|
460
|
+
throw new TypeError('invalid field element: expected object, got ' + typeof num);
|
|
461
|
+
const { c0, c1, c2 } = num;
|
|
373
462
|
const { Fp2 } = this;
|
|
374
463
|
return Fp2.isValid(c0) && Fp2.isValid(c1) && Fp2.isValid(c2);
|
|
375
464
|
}
|
|
376
|
-
is0(
|
|
465
|
+
is0(num) {
|
|
466
|
+
if (!isObj(num))
|
|
467
|
+
return false;
|
|
468
|
+
const { c0, c1, c2 } = num;
|
|
377
469
|
const { Fp2 } = this;
|
|
378
470
|
return Fp2.is0(c0) && Fp2.is0(c1) && Fp2.is0(c2);
|
|
379
471
|
}
|
|
@@ -382,13 +474,16 @@ class _Field6 {
|
|
|
382
474
|
}
|
|
383
475
|
neg({ c0, c1, c2 }) {
|
|
384
476
|
const { Fp2 } = this;
|
|
385
|
-
return { c0: Fp2.neg(c0), c1: Fp2.neg(c1), c2: Fp2.neg(c2) };
|
|
477
|
+
return Object.freeze({ c0: Fp2.neg(c0), c1: Fp2.neg(c1), c2: Fp2.neg(c2) });
|
|
386
478
|
}
|
|
387
479
|
eql({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) {
|
|
388
480
|
const { Fp2 } = this;
|
|
389
481
|
return Fp2.eql(c0, r0) && Fp2.eql(c1, r1) && Fp2.eql(c2, r2);
|
|
390
482
|
}
|
|
391
483
|
sqrt(_) {
|
|
484
|
+
// Sextic extensions can use generic odd-field Tonelli-Shanks, but the helper must work
|
|
485
|
+
// over `IField<T>` with a quadratic non-residue from Fp6 itself. The current
|
|
486
|
+
// `mod.tonelliShanks(P)` precomputation only searches integer residues in the base field.
|
|
392
487
|
return notImplemented();
|
|
393
488
|
}
|
|
394
489
|
// Do we need division by bigint at all? Should be done via order:
|
|
@@ -410,19 +505,20 @@ class _Field6 {
|
|
|
410
505
|
let t2 = Fp2.sub(Fp2.sqr(c1), Fp2.mul(c0, c2)); // c1² - c0 * c2
|
|
411
506
|
// 1/(((c2 * T1 + c1 * T2) * v) + c0 * T0)
|
|
412
507
|
let t4 = Fp2.inv(Fp2.add(Fp2.mulByNonresidue(Fp2.add(Fp2.mul(c2, t1), Fp2.mul(c1, t2))), Fp2.mul(c0, t0)));
|
|
413
|
-
return { c0: Fp2.mul(t4, t0), c1: Fp2.mul(t4, t1), c2: Fp2.mul(t4, t2) };
|
|
508
|
+
return Object.freeze({ c0: Fp2.mul(t4, t0), c1: Fp2.mul(t4, t1), c2: Fp2.mul(t4, t2) });
|
|
414
509
|
}
|
|
415
510
|
// Bytes utils
|
|
416
511
|
fromBytes(b) {
|
|
417
512
|
const { Fp2 } = this;
|
|
513
|
+
abytes(b);
|
|
418
514
|
if (b.length !== this.BYTES)
|
|
419
515
|
throw new Error('fromBytes invalid length=' + b.length);
|
|
420
516
|
const B2 = Fp2.BYTES;
|
|
421
|
-
return {
|
|
517
|
+
return this.create({
|
|
422
518
|
c0: Fp2.fromBytes(b.subarray(0, B2)),
|
|
423
519
|
c1: Fp2.fromBytes(b.subarray(B2, B2 * 2)),
|
|
424
520
|
c2: Fp2.fromBytes(b.subarray(2 * B2)),
|
|
425
|
-
};
|
|
521
|
+
});
|
|
426
522
|
}
|
|
427
523
|
toBytes({ c0, c1, c2 }) {
|
|
428
524
|
const { Fp2 } = this;
|
|
@@ -430,66 +526,73 @@ class _Field6 {
|
|
|
430
526
|
}
|
|
431
527
|
cmov({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }, c) {
|
|
432
528
|
const { Fp2 } = this;
|
|
433
|
-
return {
|
|
529
|
+
return this.create({
|
|
434
530
|
c0: Fp2.cmov(c0, r0, c),
|
|
435
531
|
c1: Fp2.cmov(c1, r1, c),
|
|
436
532
|
c2: Fp2.cmov(c2, r2, c),
|
|
437
|
-
};
|
|
533
|
+
});
|
|
438
534
|
}
|
|
439
|
-
fromBigSix(
|
|
535
|
+
fromBigSix(tuple) {
|
|
440
536
|
const { Fp2 } = this;
|
|
441
|
-
if (!Array.isArray(
|
|
442
|
-
throw new Error('invalid Fp6
|
|
443
|
-
|
|
537
|
+
if (!Array.isArray(tuple) || tuple.length !== 6)
|
|
538
|
+
throw new Error('invalid Fp6.fromBigSix');
|
|
539
|
+
for (let i = 0; i < 6; i++)
|
|
540
|
+
if (typeof tuple[i] !== 'bigint')
|
|
541
|
+
throw new Error('invalid Fp6.fromBigSix');
|
|
542
|
+
const t = tuple;
|
|
543
|
+
return this.create({
|
|
444
544
|
c0: Fp2.fromBigTuple(t.slice(0, 2)),
|
|
445
545
|
c1: Fp2.fromBigTuple(t.slice(2, 4)),
|
|
446
546
|
c2: Fp2.fromBigTuple(t.slice(4, 6)),
|
|
447
|
-
};
|
|
547
|
+
});
|
|
448
548
|
}
|
|
449
549
|
frobeniusMap({ c0, c1, c2 }, power) {
|
|
450
550
|
const { Fp2 } = this;
|
|
451
|
-
return {
|
|
551
|
+
return Object.freeze({
|
|
452
552
|
c0: Fp2.frobeniusMap(c0, power),
|
|
453
553
|
c1: Fp2.mul(Fp2.frobeniusMap(c1, power), this.FROBENIUS_COEFFICIENTS_1[power % 6]),
|
|
454
554
|
c2: Fp2.mul(Fp2.frobeniusMap(c2, power), this.FROBENIUS_COEFFICIENTS_2[power % 6]),
|
|
455
|
-
};
|
|
555
|
+
});
|
|
456
556
|
}
|
|
457
557
|
mulByFp2({ c0, c1, c2 }, rhs) {
|
|
458
558
|
const { Fp2 } = this;
|
|
459
|
-
return {
|
|
559
|
+
return Object.freeze({
|
|
460
560
|
c0: Fp2.mul(c0, rhs),
|
|
461
561
|
c1: Fp2.mul(c1, rhs),
|
|
462
562
|
c2: Fp2.mul(c2, rhs),
|
|
463
|
-
};
|
|
563
|
+
});
|
|
464
564
|
}
|
|
465
565
|
mulByNonresidue({ c0, c1, c2 }) {
|
|
466
566
|
const { Fp2 } = this;
|
|
467
|
-
return { c0: Fp2.mulByNonresidue(c2), c1: c0, c2: c1 };
|
|
567
|
+
return Object.freeze({ c0: Fp2.mulByNonresidue(c2), c1: c0, c2: c1 });
|
|
468
568
|
}
|
|
469
569
|
// Sparse multiplication
|
|
470
570
|
mul1({ c0, c1, c2 }, b1) {
|
|
471
571
|
const { Fp2 } = this;
|
|
472
|
-
return {
|
|
572
|
+
return Object.freeze({
|
|
473
573
|
c0: Fp2.mulByNonresidue(Fp2.mul(c2, b1)),
|
|
474
574
|
c1: Fp2.mul(c0, b1),
|
|
475
575
|
c2: Fp2.mul(c1, b1),
|
|
476
|
-
};
|
|
576
|
+
});
|
|
477
577
|
}
|
|
478
578
|
// Sparse multiplication
|
|
479
579
|
mul01({ c0, c1, c2 }, b0, b1) {
|
|
480
580
|
const { Fp2 } = this;
|
|
481
581
|
let t0 = Fp2.mul(c0, b0); // c0 * b0
|
|
482
582
|
let t1 = Fp2.mul(c1, b1); // c1 * b1
|
|
483
|
-
return {
|
|
583
|
+
return Object.freeze({
|
|
484
584
|
// ((c1 + c2) * b1 - T1) * (u + 1) + T0
|
|
485
585
|
c0: Fp2.add(Fp2.mulByNonresidue(Fp2.sub(Fp2.mul(Fp2.add(c1, c2), b1), t1)), t0),
|
|
486
586
|
// (b0 + b1) * (c0 + c1) - T0 - T1
|
|
487
587
|
c1: Fp2.sub(Fp2.sub(Fp2.mul(Fp2.add(b0, b1), Fp2.add(c0, c1)), t0), t1),
|
|
488
588
|
// (c0 + c2) * b0 - T0 + T1
|
|
489
589
|
c2: Fp2.add(Fp2.sub(Fp2.mul(Fp2.add(c0, c2), b0), t0), t1),
|
|
490
|
-
};
|
|
590
|
+
});
|
|
491
591
|
}
|
|
492
592
|
}
|
|
593
|
+
// Keep lazy tower caches off-object: field instances stay frozen, and debugger output
|
|
594
|
+
// stays readable without JS private slots while second/subsequent lookups still hit cache.
|
|
595
|
+
const _FROBENIUS_COEFFICIENTS_6 = new WeakMap();
|
|
493
596
|
class _Field12 {
|
|
494
597
|
ORDER;
|
|
495
598
|
BITS;
|
|
@@ -498,31 +601,63 @@ class _Field12 {
|
|
|
498
601
|
ZERO;
|
|
499
602
|
ONE;
|
|
500
603
|
Fp6;
|
|
501
|
-
FROBENIUS_COEFFICIENTS;
|
|
502
604
|
X_LEN;
|
|
503
605
|
finalExponentiate;
|
|
504
606
|
constructor(Fp6, opts) {
|
|
607
|
+
const { X_LEN, Fp12finalExponentiate } = opts;
|
|
505
608
|
const { Fp2 } = Fp6;
|
|
506
609
|
const { Fp } = Fp2;
|
|
507
610
|
this.Fp6 = Fp6;
|
|
508
|
-
|
|
611
|
+
// `IField.ORDER` is the field cardinality `q`; for degree-12 extensions that is `p^12`.
|
|
612
|
+
// Keeping `p^2` here breaks generic field identities like `x^q = x` on Fp12.
|
|
613
|
+
this.ORDER = Fp.ORDER ** _12n;
|
|
509
614
|
this.BITS = 2 * Fp6.BITS;
|
|
510
615
|
this.BYTES = 2 * Fp6.BYTES;
|
|
511
616
|
this.isLE = Fp6.isLE;
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
this.
|
|
515
|
-
this.
|
|
516
|
-
this.
|
|
617
|
+
// Returned tower values are frozen, so larger constants can safely reuse
|
|
618
|
+
// already-frozen child coefficients instead of cloning them.
|
|
619
|
+
this.ZERO = this.create({ c0: Fp6.ZERO, c1: Fp6.ZERO });
|
|
620
|
+
this.ONE = this.create({ c0: Fp6.ONE, c1: Fp6.ZERO });
|
|
621
|
+
this.X_LEN = X_LEN;
|
|
622
|
+
this.finalExponentiate = (num) => {
|
|
623
|
+
const copy2 = ({ c0, c1 }) => Object.freeze({ c0, c1 });
|
|
624
|
+
const copy6 = ({ c0, c1, c2 }) => Object.freeze({ c0: copy2(c0), c1: copy2(c1), c2: copy2(c2) });
|
|
625
|
+
// This config hook is trusted to return a canonical Fp12 value already.
|
|
626
|
+
// Copy+freeze it to keep the tower immutability invariant without mutating caller objects.
|
|
627
|
+
const res = Fp12finalExponentiate(num);
|
|
628
|
+
return Object.freeze({ c0: copy6(res.c0), c1: copy6(res.c1) });
|
|
629
|
+
};
|
|
630
|
+
Object.freeze(this);
|
|
631
|
+
}
|
|
632
|
+
// Keep the degree-12 Frobenius row lazy too; after the first lookup the cached
|
|
633
|
+
// array is reused exactly like the old eager table.
|
|
634
|
+
get FROBENIUS_COEFFICIENTS() {
|
|
635
|
+
const frob = _FROBENIUS_COEFFICIENTS_12.get(this);
|
|
636
|
+
if (frob)
|
|
637
|
+
return frob;
|
|
638
|
+
const { Fp2 } = this.Fp6;
|
|
639
|
+
const { Fp } = Fp2;
|
|
640
|
+
const cache = Object.freeze(calcFrobeniusCoefficients(Fp2, Fp2.NONRESIDUE, Fp.ORDER, 12, 1, 6)[0]);
|
|
641
|
+
_FROBENIUS_COEFFICIENTS_12.set(this, cache);
|
|
642
|
+
return cache;
|
|
517
643
|
}
|
|
518
644
|
create(num) {
|
|
519
|
-
|
|
645
|
+
const { Fp6 } = this;
|
|
646
|
+
const c0 = Fp6.create(num.c0);
|
|
647
|
+
const c1 = Fp6.create(num.c1);
|
|
648
|
+
return Object.freeze({ c0, c1 });
|
|
520
649
|
}
|
|
521
|
-
isValid(
|
|
650
|
+
isValid(num) {
|
|
651
|
+
if (!isObj(num))
|
|
652
|
+
throw new TypeError('invalid field element: expected object, got ' + typeof num);
|
|
653
|
+
const { c0, c1 } = num;
|
|
522
654
|
const { Fp6 } = this;
|
|
523
655
|
return Fp6.isValid(c0) && Fp6.isValid(c1);
|
|
524
656
|
}
|
|
525
|
-
is0(
|
|
657
|
+
is0(num) {
|
|
658
|
+
if (!isObj(num))
|
|
659
|
+
return false;
|
|
660
|
+
const { c0, c1 } = num;
|
|
526
661
|
const { Fp6 } = this;
|
|
527
662
|
return Fp6.is0(c0) && Fp6.is0(c1);
|
|
528
663
|
}
|
|
@@ -531,19 +666,23 @@ class _Field12 {
|
|
|
531
666
|
}
|
|
532
667
|
neg({ c0, c1 }) {
|
|
533
668
|
const { Fp6 } = this;
|
|
534
|
-
return { c0: Fp6.neg(c0), c1: Fp6.neg(c1) };
|
|
669
|
+
return Object.freeze({ c0: Fp6.neg(c0), c1: Fp6.neg(c1) });
|
|
535
670
|
}
|
|
536
671
|
eql({ c0, c1 }, { c0: r0, c1: r1 }) {
|
|
537
672
|
const { Fp6 } = this;
|
|
538
673
|
return Fp6.eql(c0, r0) && Fp6.eql(c1, r1);
|
|
539
674
|
}
|
|
540
675
|
sqrt(_) {
|
|
541
|
-
|
|
676
|
+
// Fp12 is quadratic over Fp6, so a dedicated quadratic-extension sqrt is possible here
|
|
677
|
+
// once Fp6.sqrt() exists. Without that lower-level sqrt, only a field-generic
|
|
678
|
+
// Tonelli-Shanks path over Fp12 itself would work.
|
|
679
|
+
return notImplemented();
|
|
542
680
|
}
|
|
543
681
|
inv({ c0, c1 }) {
|
|
544
682
|
const { Fp6 } = this;
|
|
545
683
|
let t = Fp6.inv(Fp6.sub(Fp6.sqr(c0), Fp6.mulByNonresidue(Fp6.sqr(c1)))); // 1 / (c0² - c1² * v)
|
|
546
|
-
|
|
684
|
+
// ((C0 * T) * T) + (-C1 * T) * w
|
|
685
|
+
return Object.freeze({ c0: Fp6.mul(c0, t), c1: Fp6.neg(Fp6.mul(c1, t)) });
|
|
547
686
|
}
|
|
548
687
|
div(lhs, rhs) {
|
|
549
688
|
const { Fp6 } = this;
|
|
@@ -560,39 +699,39 @@ class _Field12 {
|
|
|
560
699
|
// Normalized
|
|
561
700
|
add({ c0, c1 }, { c0: r0, c1: r1 }) {
|
|
562
701
|
const { Fp6 } = this;
|
|
563
|
-
return {
|
|
702
|
+
return Object.freeze({
|
|
564
703
|
c0: Fp6.add(c0, r0),
|
|
565
704
|
c1: Fp6.add(c1, r1),
|
|
566
|
-
};
|
|
705
|
+
});
|
|
567
706
|
}
|
|
568
707
|
sub({ c0, c1 }, { c0: r0, c1: r1 }) {
|
|
569
708
|
const { Fp6 } = this;
|
|
570
|
-
return {
|
|
709
|
+
return Object.freeze({
|
|
571
710
|
c0: Fp6.sub(c0, r0),
|
|
572
711
|
c1: Fp6.sub(c1, r1),
|
|
573
|
-
};
|
|
712
|
+
});
|
|
574
713
|
}
|
|
575
714
|
mul({ c0, c1 }, rhs) {
|
|
576
715
|
const { Fp6 } = this;
|
|
577
716
|
if (typeof rhs === 'bigint')
|
|
578
|
-
return { c0: Fp6.mul(c0, rhs), c1: Fp6.mul(c1, rhs) };
|
|
717
|
+
return Object.freeze({ c0: Fp6.mul(c0, rhs), c1: Fp6.mul(c1, rhs) });
|
|
579
718
|
let { c0: r0, c1: r1 } = rhs;
|
|
580
719
|
let t1 = Fp6.mul(c0, r0); // c0 * r0
|
|
581
720
|
let t2 = Fp6.mul(c1, r1); // c1 * r1
|
|
582
|
-
return {
|
|
721
|
+
return Object.freeze({
|
|
583
722
|
c0: Fp6.add(t1, Fp6.mulByNonresidue(t2)), // T1 + T2 * v
|
|
584
723
|
// (c0 + c1) * (r0 + r1) - (T1 + T2)
|
|
585
724
|
c1: Fp6.sub(Fp6.mul(Fp6.add(c0, c1), Fp6.add(r0, r1)), Fp6.add(t1, t2)),
|
|
586
|
-
};
|
|
725
|
+
});
|
|
587
726
|
}
|
|
588
727
|
sqr({ c0, c1 }) {
|
|
589
728
|
const { Fp6 } = this;
|
|
590
729
|
let ab = Fp6.mul(c0, c1); // c0 * c1
|
|
591
|
-
return {
|
|
730
|
+
return Object.freeze({
|
|
592
731
|
// (c1 * v + c0) * (c0 + c1) - AB - AB * v
|
|
593
732
|
c0: Fp6.sub(Fp6.sub(Fp6.mul(Fp6.add(Fp6.mulByNonresidue(c1), c0), Fp6.add(c0, c1)), ab), Fp6.mulByNonresidue(ab)),
|
|
594
733
|
c1: Fp6.add(ab, ab),
|
|
595
|
-
}; // AB + AB
|
|
734
|
+
}); // AB + AB
|
|
596
735
|
}
|
|
597
736
|
// NonNormalized stuff
|
|
598
737
|
addN(a, b) {
|
|
@@ -610,12 +749,13 @@ class _Field12 {
|
|
|
610
749
|
// Bytes utils
|
|
611
750
|
fromBytes(b) {
|
|
612
751
|
const { Fp6 } = this;
|
|
752
|
+
abytes(b);
|
|
613
753
|
if (b.length !== this.BYTES)
|
|
614
754
|
throw new Error('fromBytes invalid length=' + b.length);
|
|
615
|
-
return {
|
|
755
|
+
return this.create({
|
|
616
756
|
c0: Fp6.fromBytes(b.subarray(0, Fp6.BYTES)),
|
|
617
757
|
c1: Fp6.fromBytes(b.subarray(Fp6.BYTES)),
|
|
618
|
-
};
|
|
758
|
+
});
|
|
619
759
|
}
|
|
620
760
|
toBytes({ c0, c1 }) {
|
|
621
761
|
const { Fp6 } = this;
|
|
@@ -623,10 +763,10 @@ class _Field12 {
|
|
|
623
763
|
}
|
|
624
764
|
cmov({ c0, c1 }, { c0: r0, c1: r1 }, c) {
|
|
625
765
|
const { Fp6 } = this;
|
|
626
|
-
return {
|
|
766
|
+
return this.create({
|
|
627
767
|
c0: Fp6.cmov(c0, r0, c),
|
|
628
768
|
c1: Fp6.cmov(c1, r1, c),
|
|
629
|
-
};
|
|
769
|
+
});
|
|
630
770
|
}
|
|
631
771
|
// Utils
|
|
632
772
|
// toString() {
|
|
@@ -635,12 +775,18 @@ class _Field12 {
|
|
|
635
775
|
// fromTuple(c: [Fp6, Fp6]) {
|
|
636
776
|
// return new Fp12(...c);
|
|
637
777
|
// }
|
|
638
|
-
fromBigTwelve(
|
|
778
|
+
fromBigTwelve(tuple) {
|
|
639
779
|
const { Fp6 } = this;
|
|
640
|
-
|
|
780
|
+
if (!Array.isArray(tuple) || tuple.length !== 12)
|
|
781
|
+
throw new Error('invalid Fp12.fromBigTwelve');
|
|
782
|
+
for (let i = 0; i < 12; i++)
|
|
783
|
+
if (typeof tuple[i] !== 'bigint')
|
|
784
|
+
throw new Error('invalid Fp12.fromBigTwelve');
|
|
785
|
+
const t = tuple;
|
|
786
|
+
return this.create({
|
|
641
787
|
c0: Fp6.fromBigSix(t.slice(0, 6)),
|
|
642
788
|
c1: Fp6.fromBigSix(t.slice(6, 12)),
|
|
643
|
-
};
|
|
789
|
+
});
|
|
644
790
|
}
|
|
645
791
|
// Raises to q**i -th power
|
|
646
792
|
frobeniusMap(lhs, power) {
|
|
@@ -648,24 +794,25 @@ class _Field12 {
|
|
|
648
794
|
const { Fp2 } = Fp6;
|
|
649
795
|
const { c0, c1, c2 } = Fp6.frobeniusMap(lhs.c1, power);
|
|
650
796
|
const coeff = this.FROBENIUS_COEFFICIENTS[power % 12];
|
|
651
|
-
return {
|
|
797
|
+
return Object.freeze({
|
|
652
798
|
c0: Fp6.frobeniusMap(lhs.c0, power),
|
|
653
|
-
c1:
|
|
799
|
+
c1: Object.freeze({
|
|
654
800
|
c0: Fp2.mul(c0, coeff),
|
|
655
801
|
c1: Fp2.mul(c1, coeff),
|
|
656
802
|
c2: Fp2.mul(c2, coeff),
|
|
657
803
|
}),
|
|
658
|
-
};
|
|
804
|
+
});
|
|
659
805
|
}
|
|
660
806
|
mulByFp2({ c0, c1 }, rhs) {
|
|
661
807
|
const { Fp6 } = this;
|
|
662
|
-
return {
|
|
808
|
+
return Object.freeze({
|
|
663
809
|
c0: Fp6.mulByFp2(c0, rhs),
|
|
664
810
|
c1: Fp6.mulByFp2(c1, rhs),
|
|
665
|
-
};
|
|
811
|
+
});
|
|
666
812
|
}
|
|
667
813
|
conjugate({ c0, c1 }) {
|
|
668
|
-
|
|
814
|
+
// Reuse `c0` by reference and only negate the `w` coefficient.
|
|
815
|
+
return Object.freeze({ c0, c1: this.Fp6.neg(c1) });
|
|
669
816
|
}
|
|
670
817
|
// Sparse multiplication
|
|
671
818
|
mul014({ c0, c1 }, o0, o1, o4) {
|
|
@@ -673,26 +820,26 @@ class _Field12 {
|
|
|
673
820
|
const { Fp2 } = Fp6;
|
|
674
821
|
let t0 = Fp6.mul01(c0, o0, o1);
|
|
675
822
|
let t1 = Fp6.mul1(c1, o4);
|
|
676
|
-
return {
|
|
823
|
+
return Object.freeze({
|
|
677
824
|
c0: Fp6.add(Fp6.mulByNonresidue(t1), t0), // T1 * v + T0
|
|
678
825
|
// (c1 + c0) * [o0, o1+o4] - T0 - T1
|
|
679
826
|
c1: Fp6.sub(Fp6.sub(Fp6.mul01(Fp6.add(c1, c0), o0, Fp2.add(o1, o4)), t0), t1),
|
|
680
|
-
};
|
|
827
|
+
});
|
|
681
828
|
}
|
|
682
829
|
mul034({ c0, c1 }, o0, o3, o4) {
|
|
683
830
|
const { Fp6 } = this;
|
|
684
831
|
const { Fp2 } = Fp6;
|
|
685
|
-
const a =
|
|
832
|
+
const a = Object.freeze({
|
|
686
833
|
c0: Fp2.mul(c0.c0, o0),
|
|
687
834
|
c1: Fp2.mul(c0.c1, o0),
|
|
688
835
|
c2: Fp2.mul(c0.c2, o0),
|
|
689
836
|
});
|
|
690
837
|
const b = Fp6.mul01(c1, o3, o4);
|
|
691
838
|
const e = Fp6.mul01(Fp6.add(c0, c1), Fp2.add(o0, o3), o4);
|
|
692
|
-
return {
|
|
839
|
+
return Object.freeze({
|
|
693
840
|
c0: Fp6.add(Fp6.mulByNonresidue(b), a),
|
|
694
841
|
c1: Fp6.sub(e, Fp6.add(a, b)),
|
|
695
|
-
};
|
|
842
|
+
});
|
|
696
843
|
}
|
|
697
844
|
// A cyclotomic group is a subgroup of Fp^n defined by
|
|
698
845
|
// GΦₙ(p) = {α ∈ Fpⁿ : α^Φₙ(p) = 1}
|
|
@@ -708,21 +855,24 @@ class _Field12 {
|
|
|
708
855
|
const { first: t5, second: t6 } = Fp2.Fp4Square(c1c0, c0c2);
|
|
709
856
|
const { first: t7, second: t8 } = Fp2.Fp4Square(c0c1, c1c2);
|
|
710
857
|
const t9 = Fp2.mulByNonresidue(t8); // T8 * (u + 1)
|
|
711
|
-
return {
|
|
712
|
-
c0:
|
|
858
|
+
return Object.freeze({
|
|
859
|
+
c0: Object.freeze({
|
|
713
860
|
c0: Fp2.add(Fp2.mul(Fp2.sub(t3, c0c0), _2n), t3), // 2 * (T3 - c0c0) + T3
|
|
714
861
|
c1: Fp2.add(Fp2.mul(Fp2.sub(t5, c0c1), _2n), t5), // 2 * (T5 - c0c1) + T5
|
|
715
862
|
c2: Fp2.add(Fp2.mul(Fp2.sub(t7, c0c2), _2n), t7),
|
|
716
863
|
}), // 2 * (T7 - c0c2) + T7
|
|
717
|
-
c1:
|
|
864
|
+
c1: Object.freeze({
|
|
718
865
|
c0: Fp2.add(Fp2.mul(Fp2.add(t9, c1c0), _2n), t9), // 2 * (T9 + c1c0) + T9
|
|
719
866
|
c1: Fp2.add(Fp2.mul(Fp2.add(t4, c1c1), _2n), t4), // 2 * (T4 + c1c1) + T4
|
|
720
867
|
c2: Fp2.add(Fp2.mul(Fp2.add(t6, c1c2), _2n), t6),
|
|
721
868
|
}),
|
|
722
|
-
}; // 2 * (T6 + c1c2) + T6
|
|
869
|
+
}); // 2 * (T6 + c1c2) + T6
|
|
723
870
|
}
|
|
724
871
|
// https://eprint.iacr.org/2009/565.pdf
|
|
725
872
|
_cyclotomicExp(num, n) {
|
|
873
|
+
// The loop only consumes `X_LEN` bits, so out-of-range exponents would otherwise get silently
|
|
874
|
+
// truncated (or sign-extended for negatives) instead of matching the caller's requested power.
|
|
875
|
+
aInRange('cyclotomic exponent', n, _0n, _1n << BigInt(this.X_LEN));
|
|
726
876
|
let z = this.ONE;
|
|
727
877
|
for (let i = this.X_LEN - 1; i >= 0; i--) {
|
|
728
878
|
z = this._cyclotomicSquare(z);
|
|
@@ -732,7 +882,41 @@ class _Field12 {
|
|
|
732
882
|
return z;
|
|
733
883
|
}
|
|
734
884
|
}
|
|
885
|
+
const _FROBENIUS_COEFFICIENTS_12 = new WeakMap();
|
|
886
|
+
/**
|
|
887
|
+
* @param opts - Tower construction options. See {@link Tower12Opts}.
|
|
888
|
+
* @returns BLS tower fields.
|
|
889
|
+
* @throws If the tower options or derived Frobenius helpers are invalid. {@link Error}
|
|
890
|
+
* @example
|
|
891
|
+
* Construct the Fp2/Fp6/Fp12 tower used by a pairing-friendly curve.
|
|
892
|
+
*
|
|
893
|
+
* ```ts
|
|
894
|
+
* const fields = tower12({
|
|
895
|
+
* ORDER: 17n,
|
|
896
|
+
* X_LEN: 4,
|
|
897
|
+
* FP2_NONRESIDUE: [1n, 1n],
|
|
898
|
+
* Fp2mulByB: (num) => num,
|
|
899
|
+
* Fp12finalExponentiate: (num) => num,
|
|
900
|
+
* });
|
|
901
|
+
* const fp12 = fields.Fp12.ONE;
|
|
902
|
+
* ```
|
|
903
|
+
*/
|
|
735
904
|
export function tower12(opts) {
|
|
905
|
+
validateObject(opts, {
|
|
906
|
+
ORDER: 'bigint',
|
|
907
|
+
X_LEN: 'number',
|
|
908
|
+
FP2_NONRESIDUE: 'object',
|
|
909
|
+
Fp2mulByB: 'function',
|
|
910
|
+
Fp12finalExponentiate: 'function',
|
|
911
|
+
}, { NONRESIDUE: 'bigint' });
|
|
912
|
+
asafenumber(opts.X_LEN, 'X_LEN');
|
|
913
|
+
if (opts.X_LEN < 1)
|
|
914
|
+
throw new Error('invalid X_LEN');
|
|
915
|
+
const nonresidue = opts.FP2_NONRESIDUE;
|
|
916
|
+
if (!Array.isArray(nonresidue) || nonresidue.length !== 2)
|
|
917
|
+
throw new Error('invalid FP2_NONRESIDUE');
|
|
918
|
+
if (typeof nonresidue[0] !== 'bigint' || typeof nonresidue[1] !== 'bigint')
|
|
919
|
+
throw new Error('invalid FP2_NONRESIDUE');
|
|
736
920
|
const Fp = mod.Field(opts.ORDER);
|
|
737
921
|
const Fp2 = new _Field2(Fp, opts);
|
|
738
922
|
const Fp6 = new _Field6(Fp2);
|