@noble/curves 1.4.2 → 1.5.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 +135 -123
- package/_shortw_utils.d.ts.map +1 -1
- package/abstract/bls.d.ts +37 -34
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/bls.js +167 -115
- package/abstract/bls.js.map +1 -1
- package/abstract/curve.d.ts +2 -1
- package/abstract/curve.d.ts.map +1 -1
- package/abstract/curve.js +22 -7
- package/abstract/curve.js.map +1 -1
- package/abstract/edwards.d.ts +11 -0
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +79 -75
- package/abstract/edwards.js.map +1 -1
- package/abstract/modular.d.ts +4 -0
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +13 -2
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +4 -9
- package/abstract/montgomery.js.map +1 -1
- package/abstract/tower.d.ts +106 -0
- package/abstract/tower.d.ts.map +1 -0
- package/abstract/tower.js +497 -0
- package/abstract/tower.js.map +1 -0
- package/abstract/utils.d.ts +17 -0
- package/abstract/utils.d.ts.map +1 -1
- package/abstract/utils.js +50 -1
- package/abstract/utils.js.map +1 -1
- package/abstract/weierstrass.d.ts +7 -0
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +88 -72
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts +1 -65
- package/bls12-381.d.ts.map +1 -1
- package/bls12-381.js +48 -575
- package/bls12-381.js.map +1 -1
- package/bn254.d.ts +10 -6
- package/bn254.d.ts.map +1 -1
- package/bn254.js +207 -10
- package/bn254.js.map +1 -1
- package/ed25519.d.ts +7 -4
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +3 -0
- package/ed25519.js.map +1 -1
- package/esm/_shortw_utils.d.ts.map +1 -1
- package/esm/abstract/bls.d.ts +37 -34
- package/esm/abstract/bls.d.ts.map +1 -1
- package/esm/abstract/bls.js +168 -116
- package/esm/abstract/bls.js.map +1 -1
- package/esm/abstract/curve.d.ts +2 -1
- package/esm/abstract/curve.d.ts.map +1 -1
- package/esm/abstract/curve.js +22 -7
- package/esm/abstract/curve.js.map +1 -1
- package/esm/abstract/edwards.d.ts +11 -0
- package/esm/abstract/edwards.d.ts.map +1 -1
- package/esm/abstract/edwards.js +80 -76
- package/esm/abstract/edwards.js.map +1 -1
- package/esm/abstract/modular.d.ts +4 -0
- package/esm/abstract/modular.d.ts.map +1 -1
- package/esm/abstract/modular.js +12 -2
- package/esm/abstract/modular.js.map +1 -1
- package/esm/abstract/montgomery.d.ts.map +1 -1
- package/esm/abstract/montgomery.js +5 -10
- package/esm/abstract/montgomery.js.map +1 -1
- package/esm/abstract/tower.d.ts +106 -0
- package/esm/abstract/tower.d.ts.map +1 -0
- package/esm/abstract/tower.js +493 -0
- package/esm/abstract/tower.js.map +1 -0
- package/esm/abstract/utils.d.ts +17 -0
- package/esm/abstract/utils.d.ts.map +1 -1
- package/esm/abstract/utils.js +44 -0
- package/esm/abstract/utils.js.map +1 -1
- package/esm/abstract/weierstrass.d.ts +7 -0
- package/esm/abstract/weierstrass.d.ts.map +1 -1
- package/esm/abstract/weierstrass.js +89 -73
- package/esm/abstract/weierstrass.js.map +1 -1
- package/esm/bls12-381.d.ts +1 -65
- package/esm/bls12-381.d.ts.map +1 -1
- package/esm/bls12-381.js +50 -577
- package/esm/bls12-381.js.map +1 -1
- package/esm/bn254.d.ts +10 -6
- package/esm/bn254.d.ts.map +1 -1
- package/esm/bn254.js +206 -9
- package/esm/bn254.js.map +1 -1
- package/esm/ed25519.d.ts +7 -4
- package/esm/ed25519.d.ts.map +1 -1
- package/esm/ed25519.js +3 -0
- package/esm/ed25519.js.map +1 -1
- package/esm/p256.d.ts.map +1 -1
- package/esm/p384.d.ts.map +1 -1
- package/esm/p521.d.ts.map +1 -1
- package/esm/secp256k1.d.ts +6 -0
- package/esm/secp256k1.d.ts.map +1 -1
- package/esm/secp256k1.js +17 -13
- package/esm/secp256k1.js.map +1 -1
- package/p256.d.ts.map +1 -1
- package/p384.d.ts.map +1 -1
- package/p521.d.ts.map +1 -1
- package/package.json +2 -1
- package/secp256k1.d.ts +6 -0
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +16 -12
- package/secp256k1.js.map +1 -1
- package/src/abstract/bls.ts +222 -168
- package/src/abstract/curve.ts +23 -7
- package/src/abstract/edwards.ts +81 -68
- package/src/abstract/modular.ts +13 -3
- package/src/abstract/montgomery.ts +11 -10
- package/src/abstract/tower.ts +604 -0
- package/src/abstract/utils.ts +49 -0
- package/src/abstract/weierstrass.ts +85 -68
- package/src/bls12-381.ts +53 -707
- package/src/bn254.ts +224 -9
- package/src/ed25519.ts +5 -2
- package/src/secp256k1.ts +24 -12
package/src/abstract/bls.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2
2
|
// BLS (Barreto-Lynn-Scott) family of pairing-friendly curves.
|
|
3
|
-
import { AffinePoint } from './curve.js';
|
|
3
|
+
// TODO: import { AffinePoint } from './curve.js';
|
|
4
4
|
import { IField, getMinHashLength, mapHashToField } from './modular.js';
|
|
5
|
-
import { Hex, PrivKey, CHash,
|
|
5
|
+
import { Hex, PrivKey, CHash, ensureBytes, memoized } from './utils.js';
|
|
6
6
|
// prettier-ignore
|
|
7
7
|
import {
|
|
8
8
|
MapToCurve, Opts as HTFOpts, H2CPointConstructor, htfBasicOpts,
|
|
@@ -14,24 +14,30 @@ import {
|
|
|
14
14
|
CurvePointsRes,
|
|
15
15
|
weierstrassPoints,
|
|
16
16
|
} from './weierstrass.js';
|
|
17
|
+
import type { Fp2, Fp6, Fp12, Fp2Bls, Fp12Bls } from './tower.js';
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
|
-
* BLS
|
|
20
|
-
*
|
|
20
|
+
* BLS != BLS.
|
|
21
|
+
* The file implements BLS (Boneh-Lynn-Shacham) signatures.
|
|
22
|
+
* Used in both BLS (Barreto-Lynn-Scott) and BN (Barreto-Naehrig)
|
|
23
|
+
* families of pairing-friendly curves.
|
|
21
24
|
* Consists of two curves: G1 and G2:
|
|
22
25
|
* - G1 is a subgroup of (x, y) E(Fq) over y² = x³ + 4.
|
|
23
26
|
* - G2 is a subgroup of ((x₁, x₂+i), (y₁, y₂+i)) E(Fq²) over y² = x³ + 4(1 + i) where i is √-1
|
|
24
27
|
* - Gt, created by bilinear (ate) pairing e(G1, G2), consists of p-th roots of unity in
|
|
25
28
|
* Fq^k where k is embedding degree. Only degree 12 is currently supported, 24 is not.
|
|
26
29
|
* Pairing is used to aggregate and verify signatures.
|
|
27
|
-
*
|
|
28
|
-
*
|
|
30
|
+
* There are two main ways to use it:
|
|
31
|
+
* 1. Fp for short private keys, Fp₂ for signatures
|
|
32
|
+
* 2. Fp for short signatures, Fp₂ for private keys
|
|
29
33
|
**/
|
|
30
34
|
|
|
31
35
|
type Fp = bigint; // Can be different field?
|
|
32
36
|
|
|
33
37
|
// prettier-ignore
|
|
34
|
-
const _2n = BigInt(2), _3n = BigInt(3);
|
|
38
|
+
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
|
|
39
|
+
|
|
40
|
+
export type TwistType = 'multiplicative' | 'divisive';
|
|
35
41
|
|
|
36
42
|
export type ShortSignatureCoder<Fp> = {
|
|
37
43
|
fromHex(hex: Hex): ProjPointType<Fp>;
|
|
@@ -39,26 +45,13 @@ export type ShortSignatureCoder<Fp> = {
|
|
|
39
45
|
toHex(point: ProjPointType<Fp>): string;
|
|
40
46
|
};
|
|
41
47
|
|
|
42
|
-
export type SignatureCoder<
|
|
43
|
-
fromHex(hex: Hex): ProjPointType<
|
|
44
|
-
toRawBytes(point: ProjPointType<
|
|
45
|
-
toHex(point: ProjPointType<
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
type Fp2Bls<Fp, Fp2> = IField<Fp2> & {
|
|
49
|
-
reim: (num: Fp2) => { re: Fp; im: Fp };
|
|
50
|
-
multiplyByB: (num: Fp2) => Fp2;
|
|
51
|
-
frobeniusMap(num: Fp2, power: number): Fp2;
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
type Fp12Bls<Fp2, Fp12> = IField<Fp12> & {
|
|
55
|
-
frobeniusMap(num: Fp12, power: number): Fp12;
|
|
56
|
-
multiplyBy014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
|
|
57
|
-
conjugate(num: Fp12): Fp12;
|
|
58
|
-
finalExponentiate(num: Fp12): Fp12;
|
|
48
|
+
export type SignatureCoder<Fp> = {
|
|
49
|
+
fromHex(hex: Hex): ProjPointType<Fp>;
|
|
50
|
+
toRawBytes(point: ProjPointType<Fp>): Uint8Array;
|
|
51
|
+
toHex(point: ProjPointType<Fp>): string;
|
|
59
52
|
};
|
|
60
53
|
|
|
61
|
-
export type CurveType
|
|
54
|
+
export type CurveType = {
|
|
62
55
|
G1: Omit<CurvePointsType<Fp>, 'n'> & {
|
|
63
56
|
ShortSignature: SignatureCoder<Fp>;
|
|
64
57
|
mapToCurve: MapToCurve<Fp>;
|
|
@@ -72,20 +65,37 @@ export type CurveType<Fp, Fp2, Fp6, Fp12> = {
|
|
|
72
65
|
fields: {
|
|
73
66
|
Fp: IField<Fp>;
|
|
74
67
|
Fr: IField<bigint>;
|
|
75
|
-
Fp2: Fp2Bls
|
|
68
|
+
Fp2: Fp2Bls;
|
|
76
69
|
Fp6: IField<Fp6>;
|
|
77
|
-
Fp12: Fp12Bls
|
|
70
|
+
Fp12: Fp12Bls;
|
|
78
71
|
};
|
|
79
72
|
params: {
|
|
80
|
-
|
|
73
|
+
// NOTE: MSB is always ignored and used as marker for length,
|
|
74
|
+
// otherwise leading zeros will be lost.
|
|
75
|
+
// Can be different from 'X' (seed) param!
|
|
76
|
+
ateLoopSize: bigint;
|
|
77
|
+
xNegative: boolean;
|
|
81
78
|
r: bigint;
|
|
79
|
+
twistType: TwistType; // BLS12-381: Multiplicative, BN254: Divisive
|
|
82
80
|
};
|
|
83
81
|
htfDefaults: HTFOpts;
|
|
84
82
|
hash: CHash; // Because we need outputLen for DRBG
|
|
85
83
|
randomBytes: (bytesLength?: number) => Uint8Array;
|
|
84
|
+
// This is super ugly hack for untwist point in BN254 after miller loop
|
|
85
|
+
postPrecompute?: (
|
|
86
|
+
Rx: Fp2,
|
|
87
|
+
Ry: Fp2,
|
|
88
|
+
Rz: Fp2,
|
|
89
|
+
Qx: Fp2,
|
|
90
|
+
Qy: Fp2,
|
|
91
|
+
pointAdd: (Rx: Fp2, Ry: Fp2, Rz: Fp2, Qx: Fp2, Qy: Fp2) => { Rx: Fp2; Ry: Fp2; Rz: Fp2 }
|
|
92
|
+
) => void;
|
|
86
93
|
};
|
|
87
94
|
|
|
88
|
-
|
|
95
|
+
type PrecomputeSingle = [Fp2, Fp2, Fp2][];
|
|
96
|
+
type Precompute = PrecomputeSingle[];
|
|
97
|
+
|
|
98
|
+
export type CurveFn = {
|
|
89
99
|
getPublicKey: (privateKey: PrivKey) => Uint8Array;
|
|
90
100
|
getPublicKeyForShortSignatures: (privateKey: PrivKey) => Uint8Array;
|
|
91
101
|
sign: {
|
|
@@ -126,109 +136,54 @@ export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
|
|
|
126
136
|
(signatures: Hex[]): Uint8Array;
|
|
127
137
|
(signatures: ProjPointType<Fp>[]): ProjPointType<Fp>;
|
|
128
138
|
};
|
|
129
|
-
|
|
139
|
+
millerLoopBatch: (pairs: [Precompute, Fp, Fp][]) => Fp12;
|
|
130
140
|
pairing: (P: ProjPointType<Fp>, Q: ProjPointType<Fp2>, withFinalExponent?: boolean) => Fp12;
|
|
141
|
+
pairingBatch: (
|
|
142
|
+
pairs: { g1: ProjPointType<Fp>; g2: ProjPointType<Fp2> }[],
|
|
143
|
+
withFinalExponent?: boolean
|
|
144
|
+
) => Fp12;
|
|
131
145
|
G1: CurvePointsRes<Fp> & ReturnType<typeof createHasher<Fp>>;
|
|
132
146
|
G2: CurvePointsRes<Fp2> & ReturnType<typeof createHasher<Fp2>>;
|
|
133
147
|
Signature: SignatureCoder<Fp2>;
|
|
134
148
|
ShortSignature: ShortSignatureCoder<Fp>;
|
|
135
149
|
params: {
|
|
136
|
-
|
|
150
|
+
ateLoopSize: bigint;
|
|
137
151
|
r: bigint;
|
|
138
152
|
G1b: bigint;
|
|
139
153
|
G2b: Fp2;
|
|
140
154
|
};
|
|
141
155
|
fields: {
|
|
142
156
|
Fp: IField<Fp>;
|
|
143
|
-
Fp2: Fp2Bls
|
|
157
|
+
Fp2: Fp2Bls;
|
|
144
158
|
Fp6: IField<Fp6>;
|
|
145
|
-
Fp12: Fp12Bls
|
|
159
|
+
Fp12: Fp12Bls;
|
|
146
160
|
Fr: IField<bigint>;
|
|
147
161
|
};
|
|
148
162
|
utils: {
|
|
149
163
|
randomPrivateKey: () => Uint8Array;
|
|
150
|
-
calcPairingPrecomputes: (p:
|
|
164
|
+
calcPairingPrecomputes: (p: ProjPointType<Fp2>) => Precompute;
|
|
151
165
|
};
|
|
152
166
|
};
|
|
153
167
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
//
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const { x, y } = p;
|
|
165
|
-
// prettier-ignore
|
|
166
|
-
const Qx = x, Qy = y, Qz = Fp2.ONE;
|
|
167
|
-
// prettier-ignore
|
|
168
|
-
let Rx = Qx, Ry = Qy, Rz = Qz;
|
|
169
|
-
let ell_coeff: [Fp2, Fp2, Fp2][] = [];
|
|
170
|
-
for (let i = BLS_X_LEN - 2; i >= 0; i--) {
|
|
171
|
-
// Double
|
|
172
|
-
let t0 = Fp2.sqr(Ry); // Ry²
|
|
173
|
-
let t1 = Fp2.sqr(Rz); // Rz²
|
|
174
|
-
let t2 = Fp2.multiplyByB(Fp2.mul(t1, _3n)); // 3 * T1 * B
|
|
175
|
-
let t3 = Fp2.mul(t2, _3n); // 3 * T2
|
|
176
|
-
let t4 = Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(Ry, Rz)), t1), t0); // (Ry + Rz)² - T1 - T0
|
|
177
|
-
ell_coeff.push([
|
|
178
|
-
Fp2.sub(t2, t0), // T2 - T0
|
|
179
|
-
Fp2.mul(Fp2.sqr(Rx), _3n), // 3 * Rx²
|
|
180
|
-
Fp2.neg(t4), // -T4
|
|
181
|
-
]);
|
|
182
|
-
Rx = Fp2.div(Fp2.mul(Fp2.mul(Fp2.sub(t0, t3), Rx), Ry), _2n); // ((T0 - T3) * Rx * Ry) / 2
|
|
183
|
-
Ry = Fp2.sub(Fp2.sqr(Fp2.div(Fp2.add(t0, t3), _2n)), Fp2.mul(Fp2.sqr(t2), _3n)); // ((T0 + T3) / 2)² - 3 * T2²
|
|
184
|
-
Rz = Fp2.mul(t0, t4); // T0 * T4
|
|
185
|
-
if (bitGet(CURVE.params.x, i)) {
|
|
186
|
-
// Addition
|
|
187
|
-
let t0 = Fp2.sub(Ry, Fp2.mul(Qy, Rz)); // Ry - Qy * Rz
|
|
188
|
-
let t1 = Fp2.sub(Rx, Fp2.mul(Qx, Rz)); // Rx - Qx * Rz
|
|
189
|
-
ell_coeff.push([
|
|
190
|
-
Fp2.sub(Fp2.mul(t0, Qx), Fp2.mul(t1, Qy)), // T0 * Qx - T1 * Qy
|
|
191
|
-
Fp2.neg(t0), // -T0
|
|
192
|
-
t1, // T1
|
|
193
|
-
]);
|
|
194
|
-
let t2 = Fp2.sqr(t1); // T1²
|
|
195
|
-
let t3 = Fp2.mul(t2, t1); // T2 * T1
|
|
196
|
-
let t4 = Fp2.mul(t2, Rx); // T2 * Rx
|
|
197
|
-
let t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4, _2n)), Fp2.mul(Fp2.sqr(t0), Rz)); // T3 - 2 * T4 + T0² * Rz
|
|
198
|
-
Rx = Fp2.mul(t1, t5); // T1 * T5
|
|
199
|
-
Ry = Fp2.sub(Fp2.mul(Fp2.sub(t4, t5), t0), Fp2.mul(t3, Ry)); // (T4 - T5) * T0 - T3 * Ry
|
|
200
|
-
Rz = Fp2.mul(Rz, t3); // Rz * T3
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
return ell_coeff;
|
|
168
|
+
// Not used with BLS12-381 (no sequential `11` in X). Useful for other curves.
|
|
169
|
+
function NAfDecomposition(a: bigint) {
|
|
170
|
+
const res = [];
|
|
171
|
+
// a>1 because of marker bit
|
|
172
|
+
for (; a > _1n; a >>= _1n) {
|
|
173
|
+
if ((a & _1n) === _0n) res.unshift(0);
|
|
174
|
+
else if ((a & _3n) === _3n) {
|
|
175
|
+
res.unshift(-1);
|
|
176
|
+
a += _1n;
|
|
177
|
+
} else res.unshift(1);
|
|
204
178
|
}
|
|
179
|
+
return res;
|
|
180
|
+
}
|
|
205
181
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
for (let j = 0, i = BLS_X_LEN - 2; i >= 0; i--, j++) {
|
|
212
|
-
const E = ell[j];
|
|
213
|
-
f12 = Fp12.multiplyBy014(f12, E[0], Fp2.mul(E[1], Px), Fp2.mul(E[2], Py));
|
|
214
|
-
if (bitGet(x, i)) {
|
|
215
|
-
j += 1;
|
|
216
|
-
const F = ell[j];
|
|
217
|
-
f12 = Fp12.multiplyBy014(f12, F[0], Fp2.mul(F[1], Px), Fp2.mul(F[2], Py));
|
|
218
|
-
}
|
|
219
|
-
if (i !== 0) f12 = Fp12.sqr(f12);
|
|
220
|
-
}
|
|
221
|
-
return Fp12.conjugate(f12);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const utils = {
|
|
225
|
-
randomPrivateKey: (): Uint8Array => {
|
|
226
|
-
const length = getMinHashLength(Fr.ORDER);
|
|
227
|
-
return mapHashToField(CURVE.randomBytes(length), Fr.ORDER);
|
|
228
|
-
},
|
|
229
|
-
calcPairingPrecomputes,
|
|
230
|
-
};
|
|
231
|
-
|
|
182
|
+
export function bls(CURVE: CurveType): CurveFn {
|
|
183
|
+
// Fields are specific for curve, so for now we'll need to pass them with opts
|
|
184
|
+
const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE.fields;
|
|
185
|
+
const BLS_X_IS_NEGATIVE = CURVE.params.xNegative;
|
|
186
|
+
const TWIST: TwistType = CURVE.params.twistType;
|
|
232
187
|
// Point on G1 curve: (x, y)
|
|
233
188
|
const G1_ = weierstrassPoints({ n: Fr.ORDER, ...CURVE.G1 });
|
|
234
189
|
const G1 = Object.assign(
|
|
@@ -238,23 +193,6 @@ export function bls<Fp2, Fp6, Fp12>(
|
|
|
238
193
|
...CURVE.G1.htfDefaults,
|
|
239
194
|
})
|
|
240
195
|
);
|
|
241
|
-
|
|
242
|
-
// Sparse multiplication against precomputed coefficients
|
|
243
|
-
// TODO: replace with weakmap?
|
|
244
|
-
type withPairingPrecomputes = { _PPRECOMPUTES: [Fp2, Fp2, Fp2][] | undefined };
|
|
245
|
-
function pairingPrecomputes(point: G2): [Fp2, Fp2, Fp2][] {
|
|
246
|
-
const p = point as G2 & withPairingPrecomputes;
|
|
247
|
-
if (p._PPRECOMPUTES) return p._PPRECOMPUTES;
|
|
248
|
-
p._PPRECOMPUTES = calcPairingPrecomputes(point.toAffine());
|
|
249
|
-
return p._PPRECOMPUTES;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// TODO: export
|
|
253
|
-
// function clearPairingPrecomputes(point: G2) {
|
|
254
|
-
// const p = point as G2 & withPairingPrecomputes;
|
|
255
|
-
// p._PPRECOMPUTES = undefined;
|
|
256
|
-
// }
|
|
257
|
-
|
|
258
196
|
// Point on G2 curve (complex numbers): (x₁, x₂+i), (y₁, y₂+i)
|
|
259
197
|
const G2_ = weierstrassPoints({ n: Fr.ORDER, ...CURVE.G2 });
|
|
260
198
|
const G2 = Object.assign(
|
|
@@ -264,23 +202,138 @@ export function bls<Fp2, Fp6, Fp12>(
|
|
|
264
202
|
...CURVE.G2.htfDefaults,
|
|
265
203
|
})
|
|
266
204
|
);
|
|
205
|
+
type G1 = typeof G1.ProjectivePoint.BASE;
|
|
206
|
+
type G2 = typeof G2.ProjectivePoint.BASE;
|
|
267
207
|
|
|
268
|
-
|
|
269
|
-
|
|
208
|
+
// Applies sparse multiplication as line function
|
|
209
|
+
let lineFunction: (c0: Fp2, c1: Fp2, c2: Fp2, f: Fp12, Px: Fp, Py: Fp) => Fp12;
|
|
210
|
+
if (TWIST === 'multiplicative') {
|
|
211
|
+
lineFunction = (c0: Fp2, c1: Fp2, c2: Fp2, f: Fp12, Px: Fp, Py: Fp) =>
|
|
212
|
+
Fp12.mul014(f, c0, Fp2.mul(c1, Px), Fp2.mul(c2, Py));
|
|
213
|
+
} else if (TWIST === 'divisive') {
|
|
214
|
+
// NOTE: it should be [c0, c1, c2], but we use different order here to reduce complexity of
|
|
215
|
+
// precompute calculations.
|
|
216
|
+
lineFunction = (c0: Fp2, c1: Fp2, c2: Fp2, f: Fp12, Px: Fp, Py: Fp) =>
|
|
217
|
+
Fp12.mul034(f, Fp2.mul(c2, Py), Fp2.mul(c1, Px), c0);
|
|
218
|
+
} else throw new Error('bls: unknown twist type');
|
|
219
|
+
|
|
220
|
+
const Fp2div2 = Fp2.div(Fp2.ONE, Fp2.mul(Fp2.ONE, _2n));
|
|
221
|
+
function pointDouble(ell: PrecomputeSingle, Rx: Fp2, Ry: Fp2, Rz: Fp2) {
|
|
222
|
+
const t0 = Fp2.sqr(Ry); // Ry²
|
|
223
|
+
const t1 = Fp2.sqr(Rz); // Rz²
|
|
224
|
+
const t2 = Fp2.mulByB(Fp2.mul(t1, _3n)); // 3 * T1 * B
|
|
225
|
+
const t3 = Fp2.mul(t2, _3n); // 3 * T2
|
|
226
|
+
const t4 = Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(Ry, Rz)), t1), t0); // (Ry + Rz)² - T1 - T0
|
|
227
|
+
const c0 = Fp2.sub(t2, t0); // T2 - T0 (i)
|
|
228
|
+
const c1 = Fp2.mul(Fp2.sqr(Rx), _3n); // 3 * Rx²
|
|
229
|
+
const c2 = Fp2.neg(t4); // -T4 (-h)
|
|
230
|
+
|
|
231
|
+
ell.push([c0, c1, c2]);
|
|
232
|
+
|
|
233
|
+
Rx = Fp2.mul(Fp2.mul(Fp2.mul(Fp2.sub(t0, t3), Rx), Ry), Fp2div2); // ((T0 - T3) * Rx * Ry) / 2
|
|
234
|
+
Ry = Fp2.sub(Fp2.sqr(Fp2.mul(Fp2.add(t0, t3), Fp2div2)), Fp2.mul(Fp2.sqr(t2), _3n)); // ((T0 + T3) / 2)² - 3 * T2²
|
|
235
|
+
Rz = Fp2.mul(t0, t4); // T0 * T4
|
|
236
|
+
return { Rx, Ry, Rz };
|
|
237
|
+
}
|
|
238
|
+
function pointAdd(ell: PrecomputeSingle, Rx: Fp2, Ry: Fp2, Rz: Fp2, Qx: Fp2, Qy: Fp2) {
|
|
239
|
+
// Addition
|
|
240
|
+
const t0 = Fp2.sub(Ry, Fp2.mul(Qy, Rz)); // Ry - Qy * Rz
|
|
241
|
+
const t1 = Fp2.sub(Rx, Fp2.mul(Qx, Rz)); // Rx - Qx * Rz
|
|
242
|
+
const c0 = Fp2.sub(Fp2.mul(t0, Qx), Fp2.mul(t1, Qy)); // T0 * Qx - T1 * Qy == Ry * Qx - Rx * Qy
|
|
243
|
+
const c1 = Fp2.neg(t0); // -T0 == Qy * Rz - Ry
|
|
244
|
+
const c2 = t1; // == Rx - Qx * Rz
|
|
245
|
+
|
|
246
|
+
ell.push([c0, c1, c2]);
|
|
247
|
+
|
|
248
|
+
const t2 = Fp2.sqr(t1); // T1²
|
|
249
|
+
const t3 = Fp2.mul(t2, t1); // T2 * T1
|
|
250
|
+
const t4 = Fp2.mul(t2, Rx); // T2 * Rx
|
|
251
|
+
const t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4, _2n)), Fp2.mul(Fp2.sqr(t0), Rz)); // T3 - 2 * T4 + T0² * Rz
|
|
252
|
+
Rx = Fp2.mul(t1, t5); // T1 * T5
|
|
253
|
+
Ry = Fp2.sub(Fp2.mul(Fp2.sub(t4, t5), t0), Fp2.mul(t3, Ry)); // (T4 - T5) * T0 - T3 * Ry
|
|
254
|
+
Rz = Fp2.mul(Rz, t3); // Rz * T3
|
|
255
|
+
return { Rx, Ry, Rz };
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Pre-compute coefficients for sparse multiplication
|
|
259
|
+
// Point addition and point double calculations is reused for coefficients
|
|
260
|
+
// pointAdd happens only if bit set, so wNAF is reasonable. Unfortunately we cannot combine
|
|
261
|
+
// add + double in windowed precomputes here, otherwise it would be single op (since X is static)
|
|
262
|
+
const ATE_NAF = NAfDecomposition(CURVE.params.ateLoopSize);
|
|
270
263
|
|
|
264
|
+
const calcPairingPrecomputes = memoized((point: G2) => {
|
|
265
|
+
const p = point;
|
|
266
|
+
const { x, y } = p.toAffine();
|
|
267
|
+
// prettier-ignore
|
|
268
|
+
const Qx = x, Qy = y, negQy = Fp2.neg(y);
|
|
269
|
+
// prettier-ignore
|
|
270
|
+
let Rx = Qx, Ry = Qy, Rz = Fp2.ONE;
|
|
271
|
+
const ell: Precompute = [];
|
|
272
|
+
for (const bit of ATE_NAF) {
|
|
273
|
+
const cur: PrecomputeSingle = [];
|
|
274
|
+
({ Rx, Ry, Rz } = pointDouble(cur, Rx, Ry, Rz));
|
|
275
|
+
if (bit) ({ Rx, Ry, Rz } = pointAdd(cur, Rx, Ry, Rz, Qx, bit === -1 ? negQy : Qy));
|
|
276
|
+
ell.push(cur);
|
|
277
|
+
}
|
|
278
|
+
if (CURVE.postPrecompute) {
|
|
279
|
+
const last = ell[ell.length - 1];
|
|
280
|
+
CURVE.postPrecompute(Rx, Ry, Rz, Qx, Qy, pointAdd.bind(null, last));
|
|
281
|
+
}
|
|
282
|
+
return ell;
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// Main pairing logic is here. Computes product of miller loops + final exponentiate
|
|
286
|
+
// Applies calculated precomputes
|
|
287
|
+
type MillerInput = [Precompute, Fp, Fp][];
|
|
288
|
+
function millerLoopBatch(pairs: MillerInput, withFinalExponent: boolean = false) {
|
|
289
|
+
let f12 = Fp12.ONE;
|
|
290
|
+
if (pairs.length) {
|
|
291
|
+
const ellLen = pairs[0][0].length;
|
|
292
|
+
for (let i = 0; i < ellLen; i++) {
|
|
293
|
+
f12 = Fp12.sqr(f12); // This allows us to do sqr only one time for all pairings
|
|
294
|
+
// NOTE: we apply multiple pairings in parallel here
|
|
295
|
+
for (const [ell, Px, Py] of pairs) {
|
|
296
|
+
for (const [c0, c1, c2] of ell[i]) f12 = lineFunction(c0, c1, c2, f12, Px, Py);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if (BLS_X_IS_NEGATIVE) f12 = Fp12.conjugate(f12);
|
|
301
|
+
return withFinalExponent ? Fp12.finalExponentiate(f12) : f12;
|
|
302
|
+
}
|
|
303
|
+
type PairingInput = { g1: G1; g2: G2 };
|
|
304
|
+
// Calculates product of multiple pairings
|
|
305
|
+
// This up to x2 faster than just `map(({g1, g2})=>pairing({g1,g2}))`
|
|
306
|
+
function pairingBatch(pairs: PairingInput[], withFinalExponent: boolean = true) {
|
|
307
|
+
const res: MillerInput = [];
|
|
308
|
+
// This cache precomputed toAffine for all points
|
|
309
|
+
G1.ProjectivePoint.normalizeZ(pairs.map(({ g1 }) => g1));
|
|
310
|
+
G2.ProjectivePoint.normalizeZ(pairs.map(({ g2 }) => g2));
|
|
311
|
+
for (const { g1, g2 } of pairs) {
|
|
312
|
+
if (g1.equals(G1.ProjectivePoint.ZERO) || g2.equals(G2.ProjectivePoint.ZERO))
|
|
313
|
+
throw new Error('pairing is not available for ZERO point');
|
|
314
|
+
// This uses toAffine inside
|
|
315
|
+
g1.assertValidity();
|
|
316
|
+
g2.assertValidity();
|
|
317
|
+
const Qa = g1.toAffine();
|
|
318
|
+
res.push([calcPairingPrecomputes(g2), Qa.x, Qa.y]);
|
|
319
|
+
}
|
|
320
|
+
return millerLoopBatch(res, withFinalExponent);
|
|
321
|
+
}
|
|
271
322
|
// Calculates bilinear pairing
|
|
272
323
|
function pairing(Q: G1, P: G2, withFinalExponent: boolean = true): Fp12 {
|
|
273
|
-
|
|
274
|
-
throw new Error('pairing is not available for ZERO point');
|
|
275
|
-
Q.assertValidity();
|
|
276
|
-
P.assertValidity();
|
|
277
|
-
// Performance: 9ms for millerLoop and ~14ms for exp.
|
|
278
|
-
const Qa = Q.toAffine();
|
|
279
|
-
const looped = millerLoop(pairingPrecomputes(P), [Qa.x, Qa.y]);
|
|
280
|
-
return withFinalExponent ? Fp12.finalExponentiate(looped) : looped;
|
|
324
|
+
return pairingBatch([{ g1: Q, g2: P }], withFinalExponent);
|
|
281
325
|
}
|
|
282
|
-
|
|
283
|
-
|
|
326
|
+
|
|
327
|
+
const utils = {
|
|
328
|
+
randomPrivateKey: (): Uint8Array => {
|
|
329
|
+
const length = getMinHashLength(Fr.ORDER);
|
|
330
|
+
return mapHashToField(CURVE.randomBytes(length), Fr.ORDER);
|
|
331
|
+
},
|
|
332
|
+
calcPairingPrecomputes,
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
const { ShortSignature } = CURVE.G1;
|
|
336
|
+
const { Signature } = CURVE.G2;
|
|
284
337
|
|
|
285
338
|
type G1Hex = Hex | G1;
|
|
286
339
|
type G2Hex = Hex | G2;
|
|
@@ -355,11 +408,10 @@ export function bls<Fp2, Fp6, Fp12>(
|
|
|
355
408
|
const Hm = normP2Hash(message, htfOpts);
|
|
356
409
|
const G = G1.ProjectivePoint.BASE;
|
|
357
410
|
const S = normP2(signature);
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
const exp = Fp12.finalExponentiate(Fp12.mul(eGS, ePHm));
|
|
411
|
+
const exp = pairingBatch([
|
|
412
|
+
{ g1: P.negate(), g2: Hm }, // ePHM = pairing(P.negate(), Hm, false);
|
|
413
|
+
{ g1: G, g2: S }, // eGS = pairing(G, S, false);
|
|
414
|
+
]);
|
|
363
415
|
return Fp12.eql(exp, Fp12.ONE);
|
|
364
416
|
}
|
|
365
417
|
|
|
@@ -375,11 +427,10 @@ export function bls<Fp2, Fp6, Fp12>(
|
|
|
375
427
|
const Hm = normP1Hash(message, htfOpts);
|
|
376
428
|
const G = G2.ProjectivePoint.BASE;
|
|
377
429
|
const S = normP1(signature);
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
const exp = Fp12.finalExponentiate(Fp12.mul(eSG, eHmP));
|
|
430
|
+
const exp = pairingBatch([
|
|
431
|
+
{ g1: Hm, g2: P }, // eHmP = pairing(Hm, P, false);
|
|
432
|
+
{ g1: S, g2: G.negate() }, // eSG = pairing(S, G.negate(), false);
|
|
433
|
+
]);
|
|
383
434
|
return Fp12.eql(exp, Fp12.ONE);
|
|
384
435
|
}
|
|
385
436
|
|
|
@@ -431,35 +482,37 @@ export function bls<Fp2, Fp6, Fp12>(
|
|
|
431
482
|
// e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))
|
|
432
483
|
function verifyBatch(
|
|
433
484
|
signature: G2Hex,
|
|
485
|
+
// TODO: maybe `{message: G2Hex, publicKey: G1Hex}[]` instead?
|
|
434
486
|
messages: G2Hex[],
|
|
435
487
|
publicKeys: G1Hex[],
|
|
436
488
|
htfOpts?: htfBasicOpts
|
|
437
489
|
): boolean {
|
|
438
|
-
// @ts-ignore
|
|
439
|
-
// console.log('verifyBatch', bytesToHex(signature as any), messages, publicKeys.map(bytesToHex));
|
|
440
|
-
|
|
441
490
|
if (!messages.length) throw new Error('Expected non-empty messages array');
|
|
442
491
|
if (publicKeys.length !== messages.length)
|
|
443
492
|
throw new Error('Pubkey count should equal msg count');
|
|
444
493
|
const sig = normP2(signature);
|
|
445
494
|
const nMessages = messages.map((i) => normP2Hash(i, htfOpts));
|
|
446
495
|
const nPublicKeys = publicKeys.map(normP1);
|
|
496
|
+
// NOTE: this works only for exact same object
|
|
497
|
+
const messagePubKeyMap = new Map<G2, G1[]>();
|
|
498
|
+
for (let i = 0; i < nPublicKeys.length; i++) {
|
|
499
|
+
const pub = nPublicKeys[i];
|
|
500
|
+
const msg = nMessages[i];
|
|
501
|
+
let keys = messagePubKeyMap.get(msg);
|
|
502
|
+
if (keys === undefined) {
|
|
503
|
+
keys = [];
|
|
504
|
+
messagePubKeyMap.set(msg, keys);
|
|
505
|
+
}
|
|
506
|
+
keys.push(pub);
|
|
507
|
+
}
|
|
508
|
+
const paired = [];
|
|
447
509
|
try {
|
|
448
|
-
const
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
(groupPublicKey, subMessage, i) =>
|
|
452
|
-
subMessage === message ? groupPublicKey.add(nPublicKeys[i]) : groupPublicKey,
|
|
453
|
-
G1.ProjectivePoint.ZERO
|
|
454
|
-
);
|
|
455
|
-
// const msg = message instanceof PointG2 ? message : await PointG2.hashToCurve(message);
|
|
456
|
-
// Possible to batch pairing for same msg with different groupPublicKey here
|
|
457
|
-
paired.push(pairing(groupPublicKey, message, false));
|
|
510
|
+
for (const [msg, keys] of messagePubKeyMap) {
|
|
511
|
+
const groupPublicKey = keys.reduce((acc, msg) => acc.add(msg));
|
|
512
|
+
paired.push({ g1: groupPublicKey, g2: msg });
|
|
458
513
|
}
|
|
459
|
-
paired.push(
|
|
460
|
-
|
|
461
|
-
const exp = Fp12.finalExponentiate(product);
|
|
462
|
-
return Fp12.eql(exp, Fp12.ONE);
|
|
514
|
+
paired.push({ g1: G1.ProjectivePoint.BASE.negate(), g2: sig });
|
|
515
|
+
return Fp12.eql(pairingBatch(paired), Fp12.ONE);
|
|
463
516
|
} catch {
|
|
464
517
|
return false;
|
|
465
518
|
}
|
|
@@ -478,8 +531,9 @@ export function bls<Fp2, Fp6, Fp12>(
|
|
|
478
531
|
aggregatePublicKeys,
|
|
479
532
|
aggregateSignatures,
|
|
480
533
|
aggregateShortSignatures,
|
|
481
|
-
|
|
534
|
+
millerLoopBatch,
|
|
482
535
|
pairing,
|
|
536
|
+
pairingBatch,
|
|
483
537
|
G1,
|
|
484
538
|
G2,
|
|
485
539
|
Signature,
|
|
@@ -492,7 +546,7 @@ export function bls<Fp2, Fp6, Fp12>(
|
|
|
492
546
|
Fp12,
|
|
493
547
|
},
|
|
494
548
|
params: {
|
|
495
|
-
|
|
549
|
+
ateLoopSize: CURVE.params.ateLoopSize,
|
|
496
550
|
r: CURVE.params.r,
|
|
497
551
|
G1b: CURVE.G1.b,
|
|
498
552
|
G2b: CURVE.G2.b,
|
package/src/abstract/curve.ts
CHANGED
|
@@ -25,6 +25,11 @@ export type GroupConstructor<T> = {
|
|
|
25
25
|
};
|
|
26
26
|
export type Mapper<T> = (i: T[]) => T[];
|
|
27
27
|
|
|
28
|
+
// Since points in different groups cannot be equal (different object constructor),
|
|
29
|
+
// we can have single place to store precomputes
|
|
30
|
+
const pointPrecomputes = new WeakMap<any, any[]>();
|
|
31
|
+
const pointWindowSizes = new WeakMap<any, number>(); // This allows use make points immutable (nothing changes inside)
|
|
32
|
+
|
|
28
33
|
// Elliptic curve multiplication of Point by scalar. Fragile.
|
|
29
34
|
// Scalars should always be less than curve order: this should be checked inside of a curve itself.
|
|
30
35
|
// Creates precomputation tables for fast multiplication:
|
|
@@ -41,7 +46,12 @@ export function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number) {
|
|
|
41
46
|
const neg = item.negate();
|
|
42
47
|
return condition ? neg : item;
|
|
43
48
|
};
|
|
49
|
+
const validateW = (W: number) => {
|
|
50
|
+
if (!Number.isSafeInteger(W) || W <= 0 || W > bits)
|
|
51
|
+
throw new Error(`Wrong window size=${W}, should be [1..${bits}]`);
|
|
52
|
+
};
|
|
44
53
|
const opts = (W: number) => {
|
|
54
|
+
validateW(W);
|
|
45
55
|
const windows = Math.ceil(bits / W) + 1; // +1, because
|
|
46
56
|
const windowSize = 2 ** (W - 1); // -1 because we skip zero
|
|
47
57
|
return { windows, windowSize };
|
|
@@ -149,19 +159,25 @@ export function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number) {
|
|
|
149
159
|
return { p, f };
|
|
150
160
|
},
|
|
151
161
|
|
|
152
|
-
wNAFCached(P: T,
|
|
153
|
-
|
|
154
|
-
const W: number = P._WINDOW_SIZE || 1;
|
|
162
|
+
wNAFCached(P: T, n: bigint, transform: Mapper<T>): { p: T; f: T } {
|
|
163
|
+
const W: number = pointWindowSizes.get(P) || 1;
|
|
155
164
|
// Calculate precomputes on a first run, reuse them after
|
|
156
|
-
let comp =
|
|
165
|
+
let comp = pointPrecomputes.get(P);
|
|
157
166
|
if (!comp) {
|
|
158
167
|
comp = this.precomputeWindow(P, W) as T[];
|
|
159
|
-
if (W !== 1)
|
|
160
|
-
precomputesMap.set(P, transform(comp));
|
|
161
|
-
}
|
|
168
|
+
if (W !== 1) pointPrecomputes.set(P, transform(comp));
|
|
162
169
|
}
|
|
163
170
|
return this.wNAF(W, comp, n);
|
|
164
171
|
},
|
|
172
|
+
// We calculate precomputes for elliptic curve point multiplication
|
|
173
|
+
// using windowed method. This specifies window size and
|
|
174
|
+
// stores precomputed values. Usually only base point would be precomputed.
|
|
175
|
+
|
|
176
|
+
setWindowSize(P: T, W: number) {
|
|
177
|
+
validateW(W);
|
|
178
|
+
pointWindowSizes.set(P, W);
|
|
179
|
+
pointPrecomputes.delete(P);
|
|
180
|
+
},
|
|
165
181
|
};
|
|
166
182
|
}
|
|
167
183
|
|