@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.
Files changed (116) hide show
  1. package/README.md +135 -123
  2. package/_shortw_utils.d.ts.map +1 -1
  3. package/abstract/bls.d.ts +37 -34
  4. package/abstract/bls.d.ts.map +1 -1
  5. package/abstract/bls.js +167 -115
  6. package/abstract/bls.js.map +1 -1
  7. package/abstract/curve.d.ts +2 -1
  8. package/abstract/curve.d.ts.map +1 -1
  9. package/abstract/curve.js +22 -7
  10. package/abstract/curve.js.map +1 -1
  11. package/abstract/edwards.d.ts +11 -0
  12. package/abstract/edwards.d.ts.map +1 -1
  13. package/abstract/edwards.js +79 -75
  14. package/abstract/edwards.js.map +1 -1
  15. package/abstract/modular.d.ts +4 -0
  16. package/abstract/modular.d.ts.map +1 -1
  17. package/abstract/modular.js +13 -2
  18. package/abstract/modular.js.map +1 -1
  19. package/abstract/montgomery.d.ts.map +1 -1
  20. package/abstract/montgomery.js +4 -9
  21. package/abstract/montgomery.js.map +1 -1
  22. package/abstract/tower.d.ts +106 -0
  23. package/abstract/tower.d.ts.map +1 -0
  24. package/abstract/tower.js +497 -0
  25. package/abstract/tower.js.map +1 -0
  26. package/abstract/utils.d.ts +17 -0
  27. package/abstract/utils.d.ts.map +1 -1
  28. package/abstract/utils.js +50 -1
  29. package/abstract/utils.js.map +1 -1
  30. package/abstract/weierstrass.d.ts +7 -0
  31. package/abstract/weierstrass.d.ts.map +1 -1
  32. package/abstract/weierstrass.js +88 -72
  33. package/abstract/weierstrass.js.map +1 -1
  34. package/bls12-381.d.ts +1 -65
  35. package/bls12-381.d.ts.map +1 -1
  36. package/bls12-381.js +48 -575
  37. package/bls12-381.js.map +1 -1
  38. package/bn254.d.ts +10 -6
  39. package/bn254.d.ts.map +1 -1
  40. package/bn254.js +207 -10
  41. package/bn254.js.map +1 -1
  42. package/ed25519.d.ts +7 -4
  43. package/ed25519.d.ts.map +1 -1
  44. package/ed25519.js +3 -0
  45. package/ed25519.js.map +1 -1
  46. package/esm/_shortw_utils.d.ts.map +1 -1
  47. package/esm/abstract/bls.d.ts +37 -34
  48. package/esm/abstract/bls.d.ts.map +1 -1
  49. package/esm/abstract/bls.js +168 -116
  50. package/esm/abstract/bls.js.map +1 -1
  51. package/esm/abstract/curve.d.ts +2 -1
  52. package/esm/abstract/curve.d.ts.map +1 -1
  53. package/esm/abstract/curve.js +22 -7
  54. package/esm/abstract/curve.js.map +1 -1
  55. package/esm/abstract/edwards.d.ts +11 -0
  56. package/esm/abstract/edwards.d.ts.map +1 -1
  57. package/esm/abstract/edwards.js +80 -76
  58. package/esm/abstract/edwards.js.map +1 -1
  59. package/esm/abstract/modular.d.ts +4 -0
  60. package/esm/abstract/modular.d.ts.map +1 -1
  61. package/esm/abstract/modular.js +12 -2
  62. package/esm/abstract/modular.js.map +1 -1
  63. package/esm/abstract/montgomery.d.ts.map +1 -1
  64. package/esm/abstract/montgomery.js +5 -10
  65. package/esm/abstract/montgomery.js.map +1 -1
  66. package/esm/abstract/tower.d.ts +106 -0
  67. package/esm/abstract/tower.d.ts.map +1 -0
  68. package/esm/abstract/tower.js +493 -0
  69. package/esm/abstract/tower.js.map +1 -0
  70. package/esm/abstract/utils.d.ts +17 -0
  71. package/esm/abstract/utils.d.ts.map +1 -1
  72. package/esm/abstract/utils.js +44 -0
  73. package/esm/abstract/utils.js.map +1 -1
  74. package/esm/abstract/weierstrass.d.ts +7 -0
  75. package/esm/abstract/weierstrass.d.ts.map +1 -1
  76. package/esm/abstract/weierstrass.js +89 -73
  77. package/esm/abstract/weierstrass.js.map +1 -1
  78. package/esm/bls12-381.d.ts +1 -65
  79. package/esm/bls12-381.d.ts.map +1 -1
  80. package/esm/bls12-381.js +50 -577
  81. package/esm/bls12-381.js.map +1 -1
  82. package/esm/bn254.d.ts +10 -6
  83. package/esm/bn254.d.ts.map +1 -1
  84. package/esm/bn254.js +206 -9
  85. package/esm/bn254.js.map +1 -1
  86. package/esm/ed25519.d.ts +7 -4
  87. package/esm/ed25519.d.ts.map +1 -1
  88. package/esm/ed25519.js +3 -0
  89. package/esm/ed25519.js.map +1 -1
  90. package/esm/p256.d.ts.map +1 -1
  91. package/esm/p384.d.ts.map +1 -1
  92. package/esm/p521.d.ts.map +1 -1
  93. package/esm/secp256k1.d.ts +6 -0
  94. package/esm/secp256k1.d.ts.map +1 -1
  95. package/esm/secp256k1.js +17 -13
  96. package/esm/secp256k1.js.map +1 -1
  97. package/p256.d.ts.map +1 -1
  98. package/p384.d.ts.map +1 -1
  99. package/p521.d.ts.map +1 -1
  100. package/package.json +2 -1
  101. package/secp256k1.d.ts +6 -0
  102. package/secp256k1.d.ts.map +1 -1
  103. package/secp256k1.js +16 -12
  104. package/secp256k1.js.map +1 -1
  105. package/src/abstract/bls.ts +222 -168
  106. package/src/abstract/curve.ts +23 -7
  107. package/src/abstract/edwards.ts +81 -68
  108. package/src/abstract/modular.ts +13 -3
  109. package/src/abstract/montgomery.ts +11 -10
  110. package/src/abstract/tower.ts +604 -0
  111. package/src/abstract/utils.ts +49 -0
  112. package/src/abstract/weierstrass.ts +85 -68
  113. package/src/bls12-381.ts +53 -707
  114. package/src/bn254.ts +224 -9
  115. package/src/ed25519.ts +5 -2
  116. package/src/secp256k1.ts +24 -12
@@ -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, bitLen, bitGet, ensureBytes } from './utils.js';
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 (Barreto-Lynn-Scott) family of pairing-friendly curves.
20
- * Implements BLS (Boneh-Lynn-Shacham) signatures.
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
- * We are using Fp for private keys (shorter) and Fp₂ for signatures (longer).
28
- * Some projects may prefer to swap this relation, it is not supported for now.
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<Fp2> = {
43
- fromHex(hex: Hex): ProjPointType<Fp2>;
44
- toRawBytes(point: ProjPointType<Fp2>): Uint8Array;
45
- toHex(point: ProjPointType<Fp2>): string;
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<Fp, Fp2, Fp6, Fp12> = {
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<Fp, Fp2>;
68
+ Fp2: Fp2Bls;
76
69
  Fp6: IField<Fp6>;
77
- Fp12: Fp12Bls<Fp2, Fp12>;
70
+ Fp12: Fp12Bls;
78
71
  };
79
72
  params: {
80
- x: bigint;
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
- export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
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
- millerLoop: (ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]) => Fp12;
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
- x: bigint;
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<Fp, Fp2>;
157
+ Fp2: Fp2Bls;
144
158
  Fp6: IField<Fp6>;
145
- Fp12: Fp12Bls<Fp2, Fp12>;
159
+ Fp12: Fp12Bls;
146
160
  Fr: IField<bigint>;
147
161
  };
148
162
  utils: {
149
163
  randomPrivateKey: () => Uint8Array;
150
- calcPairingPrecomputes: (p: AffinePoint<Fp2>) => [Fp2, Fp2, Fp2][];
164
+ calcPairingPrecomputes: (p: ProjPointType<Fp2>) => Precompute;
151
165
  };
152
166
  };
153
167
 
154
- export function bls<Fp2, Fp6, Fp12>(
155
- CURVE: CurveType<Fp, Fp2, Fp6, Fp12>
156
- ): CurveFn<Fp, Fp2, Fp6, Fp12> {
157
- // Fields are specific for curve, so for now we'll need to pass them with opts
158
- const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE.fields;
159
- const BLS_X_LEN = bitLen(CURVE.params.x);
160
-
161
- // Pre-compute coefficients for sparse multiplication
162
- // Point addition and point double calculations is reused for coefficients
163
- function calcPairingPrecomputes(p: AffinePoint<Fp2>) {
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
- function millerLoop(ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]): Fp12 {
207
- const { x } = CURVE.params;
208
- const Px = g1[0];
209
- const Py = g1[1];
210
- let f12 = Fp12.ONE;
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
- const { ShortSignature } = CURVE.G1;
269
- const { Signature } = CURVE.G2;
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
- if (Q.equals(G1.ProjectivePoint.ZERO) || P.equals(G2.ProjectivePoint.ZERO))
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
- type G1 = typeof G1.ProjectivePoint.BASE;
283
- type G2 = typeof G2.ProjectivePoint.BASE;
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
- // Instead of doing 2 exponentiations, we use property of billinear maps
359
- // and do one exp after multiplying 2 points.
360
- const ePHm = pairing(P.negate(), Hm, false);
361
- const eGS = pairing(G, S, false);
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
- // Instead of doing 2 exponentiations, we use property of billinear maps
379
- // and do one exp after multiplying 2 points.
380
- const eHmP = pairing(Hm, P, false);
381
- const eSG = pairing(S, G.negate(), false);
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 paired = [];
449
- for (const message of new Set(nMessages)) {
450
- const groupPublicKey = nMessages.reduce(
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(pairing(G1.ProjectivePoint.BASE.negate(), sig, false));
460
- const product = paired.reduce((a, b) => Fp12.mul(a, b), Fp12.ONE);
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
- millerLoop,
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
- x: CURVE.params.x,
549
+ ateLoopSize: CURVE.params.ateLoopSize,
496
550
  r: CURVE.params.r,
497
551
  G1b: CURVE.G1.b,
498
552
  G2b: CURVE.G2.b,
@@ -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, precomputesMap: Map<T, T[]>, n: bigint, transform: Mapper<T>): { p: T; f: T } {
153
- // @ts-ignore
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 = precomputesMap.get(P);
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