@leofcoin/chain 1.10.16 → 1.10.17

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 (30) hide show
  1. package/exports/beacon-envelope.js +6 -6
  2. package/exports/beacon-epoch.js +1 -1
  3. package/exports/beacon-lifecycle.js +1 -1
  4. package/exports/beacon-round.js +1 -1
  5. package/exports/beacon.js +15 -14
  6. package/exports/browser/_commonjsHelpers-B85MJLTf.js +5 -0
  7. package/exports/browser/beacon-envelope.js +61 -150
  8. package/exports/browser/beacon-epoch.js +2 -2
  9. package/exports/browser/beacon-lifecycle.js +3 -3
  10. package/exports/browser/beacon-round.js +2 -2
  11. package/exports/browser/beacon-wire.js +3 -3
  12. package/exports/browser/beacon.js +2483 -1228
  13. package/exports/browser/{browser-CCfR9KOM-CvhDZrGu.js → browser-CCfR9KOM-CpStHgTj.js} +4 -4
  14. package/exports/browser/{browser-crypto-CeE-yId5.js → browser-crypto-CH0ua22L.js} +1 -1
  15. package/exports/browser/chain.js +5 -8
  16. package/exports/browser/{client-CAC7RVMf-Dmz_2A_5.js → client-C_pBoi1g-_pfKzCvX.js} +256 -80
  17. package/exports/browser/{connection-monitor-Cmv00mwl.js → connection-monitor-D-krNiRH.js} +1 -1
  18. package/exports/browser/{hkdf-CnZvwpZ9.js → hkdf-BUHINPFj.js} +2 -94
  19. package/exports/browser/{index-BC-KRBgE.js → index-DmuI-29u.js} +1 -1
  20. package/exports/browser/{index-dTQVgkpf-CuHJWO2l.js → index-dTQVgkpf-BbdshWB0.js} +4 -4
  21. package/exports/browser/index.browser-CFfJpgKV.js +857 -0
  22. package/exports/browser/{messages-Dw86z0zq-DrPw_nlZ.js → messages-BGHaihSi-eHZVl0y9.js} +4 -4
  23. package/exports/browser/{node-browser-DWSdi-XP.js → node-browser-BNe-en1l.js} +5 -5
  24. package/exports/browser/node-browser.js +4 -4
  25. package/exports/browser/{sha2-piimb6xL.js → sha2-BKcEJzgd.js} +144 -3
  26. package/exports/browser/weierstrass-BVfhtHbl.js +3596 -0
  27. package/exports/browser/workers/machine-worker.js +42 -14
  28. package/exports/workers/machine-worker.js +42 -14
  29. package/package.json +14 -13
  30. package/exports/browser/weierstrass-Q0QND1ZT.js +0 -2156
@@ -1,20 +1,20 @@
1
- import { v as validateObject, m as mod, u as utf8ToBytes, a as concatBytes, d as bytesToNumberBE, f as bitLen, g as weierstrassPoints, i as getMinHashLength, j as mapHashToField, k as bitGet, l as ensureBytes, F as Field, s as sha256, n as FpInvertBatch, o as FpPow, q as bitMask, x as mapToCurveSimpleSWU, y as bitSet, r as randomBytes, z as bytesToHex, A as numberToBytesBE } from './weierstrass-Q0QND1ZT.js';
2
- import { s as sha256$1 } from './sha2-piimb6xL.js';
1
+ import { s as sha256 } from './sha2-BKcEJzgd.js';
2
+ import { v as validateObject, i as isBytes, c as copyBytes, a as asafenumber, b as abytes, m as mod, d as concatBytes, f as asciiToBytes, g as bytesToNumberBE, j as validateField, k as aarray, l as FpInvertBatch, n as FpIsSquare, o as notImplemented, q as getMinHashLength, r as mapHashToField, s as randomBytes, F as Field, t as bitLen, u as FpPow, x as FpLegendre, y as aInRange, z as bitGet, A as validatePointCons, w as weierstrass, B as bytesToHex, C as hexToBytes, D as numberToBytesBE } from './weierstrass-BVfhtHbl.js';
3
3
 
4
- function validateDST(dst) {
5
- if (dst instanceof Uint8Array)
6
- return dst;
7
- if (typeof dst === 'string')
8
- return utf8ToBytes(dst);
9
- throw new Error('DST must be Uint8Array or string');
10
- }
4
+ // prettier-ignore
5
+ const _0n$3 = /* @__PURE__ */ BigInt(0), _1n$3 = /* @__PURE__ */ BigInt(1), _2n$3 = /* @__PURE__ */ BigInt(2), _3n$3 = /* @__PURE__ */ BigInt(3), _4n$1 = /* @__PURE__ */ BigInt(4);
11
6
  // Octet Stream to Integer. "spec" implementation of os2ip is 2.5x slower vs bytesToNumberBE.
12
7
  const os2ip = bytesToNumberBE;
13
- // Integer to Octet Stream (numberToBytesBE)
8
+ // Integer to Octet Stream (numberToBytesBE).
14
9
  function i2osp(value, length) {
15
- if (value < 0 || value >= 1 << (8 * length)) {
16
- throw new Error(`bad I2OSP call: value=${value} length=${length}`);
17
- }
10
+ asafenumber(value);
11
+ asafenumber(length);
12
+ // This helper stays on the JS bitwise/u32 fast-path. Callers that need wider encodings should
13
+ // use bigint + numberToBytesBE instead of routing large widths through this small helper.
14
+ if (length < 0 || length > 4)
15
+ throw new Error('invalid I2OSP length: ' + length);
16
+ if (value < 0 || value > 2 ** (8 * length) - 1)
17
+ throw new Error('invalid I2OSP input: ' + value);
18
18
  const res = Array.from({ length }).fill(0);
19
19
  for (let i = length - 1; i >= 0; i--) {
20
20
  res[i] = value & 0xff;
@@ -22,6 +22,7 @@ function i2osp(value, length) {
22
22
  }
23
23
  return new Uint8Array(res);
24
24
  }
25
+ // RFC 9380 only applies strxor() to equal-length strings; callers must preserve that invariant.
25
26
  function strxor(a, b) {
26
27
  const arr = new Uint8Array(a.length);
27
28
  for (let i = 0; i < a.length; i++) {
@@ -29,57 +30,120 @@ function strxor(a, b) {
29
30
  }
30
31
  return arr;
31
32
  }
32
- function isBytes(item) {
33
- if (!(item instanceof Uint8Array))
34
- throw new Error('Uint8Array expected');
33
+ // User can always use utf8 if they want, by passing Uint8Array.
34
+ // If string is passed, we treat it as ASCII: other formats are likely a mistake.
35
+ function normDST(DST) {
36
+ if (!isBytes(DST) && typeof DST !== 'string')
37
+ throw new Error('DST must be Uint8Array or ascii string');
38
+ const dst = typeof DST === 'string' ? asciiToBytes(DST) : DST;
39
+ // RFC 9380 §3.1 requirement 2: tags "MUST have nonzero length".
40
+ if (dst.length === 0)
41
+ throw new Error('DST must be non-empty');
42
+ return dst;
35
43
  }
36
- function isNum(item) {
37
- if (!Number.isSafeInteger(item))
38
- throw new Error('number expected');
39
- }
40
- // Produces a uniformly random byte string using a cryptographic hash function H that outputs b bits
41
- // https://www.rfc-editor.org/rfc/rfc9380#section-5.3.1
44
+ /**
45
+ * Produces a uniformly random byte string using a cryptographic hash
46
+ * function H that outputs b bits.
47
+ * See {@link https://www.rfc-editor.org/rfc/rfc9380#section-5.3.1 | RFC 9380 section 5.3.1}.
48
+ * @param msg - Input message.
49
+ * @param DST - Domain separation tag. This helper normalizes DST, rejects empty DSTs, and
50
+ * oversize-hashes DST when needed.
51
+ * @param lenInBytes - Output length.
52
+ * @param H - Hash function.
53
+ * @returns Uniform byte string.
54
+ * @throws If the message, DST, hash, or output length is invalid. {@link Error}
55
+ * @example
56
+ * Expand one message into uniform bytes with the XMD construction.
57
+ *
58
+ * ```ts
59
+ * import { expand_message_xmd } from '@noble/curves/abstract/hash-to-curve.js';
60
+ * import { sha256 } from '@noble/hashes/sha2.js';
61
+ * const uniform = expand_message_xmd(new TextEncoder().encode('hello noble'), 'DST', 32, sha256);
62
+ * ```
63
+ */
42
64
  function expand_message_xmd(msg, DST, lenInBytes, H) {
43
- isBytes(msg);
44
- isBytes(DST);
45
- isNum(lenInBytes);
65
+ abytes(msg);
66
+ asafenumber(lenInBytes);
67
+ if (typeof H !== 'function')
68
+ throw new Error('expand_message_xmd: expected hash function');
69
+ asafenumber(H.outputLen, 'hash.outputLen');
70
+ asafenumber(H.blockLen, 'hash.blockLen');
71
+ DST = normDST(DST);
46
72
  // https://www.rfc-editor.org/rfc/rfc9380#section-5.3.3
47
73
  if (DST.length > 255)
48
- DST = H(concatBytes(utf8ToBytes('H2C-OVERSIZE-DST-'), DST));
74
+ DST = H(concatBytes(asciiToBytes('H2C-OVERSIZE-DST-'), DST));
49
75
  const { outputLen: b_in_bytes, blockLen: r_in_bytes } = H;
50
76
  const ell = Math.ceil(lenInBytes / b_in_bytes);
51
- if (ell > 255)
52
- throw new Error('Invalid xmd length');
77
+ if (lenInBytes > 65535 || ell > 255)
78
+ throw new Error('expand_message_xmd: invalid lenInBytes');
53
79
  const DST_prime = concatBytes(DST, i2osp(DST.length, 1));
54
- const Z_pad = i2osp(0, r_in_bytes);
80
+ const Z_pad = new Uint8Array(r_in_bytes); // RFC 9380: Z_pad = I2OSP(0, s_in_bytes)
55
81
  const l_i_b_str = i2osp(lenInBytes, 2); // len_in_bytes_str
56
82
  const b = new Array(ell);
57
83
  const b_0 = H(concatBytes(Z_pad, msg, l_i_b_str, i2osp(0, 1), DST_prime));
58
84
  b[0] = H(concatBytes(b_0, i2osp(1, 1), DST_prime));
59
- for (let i = 1; i <= ell; i++) {
85
+ // `b[0]` already stores RFC `b_1`, so only derive `b_2..b_ell` here. The old `<= ell`
86
+ // loop computed one extra tail block, which was usually sliced away but broke at max `ell=255`
87
+ // by reaching `I2OSP(256, 1)`.
88
+ for (let i = 1; i < ell; i++) {
60
89
  const args = [strxor(b_0, b[i - 1]), i2osp(i + 1, 1), DST_prime];
61
90
  b[i] = H(concatBytes(...args));
62
91
  }
63
92
  const pseudo_random_bytes = concatBytes(...b);
64
93
  return pseudo_random_bytes.slice(0, lenInBytes);
65
94
  }
66
- // Produces a uniformly random byte string using an extendable-output function (XOF) H.
67
- // 1. The collision resistance of H MUST be at least k bits.
68
- // 2. H MUST be an XOF that has been proved indifferentiable from
69
- // a random oracle under a reasonable cryptographic assumption.
70
- // https://www.rfc-editor.org/rfc/rfc9380#section-5.3.2
95
+ /**
96
+ * Produces a uniformly random byte string using an extendable-output function (XOF) H.
97
+ * 1. The collision resistance of H MUST be at least k bits.
98
+ * 2. H MUST be an XOF that has been proved indifferentiable from
99
+ * a random oracle under a reasonable cryptographic assumption.
100
+ * See {@link https://www.rfc-editor.org/rfc/rfc9380#section-5.3.2 | RFC 9380 section 5.3.2}.
101
+ * @param msg - Input message.
102
+ * @param DST - Domain separation tag. This helper normalizes DST, rejects empty DSTs, and
103
+ * oversize-hashes DST when needed.
104
+ * @param lenInBytes - Output length.
105
+ * @param k - Target security level.
106
+ * @param H - XOF hash function.
107
+ * @returns Uniform byte string.
108
+ * @throws If the message, DST, XOF, or output length is invalid. {@link Error}
109
+ * @example
110
+ * Expand one message into uniform bytes with the XOF construction.
111
+ *
112
+ * ```ts
113
+ * import { expand_message_xof } from '@noble/curves/abstract/hash-to-curve.js';
114
+ * import { shake256 } from '@noble/hashes/sha3.js';
115
+ * const uniform = expand_message_xof(
116
+ * new TextEncoder().encode('hello noble'),
117
+ * 'DST',
118
+ * 32,
119
+ * 128,
120
+ * shake256
121
+ * );
122
+ * ```
123
+ */
71
124
  function expand_message_xof(msg, DST, lenInBytes, k, H) {
72
- isBytes(msg);
73
- isBytes(DST);
74
- isNum(lenInBytes);
125
+ abytes(msg);
126
+ asafenumber(lenInBytes);
127
+ asafenumber(k, 'k');
128
+ if (k < 0)
129
+ throw new Error('expand_message_xof: invalid k');
130
+ if (typeof H !== 'function')
131
+ throw new Error('expand_message_xof: expected XOF function');
132
+ if (typeof H.create !== 'function')
133
+ throw new Error('expand_message_xof: expected XOF create');
134
+ DST = normDST(DST);
135
+ if (lenInBytes < 0 || lenInBytes > 65535)
136
+ throw new Error('expand_message_xof: invalid lenInBytes');
75
137
  // https://www.rfc-editor.org/rfc/rfc9380#section-5.3.3
76
- // DST = H('H2C-OVERSIZE-DST-' || a_very_long_DST, Math.ceil((lenInBytes * k) / 8));
138
+ // RFC 9380 §5.3.3: DST = H("H2C-OVERSIZE-DST-" || a_very_long_DST, ceil(2 * k / 8)).
77
139
  if (DST.length > 255) {
78
140
  const dkLen = Math.ceil((2 * k) / 8);
79
- DST = H.create({ dkLen }).update(utf8ToBytes('H2C-OVERSIZE-DST-')).update(DST).digest();
141
+ DST = H.create({ dkLen }).update(asciiToBytes('H2C-OVERSIZE-DST-')).update(DST).digest();
80
142
  }
81
- if (lenInBytes > 65535 || DST.length > 255)
82
- throw new Error('expand_message_xof: invalid lenInBytes');
143
+ // Oversize DSTs are compressed above; fail closed if a custom XOF still returns one
144
+ // (possible when k > 1020 makes the compression dkLen itself exceed 255 bytes).
145
+ if (DST.length > 255)
146
+ throw new Error('expand_message_xof: invalid DST');
83
147
  return (H.create({ dkLen: lenInBytes })
84
148
  .update(msg)
85
149
  .update(i2osp(lenInBytes, 2))
@@ -89,25 +153,53 @@ function expand_message_xof(msg, DST, lenInBytes, k, H) {
89
153
  .digest());
90
154
  }
91
155
  /**
92
- * Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F
93
- * https://www.rfc-editor.org/rfc/rfc9380#section-5.2
94
- * @param msg a byte string containing the message to hash
95
- * @param count the number of elements of F to output
96
- * @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`, see above
97
- * @returns [u_0, ..., u_(count - 1)], a list of field elements.
156
+ * Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F.
157
+ * See {@link https://www.rfc-editor.org/rfc/rfc9380#section-5.2 | RFC 9380 section 5.2}.
158
+ * @param msg - Input message bytes.
159
+ * @param count - Number of field elements to derive. Must be `>= 1`.
160
+ * @param options - RFC 9380 options. See {@link H2COpts}. `m` must be `>= 1`.
161
+ * @returns `[u_0, ..., u_(count - 1)]`, a list of field elements.
162
+ * @throws If the expander choice or RFC 9380 options are invalid. {@link Error}
163
+ * @example
164
+ * Hash one message into field elements before mapping it onto a curve.
165
+ *
166
+ * ```ts
167
+ * import { hash_to_field } from '@noble/curves/abstract/hash-to-curve.js';
168
+ * import { sha256 } from '@noble/hashes/sha2.js';
169
+ * const scalars = hash_to_field(new TextEncoder().encode('hello noble'), 2, {
170
+ * DST: 'DST',
171
+ * p: 17n,
172
+ * m: 1,
173
+ * k: 128,
174
+ * expand: 'xmd',
175
+ * hash: sha256,
176
+ * });
177
+ * ```
98
178
  */
99
179
  function hash_to_field(msg, count, options) {
100
180
  validateObject(options, {
101
- DST: 'stringOrUint8Array',
102
181
  p: 'bigint',
103
- m: 'isSafeInteger',
104
- k: 'isSafeInteger',
105
- hash: 'hash',
182
+ m: 'number',
183
+ k: 'number',
184
+ hash: 'function',
106
185
  });
107
- const { p, k, m, hash, expand, DST: _DST } = options;
108
- isBytes(msg);
109
- isNum(count);
110
- const DST = validateDST(_DST);
186
+ const { p, k, m, hash, expand, DST } = options;
187
+ asafenumber(hash.outputLen, 'valid hash');
188
+ abytes(msg);
189
+ asafenumber(count);
190
+ asafenumber(m, 'm');
191
+ asafenumber(k, 'k');
192
+ // RFC 9380 §5.2 defines hash_to_field over a list of one or more field elements and an integer
193
+ // extension degree `m >= 1`; rejecting here avoids degenerate `[]` / `[[]]` helper outputs.
194
+ // The RFC also treats `p` as a finite-field characteristic; bad values degenerate log2/mod.
195
+ if (p <= BigInt(1))
196
+ throw new Error('hash_to_field: expected valid field characteristic');
197
+ if (count < 1)
198
+ throw new Error('hash_to_field: expected count >= 1');
199
+ if (m < 1)
200
+ throw new Error('hash_to_field: expected m >= 1');
201
+ if (k < 0)
202
+ throw new Error('hash_to_field: invalid k');
111
203
  const log2p = p.toString(2).length;
112
204
  const L = Math.ceil((log2p + k) / 8); // section 5.1 of ietf draft link above
113
205
  const len_in_bytes = count * m * L;
@@ -119,7 +211,8 @@ function hash_to_field(msg, count, options) {
119
211
  prb = expand_message_xof(msg, DST, len_in_bytes, k, hash);
120
212
  }
121
213
  else if (expand === '_internal_pass') {
122
- // for internal tests only
214
+ // for internal tests only: msg is used as the uniform bytes directly. Short msg is allowed
215
+ // on purpose (subarray() slices are short): zkcrypto map_scalar vectors feed empty okm.
123
216
  prb = msg;
124
217
  }
125
218
  else {
@@ -137,371 +230,1179 @@ function hash_to_field(msg, count, options) {
137
230
  }
138
231
  return u;
139
232
  }
233
+ /**
234
+ * @param field - Field implementation.
235
+ * @param map - Isogeny coefficients.
236
+ * @returns Isogeny mapping helper.
237
+ * @example
238
+ * Build one rational isogeny map, then apply it to affine x/y coordinates.
239
+ *
240
+ * ```ts
241
+ * import { isogenyMap } from '@noble/curves/abstract/hash-to-curve.js';
242
+ * import { Field } from '@noble/curves/abstract/modular.js';
243
+ * const Fp = Field(17n);
244
+ * const iso = isogenyMap(Fp, [[0n, 1n], [1n], [1n], [1n]]);
245
+ * const point = iso(3n, 5n);
246
+ * ```
247
+ */
140
248
  function isogenyMap(field, map) {
249
+ validateField(field);
141
250
  // Make same order as in spec
142
- const COEFF = map.map((i) => Array.from(i).reverse());
251
+ aarray(map, 'map');
252
+ const coeff = map.map((i, row) => {
253
+ aarray(i, 'map[' + row + ']');
254
+ if (i.length < 1)
255
+ throw new Error('isogenyMap: expected non-empty coefficients');
256
+ return Array.from(i).reverse();
257
+ });
143
258
  return (x, y) => {
144
- const [xNum, xDen, yNum, yDen] = COEFF.map((val) => val.reduce((acc, i) => field.add(field.mul(acc, x), i)));
145
- x = field.div(xNum, xDen); // xNum / xDen
146
- y = field.mul(y, field.div(yNum, yDen)); // y * (yNum / yDev)
147
- return { x, y };
259
+ const [xn, xd, yn, yd] = coeff.map((val) => val.reduce((acc, i) => field.add(field.mul(acc, x), i)));
260
+ const isZero = field.is0(xd) || field.is0(yd);
261
+ // Shipped Weierstrass consumers encode that affine identity as all-zero
262
+ // coordinates, so `passZero=true` intentionally collapses zero
263
+ // denominators to `{ x: 0, y: 0 }`.
264
+ const [xd_inv, yd_inv] = FpInvertBatch(field, [xd, yd], true);
265
+ x = field.mul(xn, xd_inv); // xNum / xDen
266
+ y = field.mul(y, field.mul(yn, yd_inv)); // y * (yNum / yDev)
267
+ // RFC 9380 §6.6.3: if the denominator of either isogeny rational function is
268
+ // zero, the exceptional case must return the identity point on E.
269
+ return isZero ? { x: field.ZERO, y: field.ZERO } : { x, y };
148
270
  };
149
271
  }
150
- function createHasher(Point, mapToCurve, def) {
272
+ // Keep the shared DST removable when the selected bundle never hashes to scalar.
273
+ // Callers that need protocol-specific scalar domain separation must override this generic default.
274
+ // RFC 9497 §§4.1-4.5 use this ASCII prefix before appending the ciphersuite context string.
275
+ // Export a string instead of mutable bytes so callers cannot poison default hash-to-scalar behavior
276
+ // by mutating a shared Uint8Array in place.
277
+ const _DST_scalar = 'HashToScalar-';
278
+ /**
279
+ * Creates hash-to-curve methods from EC Point and mapToCurve function. See {@link H2CHasher}.
280
+ * @param Point - Point constructor.
281
+ * @param mapToCurve - Map-to-curve function.
282
+ * @param defaults - Default hash-to-curve options. A frozen detached snapshot is reused as the
283
+ * shared defaults bundle for the returned helpers.
284
+ * @returns Hash-to-curve helper namespace.
285
+ * @throws If the map-to-curve callback or default hash-to-curve options are invalid. {@link Error}
286
+ * @example
287
+ * Bundle hash-to-curve, hash-to-scalar, and encode-to-curve helpers for one curve.
288
+ *
289
+ * ```ts
290
+ * import { createHasher } from '@noble/curves/abstract/hash-to-curve.js';
291
+ * import { p256 } from '@noble/curves/nist.js';
292
+ * import { sha256 } from '@noble/hashes/sha2.js';
293
+ * const hasher = createHasher(p256.Point, () => p256.Point.BASE.toAffine(), {
294
+ * DST: 'P256_XMD:SHA-256_SSWU_RO_',
295
+ * encodeDST: 'P256_XMD:SHA-256_SSWU_NU_',
296
+ * p: p256.Point.Fp.ORDER,
297
+ * m: 1,
298
+ * k: 128,
299
+ * expand: 'xmd',
300
+ * hash: sha256,
301
+ * });
302
+ * const point = hasher.encodeToCurve(new TextEncoder().encode('hello noble'));
303
+ * ```
304
+ */
305
+ function createHasher(Point, mapToCurve, defaults) {
151
306
  if (typeof mapToCurve !== 'function')
152
307
  throw new Error('mapToCurve() must be defined');
153
- return {
154
- // Encodes byte string to elliptic curve.
155
- // hash_to_curve from https://www.rfc-editor.org/rfc/rfc9380#section-3
308
+ validateObject(defaults);
309
+ // `Point` is intentionally not shape-validated eagerly here: point constructors vary across
310
+ // curve families, so this helper only checks the hooks it can validate cheaply. Misconfigured
311
+ // suites fail later when hashing first touches Point.fromAffine / Point.ZERO / clearCofactor().
312
+ const snapshot = (src) => Object.freeze({
313
+ ...src,
314
+ DST: isBytes(src.DST) ? copyBytes(src.DST) : src.DST,
315
+ ...(src.encodeDST === undefined
316
+ ? {}
317
+ : { encodeDST: isBytes(src.encodeDST) ? copyBytes(src.encodeDST) : src.encodeDST }),
318
+ });
319
+ // Keep one private defaults snapshot for actual hashing and expose fresh
320
+ // detached snapshots via the public getter.
321
+ // Otherwise a caller could mutate `hasher.defaults.DST` in place and poison
322
+ // the singleton hasher for every other consumer in the same process.
323
+ const safeDefaults = snapshot(defaults);
324
+ // Per-call options are H2CDSTOpts: only DST may be overridden. Copying just that key keeps
325
+ // off-type option objects from silently replacing suite parameters (p/m/k/hash/expand) at
326
+ // runtime — same pinning hashToScalar always did for p/m.
327
+ const dstOverride = (options) => options && options.DST !== undefined ? { DST: options.DST } : undefined;
328
+ function map(num) {
329
+ return Point.fromAffine(mapToCurve(num));
330
+ }
331
+ function clear(initial) {
332
+ const P = initial.clearCofactor();
333
+ // Keep ZERO as the algebraic cofactor-clearing result here; strict public point-validity
334
+ // surfaces may still reject it later, but createHasher.clear() itself is not that boundary.
335
+ if (P.equals(Point.ZERO))
336
+ return Point.ZERO;
337
+ P.assertValidity();
338
+ return P;
339
+ }
340
+ return Object.freeze({
341
+ get defaults() {
342
+ return snapshot(safeDefaults);
343
+ },
344
+ Point,
156
345
  hashToCurve(msg, options) {
157
- const u = hash_to_field(msg, 2, { ...def, DST: def.DST, ...options });
158
- const u0 = Point.fromAffine(mapToCurve(u[0]));
159
- const u1 = Point.fromAffine(mapToCurve(u[1]));
160
- const P = u0.add(u1).clearCofactor();
161
- P.assertValidity();
162
- return P;
346
+ const opts = Object.assign({}, safeDefaults, dstOverride(options));
347
+ const u = hash_to_field(msg, 2, opts);
348
+ const u0 = map(u[0]);
349
+ const u1 = map(u[1]);
350
+ return clear(u0.add(u1));
163
351
  },
164
- // Encodes byte string to elliptic curve.
165
- // encode_to_curve from https://www.rfc-editor.org/rfc/rfc9380#section-3
166
352
  encodeToCurve(msg, options) {
167
- const u = hash_to_field(msg, 1, { ...def, DST: def.encodeDST, ...options });
168
- const P = Point.fromAffine(mapToCurve(u[0])).clearCofactor();
169
- P.assertValidity();
170
- return P;
353
+ const optsDst = safeDefaults.encodeDST === undefined ? {} : { DST: safeDefaults.encodeDST };
354
+ const opts = Object.assign({}, safeDefaults, optsDst, dstOverride(options));
355
+ const u = hash_to_field(msg, 1, opts);
356
+ const u0 = map(u[0]);
357
+ return clear(u0);
171
358
  },
359
+ /** See {@link H2CHasher} */
360
+ mapToCurve(scalars) {
361
+ // Curves with m=1 accept only single scalar
362
+ if (safeDefaults.m === 1) {
363
+ if (typeof scalars !== 'bigint')
364
+ throw new Error('expected bigint (m=1)');
365
+ return clear(map([scalars]));
366
+ }
367
+ if (!Array.isArray(scalars))
368
+ throw new Error('expected array of bigints');
369
+ // RFC 9380 represents one GF(p^m) element as exactly m base-field scalars.
370
+ if (scalars.length !== safeDefaults.m)
371
+ throw new Error(`expected array of ${safeDefaults.m} bigints`);
372
+ for (const i of scalars)
373
+ if (typeof i !== 'bigint')
374
+ throw new Error('expected array of bigints');
375
+ return clear(map(scalars));
376
+ },
377
+ // hash_to_scalar can produce 0: https://www.rfc-editor.org/errata/eid8393
378
+ // RFC 9380, draft-irtf-cfrg-bbs-signatures-08. Default scalar DST is the shared generic
379
+ // `HashToScalar-` prefix above unless the caller overrides it per invocation.
380
+ hashToScalar(msg, options) {
381
+ // @ts-ignore
382
+ const N = Point.Fn.ORDER;
383
+ const opts = Object.assign({}, safeDefaults, { DST: _DST_scalar }, dstOverride(options), {
384
+ p: N,
385
+ m: 1,
386
+ });
387
+ return hash_to_field(msg, 1, opts)[0][0];
388
+ },
389
+ });
390
+ }
391
+ /**
392
+ * Implementation of the Shallue and van de Woestijne method for any weierstrass curve.
393
+ * TODO: check if there is a way to merge this with uvRatio in Edwards; move to modular.
394
+ * b = True and y = sqrt(u / v) if (u / v) is square in F, and
395
+ * b = False and y = sqrt(Z * (u / v)) otherwise.
396
+ * RFC 9380 expects callers to provide `v != 0`; this helper does not enforce it.
397
+ * @param Fp - Field implementation.
398
+ * @param Z - Simplified SWU map parameter.
399
+ * @returns Square-root ratio helper.
400
+ * @example
401
+ * Build the square-root ratio helper used by SWU map implementations.
402
+ *
403
+ * ```ts
404
+ * import { SWUFpSqrtRatio } from '@noble/curves/abstract/hash-to-curve.js';
405
+ * import { Field } from '@noble/curves/abstract/modular.js';
406
+ * const Fp = Field(17n);
407
+ * const sqrtRatio = SWUFpSqrtRatio(Fp, 3n);
408
+ * const out = sqrtRatio(4n, 1n);
409
+ * ```
410
+ */
411
+ function SWUFpSqrtRatio(Fp, Z) {
412
+ // Fail with the usual field-shape error before touching pow/cmov on malformed field shims.
413
+ const F = validateField(Fp);
414
+ // Generic implementation
415
+ const q = F.ORDER;
416
+ let l = _0n$3;
417
+ for (let o = q - _1n$3; o % _2n$3 === _0n$3; o /= _2n$3)
418
+ l += _1n$3;
419
+ const c1 = l; // 1. c1, the largest integer such that 2^c1 divides q - 1.
420
+ // We need 2n ** c1 and 2n ** (c1-1). We can't use **; but we can use <<.
421
+ // 2n ** c1 == 2n << (c1-1)
422
+ const _2n_pow_c1_1 = _2n$3 << (c1 - _1n$3 - _1n$3);
423
+ const _2n_pow_c1 = _2n_pow_c1_1 * _2n$3;
424
+ const c2 = (q - _1n$3) / _2n_pow_c1; // 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
425
+ const c3 = (c2 - _1n$3) / _2n$3; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
426
+ const c4 = _2n_pow_c1 - _1n$3; // 4. c4 = 2^c1 - 1 # Integer arithmetic
427
+ const c5 = _2n_pow_c1_1; // 5. c5 = 2^(c1 - 1) # Integer arithmetic
428
+ const c6 = F.pow(Z, c2); // 6. c6 = Z^c2
429
+ const c7 = F.pow(Z, (c2 + _1n$3) / _2n$3); // 7. c7 = Z^((c2 + 1) / 2)
430
+ // RFC 9380 Appendix F.2.1.1 defines sqrt_ratio(u, v) only for v != 0.
431
+ // We keep v=0 on the regular result path with isValid=false instead of
432
+ // throwing so the helper stays closer to the RFC's fixed control flow.
433
+ let sqrtRatio = (u, v) => {
434
+ let tv1 = c6; // 1. tv1 = c6
435
+ let tv2 = F.pow(v, c4); // 2. tv2 = v^c4
436
+ let tv3 = F.sqr(tv2); // 3. tv3 = tv2^2
437
+ tv3 = F.mul(tv3, v); // 4. tv3 = tv3 * v
438
+ let tv5 = F.mul(u, tv3); // 5. tv5 = u * tv3
439
+ tv5 = F.pow(tv5, c3); // 6. tv5 = tv5^c3
440
+ tv5 = F.mul(tv5, tv2); // 7. tv5 = tv5 * tv2
441
+ tv2 = F.mul(tv5, v); // 8. tv2 = tv5 * v
442
+ tv3 = F.mul(tv5, u); // 9. tv3 = tv5 * u
443
+ let tv4 = F.mul(tv3, tv2); // 10. tv4 = tv3 * tv2
444
+ tv5 = F.pow(tv4, c5); // 11. tv5 = tv4^c5
445
+ let isQR = F.eql(tv5, F.ONE); // 12. isQR = tv5 == 1
446
+ tv2 = F.mul(tv3, c7); // 13. tv2 = tv3 * c7
447
+ tv5 = F.mul(tv4, tv1); // 14. tv5 = tv4 * tv1
448
+ tv3 = F.cmov(tv2, tv3, isQR); // 15. tv3 = CMOV(tv2, tv3, isQR)
449
+ tv4 = F.cmov(tv5, tv4, isQR); // 16. tv4 = CMOV(tv5, tv4, isQR)
450
+ // 17. for i in (c1, c1 - 1, ..., 2):
451
+ for (let i = c1; i > _1n$3; i--) {
452
+ let tv5 = i - _2n$3; // 18. tv5 = i - 2
453
+ tv5 = _2n$3 << (tv5 - _1n$3); // 19. tv5 = 2^tv5
454
+ let tvv5 = F.pow(tv4, tv5); // 20. tv5 = tv4^tv5
455
+ const e1 = F.eql(tvv5, F.ONE); // 21. e1 = tv5 == 1
456
+ tv2 = F.mul(tv3, tv1); // 22. tv2 = tv3 * tv1
457
+ tv1 = F.mul(tv1, tv1); // 23. tv1 = tv1 * tv1
458
+ tvv5 = F.mul(tv4, tv1); // 24. tv5 = tv4 * tv1
459
+ tv3 = F.cmov(tv2, tv3, e1); // 25. tv3 = CMOV(tv2, tv3, e1)
460
+ tv4 = F.cmov(tvv5, tv4, e1); // 26. tv4 = CMOV(tv5, tv4, e1)
461
+ }
462
+ // RFC 9380 Appendix F.2.1.1 defines sqrt_ratio(u, v) for v != 0.
463
+ // When u = 0 and v != 0, u / v = 0 is square and the computed root is
464
+ // still 0, so widen only the final flag and keep the full control flow.
465
+ return { isValid: !F.is0(v) && (isQR || F.is0(u)), value: tv3 };
466
+ };
467
+ if (F.ORDER % _4n$1 === _3n$3) {
468
+ // sqrt_ratio_3mod4(u, v)
469
+ const c1 = (F.ORDER - _3n$3) / _4n$1; // 1. c1 = (q - 3) / 4 # Integer arithmetic
470
+ const c2 = F.sqrt(F.neg(Z)); // 2. c2 = sqrt(-Z)
471
+ sqrtRatio = (u, v) => {
472
+ let tv1 = F.sqr(v); // 1. tv1 = v^2
473
+ const tv2 = F.mul(u, v); // 2. tv2 = u * v
474
+ tv1 = F.mul(tv1, tv2); // 3. tv1 = tv1 * tv2
475
+ let y1 = F.pow(tv1, c1); // 4. y1 = tv1^c1
476
+ y1 = F.mul(y1, tv2); // 5. y1 = y1 * tv2
477
+ const y2 = F.mul(y1, c2); // 6. y2 = y1 * c2
478
+ const tv3 = F.mul(F.sqr(y1), v); // 7. tv3 = y1^2; 8. tv3 = tv3 * v
479
+ const isQR = F.eql(tv3, u); // 9. isQR = tv3 == u
480
+ let y = F.cmov(y2, y1, isQR); // 10. y = CMOV(y2, y1, isQR)
481
+ return { isValid: !F.is0(v) && isQR, value: y }; // 11. return (isQR, y) isQR ? y : y*c2
482
+ };
483
+ }
484
+ // No curves uses that
485
+ // if (Fp.ORDER % _8n === _5n) // sqrt_ratio_5mod8
486
+ return sqrtRatio;
487
+ }
488
+ /**
489
+ * Simplified Shallue-van de Woestijne-Ulas Method
490
+ * See {@link https://www.rfc-editor.org/rfc/rfc9380#section-6.6.2 | RFC 9380 section 6.6.2}.
491
+ * @param Fp - Field implementation.
492
+ * @param opts - SWU parameters:
493
+ * - `A`: Curve parameter `A`.
494
+ * - `B`: Curve parameter `B`.
495
+ * - `Z`: Simplified SWU map parameter.
496
+ * @returns Deterministic map-to-curve function.
497
+ * @throws If the SWU parameters are invalid or the field lacks the required helpers. {@link Error}
498
+ * @example
499
+ * Map one field element to a Weierstrass curve point with the SWU recipe.
500
+ *
501
+ * ```ts
502
+ * import { mapToCurveSimpleSWU } from '@noble/curves/abstract/hash-to-curve.js';
503
+ * import { Field } from '@noble/curves/abstract/modular.js';
504
+ * const Fp = Field(17n);
505
+ * const map = mapToCurveSimpleSWU(Fp, { A: 1n, B: 2n, Z: 3n });
506
+ * const point = map(5n);
507
+ * ```
508
+ */
509
+ function mapToCurveSimpleSWU(Fp, opts) {
510
+ const F = validateField(Fp);
511
+ validateObject(opts, {}, {}, 'opts');
512
+ const { A, B, Z } = opts;
513
+ if (!F.isValidNot0(A) || !F.isValidNot0(B) || !F.isValid(Z))
514
+ throw new Error('mapToCurveSimpleSWU: invalid opts');
515
+ // RFC 9380 §6.6.2 and Appendix H.2 require:
516
+ // 1. Z is non-square in F
517
+ // 2. Z != -1 in F
518
+ // 3. g(x) - Z is irreducible over F
519
+ // 4. g(B / (Z * A)) is square in F
520
+ // We can enforce 1, 2, and 4 with the current field API.
521
+ // Criterion 3 is not checked here because generic `IField<T>` does not expose
522
+ // polynomial-ring / irreducibility operations, and this helper is used for
523
+ // both prime and extension fields.
524
+ if (F.eql(Z, F.neg(F.ONE)) || FpIsSquare(F, Z))
525
+ throw new Error('mapToCurveSimpleSWU: invalid opts');
526
+ // RFC 9380 Appendix H.2 criterion 4: g(B / (Z * A)) is square in F.
527
+ // x = B / (Z * A)
528
+ const x = F.mul(B, F.inv(F.mul(Z, A)));
529
+ // g(x) = x^3 + A*x + B
530
+ const gx = F.add(F.add(F.mul(F.sqr(x), x), F.mul(A, x)), B);
531
+ if (!FpIsSquare(F, gx))
532
+ throw new Error('mapToCurveSimpleSWU: invalid opts');
533
+ const sqrtRatio = SWUFpSqrtRatio(F, Z);
534
+ if (!F.isOdd)
535
+ throw new Error('Field does not have .isOdd()');
536
+ // Input: u, an element of F.
537
+ // Output: (x, y), a point on E.
538
+ return (u) => {
539
+ // prettier-ignore
540
+ let tv1, tv2, tv3, tv4, tv5, tv6, x, y;
541
+ tv1 = F.sqr(u); // 1. tv1 = u^2
542
+ tv1 = F.mul(tv1, Z); // 2. tv1 = Z * tv1
543
+ tv2 = F.sqr(tv1); // 3. tv2 = tv1^2
544
+ tv2 = F.add(tv2, tv1); // 4. tv2 = tv2 + tv1
545
+ tv3 = F.add(tv2, F.ONE); // 5. tv3 = tv2 + 1
546
+ tv3 = F.mul(tv3, B); // 6. tv3 = B * tv3
547
+ tv4 = F.cmov(Z, F.neg(tv2), !F.eql(tv2, F.ZERO)); // 7. tv4 = CMOV(Z, -tv2, tv2 != 0)
548
+ tv4 = F.mul(tv4, A); // 8. tv4 = A * tv4
549
+ tv2 = F.sqr(tv3); // 9. tv2 = tv3^2
550
+ tv6 = F.sqr(tv4); // 10. tv6 = tv4^2
551
+ tv5 = F.mul(tv6, A); // 11. tv5 = A * tv6
552
+ tv2 = F.add(tv2, tv5); // 12. tv2 = tv2 + tv5
553
+ tv2 = F.mul(tv2, tv3); // 13. tv2 = tv2 * tv3
554
+ tv6 = F.mul(tv6, tv4); // 14. tv6 = tv6 * tv4
555
+ tv5 = F.mul(tv6, B); // 15. tv5 = B * tv6
556
+ tv2 = F.add(tv2, tv5); // 16. tv2 = tv2 + tv5
557
+ x = F.mul(tv1, tv3); // 17. x = tv1 * tv3
558
+ const { isValid, value } = sqrtRatio(tv2, tv6); // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6)
559
+ y = F.mul(tv1, u); // 19. y = tv1 * u -> Z * u^3 * y1
560
+ y = F.mul(y, value); // 20. y = y * y1
561
+ x = F.cmov(x, tv3, isValid); // 21. x = CMOV(x, tv3, is_gx1_square)
562
+ y = F.cmov(y, value, isValid); // 22. y = CMOV(y, y1, is_gx1_square)
563
+ const e1 = F.isOdd(u) === F.isOdd(y); // 23. e1 = sgn0(u) == sgn0(y)
564
+ y = F.cmov(F.neg(y), y, e1); // 24. y = CMOV(-y, y, e1)
565
+ const tv4_inv = FpInvertBatch(F, [tv4], true)[0];
566
+ x = F.mul(x, tv4_inv); // 25. x = x / tv4
567
+ return { x, y };
172
568
  };
173
569
  }
174
570
 
571
+ /**
572
+ * BLS != BLS.
573
+ * The file implements BLS (Boneh-Lynn-Shacham) signatures.
574
+ * Used in both BLS (Barreto-Lynn-Scott) and BN (Barreto-Naehrig)
575
+ * families of pairing-friendly curves.
576
+ * Consists of two curves: G1 and G2:
577
+ * - G1 is a subgroup of (x, y) E(Fq) over y² = x³ + 4.
578
+ * - G2 is a subgroup of ((x₁, x₂+i), (y₁, y₂+i)) E(Fq²) over y² = x³ + 4(1 + i) where i is √-1
579
+ * - Gt, created by bilinear (ate) pairing e(G1, G2), consists of p-th roots of unity in
580
+ * Fq^k where k is embedding degree. Only degree 12 is currently supported, 24 is not.
581
+ * Pairing is used to aggregate and verify signatures.
582
+ * There are two modes of operation:
583
+ * - Long signatures: X-byte keys + 2X-byte sigs (G1 keys + G2 sigs).
584
+ * - Short signatures: 2X-byte keys + X-byte sigs (G2 keys + G1 sigs).
585
+ * @module
586
+ **/
587
+ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
175
588
  // prettier-ignore
176
- const _2n$1 = BigInt(2), _3n$1 = BigInt(3);
177
- function bls(CURVE) {
178
- // Fields are specific for curve, so for now we'll need to pass them with opts
179
- const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE.fields;
180
- const BLS_X_LEN = bitLen(CURVE.params.x);
589
+ const _0n$2 = BigInt(0), _1n$2 = BigInt(1), _2n$2 = BigInt(2), _3n$2 = BigInt(3);
590
+ // Signed non-adjacent decomposition of the spec-defined Miller-loop parameter.
591
+ // BN254 benefits most because `6x+2` has multiple adjacent `11` runs, but BLS12-381's
592
+ // stored `|x|` still starts with `11`, so the Miller loop must also handle one `-1` digit there.
593
+ function NAfDecomposition(a) {
594
+ const res = [];
595
+ // a>1 because of marker bit
596
+ for (; a > _1n$2; a >>= _1n$2) {
597
+ if ((a & _1n$2) === _0n$2)
598
+ res.unshift(0);
599
+ else if ((a & _3n$2) === _3n$2) {
600
+ res.unshift(-1);
601
+ a += _1n$2;
602
+ }
603
+ else
604
+ res.unshift(1);
605
+ }
606
+ return res;
607
+ }
608
+ function aNonEmpty(arr) {
609
+ // Aggregate helpers use this to reject empty variable-length inputs consistently.
610
+ // Without the guard, each caller would fall through into a different empty-input / identity
611
+ // case and hide missing inputs behind outputs that still look structurally valid.
612
+ if (!Array.isArray(arr) || arr.length === 0)
613
+ throw new Error('expected non-empty array');
614
+ }
615
+ // This should be enough for bn254, no need to export full stuff?
616
+ function createBlsPairing(fields, G1, G2, params) {
617
+ validateObject(fields, { Fp: 'object', Fr: 'object', Fp2: 'object', Fp12: 'object' }, { Fp6: 'object' }, 'fields');
618
+ if (typeof G1 !== 'function')
619
+ throw new TypeError('"G1_Point" expected point constructor, got type=' + typeof G1);
620
+ if (typeof G2 !== 'function')
621
+ throw new TypeError('"G2_Point" expected point constructor, got type=' + typeof G2);
622
+ validateObject(params, { ateLoopSize: 'bigint', xNegative: 'boolean', twistType: 'string' }, { randomBytes: 'function', postPrecompute: 'function' }, 'params');
623
+ const { Fp, Fr, Fp2, Fp12 } = fields;
624
+ const { twistType, ateLoopSize, xNegative, postPrecompute } = params;
625
+ const fp2 = (c0, c1) => ({ c0, c1 });
626
+ const fp2f = ({ c0, c1 }) => Object.freeze({ c0, c1 });
627
+ const add2 = (a, b) => fp2(Fp.add(a.c0, b.c0), Fp.add(a.c1, b.c1));
628
+ const sub2 = (a, b) => fp2(Fp.sub(a.c0, b.c0), Fp.sub(a.c1, b.c1));
629
+ const mul2 = (a, b) => {
630
+ const t0 = Fp.mul(a.c0, b.c0);
631
+ const t1 = Fp.mul(a.c1, b.c1);
632
+ return fp2(Fp.sub(t0, t1), Fp.sub(Fp.mul(Fp.add(a.c0, a.c1), Fp.add(b.c0, b.c1)), Fp.add(t0, t1)));
633
+ };
634
+ const mul2ByFp = (a, rhs) => fp2(Fp.mul(a.c0, rhs), Fp.mul(a.c1, rhs));
635
+ // Delegates to the tower's mulByNonresidue: it has fast paths for ξ = u+1 / ξ = a+u
636
+ // (adds/scalar-muls instead of a full Karatsuba Fp2 multiplication).
637
+ const mul2ByNonresidue = (a) => Fp2.mulByNonresidue(a);
638
+ const mul014ByLine = ({ c0: f0, c1: f1 }, o0, l1, l4, Px, Py) => {
639
+ const o1 = mul2ByFp(l1, Px);
640
+ const o4 = mul2ByFp(l4, Py);
641
+ const { c0: a0, c1: a1, c2: a2 } = f0;
642
+ const { c0: b0, c1: b1, c2: b2 } = f1;
643
+ // t0 = Fp6.mul01(f0, o0, o1)
644
+ const t0_0 = mul2(a0, o0);
645
+ const t0_1 = mul2(a1, o1);
646
+ const t0_c0 = add2(mul2ByNonresidue(sub2(mul2(add2(a1, a2), o1), t0_1)), t0_0);
647
+ const t0_c1 = sub2(sub2(mul2(add2(o0, o1), add2(a0, a1)), t0_0), t0_1);
648
+ const t0_c2 = add2(sub2(mul2(add2(a0, a2), o0), t0_0), t0_1);
649
+ // t1 = Fp6.mul1(f1, o4)
650
+ const t1_c0 = mul2ByNonresidue(mul2(b2, o4));
651
+ const t1_c1 = mul2(b0, o4);
652
+ const t1_c2 = mul2(b1, o4);
653
+ // t2 = Fp6.mul01(Fp6.add(f0, f1), o0, Fp2.add(o1, o4))
654
+ const s0 = add2(a0, b0);
655
+ const s1 = add2(a1, b1);
656
+ const s2 = add2(a2, b2);
657
+ const o14 = add2(o1, o4);
658
+ const t2_0 = mul2(s0, o0);
659
+ const t2_1 = mul2(s1, o14);
660
+ const t2_c0 = add2(mul2ByNonresidue(sub2(mul2(add2(s1, s2), o14), t2_1)), t2_0);
661
+ const t2_c1 = sub2(sub2(mul2(add2(o0, o14), add2(s0, s1)), t2_0), t2_1);
662
+ const t2_c2 = add2(sub2(mul2(add2(s0, s2), o0), t2_0), t2_1);
663
+ return Object.freeze({
664
+ c0: Object.freeze({
665
+ c0: fp2f(add2(mul2ByNonresidue(t1_c2), t0_c0)),
666
+ c1: fp2f(add2(t1_c0, t0_c1)),
667
+ c2: fp2f(add2(t1_c1, t0_c2)),
668
+ }),
669
+ c1: Object.freeze({
670
+ c0: fp2f(sub2(sub2(t2_c0, t0_c0), t1_c0)),
671
+ c1: fp2f(sub2(sub2(t2_c1, t0_c1), t1_c1)),
672
+ c2: fp2f(sub2(sub2(t2_c2, t0_c2), t1_c2)),
673
+ }),
674
+ });
675
+ };
676
+ // Like mul014ByLine, params are named after the sparse slot they end up in: l0 is scaled by
677
+ // Py into o0, l3 is scaled by Px into o3, o4 is used as-is.
678
+ const mul034ByLine = ({ c0: f0, c1: f1 }, l0, l3, o4, Px, Py) => {
679
+ const o0 = mul2ByFp(l0, Py);
680
+ const o3 = mul2ByFp(l3, Px);
681
+ const { c0: a0, c1: a1, c2: a2 } = f0;
682
+ const { c0: b0, c1: b1, c2: b2 } = f1;
683
+ // a = f0 * o0
684
+ const a_c0 = mul2(a0, o0);
685
+ const a_c1 = mul2(a1, o0);
686
+ const a_c2 = mul2(a2, o0);
687
+ // b = Fp6.mul01(f1, o3, o4)
688
+ const b0m = mul2(b0, o3);
689
+ const b1m = mul2(b1, o4);
690
+ const b_c0 = add2(mul2ByNonresidue(sub2(mul2(add2(b1, b2), o4), b1m)), b0m);
691
+ const b_c1 = sub2(sub2(mul2(add2(o3, o4), add2(b0, b1)), b0m), b1m);
692
+ const b_c2 = add2(sub2(mul2(add2(b0, b2), o3), b0m), b1m);
693
+ // e = Fp6.mul01(Fp6.add(f0, f1), Fp2.add(o0, o3), o4)
694
+ const s0 = add2(a0, b0);
695
+ const s1 = add2(a1, b1);
696
+ const s2 = add2(a2, b2);
697
+ const o03 = add2(o0, o3);
698
+ const e0m = mul2(s0, o03);
699
+ const e1m = mul2(s1, o4);
700
+ const e_c0 = add2(mul2ByNonresidue(sub2(mul2(add2(s1, s2), o4), e1m)), e0m);
701
+ const e_c1 = sub2(sub2(mul2(add2(o03, o4), add2(s0, s1)), e0m), e1m);
702
+ const e_c2 = add2(sub2(mul2(add2(s0, s2), o03), e0m), e1m);
703
+ return Object.freeze({
704
+ c0: Object.freeze({
705
+ c0: fp2f(add2(mul2ByNonresidue(b_c2), a_c0)),
706
+ c1: fp2f(add2(b_c0, a_c1)),
707
+ c2: fp2f(add2(b_c1, a_c2)),
708
+ }),
709
+ c1: Object.freeze({
710
+ c0: fp2f(sub2(sub2(e_c0, a_c0), b_c0)),
711
+ c1: fp2f(sub2(sub2(e_c1, a_c1), b_c1)),
712
+ c2: fp2f(sub2(sub2(e_c2, a_c2), b_c2)),
713
+ }),
714
+ });
715
+ };
716
+ // Applies sparse multiplication as line function
717
+ let lineFunction;
718
+ if (twistType === 'multiplicative') {
719
+ lineFunction = (c0, c1, c2, f, Px, Py) => mul014ByLine(f, c0, c1, c2, Px, Py);
720
+ }
721
+ else if (twistType === 'divisive') {
722
+ // NOTE: it should be [c0, c1, c2], but we use different order here to reduce complexity of
723
+ // precompute calculations.
724
+ lineFunction = (c0, c1, c2, f, Px, Py) => mul034ByLine(f, c2, c1, c0, Px, Py);
725
+ }
726
+ else
727
+ throw new Error('bls: unknown twist type');
728
+ const Fp2div2 = Fp2.div(Fp2.ONE, Fp2.mul(Fp2.ONE, _2n$2));
729
+ function pointDouble(ell, Rx, Ry, Rz) {
730
+ const t0 = Fp2.sqr(Ry); // Ry²
731
+ const t1 = Fp2.sqr(Rz); // Rz²
732
+ const t2 = Fp2.mulByB(Fp2.mul(t1, _3n$2)); // 3 * T1 * B
733
+ const t3 = Fp2.mul(t2, _3n$2); // 3 * T2
734
+ const t4 = Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(Ry, Rz)), t1), t0); // (Ry + Rz)² - T1 - T0
735
+ const c0 = Fp2.sub(t2, t0); // T2 - T0 (i)
736
+ const c1 = Fp2.mul(Fp2.sqr(Rx), _3n$2); // 3 * Rx²
737
+ const c2 = Fp2.neg(t4); // -T4 (-h)
738
+ ell.push([c0, c1, c2]);
739
+ Rx = Fp2.mul(Fp2.mul(Fp2.mul(Fp2.sub(t0, t3), Rx), Ry), Fp2div2); // ((T0 - T3) * Rx * Ry) / 2
740
+ // ((T0 + T3) / 2)² - 3 * T2²
741
+ Ry = Fp2.sub(Fp2.sqr(Fp2.mul(Fp2.add(t0, t3), Fp2div2)), Fp2.mul(Fp2.sqr(t2), _3n$2));
742
+ Rz = Fp2.mul(t0, t4); // T0 * T4
743
+ return { Rx, Ry, Rz };
744
+ }
745
+ function pointAdd(ell, Rx, Ry, Rz, Qx, Qy) {
746
+ // Addition
747
+ const t0 = Fp2.sub(Ry, Fp2.mul(Qy, Rz)); // Ry - Qy * Rz
748
+ const t1 = Fp2.sub(Rx, Fp2.mul(Qx, Rz)); // Rx - Qx * Rz
749
+ const c0 = Fp2.sub(Fp2.mul(t0, Qx), Fp2.mul(t1, Qy)); // T0 * Qx - T1 * Qy == Ry * Qx - Rx * Qy
750
+ const c1 = Fp2.neg(t0); // -T0 == Qy * Rz - Ry
751
+ const c2 = t1; // == Rx - Qx * Rz
752
+ ell.push([c0, c1, c2]);
753
+ const t2 = Fp2.sqr(t1); // T1²
754
+ const t3 = Fp2.mul(t2, t1); // T2 * T1
755
+ const t4 = Fp2.mul(t2, Rx); // T2 * Rx
756
+ // T3 - 2 * T4 + T0² * Rz
757
+ const t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4, _2n$2)), Fp2.mul(Fp2.sqr(t0), Rz));
758
+ Rx = Fp2.mul(t1, t5); // T1 * T5
759
+ Ry = Fp2.sub(Fp2.mul(Fp2.sub(t4, t5), t0), Fp2.mul(t3, Ry)); // (T4 - T5) * T0 - T3 * Ry
760
+ Rz = Fp2.mul(Rz, t3); // Rz * T3
761
+ return { Rx, Ry, Rz };
762
+ }
181
763
  // Pre-compute coefficients for sparse multiplication
182
764
  // Point addition and point double calculations is reused for coefficients
183
- function calcPairingPrecomputes(p) {
184
- const { x, y } = p;
765
+ // pointAdd happens only if bit set, so wNAF is reasonable. Unfortunately we cannot combine
766
+ // add + double in windowed precomputes here, otherwise it would be single op (since X is static)
767
+ const ATE_NAF = NAfDecomposition(ateLoopSize);
768
+ const calcPairingPrecomputes = (point) => {
769
+ if (!(point instanceof G2))
770
+ throw new TypeError('"point" expected G2 point, got type=' + typeof point);
771
+ const p = point;
772
+ const { x, y } = p.toAffine();
185
773
  // prettier-ignore
186
- const Qx = x, Qy = y, Qz = Fp2.ONE;
774
+ const Qx = x, Qy = y, negQy = Fp2.neg(y);
187
775
  // prettier-ignore
188
- let Rx = Qx, Ry = Qy, Rz = Qz;
189
- let ell_coeff = [];
190
- for (let i = BLS_X_LEN - 2; i >= 0; i--) {
191
- // Double
192
- let t0 = Fp2.sqr(Ry); // Ry²
193
- let t1 = Fp2.sqr(Rz); // Rz²
194
- let t2 = Fp2.multiplyByB(Fp2.mul(t1, _3n$1)); // 3 * T1 * B
195
- let t3 = Fp2.mul(t2, _3n$1); // 3 * T2
196
- let t4 = Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(Ry, Rz)), t1), t0); // (Ry + Rz)² - T1 - T0
197
- ell_coeff.push([
198
- Fp2.sub(t2, t0),
199
- Fp2.mul(Fp2.sqr(Rx), _3n$1),
200
- Fp2.neg(t4), // -T4
201
- ]);
202
- Rx = Fp2.div(Fp2.mul(Fp2.mul(Fp2.sub(t0, t3), Rx), Ry), _2n$1); // ((T0 - T3) * Rx * Ry) / 2
203
- Ry = Fp2.sub(Fp2.sqr(Fp2.div(Fp2.add(t0, t3), _2n$1)), Fp2.mul(Fp2.sqr(t2), _3n$1)); // ((T0 + T3) / 2)² - 3 * T2²
204
- Rz = Fp2.mul(t0, t4); // T0 * T4
205
- if (bitGet(CURVE.params.x, i)) {
206
- // Addition
207
- let t0 = Fp2.sub(Ry, Fp2.mul(Qy, Rz)); // Ry - Qy * Rz
208
- let t1 = Fp2.sub(Rx, Fp2.mul(Qx, Rz)); // Rx - Qx * Rz
209
- ell_coeff.push([
210
- Fp2.sub(Fp2.mul(t0, Qx), Fp2.mul(t1, Qy)),
211
- Fp2.neg(t0),
212
- t1, // T1
213
- ]);
214
- let t2 = Fp2.sqr(t1); // T1²
215
- let t3 = Fp2.mul(t2, t1); // T2 * T1
216
- let t4 = Fp2.mul(t2, Rx); // T2 * Rx
217
- let t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4, _2n$1)), Fp2.mul(Fp2.sqr(t0), Rz)); // T3 - 2 * T4 + T0² * Rz
218
- Rx = Fp2.mul(t1, t5); // T1 * T5
219
- Ry = Fp2.sub(Fp2.mul(Fp2.sub(t4, t5), t0), Fp2.mul(t3, Ry)); // (T4 - T5) * T0 - T3 * Ry
220
- Rz = Fp2.mul(Rz, t3); // Rz * T3
221
- }
776
+ let Rx = Qx, Ry = Qy, Rz = Fp2.ONE;
777
+ const ell = [];
778
+ for (const bit of ATE_NAF) {
779
+ const cur = [];
780
+ ({ Rx, Ry, Rz } = pointDouble(cur, Rx, Ry, Rz));
781
+ if (bit)
782
+ ({ Rx, Ry, Rz } = pointAdd(cur, Rx, Ry, Rz, Qx, bit === -1 ? negQy : Qy));
783
+ ell.push(cur);
222
784
  }
223
- return ell_coeff;
224
- }
225
- function millerLoop(ell, g1) {
226
- const { x } = CURVE.params;
227
- const Px = g1[0];
228
- const Py = g1[1];
785
+ if (postPrecompute) {
786
+ const last = ell[ell.length - 1];
787
+ postPrecompute(Rx, Ry, Rz, Qx, Qy, pointAdd.bind(null, last));
788
+ }
789
+ return ell;
790
+ };
791
+ function millerLoopBatch(pairs, withFinalExponent = false) {
792
+ aarray(pairs, 'pairs', (pair, title) => {
793
+ aarray(pair, title);
794
+ if (pair.length !== 3)
795
+ throw new TypeError(`"${title}" expected precompute tuple`);
796
+ aarray(pair[0], title + '[0]');
797
+ });
229
798
  let f12 = Fp12.ONE;
230
- for (let j = 0, i = BLS_X_LEN - 2; i >= 0; i--, j++) {
231
- const E = ell[j];
232
- f12 = Fp12.multiplyBy014(f12, E[0], Fp2.mul(E[1], Px), Fp2.mul(E[2], Py));
233
- if (bitGet(x, i)) {
234
- j += 1;
235
- const F = ell[j];
236
- f12 = Fp12.multiplyBy014(f12, F[0], Fp2.mul(F[1], Px), Fp2.mul(F[2], Py));
799
+ if (pairs.length) {
800
+ const ellLen = pairs[0][0].length;
801
+ for (let i = 0; i < ellLen; i++) {
802
+ // sqr only one time for all pairings; skip sqr(ONE) at i=0
803
+ if (i !== 0)
804
+ f12 = Fp12.sqr(f12);
805
+ // NOTE: we apply multiple pairings in parallel here
806
+ for (const [ell, Px, Py] of pairs) {
807
+ for (const [c0, c1, c2] of ell[i])
808
+ f12 = lineFunction(c0, c1, c2, f12, Px, Py);
809
+ }
237
810
  }
238
- if (i !== 0)
239
- f12 = Fp12.sqr(f12);
240
811
  }
241
- return Fp12.conjugate(f12);
812
+ if (xNegative)
813
+ f12 = Fp12.conjugate(f12);
814
+ return withFinalExponent ? Fp12.finalExponentiate(f12) : f12;
815
+ }
816
+ // Calculates product of multiple pairings
817
+ // This up to x2 faster than just `map(({g1, g2})=>pairing({g1,g2}))`
818
+ function pairingBatch(pairs, withFinalExponent = true) {
819
+ aarray(pairs, 'pairs');
820
+ const res = [];
821
+ for (let i = 0; i < pairs.length; i++) {
822
+ const pair = pairs[i];
823
+ validateObject(pair, { g1: 'object', g2: 'object' }, {}, 'pairs[' + i + ']');
824
+ const { g1, g2 } = pair;
825
+ if (!(g1 instanceof G1))
826
+ throw new TypeError('"pairs[' + i + '].g1" expected G1 point, got type=' + typeof g1);
827
+ if (!(g2 instanceof G2))
828
+ throw new TypeError('"pairs[' + i + '].g2" expected G2 point, got type=' + typeof g2);
829
+ // Mathematically, a zero pairing term contributes GT.ONE. We still reject it here because
830
+ // this API mainly backs BLS verification, where ZERO inputs usually mean broken hash /
831
+ // wiring. Silently skipping them would turn those failures into a neutral pairing product.
832
+ // Callers that want the algebraic neutral-element behavior can filter ZERO terms first.
833
+ if (g1.is0() || g2.is0())
834
+ throw new Error('pairing is not available for ZERO point');
835
+ // This uses toAffine inside
836
+ g1.assertValidity();
837
+ g2.assertValidity();
838
+ const Qa = g1.toAffine();
839
+ res.push([calcPairingPrecomputes(g2), Qa.x, Qa.y]);
840
+ }
841
+ return millerLoopBatch(res, withFinalExponent);
242
842
  }
243
- const utils = {
244
- randomPrivateKey: () => {
245
- const length = getMinHashLength(Fr.ORDER);
246
- return mapHashToField(CURVE.randomBytes(length), Fr.ORDER);
247
- },
248
- calcPairingPrecomputes,
249
- };
250
- // Point on G1 curve: (x, y)
251
- const G1_ = weierstrassPoints({ n: Fr.ORDER, ...CURVE.G1 });
252
- const G1 = Object.assign(G1_, createHasher(G1_.ProjectivePoint, CURVE.G1.mapToCurve, {
253
- ...CURVE.htfDefaults,
254
- ...CURVE.G1.htfDefaults,
255
- }));
256
- function pairingPrecomputes(point) {
257
- const p = point;
258
- if (p._PPRECOMPUTES)
259
- return p._PPRECOMPUTES;
260
- p._PPRECOMPUTES = calcPairingPrecomputes(point.toAffine());
261
- return p._PPRECOMPUTES;
262
- }
263
- // TODO: export
264
- // function clearPairingPrecomputes(point: G2) {
265
- // const p = point as G2 & withPairingPrecomputes;
266
- // p._PPRECOMPUTES = undefined;
267
- // }
268
- // Point on G2 curve (complex numbers): (x₁, x₂+i), (y₁, y₂+i)
269
- const G2_ = weierstrassPoints({ n: Fr.ORDER, ...CURVE.G2 });
270
- const G2 = Object.assign(G2_, createHasher(G2_.ProjectivePoint, CURVE.G2.mapToCurve, {
271
- ...CURVE.htfDefaults,
272
- ...CURVE.G2.htfDefaults,
273
- }));
274
- const { Signature } = CURVE.G2;
275
843
  // Calculates bilinear pairing
276
844
  function pairing(Q, P, withFinalExponent = true) {
277
- if (Q.equals(G1.ProjectivePoint.ZERO) || P.equals(G2.ProjectivePoint.ZERO))
278
- throw new Error('pairing is not available for ZERO point');
279
- Q.assertValidity();
280
- P.assertValidity();
281
- // Performance: 9ms for millerLoop and ~14ms for exp.
282
- const Qa = Q.toAffine();
283
- const looped = millerLoop(pairingPrecomputes(P), [Qa.x, Qa.y]);
284
- return withFinalExponent ? Fp12.finalExponentiate(looped) : looped;
285
- }
286
- function normP1(point) {
287
- return point instanceof G1.ProjectivePoint ? point : G1.ProjectivePoint.fromHex(point);
288
- }
289
- function normP2(point) {
290
- return point instanceof G2.ProjectivePoint ? point : Signature.fromHex(point);
291
- }
292
- function normP2Hash(point, htfOpts) {
293
- return point instanceof G2.ProjectivePoint
294
- ? point
295
- : G2.hashToCurve(ensureBytes('point', point), htfOpts);
296
- }
297
- // Multiplies generator by private key.
298
- // P = pk x G
299
- function getPublicKey(privateKey) {
300
- return G1.ProjectivePoint.fromPrivateKey(privateKey).toRawBytes(true);
301
- }
302
- function sign(message, privateKey, htfOpts) {
303
- const msgPoint = normP2Hash(message, htfOpts);
304
- msgPoint.assertValidity();
305
- const sigPoint = msgPoint.multiply(G1.normPrivateKeyToScalar(privateKey));
306
- if (message instanceof G2.ProjectivePoint)
307
- return sigPoint;
308
- return Signature.toRawBytes(sigPoint);
309
- }
310
- // Checks if pairing of public key & hash is equal to pairing of generator & signature.
311
- // e(P, H(m)) == e(G, S)
312
- function verify(signature, message, publicKey, htfOpts) {
313
- const P = normP1(publicKey);
314
- const Hm = normP2Hash(message, htfOpts);
315
- const G = G1.ProjectivePoint.BASE;
316
- const S = normP2(signature);
317
- // Instead of doing 2 exponentiations, we use property of billinear maps
318
- // and do one exp after multiplying 2 points.
319
- const ePHm = pairing(P.negate(), Hm, false);
320
- const eGS = pairing(G, S, false);
321
- const exp = Fp12.finalExponentiate(Fp12.mul(eGS, ePHm));
322
- return Fp12.eql(exp, Fp12.ONE);
323
- }
324
- function aggregatePublicKeys(publicKeys) {
325
- if (!publicKeys.length)
326
- throw new Error('Expected non-empty array');
327
- const agg = publicKeys.map(normP1).reduce((sum, p) => sum.add(p), G1.ProjectivePoint.ZERO);
328
- const aggAffine = agg; //.toAffine();
329
- if (publicKeys[0] instanceof G1.ProjectivePoint) {
330
- aggAffine.assertValidity();
331
- return aggAffine;
332
- }
333
- // toRawBytes ensures point validity
334
- return aggAffine.toRawBytes(true);
335
- }
336
- function aggregateSignatures(signatures) {
337
- if (!signatures.length)
338
- throw new Error('Expected non-empty array');
339
- const agg = signatures.map(normP2).reduce((sum, s) => sum.add(s), G2.ProjectivePoint.ZERO);
340
- const aggAffine = agg; //.toAffine();
341
- if (signatures[0] instanceof G2.ProjectivePoint) {
342
- aggAffine.assertValidity();
343
- return aggAffine;
344
- }
345
- return Signature.toRawBytes(aggAffine);
845
+ if (!(Q instanceof G1))
846
+ throw new TypeError('"Q" expected G1 point, got type=' + typeof Q);
847
+ if (!(P instanceof G2))
848
+ throw new TypeError('"P" expected G2 point, got type=' + typeof P);
849
+ return pairingBatch([{ g1: Q, g2: P }], withFinalExponent);
346
850
  }
347
- // https://ethresear.ch/t/fast-verification-of-multiple-bls-signatures/5407
348
- // e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))
349
- function verifyBatch(signature, messages, publicKeys, htfOpts) {
350
- // @ts-ignore
351
- // console.log('verifyBatch', bytesToHex(signature as any), messages, publicKeys.map(bytesToHex));
352
- if (!messages.length)
353
- throw new Error('Expected non-empty messages array');
354
- if (publicKeys.length !== messages.length)
355
- throw new Error('Pubkey count should equal msg count');
356
- const sig = normP2(signature);
357
- const nMessages = messages.map((i) => normP2Hash(i, htfOpts));
358
- const nPublicKeys = publicKeys.map(normP1);
359
- try {
851
+ const lengths = {
852
+ seed: getMinHashLength(Fr.ORDER),
853
+ };
854
+ const rand = params.randomBytes === undefined ? randomBytes : params.randomBytes;
855
+ // Seeded calls deterministically reduce exactly `lengths.seed` bytes into `1..Fr.ORDER-1`;
856
+ // omitting `seed` just fills that input buffer from the configured RNG first.
857
+ const randomSecretKey = (seed) => {
858
+ seed = seed === undefined ? rand(lengths.seed) : seed;
859
+ abytes(seed, lengths.seed, 'seed');
860
+ return mapHashToField(seed, Fr.ORDER);
861
+ };
862
+ Object.freeze(lengths);
863
+ return {
864
+ lengths,
865
+ Fr,
866
+ Fp12, // NOTE: we re-export Fp12 here because pairing results are Fp12!
867
+ millerLoopBatch,
868
+ pairing,
869
+ pairingBatch,
870
+ calcPairingPrecomputes,
871
+ randomSecretKey,
872
+ };
873
+ }
874
+ function createBlsSig(blsPairing, PubPoint, SigPoint, isSigG1, hashToSigCurve, SignatureCoder) {
875
+ const { Fr, Fp12, pairingBatch, randomSecretKey, lengths } = blsPairing;
876
+ if (!SignatureCoder) {
877
+ SignatureCoder = {
878
+ fromBytes: notImplemented,
879
+ fromHex: notImplemented,
880
+ toBytes: notImplemented,
881
+ toHex: notImplemented,
882
+ };
883
+ }
884
+ // Internal decoding must use the same owned callback snapshot exposed on `.Signature`.
885
+ // Retaining the caller's coder object would allow verification policy to change later.
886
+ const sigCoder = Object.freeze({ ...SignatureCoder });
887
+ function normPub(point) {
888
+ return point instanceof PubPoint ? point : PubPoint.fromBytes(point);
889
+ }
890
+ function normSig(point) {
891
+ return point instanceof SigPoint ? point : sigCoder.fromBytes(point);
892
+ }
893
+ // Sign/verify here take points already hashed onto the signature subgroup.
894
+ // Raw bytes and points from the other subgroup must fail this constructor-brand
895
+ // check before later validity checks run.
896
+ function amsg(m) {
897
+ if (!(m instanceof SigPoint))
898
+ throw new Error(`expected valid message hashed to ${!isSigG1 ? 'G2' : 'G1'} curve`);
899
+ return m;
900
+ }
901
+ // What matters here is what point pairing API accepts as G1 or G2, not actual size or names
902
+ const pair = !isSigG1
903
+ ? (a, b) => ({ g1: a, g2: b })
904
+ : (a, b) => ({ g1: b, g2: a });
905
+ return Object.freeze({
906
+ lengths: Object.freeze({ ...lengths, secretKey: Fr.BYTES }),
907
+ keygen(seed) {
908
+ const secretKey = randomSecretKey(seed);
909
+ const publicKey = this.getPublicKey(secretKey);
910
+ return { secretKey, publicKey };
911
+ },
912
+ // P = pk x G
913
+ getPublicKey(secretKey) {
914
+ let sec;
915
+ try {
916
+ sec = PubPoint.Fn.fromBytes(secretKey);
917
+ }
918
+ catch (error) {
919
+ // @ts-ignore
920
+ throw new Error('invalid private key: ' + typeof secretKey, { cause: error });
921
+ }
922
+ return PubPoint.BASE.multiply(sec);
923
+ },
924
+ // S = pk x H(m)
925
+ sign(message, secretKey, unusedArg) {
926
+ if (unusedArg != null)
927
+ throw new Error('sign() expects 2 arguments');
928
+ const sec = PubPoint.Fn.fromBytes(secretKey);
929
+ // BLS/BN point APIs allow infinity for compatibility; raw message bytes still fail amsg().
930
+ amsg(message).assertValidity();
931
+ return message.multiply(sec);
932
+ },
933
+ // Checks if pairing of public key & hash is equal to pairing of generator & signature.
934
+ // e(P, H(m)) == e(G, S)
935
+ // e(S, G) == e(H(m), P)
936
+ verify(signature, message, publicKey, unusedArg) {
937
+ if (unusedArg != null)
938
+ throw new Error('verify() expects 3 arguments');
939
+ signature = normSig(signature);
940
+ publicKey = normPub(publicKey);
941
+ const P = publicKey.negate();
942
+ const G = PubPoint.BASE;
943
+ const Hm = amsg(message);
944
+ const S = signature;
945
+ // This code was changed in 1.9.x:
946
+ // Before it was G.negate() in G2, now it's always pubKey.negate
947
+ // e(P, -Q)===e(-P, Q)==e(P, Q)^-1. Negate can be done anywhere (as long it is done once per pair).
948
+ // We just moving sign, but since pairing is multiplicative, we doing X * X^-1 = 1
949
+ try {
950
+ const exp = pairingBatch([pair(P, Hm), pair(G, S)]);
951
+ return Fp12.eql(exp, Fp12.ONE);
952
+ }
953
+ catch {
954
+ return false;
955
+ }
956
+ },
957
+ // https://ethresear.ch/t/fast-verification-of-multiple-bls-signatures/5407
958
+ // e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))
959
+ // TODO: maybe `{message: G2Hex, publicKey: G1Hex}[]` instead?
960
+ verifyBatch(signature, items) {
961
+ aNonEmpty(items);
962
+ const sig = normSig(signature);
963
+ const nMessages = items.map((i) => amsg(i.message));
964
+ const nPublicKeys = items.map((i) => normPub(i.publicKey));
965
+ // NOTE: this works only for exact same object
966
+ const messagePubKeyMap = new Map();
967
+ for (let i = 0; i < nPublicKeys.length; i++) {
968
+ const pub = nPublicKeys[i];
969
+ const msg = nMessages[i];
970
+ let keys = messagePubKeyMap.get(msg);
971
+ if (keys === undefined) {
972
+ keys = [];
973
+ messagePubKeyMap.set(msg, keys);
974
+ }
975
+ keys.push(pub);
976
+ }
360
977
  const paired = [];
361
- for (const message of new Set(nMessages)) {
362
- const groupPublicKey = nMessages.reduce((groupPublicKey, subMessage, i) => subMessage === message ? groupPublicKey.add(nPublicKeys[i]) : groupPublicKey, G1.ProjectivePoint.ZERO);
363
- // const msg = message instanceof PointG2 ? message : await PointG2.hashToCurve(message);
364
- // Possible to batch pairing for same msg with different groupPublicKey here
365
- paired.push(pairing(groupPublicKey, message, false));
978
+ const G = PubPoint.BASE;
979
+ try {
980
+ for (const [msg, keys] of messagePubKeyMap) {
981
+ const groupPublicKey = keys.reduce((acc, msg) => acc.add(msg));
982
+ paired.push(pair(groupPublicKey, msg));
983
+ }
984
+ paired.push(pair(G.negate(), sig));
985
+ return Fp12.eql(pairingBatch(paired), Fp12.ONE);
366
986
  }
367
- paired.push(pairing(G1.ProjectivePoint.BASE.negate(), sig, false));
368
- const product = paired.reduce((a, b) => Fp12.mul(a, b), Fp12.ONE);
369
- const exp = Fp12.finalExponentiate(product);
370
- return Fp12.eql(exp, Fp12.ONE);
371
- }
372
- catch {
373
- return false;
374
- }
375
- }
376
- G1.ProjectivePoint.BASE._setWindowSize(4);
377
- return {
378
- getPublicKey,
379
- sign,
380
- verify,
381
- verifyBatch,
382
- aggregatePublicKeys,
383
- aggregateSignatures,
384
- millerLoop,
987
+ catch {
988
+ return false;
989
+ }
990
+ },
991
+ // Adds a bunch of public key points together.
992
+ // pk1 + pk2 + pk3 = pkA
993
+ aggregatePublicKeys(publicKeys) {
994
+ aNonEmpty(publicKeys);
995
+ publicKeys = publicKeys.map((pub) => normPub(pub));
996
+ const agg = publicKeys.reduce((sum, p) => sum.add(p), PubPoint.ZERO);
997
+ agg.assertValidity();
998
+ return agg;
999
+ },
1000
+ // Adds a bunch of signature points together.
1001
+ // pk1 + pk2 + pk3 = pkA
1002
+ aggregateSignatures(signatures) {
1003
+ aNonEmpty(signatures);
1004
+ signatures = signatures.map((sig) => normSig(sig));
1005
+ const agg = signatures.reduce((sum, s) => sum.add(s), SigPoint.ZERO);
1006
+ agg.assertValidity();
1007
+ return agg;
1008
+ },
1009
+ hash(messageBytes, DST) {
1010
+ abytes(messageBytes);
1011
+ // Only omitted DST uses the default; explicit empty DST must reach normDST validation.
1012
+ const opts = DST === undefined ? undefined : { DST };
1013
+ return hashToSigCurve(messageBytes, opts);
1014
+ },
1015
+ Signature: Object.freeze({ ...sigCoder }),
1016
+ }); /*satisfies Signer */
1017
+ }
1018
+ // NOTE: separate function instead of function override, so we don't depend on hasher in bn254.
1019
+ /**
1020
+ * @param fields - Tower field implementations.
1021
+ * @param G1_Point - G1 point constructor.
1022
+ * @param G2_Point - G2 point constructor.
1023
+ * @param params - Pairing parameters. See {@link BlsPairingParams}.
1024
+ * @returns Pairing-only BLS helpers. The returned pairing surface rejects infinity inputs, while
1025
+ * empty `pairingBatch(...)` calls return the multiplicative identity in GT. This keeps the
1026
+ * low-level pairing API fail-closed for BLS-style callers, where identity points usually signal
1027
+ * broken hash / wiring instead of an intentionally neutral pairing term. This also eagerly
1028
+ * precomputes the G1 base-point table as a performance side effect.
1029
+ * @throws If the pairing parameters or underlying curve helpers are inconsistent. {@link Error}
1030
+ * @example
1031
+ * ```ts
1032
+ * import { blsBasic } from '@noble/curves/abstract/bls.js';
1033
+ * import { bn254 } from '@noble/curves/bn254.js';
1034
+ * // Rebuild the pairing-only helper from a concrete curve's public pieces.
1035
+ * const pair = blsBasic(bn254.fields, bn254.G1.Point, bn254.G2.Point, bn254.params);
1036
+ * const gt = pair.pairing(pair.G1.Point.BASE, pair.G2.Point.BASE);
1037
+ * ```
1038
+ */
1039
+ function blsBasic(fields, G1_Point, G2_Point, params) {
1040
+ // Fields are specific for curve, so for now we'll need to pass them with opts
1041
+ const { Fp, Fr, Fp2, Fp6, Fp12 } = fields;
1042
+ // Point on G1 curve: (x, y)
1043
+ // const G1_Point = weierstrass(CURVE.G1, { Fn: Fr });
1044
+ const G1 = { Point: G1_Point };
1045
+ // Point on G2 curve (complex numbers): (x₁, x₂+i), (y₁, y₂+i)
1046
+ const G2 = { Point: G2_Point };
1047
+ const pairingRes = createBlsPairing(fields, G1_Point, G2_Point, params);
1048
+ const { millerLoopBatch, pairing, pairingBatch, calcPairingPrecomputes, randomSecretKey, lengths, } = pairingRes;
1049
+ G1.Point.BASE.precompute(4);
1050
+ Object.freeze(G1);
1051
+ Object.freeze(G2);
1052
+ return Object.freeze({
1053
+ lengths: Object.freeze(lengths),
1054
+ millerLoopBatch,
385
1055
  pairing,
1056
+ pairingBatch,
386
1057
  G1,
387
1058
  G2,
388
- Signature,
389
- fields: {
390
- Fr,
391
- Fp,
392
- Fp2,
393
- Fp6,
394
- Fp12,
395
- },
396
- params: {
397
- x: CURVE.params.x,
398
- r: CURVE.params.r,
399
- G1b: CURVE.G1.b,
400
- G2b: CURVE.G2.b,
401
- },
402
- utils,
1059
+ fields: Object.freeze({ Fr, Fp, Fp2, Fp6, Fp12 }),
1060
+ params: Object.freeze({
1061
+ ateLoopSize: params.ateLoopSize,
1062
+ xNegative: params.xNegative,
1063
+ twistType: params.twistType,
1064
+ postPrecompute: params.postPrecompute,
1065
+ }),
1066
+ utils: Object.freeze({
1067
+ randomSecretKey,
1068
+ calcPairingPrecomputes,
1069
+ }),
1070
+ });
1071
+ }
1072
+ // We can export this too, but seems there is not much reasons for now? If user wants hasher, they can just create hasher.
1073
+ function blsHashers(fields, G1_Point, G2_Point, params, hasherParams) {
1074
+ const base = blsBasic(fields, G1_Point, G2_Point, params);
1075
+ validateObject(hasherParams, { hasherOpts: 'object', hasherOptsG1: 'object', hasherOptsG2: 'object' }, { mapToG1: 'function', mapToG2: 'function' }, 'hasherParams');
1076
+ // Missing map hooks intentionally fail closed via notImplemented on first hash use.
1077
+ const G1Hasher = createHasher(G1_Point, hasherParams.mapToG1 === undefined ? notImplemented : hasherParams.mapToG1, {
1078
+ ...hasherParams.hasherOpts,
1079
+ ...hasherParams.hasherOptsG1,
1080
+ });
1081
+ const G2Hasher = createHasher(G2_Point, hasherParams.mapToG2 === undefined ? notImplemented : hasherParams.mapToG2, {
1082
+ ...hasherParams.hasherOpts,
1083
+ ...hasherParams.hasherOptsG2,
1084
+ });
1085
+ return Object.freeze({ ...base, G1: G1Hasher, G2: G2Hasher });
1086
+ }
1087
+ // G1_Point: ProjConstructor<bigint>, G2_Point: ProjConstructor<Fp2>,
1088
+ // Rename to blsSignatures?
1089
+ /**
1090
+ * @param fields - Tower field implementations.
1091
+ * @param G1_Point - G1 point constructor.
1092
+ * @param G2_Point - G2 point constructor.
1093
+ * @param params - Pairing parameters. See {@link BlsPairingParams}.
1094
+ * @param hasherParams - Hash-to-curve configuration. See {@link BlsHasherParams}.
1095
+ * @param signatureCoders - Signature codecs.
1096
+ * @returns BLS helpers with signers. The inherited pairing surface still rejects infinity inputs,
1097
+ * and empty `pairingBatch(...)` calls still return the multiplicative identity in GT. Aggregate
1098
+ * verification still requires proof of possession or another rogue-key defense from the caller.
1099
+ * @throws If the pairing, hashing, or signature helpers are configured inconsistently. {@link Error}
1100
+ * @example
1101
+ * ```ts
1102
+ * import { bls } from '@noble/curves/abstract/bls.js';
1103
+ * import { bls12_381 } from '@noble/curves/bls12-381.js';
1104
+ * // Rebuild a signer namespace from a concrete curve.
1105
+ * // Applications usually import bls12_381 directly.
1106
+ * const rebuilt = bls(
1107
+ * bls12_381.fields,
1108
+ * bls12_381.G1.Point,
1109
+ * bls12_381.G2.Point,
1110
+ * bls12_381.params,
1111
+ * {
1112
+ * hasherOpts: bls12_381.G2.defaults,
1113
+ * hasherOptsG1: bls12_381.G1.defaults,
1114
+ * hasherOptsG2: bls12_381.G2.defaults,
1115
+ * },
1116
+ * {}
1117
+ * );
1118
+ * const { secretKey, publicKey } = rebuilt.longSignatures.keygen();
1119
+ * ```
1120
+ */
1121
+ function bls(fields, G1_Point, G2_Point, params, hasherParams, signatureCoders) {
1122
+ const base = blsHashers(fields, G1_Point, G2_Point, params, hasherParams);
1123
+ const pairingRes = {
1124
+ ...base,
1125
+ Fr: base.fields.Fr,
1126
+ Fp12: base.fields.Fp12,
1127
+ calcPairingPrecomputes: base.utils.calcPairingPrecomputes,
1128
+ randomSecretKey: base.utils.randomSecretKey,
403
1129
  };
1130
+ const longSignatures = createBlsSig(pairingRes, G1_Point, G2_Point, false, base.G2.hashToCurve, signatureCoders?.LongSignature);
1131
+ const shortSignatures = createBlsSig(pairingRes, G2_Point, G1_Point, true, base.G1.hashToCurve, signatureCoders?.ShortSignature);
1132
+ return Object.freeze({ ...base, longSignatures, shortSignatures });
404
1133
  }
405
1134
 
1135
+ /**
1136
+ * Towered extension fields.
1137
+ * Rather than implementing a massive 12th-degree extension directly, it is more efficient
1138
+ * to build it up from smaller extensions: a tower of extensions.
1139
+ *
1140
+ * For BLS12-381, the Fp12 field is implemented as a quadratic (degree two) extension,
1141
+ * on top of a cubic (degree three) extension, on top of a quadratic extension of Fp.
1142
+ *
1143
+ * For more info: "Pairings for beginners" by Costello, section 7.3.
1144
+ * @module
1145
+ */
406
1146
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
407
- // bls12-381 pairing-friendly Barreto-Lynn-Scott elliptic curve construction allows to:
408
- // - Construct zk-SNARKs at the 128-bit security
409
- // - Use threshold signatures, which allows a user to sign lots of messages with one signature and
410
- // verify them swiftly in a batch, using Boneh-Lynn-Shacham signature scheme.
411
- //
412
- // The library uses G1 for public keys and G2 for signatures. Support for G1 signatures is planned.
413
- // Compatible with Algorand, Chia, Dfinity, Ethereum, FIL, Zcash. Matches specs
414
- // [pairing-curves-11](https://tools.ietf.org/html/draft-irtf-cfrg-pairing-friendly-curves-11),
415
- // [bls-sigs-04](https:/cfrg-hash-to/tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04),
416
- // [hash-to-curve-12](https://tools.ietf.org/html/draft-irtf--curve-12).
417
- //
418
- // ### Summary
419
- // 1. BLS Relies on Bilinear Pairing (expensive)
420
- // 2. Private Keys: 32 bytes
421
- // 3. Public Keys: 48 bytes: 381 bit affine x coordinate, encoded into 48 big-endian bytes.
422
- // 4. Signatures: 96 bytes: two 381 bit integers (affine x coordinate), encoded into two 48 big-endian byte arrays.
423
- // - The signature is a point on the G2 subgroup, which is defined over a finite field
424
- // with elements twice as big as the G1 curve (G2 is over Fp2 rather than Fp. Fp2 is analogous to the complex numbers).
425
- // 5. The 12 stands for the Embedding degree.
426
- //
427
- // ### Formulas
428
- // - `P = pk x G` - public keys
429
- // - `S = pk x H(m)` - signing
430
- // - `e(P, H(m)) == e(G, S)` - verification using pairings
431
- // - `e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))` - signature aggregation
432
- // Filecoin uses little endian byte arrays for private keys -
433
- // so ensure to reverse byte order if you'll use it with FIL.
434
1147
  // Be friendly to bad ECMAScript parsers by not using bigint literals
435
1148
  // prettier-ignore
436
- const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
437
- // prettier-ignore
438
- const _8n = BigInt(8), _16n = BigInt(16);
439
- // CURVE FIELDS
440
- // Finite field over p.
441
- const Fp_raw = BigInt('0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab');
442
- const Fp = Field(Fp_raw);
443
- // Finite field over r.
444
- // This particular field is not used anywhere in bls12-381, but it is still useful.
445
- const Fr$1 = Field(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001'));
446
- const Fp2Add = ({ c0, c1 }, { c0: r0, c1: r1 }) => ({
447
- c0: Fp.add(c0, r0),
448
- c1: Fp.add(c1, r1),
449
- });
450
- const Fp2Subtract = ({ c0, c1 }, { c0: r0, c1: r1 }) => ({
451
- c0: Fp.sub(c0, r0),
452
- c1: Fp.sub(c1, r1),
453
- });
454
- const Fp2Multiply = ({ c0, c1 }, rhs) => {
455
- if (typeof rhs === 'bigint')
456
- return { c0: Fp.mul(c0, rhs), c1: Fp.mul(c1, rhs) };
457
- // (a+bi)(c+di) = (ac−bd) + (ad+bc)i
458
- const { c0: r0, c1: r1 } = rhs;
459
- let t1 = Fp.mul(c0, r0); // c0 * o0
460
- let t2 = Fp.mul(c1, r1); // c1 * o1
461
- // (T1 - T2) + ((c0 + c1) * (r0 + r1) - (T1 + T2))*i
462
- const o0 = Fp.sub(t1, t2);
463
- const o1 = Fp.sub(Fp.mul(Fp.add(c0, c1), Fp.add(r0, r1)), Fp.add(t1, t2));
464
- return { c0: o0, c1: o1 };
465
- };
466
- const Fp2Square = ({ c0, c1 }) => {
467
- const a = Fp.add(c0, c1);
468
- const b = Fp.sub(c0, c1);
469
- const c = Fp.add(c0, c0);
470
- return { c0: Fp.mul(a, b), c1: Fp.mul(c, c1) };
471
- };
472
- // G2 is the order-q subgroup of E2(Fp²) : y² = x³+4(1+√−1),
473
- // where Fp2 is Fp[√−1]/(x2+1). #E2(Fp2 ) = h2q, where
474
- // G² - 1
475
- // h2q
476
- // NOTE: ORDER was wrong!
477
- const FP2_ORDER = Fp_raw * Fp_raw;
478
- const Fp2 = {
479
- ORDER: FP2_ORDER,
480
- BITS: bitLen(FP2_ORDER),
481
- BYTES: Math.ceil(bitLen(FP2_ORDER) / 8),
482
- MASK: bitMask(bitLen(FP2_ORDER)),
483
- ZERO: { c0: Fp.ZERO, c1: Fp.ZERO },
484
- ONE: { c0: Fp.ONE, c1: Fp.ZERO },
485
- create: (num) => num,
486
- isValid: ({ c0, c1 }) => typeof c0 === 'bigint' && typeof c1 === 'bigint',
487
- is0: ({ c0, c1 }) => Fp.is0(c0) && Fp.is0(c1),
488
- eql: ({ c0, c1 }, { c0: r0, c1: r1 }) => Fp.eql(c0, r0) && Fp.eql(c1, r1),
489
- neg: ({ c0, c1 }) => ({ c0: Fp.neg(c0), c1: Fp.neg(c1) }),
490
- pow: (num, power) => FpPow(Fp2, num, power),
491
- invertBatch: (nums) => FpInvertBatch(Fp2, nums),
1149
+ const _0n$1 = /* @__PURE__ */ BigInt(0), _1n$1 = /* @__PURE__ */ BigInt(1), _2n$1 = /* @__PURE__ */ BigInt(2), _3n$1 = /* @__PURE__ */ BigInt(3), _6n = /* @__PURE__ */ BigInt(6), _12n = /* @__PURE__ */ BigInt(12);
1150
+ const isObj = (value) => !!value && typeof value === 'object';
1151
+ function calcFrobeniusCoefficients(Fp, nonResidue, modulus, degree, num = 1, divisor) {
1152
+ asafenumber(num, 'num');
1153
+ asafenumber(degree, 'degree');
1154
+ const divisorN = divisor === undefined ? degree : divisor;
1155
+ asafenumber(divisorN, 'divisor');
1156
+ const F = Fp;
1157
+ // Generic callers reach this through `__TEST`; validate before bigint operators can throw raw
1158
+ // native RangeError/TypeError diagnostics for malformed tower parameters.
1159
+ if (typeof modulus !== 'bigint' || modulus <= _1n$1)
1160
+ throw new Error('calcFrobeniusCoefficients: expected valid modulus, got ' + modulus);
1161
+ if (degree <= 0)
1162
+ throw new Error('calcFrobeniusCoefficients: expected positive degree, got ' + degree);
1163
+ if (num <= 0)
1164
+ throw new Error('calcFrobeniusCoefficients: expected positive row count, got ' + num);
1165
+ if (divisorN <= 0)
1166
+ throw new Error('calcFrobeniusCoefficients: expected positive divisor, got ' + divisorN);
1167
+ const _divisor = BigInt(divisorN);
1168
+ const towerModulus = modulus ** BigInt(degree);
1169
+ const res = [];
1170
+ // Derive tower-basis multipliers for the `p^k` Frobenius action. The
1171
+ // divisions below are expected to be exact for the chosen tower parameters.
1172
+ for (let i = 0; i < num; i++) {
1173
+ const a = BigInt(i + 1);
1174
+ const powers = [];
1175
+ for (let j = 0, qPower = _1n$1; j < degree; j++) {
1176
+ const numer = a * qPower - a;
1177
+ // Shipped towers divide cleanly here, but generic callers can pick bad
1178
+ // params. Bigint division would floor and derive the wrong Frobenius table.
1179
+ if (numer % _divisor)
1180
+ throw new Error('calcFrobeniusCoefficients: inexact tower exponent');
1181
+ const power = (numer / _divisor) % towerModulus;
1182
+ powers.push(F.pow(nonResidue, power));
1183
+ qPower *= modulus;
1184
+ }
1185
+ res.push(powers);
1186
+ }
1187
+ return res;
1188
+ }
1189
+ // This works same at least for bls12-381, bn254 and bls12-377
1190
+ /**
1191
+ * @param Fp - Base field implementation.
1192
+ * @param Fp2 - Quadratic extension field.
1193
+ * @param base - Twist-specific Frobenius base whose powers yield the `c1` / `c2` constants.
1194
+ * BLS12-381 uses `1 / NONRESIDUE`; BN254 uses `NONRESIDUE`.
1195
+ * @returns Frobenius endomorphism helpers.
1196
+ * @throws If the derived Frobenius constants are inconsistent for the tower. {@link Error}
1197
+ * @example
1198
+ * Build Frobenius endomorphism helpers for a BLS extension tower.
1199
+ *
1200
+ * ```ts
1201
+ * import { psiFrobenius } from '@noble/curves/abstract/tower.js';
1202
+ * import { bls12_381 } from '@noble/curves/bls12-381.js';
1203
+ * const Fp = bls12_381.fields.Fp;
1204
+ * const Fp2 = bls12_381.fields.Fp2;
1205
+ * const frob = psiFrobenius(Fp, Fp2, Fp2.div(Fp2.ONE, Fp2.NONRESIDUE));
1206
+ * const point = frob.G2psi(bls12_381.G2.Point, bls12_381.G2.Point.BASE);
1207
+ * ```
1208
+ */
1209
+ function psiFrobenius(Fp, Fp2, base) {
1210
+ validateField(Fp);
1211
+ validateField(Fp2);
1212
+ validateObject(Fp2, {
1213
+ Fp: 'object',
1214
+ frobeniusMap: 'function',
1215
+ fromBigTuple: 'function',
1216
+ mulByB: 'function',
1217
+ mulByNonresidue: 'function',
1218
+ reim: 'function',
1219
+ Fp4Square: 'function',
1220
+ NONRESIDUE: 'object',
1221
+ }, {});
1222
+ if (!isObj(base) || Array.isArray(base))
1223
+ throw new TypeError('"base" expected Fp2 element, got type=' + typeof base);
1224
+ if (!Fp2.isValid(base))
1225
+ throw new RangeError('"base" expected valid Fp2 element');
1226
+ // GLV endomorphism Ψ(P)
1227
+ const PSI_X = Fp2.pow(base, (Fp.ORDER - _1n$1) / _3n$1); // u^((p-1)/3)
1228
+ const PSI_Y = Fp2.pow(base, (Fp.ORDER - _1n$1) / _2n$1); // u^((p-1)/2)
1229
+ function psi(x, y) {
1230
+ // This x10 faster than previous version in bls12-381
1231
+ const x2 = Fp2.mul(Fp2.frobeniusMap(x, 1), PSI_X);
1232
+ const y2 = Fp2.mul(Fp2.frobeniusMap(y, 1), PSI_Y);
1233
+ return [x2, y2];
1234
+ }
1235
+ // Ψ²(P) endomorphism (psi2(x) = psi(psi(x)))
1236
+ const PSI2_X = Fp2.pow(base, (Fp.ORDER ** _2n$1 - _1n$1) / _3n$1); // u^((p^2 - 1)/3)
1237
+ // Current towers rely on this landing on `-1`, which lets psi2 map `y` with
1238
+ // one negation instead of carrying a separate Frobenius multiplier.
1239
+ const PSI2_Y = Fp2.pow(base, (Fp.ORDER ** _2n$1 - _1n$1) / _2n$1); // u^((p^2 - 1)/2)
1240
+ if (!Fp2.eql(PSI2_Y, Fp2.neg(Fp2.ONE)))
1241
+ throw new Error('psiFrobenius: PSI2_Y!==-1');
1242
+ function psi2(x, y) {
1243
+ return [Fp2.mul(x, PSI2_X), Fp2.neg(y)];
1244
+ }
1245
+ // Map points
1246
+ const mapAffine = (fn) => (c, P) => {
1247
+ if (typeof c !== 'function')
1248
+ throw new TypeError('"c" expected point constructor, got type=' + typeof c);
1249
+ validatePointCons(c);
1250
+ if (!(P instanceof c))
1251
+ throw new TypeError('"P" expected Point instance, got type=' + typeof P);
1252
+ const affine = P.toAffine();
1253
+ const p = fn(affine.x, affine.y);
1254
+ return c.fromAffine({ x: p[0], y: p[1] });
1255
+ };
1256
+ const G2psi = mapAffine(psi);
1257
+ const G2psi2 = mapAffine(psi2);
1258
+ return { psi, psi2, G2psi, G2psi2, PSI_X, PSI_Y, PSI2_X, PSI2_Y };
1259
+ }
1260
+ class _Field2 {
1261
+ ORDER;
1262
+ BITS;
1263
+ BYTES;
1264
+ isLE;
1265
+ ZERO;
1266
+ ONE;
1267
+ Fp;
1268
+ NONRESIDUE;
1269
+ mulByB;
1270
+ Fp_NONRESIDUE;
1271
+ Fp_div2;
1272
+ constructor(Fp, opts = {}) {
1273
+ const { NONRESIDUE = BigInt(-1), FP2_NONRESIDUE, Fp2mulByB } = opts;
1274
+ const ORDER = Fp.ORDER;
1275
+ const FP2_ORDER = ORDER * ORDER;
1276
+ this.Fp = Fp;
1277
+ this.ORDER = FP2_ORDER;
1278
+ this.BITS = bitLen(FP2_ORDER);
1279
+ this.BYTES = Math.ceil(bitLen(FP2_ORDER) / 8);
1280
+ this.isLE = Fp.isLE;
1281
+ this.ZERO = this.create({ c0: Fp.ZERO, c1: Fp.ZERO });
1282
+ this.ONE = this.create({ c0: Fp.ONE, c1: Fp.ZERO });
1283
+ // These knobs only swap constants for the shipped quadratic tower shape:
1284
+ // arithmetic below assumes `u^2 = -1`, and bytes are handled as two adjacent
1285
+ // `Fp` limbs (`fromBytes` / `toBytes` expect the shipped `2 * Fp.BYTES` layout).
1286
+ this.Fp_NONRESIDUE = Fp.create(NONRESIDUE);
1287
+ this.Fp_div2 = Fp.div(Fp.ONE, _2n$1); // 1/2
1288
+ this.NONRESIDUE = this.create({ c0: FP2_NONRESIDUE[0], c1: FP2_NONRESIDUE[1] });
1289
+ // NOTE: no Fp2 FROBENIUS_COEFFICIENTS table: for the shipped `u² = -1` tower the coefficients
1290
+ // are always [1, -1] (x²+1 irreducible forces p ≡ 3 mod 4), so frobeniusMap conjugates
1291
+ // directly and the eager table computation was pure import-time waste.
1292
+ this.mulByB = (num) => {
1293
+ // This config hook is trusted to return a canonical Fp2 value already.
1294
+ // Copy+freeze it to keep the tower immutability invariant without mutating caller objects.
1295
+ const { c0, c1 } = Fp2mulByB(num);
1296
+ return Object.freeze({ c0, c1 });
1297
+ };
1298
+ Object.freeze(this);
1299
+ }
1300
+ fromBigTuple(tuple) {
1301
+ if (!Array.isArray(tuple) || tuple.length !== 2)
1302
+ throw new Error('invalid Fp2.fromBigTuple');
1303
+ const [c0, c1] = tuple;
1304
+ if (typeof c0 !== 'bigint' || typeof c1 !== 'bigint')
1305
+ throw new Error('invalid Fp2.fromBigTuple');
1306
+ return this.create({ c0, c1 });
1307
+ }
1308
+ create(num) {
1309
+ const { Fp } = this;
1310
+ const c0 = Fp.create(num.c0);
1311
+ const c1 = Fp.create(num.c1);
1312
+ // Bigint field elements are immutable values, and higher-level code relies on
1313
+ // that invariant. Copy+freeze tower values too without mutating caller-owned objects.
1314
+ return Object.freeze({ c0, c1 });
1315
+ }
1316
+ isValid(num) {
1317
+ if (!isObj(num))
1318
+ throw new TypeError('invalid field element: expected object, got ' + typeof num);
1319
+ const { c0, c1 } = num;
1320
+ const { Fp } = this;
1321
+ // Match base-field `isValid(...)`: malformed coordinate types are errors, not a `false`
1322
+ // predicate result.
1323
+ return Fp.isValid(c0) && Fp.isValid(c1);
1324
+ }
1325
+ is0(num) {
1326
+ if (!isObj(num))
1327
+ return false;
1328
+ const { c0, c1 } = num;
1329
+ const { Fp } = this;
1330
+ return Fp.is0(c0) && Fp.is0(c1);
1331
+ }
1332
+ isValidNot0(num) {
1333
+ return !this.is0(num) && this.isValid(num);
1334
+ }
1335
+ eql({ c0, c1 }, { c0: r0, c1: r1 }) {
1336
+ const { Fp } = this;
1337
+ return Fp.eql(c0, r0) && Fp.eql(c1, r1);
1338
+ }
1339
+ neg({ c0, c1 }) {
1340
+ const { Fp } = this;
1341
+ return Object.freeze({ c0: Fp.neg(c0), c1: Fp.neg(c1) });
1342
+ }
1343
+ pow(num, power) {
1344
+ return FpPow(this, num, power);
1345
+ }
1346
+ invertBatch(nums) {
1347
+ return FpInvertBatch(this, nums, true);
1348
+ }
492
1349
  // Normalized
493
- add: Fp2Add,
494
- sub: Fp2Subtract,
495
- mul: Fp2Multiply,
496
- sqr: Fp2Square,
1350
+ add(f1, f2) {
1351
+ const { Fp } = this;
1352
+ const { c0, c1 } = f1;
1353
+ const { c0: r0, c1: r1 } = f2;
1354
+ return Object.freeze({
1355
+ c0: Fp.add(c0, r0),
1356
+ c1: Fp.add(c1, r1),
1357
+ });
1358
+ }
1359
+ sub({ c0, c1 }, { c0: r0, c1: r1 }) {
1360
+ const { Fp } = this;
1361
+ return Object.freeze({
1362
+ c0: Fp.sub(c0, r0),
1363
+ c1: Fp.sub(c1, r1),
1364
+ });
1365
+ }
1366
+ mul({ c0, c1 }, rhs) {
1367
+ const { Fp } = this;
1368
+ if (typeof rhs === 'bigint')
1369
+ return Object.freeze({ c0: Fp.mul(c0, rhs), c1: Fp.mul(c1, rhs) });
1370
+ // (a+bi)(c+di) = (ac−bd) + (ad+bc)i
1371
+ const { c0: r0, c1: r1 } = rhs;
1372
+ let t1 = Fp.mul(c0, r0); // c0 * o0
1373
+ let t2 = Fp.mul(c1, r1); // c1 * o1
1374
+ // (T1 - T2) + ((c0 + c1) * (r0 + r1) - (T1 + T2))*i
1375
+ const o0 = Fp.sub(t1, t2);
1376
+ const o1 = Fp.sub(Fp.mul(Fp.add(c0, c1), Fp.add(r0, r1)), Fp.add(t1, t2));
1377
+ return Object.freeze({ c0: o0, c1: o1 });
1378
+ }
1379
+ sqr({ c0, c1 }) {
1380
+ const { Fp } = this;
1381
+ const a = Fp.add(c0, c1);
1382
+ const b = Fp.sub(c0, c1);
1383
+ const c = Fp.add(c0, c0);
1384
+ return Object.freeze({ c0: Fp.mul(a, b), c1: Fp.mul(c, c1) });
1385
+ }
497
1386
  // NonNormalized stuff
498
- addN: Fp2Add,
499
- subN: Fp2Subtract,
500
- mulN: Fp2Multiply,
501
- sqrN: Fp2Square,
1387
+ addN(a, b) {
1388
+ return this.add(a, b);
1389
+ }
1390
+ subN(a, b) {
1391
+ return this.sub(a, b);
1392
+ }
1393
+ mulN(a, b) {
1394
+ return this.mul(a, b);
1395
+ }
1396
+ sqrN(a) {
1397
+ return this.sqr(a);
1398
+ }
502
1399
  // Why inversion for bigint inside Fp instead of Fp2? it is even used in that context?
503
- div: (lhs, rhs) => Fp2.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : Fp2.inv(rhs)),
504
- inv: ({ c0: a, c1: b }) => {
1400
+ div(lhs, rhs) {
1401
+ const { Fp } = this;
1402
+ // @ts-ignore
1403
+ return this.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : this.inv(rhs));
1404
+ }
1405
+ inv({ c0: a, c1: b }) {
505
1406
  // We wish to find the multiplicative inverse of a nonzero
506
1407
  // element a + bu in Fp2. We leverage an identity
507
1408
  //
@@ -515,466 +1416,886 @@ const Fp2 = {
515
1416
  // This gives that (a - bu)/(a² + b²) is the inverse
516
1417
  // of (a + bu). Importantly, this can be computing using
517
1418
  // only a single inversion in Fp.
1419
+ const { Fp } = this;
518
1420
  const factor = Fp.inv(Fp.create(a * a + b * b));
519
- return { c0: Fp.mul(factor, Fp.create(a)), c1: Fp.mul(factor, Fp.create(-b)) };
520
- },
521
- sqrt: (num) => {
522
- if (Fp2.eql(num, Fp2.ZERO))
523
- return Fp2.ZERO; // Algo doesn't handles this case
524
- // TODO: Optimize this line. It's extremely slow.
525
- // Speeding this up would boost aggregateSignatures.
526
- // https://eprint.iacr.org/2012/685.pdf applicable?
527
- // https://github.com/zkcrypto/bls12_381/blob/080eaa74ec0e394377caa1ba302c8c121df08b07/src/fp2.rs#L250
528
- // https://github.com/supranational/blst/blob/aae0c7d70b799ac269ff5edf29d8191dbd357876/src/exp2.c#L1
529
- // Inspired by https://github.com/dalek-cryptography/curve25519-dalek/blob/17698df9d4c834204f83a3574143abacb4fc81a5/src/field.rs#L99
530
- const candidateSqrt = Fp2.pow(num, (Fp2.ORDER + _8n) / _16n);
531
- const check = Fp2.div(Fp2.sqr(candidateSqrt), num); // candidateSqrt.square().div(this);
532
- const R = FP2_ROOTS_OF_UNITY;
533
- const divisor = [R[0], R[2], R[4], R[6]].find((r) => Fp2.eql(r, check));
534
- if (!divisor)
535
- throw new Error('No root');
536
- const index = R.indexOf(divisor);
537
- const root = R[index / 2];
538
- if (!root)
539
- throw new Error('Invalid root');
540
- const x1 = Fp2.div(candidateSqrt, root);
1421
+ return Object.freeze({ c0: Fp.mul(factor, Fp.create(a)), c1: Fp.mul(factor, Fp.create(-b)) });
1422
+ }
1423
+ sqrt(num) {
1424
+ // This is generic for all quadratic extensions (Fp2)
1425
+ const { Fp } = this;
1426
+ const Fp2 = this;
1427
+ const { c0, c1 } = num;
1428
+ if (Fp.is0(c1)) {
1429
+ // if c0 is quadratic residue
1430
+ if (FpLegendre(Fp, c0) === 1)
1431
+ return Fp2.create({ c0: Fp.sqrt(c0), c1: Fp.ZERO });
1432
+ else
1433
+ return Fp2.create({ c0: Fp.ZERO, c1: Fp.sqrt(Fp.div(c0, this.Fp_NONRESIDUE)) });
1434
+ }
1435
+ const a = Fp.sqrt(Fp.sub(Fp.sqr(c0), Fp.mul(Fp.sqr(c1), this.Fp_NONRESIDUE)));
1436
+ let d = Fp.mul(Fp.add(a, c0), this.Fp_div2);
1437
+ const legendre = FpLegendre(Fp, d);
1438
+ // -1, Quadratic non residue
1439
+ if (legendre === -1)
1440
+ d = Fp.sub(d, a);
1441
+ const a0 = Fp.sqrt(d);
1442
+ const candidateSqrt = Fp2.create({ c0: a0, c1: Fp.div(Fp.mul(c1, this.Fp_div2), a0) });
1443
+ if (!Fp2.eql(Fp2.sqr(candidateSqrt), num))
1444
+ throw new Error('Cannot find square root');
1445
+ // Normalize root: at this point candidateSqrt ** 2 = num, but also -candidateSqrt ** 2 = num
1446
+ const x1 = candidateSqrt;
541
1447
  const x2 = Fp2.neg(x1);
542
1448
  const { re: re1, im: im1 } = Fp2.reim(x1);
543
1449
  const { re: re2, im: im2 } = Fp2.reim(x2);
544
1450
  if (im1 > im2 || (im1 === im2 && re1 > re2))
545
1451
  return x1;
546
1452
  return x2;
547
- },
1453
+ }
548
1454
  // Same as sgn0_m_eq_2 in RFC 9380
549
- isOdd: (x) => {
550
- const { re: x0, im: x1 } = Fp2.reim(x);
551
- const sign_0 = x0 % _2n;
552
- const zero_0 = x0 === _0n;
553
- const sign_1 = x1 % _2n;
554
- return BigInt(sign_0 || (zero_0 && sign_1)) == _1n;
555
- },
1455
+ isOdd(x) {
1456
+ const { re: x0, im: x1 } = this.reim(x);
1457
+ const sign_0 = x0 % _2n$1;
1458
+ const zero_0 = x0 === _0n$1;
1459
+ const sign_1 = x1 % _2n$1;
1460
+ return BigInt(sign_0 || (zero_0 && sign_1)) == _1n$1;
1461
+ }
556
1462
  // Bytes util
557
1463
  fromBytes(b) {
558
- if (b.length !== Fp2.BYTES)
559
- throw new Error(`fromBytes wrong length=${b.length}`);
560
- return { c0: Fp.fromBytes(b.subarray(0, Fp.BYTES)), c1: Fp.fromBytes(b.subarray(Fp.BYTES)) };
561
- },
562
- toBytes: ({ c0, c1 }) => concatBytes(Fp.toBytes(c0), Fp.toBytes(c1)),
563
- cmov: ({ c0, c1 }, { c0: r0, c1: r1 }, c) => ({
564
- c0: Fp.cmov(c0, r0, c),
565
- c1: Fp.cmov(c1, r1, c),
566
- }),
567
- // Specific utils
568
- // toString() {
569
- // return `Fp2(${this.c0} + ${this.c1}×i)`;
570
- // }
571
- reim: ({ c0, c1 }) => ({ re: c0, im: c1 }),
572
- // multiply by u + 1
573
- mulByNonresidue: ({ c0, c1 }) => ({ c0: Fp.sub(c0, c1), c1: Fp.add(c0, c1) }),
574
- multiplyByB: ({ c0, c1 }) => {
575
- let t0 = Fp.mul(c0, _4n); // 4 * c0
576
- let t1 = Fp.mul(c1, _4n); // 4 * c1
577
- // (T0-T1) + (T0+T1)*i
578
- return { c0: Fp.sub(t0, t1), c1: Fp.add(t0, t1) };
579
- },
580
- fromBigTuple: (tuple) => {
581
- if (tuple.length !== 2)
582
- throw new Error('Invalid tuple');
583
- const fps = tuple.map((n) => Fp.create(n));
584
- return { c0: fps[0], c1: fps[1] };
585
- },
586
- frobeniusMap: ({ c0, c1 }, power) => ({
587
- c0,
588
- c1: Fp.mul(c1, FP2_FROBENIUS_COEFFICIENTS[power % 2]),
589
- }),
590
- };
591
- // Finite extension field over irreducible polynominal.
592
- // Fp(u) / (u² - β) where β = -1
593
- const FP2_FROBENIUS_COEFFICIENTS = [
594
- BigInt('0x1'),
595
- BigInt('0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa'),
596
- ].map((item) => Fp.create(item));
597
- // For Fp2 roots of unity.
598
- const rv1 = BigInt('0x6af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09');
599
- // const ev1 =
600
- // BigInt('0x699be3b8c6870965e5bf892ad5d2cc7b0e85a117402dfd83b7f4a947e02d978498255a2aaec0ac627b5afbdf1bf1c90');
601
- // const ev2 =
602
- // BigInt('0x8157cd83046453f5dd0972b6e3949e4288020b5b8a9cc99ca07e27089a2ce2436d965026adad3ef7baba37f2183e9b5');
603
- // const ev3 =
604
- // BigInt('0xab1c2ffdd6c253ca155231eb3e71ba044fd562f6f72bc5bad5ec46a0b7a3b0247cf08ce6c6317f40edbc653a72dee17');
605
- // const ev4 =
606
- // BigInt('0xaa404866706722864480885d68ad0ccac1967c7544b447873cc37e0181271e006df72162a3d3e0287bf597fbf7f8fc1');
607
- // Eighth roots of unity, used for computing square roots in Fp2.
608
- // To verify or re-calculate:
609
- // Array(8).fill(new Fp2([1n, 1n])).map((fp2, k) => fp2.pow(Fp2.ORDER * BigInt(k) / 8n))
610
- const FP2_ROOTS_OF_UNITY = [
611
- [_1n, _0n],
612
- [rv1, -rv1],
613
- [_0n, _1n],
614
- [rv1, rv1],
615
- [-_1n, _0n],
616
- [-rv1, rv1],
617
- [_0n, -_1n],
618
- [-rv1, -rv1],
619
- ].map((pair) => Fp2.fromBigTuple(pair));
620
- const Fp6Add = ({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) => ({
621
- c0: Fp2.add(c0, r0),
622
- c1: Fp2.add(c1, r1),
623
- c2: Fp2.add(c2, r2),
624
- });
625
- const Fp6Subtract = ({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) => ({
626
- c0: Fp2.sub(c0, r0),
627
- c1: Fp2.sub(c1, r1),
628
- c2: Fp2.sub(c2, r2),
629
- });
630
- const Fp6Multiply = ({ c0, c1, c2 }, rhs) => {
631
- if (typeof rhs === 'bigint') {
1464
+ const { Fp } = this;
1465
+ abytes(b);
1466
+ if (b.length !== this.BYTES)
1467
+ throw new Error('fromBytes invalid length=' + b.length);
1468
+ return this.create({
1469
+ c0: Fp.fromBytes(b.subarray(0, Fp.BYTES)),
1470
+ c1: Fp.fromBytes(b.subarray(Fp.BYTES)),
1471
+ });
1472
+ }
1473
+ toBytes({ c0, c1 }) {
1474
+ return concatBytes(this.Fp.toBytes(c0), this.Fp.toBytes(c1));
1475
+ }
1476
+ cmov({ c0, c1 }, { c0: r0, c1: r1 }, c) {
1477
+ const { Fp } = this;
1478
+ return this.create({
1479
+ c0: Fp.cmov(c0, r0, c),
1480
+ c1: Fp.cmov(c1, r1, c),
1481
+ });
1482
+ }
1483
+ reim({ c0, c1 }) {
1484
+ return { re: c0, im: c1 };
1485
+ }
1486
+ Fp4Square(a, b) {
1487
+ const Fp2 = this;
1488
+ const a2 = Fp2.sqr(a);
1489
+ const b2 = Fp2.sqr(b);
632
1490
  return {
633
- c0: Fp2.mul(c0, rhs),
634
- c1: Fp2.mul(c1, rhs),
635
- c2: Fp2.mul(c2, rhs),
1491
+ first: Fp2.add(Fp2.mulByNonresidue(b2), a2), // b² * Nonresidue + a²
1492
+ second: Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(a, b)), a2), b2), // (a + b)² - a² - b²
636
1493
  };
637
1494
  }
638
- const { c0: r0, c1: r1, c2: r2 } = rhs;
639
- const t0 = Fp2.mul(c0, r0); // c0 * o0
640
- const t1 = Fp2.mul(c1, r1); // c1 * o1
641
- const t2 = Fp2.mul(c2, r2); // c2 * o2
642
- return {
643
- // t0 + (c1 + c2) * (r1 * r2) - (T1 + T2) * (u + 1)
644
- c0: Fp2.add(t0, Fp2.mulByNonresidue(Fp2.sub(Fp2.mul(Fp2.add(c1, c2), Fp2.add(r1, r2)), Fp2.add(t1, t2)))),
645
- // (c0 + c1) * (r0 + r1) - (T0 + T1) + T2 * (u + 1)
646
- c1: Fp2.add(Fp2.sub(Fp2.mul(Fp2.add(c0, c1), Fp2.add(r0, r1)), Fp2.add(t0, t1)), Fp2.mulByNonresidue(t2)),
647
- // T1 + (c0 + c2) * (r0 + r2) - T0 + T2
648
- c2: Fp2.sub(Fp2.add(t1, Fp2.mul(Fp2.add(c0, c2), Fp2.add(r0, r2))), Fp2.add(t0, t2)),
649
- };
650
- };
651
- const Fp6Square = ({ c0, c1, c2 }) => {
652
- let t0 = Fp2.sqr(c0); // c0²
653
- let t1 = Fp2.mul(Fp2.mul(c0, c1), _2n); // 2 * c0 * c1
654
- let t3 = Fp2.mul(Fp2.mul(c1, c2), _2n); // 2 * c1 * c2
655
- let t4 = Fp2.sqr(c2); // c2²
656
- return {
657
- c0: Fp2.add(Fp2.mulByNonresidue(t3), t0),
658
- c1: Fp2.add(Fp2.mulByNonresidue(t4), t1),
659
- // T1 + (c0 - c1 + c2)² + T3 - T0 - T4
660
- c2: Fp2.sub(Fp2.sub(Fp2.add(Fp2.add(t1, Fp2.sqr(Fp2.add(Fp2.sub(c0, c1), c2))), t3), t0), t4),
661
- };
662
- };
663
- const Fp6 = {
664
- ORDER: Fp2.ORDER,
665
- BITS: 3 * Fp2.BITS,
666
- BYTES: 3 * Fp2.BYTES,
667
- MASK: bitMask(3 * Fp2.BITS),
668
- ZERO: { c0: Fp2.ZERO, c1: Fp2.ZERO, c2: Fp2.ZERO },
669
- ONE: { c0: Fp2.ONE, c1: Fp2.ZERO, c2: Fp2.ZERO },
670
- create: (num) => num,
671
- isValid: ({ c0, c1, c2 }) => Fp2.isValid(c0) && Fp2.isValid(c1) && Fp2.isValid(c2),
672
- is0: ({ c0, c1, c2 }) => Fp2.is0(c0) && Fp2.is0(c1) && Fp2.is0(c2),
673
- neg: ({ c0, c1, c2 }) => ({ c0: Fp2.neg(c0), c1: Fp2.neg(c1), c2: Fp2.neg(c2) }),
674
- eql: ({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) => Fp2.eql(c0, r0) && Fp2.eql(c1, r1) && Fp2.eql(c2, r2),
675
- sqrt: () => {
676
- throw new Error('Not implemented');
677
- },
1495
+ // multiply by u + 1
1496
+ mulByNonresidue({ c0, c1 }) {
1497
+ const { Fp, NONRESIDUE: nr } = this;
1498
+ if (nr.c0 === Fp.ONE && nr.c1 === Fp.ONE) {
1499
+ return Object.freeze({ c0: Fp.sub(c0, c1), c1: Fp.add(c0, c1) });
1500
+ }
1501
+ if (nr.c1 === Fp.ONE) {
1502
+ return Object.freeze({
1503
+ c0: Fp.sub(Fp.mul(c0, nr.c0), c1),
1504
+ c1: Fp.add(c0, Fp.mul(c1, nr.c0)),
1505
+ });
1506
+ }
1507
+ return this.mul({ c0, c1 }, nr);
1508
+ }
1509
+ frobeniusMap(num, power) {
1510
+ const { c0, c1 } = num;
1511
+ const { Fp } = this;
1512
+ return Object.freeze({ c0, c1: power % 2 === 0 ? c1 : Fp.neg(c1) });
1513
+ }
1514
+ }
1515
+ class _Field6 {
1516
+ ORDER;
1517
+ BITS;
1518
+ BYTES;
1519
+ isLE;
1520
+ ZERO;
1521
+ ONE;
1522
+ Fp2;
1523
+ constructor(Fp2) {
1524
+ this.Fp2 = Fp2;
1525
+ // `IField.ORDER` is the field cardinality `q`; for sextic extensions that is `p^6`.
1526
+ // Generic helpers like Frobenius-style `x^q = x` checks rely on the literal field size here.
1527
+ this.ORDER = Fp2.Fp.ORDER ** _6n;
1528
+ this.BITS = 3 * Fp2.BITS;
1529
+ this.BYTES = 3 * Fp2.BYTES;
1530
+ this.isLE = Fp2.isLE;
1531
+ this.ZERO = this.create({ c0: Fp2.ZERO, c1: Fp2.ZERO, c2: Fp2.ZERO });
1532
+ this.ONE = this.create({ c0: Fp2.ONE, c1: Fp2.ZERO, c2: Fp2.ZERO });
1533
+ Object.freeze(this);
1534
+ }
1535
+ // Most callers never touch Frobenius maps, so keep the sextic tables lazy:
1536
+ // eagerly deriving them dominates `bls12-381.js` / `bn254.js` import time.
1537
+ get FROBENIUS_COEFFICIENTS_1() {
1538
+ const frob = _FROBENIUS_COEFFICIENTS_6.get(this);
1539
+ if (frob)
1540
+ return frob[0];
1541
+ const { Fp2 } = this;
1542
+ const { Fp } = Fp2;
1543
+ const rows = calcFrobeniusCoefficients(Fp2, Fp2.NONRESIDUE, Fp.ORDER, 6, 2, 3);
1544
+ const cache = [Object.freeze(rows[0]), Object.freeze(rows[1])];
1545
+ _FROBENIUS_COEFFICIENTS_6.set(this, cache);
1546
+ return cache[0];
1547
+ }
1548
+ get FROBENIUS_COEFFICIENTS_2() {
1549
+ const frob = _FROBENIUS_COEFFICIENTS_6.get(this);
1550
+ if (frob)
1551
+ return frob[1];
1552
+ void this.FROBENIUS_COEFFICIENTS_1;
1553
+ return _FROBENIUS_COEFFICIENTS_6.get(this)[1];
1554
+ }
1555
+ add({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) {
1556
+ const { Fp2 } = this;
1557
+ return Object.freeze({
1558
+ c0: Fp2.add(c0, r0),
1559
+ c1: Fp2.add(c1, r1),
1560
+ c2: Fp2.add(c2, r2),
1561
+ });
1562
+ }
1563
+ sub({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) {
1564
+ const { Fp2 } = this;
1565
+ return Object.freeze({
1566
+ c0: Fp2.sub(c0, r0),
1567
+ c1: Fp2.sub(c1, r1),
1568
+ c2: Fp2.sub(c2, r2),
1569
+ });
1570
+ }
1571
+ mul({ c0, c1, c2 }, rhs) {
1572
+ const { Fp2 } = this;
1573
+ if (typeof rhs === 'bigint') {
1574
+ return Object.freeze({
1575
+ c0: Fp2.mul(c0, rhs),
1576
+ c1: Fp2.mul(c1, rhs),
1577
+ c2: Fp2.mul(c2, rhs),
1578
+ });
1579
+ }
1580
+ const { c0: r0, c1: r1, c2: r2 } = rhs;
1581
+ const t0 = Fp2.mul(c0, r0); // c0 * o0
1582
+ const t1 = Fp2.mul(c1, r1); // c1 * o1
1583
+ const t2 = Fp2.mul(c2, r2); // c2 * o2
1584
+ return Object.freeze({
1585
+ // t0 + (c1 + c2) * (r1 * r2) - (T1 + T2) * (u + 1)
1586
+ c0: Fp2.add(t0, Fp2.mulByNonresidue(Fp2.sub(Fp2.mul(Fp2.add(c1, c2), Fp2.add(r1, r2)), Fp2.add(t1, t2)))),
1587
+ // (c0 + c1) * (r0 + r1) - (T0 + T1) + T2 * (u + 1)
1588
+ c1: Fp2.add(Fp2.sub(Fp2.mul(Fp2.add(c0, c1), Fp2.add(r0, r1)), Fp2.add(t0, t1)), Fp2.mulByNonresidue(t2)),
1589
+ // T1 + (c0 + c2) * (r0 + r2) - T0 + T2
1590
+ c2: Fp2.sub(Fp2.add(t1, Fp2.mul(Fp2.add(c0, c2), Fp2.add(r0, r2))), Fp2.add(t0, t2)),
1591
+ });
1592
+ }
1593
+ sqr({ c0, c1, c2 }) {
1594
+ const { Fp2 } = this;
1595
+ let t0 = Fp2.sqr(c0); // c0²
1596
+ let t1 = Fp2.mul(Fp2.mul(c0, c1), _2n$1); // 2 * c0 * c1
1597
+ let t3 = Fp2.mul(Fp2.mul(c1, c2), _2n$1); // 2 * c1 * c2
1598
+ let t4 = Fp2.sqr(c2); // c2²
1599
+ return Object.freeze({
1600
+ c0: Fp2.add(Fp2.mulByNonresidue(t3), t0), // T3 * (u + 1) + T0
1601
+ c1: Fp2.add(Fp2.mulByNonresidue(t4), t1), // T4 * (u + 1) + T1
1602
+ // T1 + (c0 - c1 + c2)² + T3 - T0 - T4
1603
+ c2: Fp2.sub(Fp2.sub(Fp2.add(Fp2.add(t1, Fp2.sqr(Fp2.add(Fp2.sub(c0, c1), c2))), t3), t0), t4),
1604
+ });
1605
+ }
1606
+ addN(a, b) {
1607
+ return this.add(a, b);
1608
+ }
1609
+ subN(a, b) {
1610
+ return this.sub(a, b);
1611
+ }
1612
+ mulN(a, b) {
1613
+ return this.mul(a, b);
1614
+ }
1615
+ sqrN(a) {
1616
+ return this.sqr(a);
1617
+ }
1618
+ create(num) {
1619
+ const { Fp2 } = this;
1620
+ const c0 = Fp2.create(num.c0);
1621
+ const c1 = Fp2.create(num.c1);
1622
+ const c2 = Fp2.create(num.c2);
1623
+ return Object.freeze({ c0, c1, c2 });
1624
+ }
1625
+ isValid(num) {
1626
+ if (!isObj(num))
1627
+ throw new TypeError('invalid field element: expected object, got ' + typeof num);
1628
+ const { c0, c1, c2 } = num;
1629
+ const { Fp2 } = this;
1630
+ return Fp2.isValid(c0) && Fp2.isValid(c1) && Fp2.isValid(c2);
1631
+ }
1632
+ is0(num) {
1633
+ if (!isObj(num))
1634
+ return false;
1635
+ const { c0, c1, c2 } = num;
1636
+ const { Fp2 } = this;
1637
+ return Fp2.is0(c0) && Fp2.is0(c1) && Fp2.is0(c2);
1638
+ }
1639
+ isValidNot0(num) {
1640
+ return !this.is0(num) && this.isValid(num);
1641
+ }
1642
+ neg({ c0, c1, c2 }) {
1643
+ const { Fp2 } = this;
1644
+ return Object.freeze({ c0: Fp2.neg(c0), c1: Fp2.neg(c1), c2: Fp2.neg(c2) });
1645
+ }
1646
+ eql({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }) {
1647
+ const { Fp2 } = this;
1648
+ return Fp2.eql(c0, r0) && Fp2.eql(c1, r1) && Fp2.eql(c2, r2);
1649
+ }
1650
+ sqrt(_) {
1651
+ // Sextic extensions can use generic odd-field Tonelli-Shanks, but the helper must work
1652
+ // over `IField<T>` with a quadratic non-residue from Fp6 itself. The current
1653
+ // `mod.tonelliShanks(P)` precomputation only searches integer residues in the base field.
1654
+ return notImplemented();
1655
+ }
678
1656
  // Do we need division by bigint at all? Should be done via order:
679
- div: (lhs, rhs) => Fp6.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : Fp6.inv(rhs)),
680
- pow: (num, power) => FpPow(Fp6, num, power),
681
- invertBatch: (nums) => FpInvertBatch(Fp6, nums),
682
- // Normalized
683
- add: Fp6Add,
684
- sub: Fp6Subtract,
685
- mul: Fp6Multiply,
686
- sqr: Fp6Square,
687
- // NonNormalized stuff
688
- addN: Fp6Add,
689
- subN: Fp6Subtract,
690
- mulN: Fp6Multiply,
691
- sqrN: Fp6Square,
692
- inv: ({ c0, c1, c2 }) => {
1657
+ div(lhs, rhs) {
1658
+ const { Fp2 } = this;
1659
+ const { Fp } = Fp2;
1660
+ return this.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : this.inv(rhs));
1661
+ }
1662
+ pow(num, power) {
1663
+ return FpPow(this, num, power);
1664
+ }
1665
+ invertBatch(nums) {
1666
+ return FpInvertBatch(this, nums, true);
1667
+ }
1668
+ inv({ c0, c1, c2 }) {
1669
+ const { Fp2 } = this;
693
1670
  let t0 = Fp2.sub(Fp2.sqr(c0), Fp2.mulByNonresidue(Fp2.mul(c2, c1))); // c0² - c2 * c1 * (u + 1)
694
1671
  let t1 = Fp2.sub(Fp2.mulByNonresidue(Fp2.sqr(c2)), Fp2.mul(c0, c1)); // c2² * (u + 1) - c0 * c1
695
1672
  let t2 = Fp2.sub(Fp2.sqr(c1), Fp2.mul(c0, c2)); // c1² - c0 * c2
696
1673
  // 1/(((c2 * T1 + c1 * T2) * v) + c0 * T0)
697
1674
  let t4 = Fp2.inv(Fp2.add(Fp2.mulByNonresidue(Fp2.add(Fp2.mul(c2, t1), Fp2.mul(c1, t2))), Fp2.mul(c0, t0)));
698
- return { c0: Fp2.mul(t4, t0), c1: Fp2.mul(t4, t1), c2: Fp2.mul(t4, t2) };
699
- },
1675
+ return Object.freeze({ c0: Fp2.mul(t4, t0), c1: Fp2.mul(t4, t1), c2: Fp2.mul(t4, t2) });
1676
+ }
700
1677
  // Bytes utils
701
- fromBytes: (b) => {
702
- if (b.length !== Fp6.BYTES)
703
- throw new Error(`fromBytes wrong length=${b.length}`);
704
- return {
705
- c0: Fp2.fromBytes(b.subarray(0, Fp2.BYTES)),
706
- c1: Fp2.fromBytes(b.subarray(Fp2.BYTES, 2 * Fp2.BYTES)),
707
- c2: Fp2.fromBytes(b.subarray(2 * Fp2.BYTES)),
708
- };
709
- },
710
- toBytes: ({ c0, c1, c2 }) => concatBytes(Fp2.toBytes(c0), Fp2.toBytes(c1), Fp2.toBytes(c2)),
711
- cmov: ({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }, c) => ({
712
- c0: Fp2.cmov(c0, r0, c),
713
- c1: Fp2.cmov(c1, r1, c),
714
- c2: Fp2.cmov(c2, r2, c),
715
- }),
716
- // Utils
717
- // fromTriple(triple: [Fp2, Fp2, Fp2]) {
718
- // return new Fp6(...triple);
719
- // }
720
- // toString() {
721
- // return `Fp6(${this.c0} + ${this.c1} * v, ${this.c2} * v^2)`;
722
- // }
723
- fromBigSix: (t) => {
724
- if (!Array.isArray(t) || t.length !== 6)
725
- throw new Error('Invalid Fp6 usage');
726
- return {
1678
+ fromBytes(b) {
1679
+ const { Fp2 } = this;
1680
+ abytes(b);
1681
+ if (b.length !== this.BYTES)
1682
+ throw new Error('fromBytes invalid length=' + b.length);
1683
+ const B2 = Fp2.BYTES;
1684
+ return this.create({
1685
+ c0: Fp2.fromBytes(b.subarray(0, B2)),
1686
+ c1: Fp2.fromBytes(b.subarray(B2, B2 * 2)),
1687
+ c2: Fp2.fromBytes(b.subarray(2 * B2)),
1688
+ });
1689
+ }
1690
+ toBytes({ c0, c1, c2 }) {
1691
+ const { Fp2 } = this;
1692
+ return concatBytes(Fp2.toBytes(c0), Fp2.toBytes(c1), Fp2.toBytes(c2));
1693
+ }
1694
+ cmov({ c0, c1, c2 }, { c0: r0, c1: r1, c2: r2 }, c) {
1695
+ const { Fp2 } = this;
1696
+ return this.create({
1697
+ c0: Fp2.cmov(c0, r0, c),
1698
+ c1: Fp2.cmov(c1, r1, c),
1699
+ c2: Fp2.cmov(c2, r2, c),
1700
+ });
1701
+ }
1702
+ fromBigSix(tuple) {
1703
+ const { Fp2 } = this;
1704
+ if (!Array.isArray(tuple) || tuple.length !== 6)
1705
+ throw new Error('invalid Fp6.fromBigSix');
1706
+ for (let i = 0; i < 6; i++)
1707
+ if (typeof tuple[i] !== 'bigint')
1708
+ throw new Error('invalid Fp6.fromBigSix');
1709
+ const t = tuple;
1710
+ return this.create({
727
1711
  c0: Fp2.fromBigTuple(t.slice(0, 2)),
728
1712
  c1: Fp2.fromBigTuple(t.slice(2, 4)),
729
1713
  c2: Fp2.fromBigTuple(t.slice(4, 6)),
730
- };
731
- },
732
- frobeniusMap: ({ c0, c1, c2 }, power) => ({
733
- c0: Fp2.frobeniusMap(c0, power),
734
- c1: Fp2.mul(Fp2.frobeniusMap(c1, power), FP6_FROBENIUS_COEFFICIENTS_1[power % 6]),
735
- c2: Fp2.mul(Fp2.frobeniusMap(c2, power), FP6_FROBENIUS_COEFFICIENTS_2[power % 6]),
736
- }),
737
- mulByNonresidue: ({ c0, c1, c2 }) => ({ c0: Fp2.mulByNonresidue(c2), c1: c0, c2: c1 }),
1714
+ });
1715
+ }
1716
+ frobeniusMap(num, power) {
1717
+ const { c0, c1, c2 } = num;
1718
+ if (power % 6 === 0)
1719
+ return Object.freeze({ c0, c1, c2 });
1720
+ const { Fp2 } = this;
1721
+ return Object.freeze({
1722
+ c0: Fp2.frobeniusMap(c0, power),
1723
+ c1: Fp2.mul(Fp2.frobeniusMap(c1, power), this.FROBENIUS_COEFFICIENTS_1[power % 6]),
1724
+ c2: Fp2.mul(Fp2.frobeniusMap(c2, power), this.FROBENIUS_COEFFICIENTS_2[power % 6]),
1725
+ });
1726
+ }
1727
+ mulByFp2({ c0, c1, c2 }, rhs) {
1728
+ const { Fp2 } = this;
1729
+ return Object.freeze({
1730
+ c0: Fp2.mul(c0, rhs),
1731
+ c1: Fp2.mul(c1, rhs),
1732
+ c2: Fp2.mul(c2, rhs),
1733
+ });
1734
+ }
1735
+ mulByNonresidue({ c0, c1, c2 }) {
1736
+ const { Fp2 } = this;
1737
+ return Object.freeze({ c0: Fp2.mulByNonresidue(c2), c1: c0, c2: c1 });
1738
+ }
738
1739
  // Sparse multiplication
739
- multiplyBy1: ({ c0, c1, c2 }, b1) => ({
740
- c0: Fp2.mulByNonresidue(Fp2.mul(c2, b1)),
741
- c1: Fp2.mul(c0, b1),
742
- c2: Fp2.mul(c1, b1),
743
- }),
1740
+ mul1({ c0, c1, c2 }, b1) {
1741
+ const { Fp2 } = this;
1742
+ return Object.freeze({
1743
+ c0: Fp2.mulByNonresidue(Fp2.mul(c2, b1)),
1744
+ c1: Fp2.mul(c0, b1),
1745
+ c2: Fp2.mul(c1, b1),
1746
+ });
1747
+ }
744
1748
  // Sparse multiplication
745
- multiplyBy01({ c0, c1, c2 }, b0, b1) {
1749
+ mul01({ c0, c1, c2 }, b0, b1) {
1750
+ const { Fp2 } = this;
746
1751
  let t0 = Fp2.mul(c0, b0); // c0 * b0
747
1752
  let t1 = Fp2.mul(c1, b1); // c1 * b1
748
- return {
1753
+ return Object.freeze({
749
1754
  // ((c1 + c2) * b1 - T1) * (u + 1) + T0
750
1755
  c0: Fp2.add(Fp2.mulByNonresidue(Fp2.sub(Fp2.mul(Fp2.add(c1, c2), b1), t1)), t0),
751
1756
  // (b0 + b1) * (c0 + c1) - T0 - T1
752
1757
  c1: Fp2.sub(Fp2.sub(Fp2.mul(Fp2.add(b0, b1), Fp2.add(c0, c1)), t0), t1),
753
1758
  // (c0 + c2) * b0 - T0 + T1
754
1759
  c2: Fp2.add(Fp2.sub(Fp2.mul(Fp2.add(c0, c2), b0), t0), t1),
755
- };
756
- },
757
- multiplyByFp2: ({ c0, c1, c2 }, rhs) => ({
758
- c0: Fp2.mul(c0, rhs),
759
- c1: Fp2.mul(c1, rhs),
760
- c2: Fp2.mul(c2, rhs),
761
- }),
762
- };
763
- const FP6_FROBENIUS_COEFFICIENTS_1 = [
764
- [BigInt('0x1'), BigInt('0x0')],
765
- [
766
- BigInt('0x0'),
767
- BigInt('0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'),
768
- ],
769
- [
770
- BigInt('0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'),
771
- BigInt('0x0'),
772
- ],
773
- [BigInt('0x0'), BigInt('0x1')],
774
- [
775
- BigInt('0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'),
776
- BigInt('0x0'),
777
- ],
778
- [
779
- BigInt('0x0'),
780
- BigInt('0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'),
781
- ],
782
- ].map((pair) => Fp2.fromBigTuple(pair));
783
- const FP6_FROBENIUS_COEFFICIENTS_2 = [
784
- [BigInt('0x1'), BigInt('0x0')],
785
- [
786
- BigInt('0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaad'),
787
- BigInt('0x0'),
788
- ],
789
- [
790
- BigInt('0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'),
791
- BigInt('0x0'),
792
- ],
793
- [
794
- BigInt('0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa'),
795
- BigInt('0x0'),
796
- ],
797
- [
798
- BigInt('0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'),
799
- BigInt('0x0'),
800
- ],
801
- [
802
- BigInt('0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffeffff'),
803
- BigInt('0x0'),
804
- ],
805
- ].map((pair) => Fp2.fromBigTuple(pair));
806
- // The BLS parameter x for BLS12-381
807
- const BLS_X = BigInt('0xd201000000010000');
808
- const BLS_X_LEN = bitLen(BLS_X);
809
- const Fp12Add = ({ c0, c1 }, { c0: r0, c1: r1 }) => ({
810
- c0: Fp6.add(c0, r0),
811
- c1: Fp6.add(c1, r1),
812
- });
813
- const Fp12Subtract = ({ c0, c1 }, { c0: r0, c1: r1 }) => ({
814
- c0: Fp6.sub(c0, r0),
815
- c1: Fp6.sub(c1, r1),
816
- });
817
- const Fp12Multiply = ({ c0, c1 }, rhs) => {
818
- if (typeof rhs === 'bigint')
819
- return { c0: Fp6.mul(c0, rhs), c1: Fp6.mul(c1, rhs) };
820
- let { c0: r0, c1: r1 } = rhs;
821
- let t1 = Fp6.mul(c0, r0); // c0 * r0
822
- let t2 = Fp6.mul(c1, r1); // c1 * r1
823
- return {
824
- c0: Fp6.add(t1, Fp6.mulByNonresidue(t2)),
825
- // (c0 + c1) * (r0 + r1) - (T1 + T2)
826
- c1: Fp6.sub(Fp6.mul(Fp6.add(c0, c1), Fp6.add(r0, r1)), Fp6.add(t1, t2)),
827
- };
828
- };
829
- const Fp12Square = ({ c0, c1 }) => {
830
- let ab = Fp6.mul(c0, c1); // c0 * c1
831
- return {
832
- // (c1 * v + c0) * (c0 + c1) - AB - AB * v
833
- c0: Fp6.sub(Fp6.sub(Fp6.mul(Fp6.add(Fp6.mulByNonresidue(c1), c0), Fp6.add(c0, c1)), ab), Fp6.mulByNonresidue(ab)),
834
- c1: Fp6.add(ab, ab),
835
- }; // AB + AB
836
- };
837
- function Fp4Square(a, b) {
838
- const a2 = Fp2.sqr(a);
839
- const b2 = Fp2.sqr(b);
840
- return {
841
- first: Fp2.add(Fp2.mulByNonresidue(b2), a2),
842
- second: Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(a, b)), a2), b2), // (a + b)² - a² - b²
843
- };
1760
+ });
1761
+ }
844
1762
  }
845
- const Fp12 = {
846
- ORDER: Fp2.ORDER,
847
- BITS: 2 * Fp2.BITS,
848
- BYTES: 2 * Fp2.BYTES,
849
- MASK: bitMask(2 * Fp2.BITS),
850
- ZERO: { c0: Fp6.ZERO, c1: Fp6.ZERO },
851
- ONE: { c0: Fp6.ONE, c1: Fp6.ZERO },
852
- create: (num) => num,
853
- isValid: ({ c0, c1 }) => Fp6.isValid(c0) && Fp6.isValid(c1),
854
- is0: ({ c0, c1 }) => Fp6.is0(c0) && Fp6.is0(c1),
855
- neg: ({ c0, c1 }) => ({ c0: Fp6.neg(c0), c1: Fp6.neg(c1) }),
856
- eql: ({ c0, c1 }, { c0: r0, c1: r1 }) => Fp6.eql(c0, r0) && Fp6.eql(c1, r1),
857
- sqrt: () => {
858
- throw new Error('Not implemented');
859
- },
860
- inv: ({ c0, c1 }) => {
1763
+ // Keep lazy tower caches off-object: field instances stay frozen, and debugger output
1764
+ // stays readable without JS private slots while second/subsequent lookups still hit cache.
1765
+ const _FROBENIUS_COEFFICIENTS_6 = new WeakMap();
1766
+ class _Field12 {
1767
+ ORDER;
1768
+ BITS;
1769
+ BYTES;
1770
+ isLE;
1771
+ ZERO;
1772
+ ONE;
1773
+ Fp6;
1774
+ X_LEN;
1775
+ finalExponentiate;
1776
+ constructor(Fp6, opts) {
1777
+ const { X_LEN, Fp12finalExponentiate } = opts;
1778
+ const { Fp2 } = Fp6;
1779
+ const { Fp } = Fp2;
1780
+ this.Fp6 = Fp6;
1781
+ // `IField.ORDER` is the field cardinality `q`; for degree-12 extensions that is `p^12`.
1782
+ // Keeping `p^2` here breaks generic field identities like `x^q = x` on Fp12.
1783
+ this.ORDER = Fp.ORDER ** _12n;
1784
+ this.BITS = 2 * Fp6.BITS;
1785
+ this.BYTES = 2 * Fp6.BYTES;
1786
+ this.isLE = Fp6.isLE;
1787
+ // Returned tower values are frozen, so larger constants can safely reuse
1788
+ // already-frozen child coefficients instead of cloning them.
1789
+ this.ZERO = this.create({ c0: Fp6.ZERO, c1: Fp6.ZERO });
1790
+ this.ONE = this.create({ c0: Fp6.ONE, c1: Fp6.ZERO });
1791
+ this.X_LEN = X_LEN;
1792
+ this.finalExponentiate = (num) => {
1793
+ const copy2 = ({ c0, c1 }) => Object.freeze({ c0, c1 });
1794
+ const copy6 = ({ c0, c1, c2 }) => Object.freeze({ c0: copy2(c0), c1: copy2(c1), c2: copy2(c2) });
1795
+ // This config hook is trusted to return a canonical Fp12 value already.
1796
+ // Copy+freeze it to keep the tower immutability invariant without mutating caller objects.
1797
+ const res = Fp12finalExponentiate(num);
1798
+ return Object.freeze({ c0: copy6(res.c0), c1: copy6(res.c1) });
1799
+ };
1800
+ Object.freeze(this);
1801
+ }
1802
+ // Keep the degree-12 Frobenius row lazy too; after the first lookup the cached
1803
+ // array is reused exactly like the old eager table.
1804
+ get FROBENIUS_COEFFICIENTS() {
1805
+ const frob = _FROBENIUS_COEFFICIENTS_12.get(this);
1806
+ if (frob)
1807
+ return frob;
1808
+ const { Fp2 } = this.Fp6;
1809
+ const { Fp } = Fp2;
1810
+ const cache = Object.freeze(calcFrobeniusCoefficients(Fp2, Fp2.NONRESIDUE, Fp.ORDER, 12, 1, 6)[0]);
1811
+ _FROBENIUS_COEFFICIENTS_12.set(this, cache);
1812
+ return cache;
1813
+ }
1814
+ create(num) {
1815
+ const { Fp6 } = this;
1816
+ const c0 = Fp6.create(num.c0);
1817
+ const c1 = Fp6.create(num.c1);
1818
+ return Object.freeze({ c0, c1 });
1819
+ }
1820
+ isValid(num) {
1821
+ if (!isObj(num))
1822
+ throw new TypeError('invalid field element: expected object, got ' + typeof num);
1823
+ const { c0, c1 } = num;
1824
+ const { Fp6 } = this;
1825
+ return Fp6.isValid(c0) && Fp6.isValid(c1);
1826
+ }
1827
+ is0(num) {
1828
+ if (!isObj(num))
1829
+ return false;
1830
+ const { c0, c1 } = num;
1831
+ const { Fp6 } = this;
1832
+ return Fp6.is0(c0) && Fp6.is0(c1);
1833
+ }
1834
+ isValidNot0(num) {
1835
+ return !this.is0(num) && this.isValid(num);
1836
+ }
1837
+ neg({ c0, c1 }) {
1838
+ const { Fp6 } = this;
1839
+ return Object.freeze({ c0: Fp6.neg(c0), c1: Fp6.neg(c1) });
1840
+ }
1841
+ eql({ c0, c1 }, { c0: r0, c1: r1 }) {
1842
+ const { Fp6 } = this;
1843
+ return Fp6.eql(c0, r0) && Fp6.eql(c1, r1);
1844
+ }
1845
+ sqrt(_) {
1846
+ // Fp12 is quadratic over Fp6, so a dedicated quadratic-extension sqrt is possible here
1847
+ // once Fp6.sqrt() exists. Without that lower-level sqrt, only a field-generic
1848
+ // Tonelli-Shanks path over Fp12 itself would work.
1849
+ return notImplemented();
1850
+ }
1851
+ inv({ c0, c1 }) {
1852
+ const { Fp6 } = this;
861
1853
  let t = Fp6.inv(Fp6.sub(Fp6.sqr(c0), Fp6.mulByNonresidue(Fp6.sqr(c1)))); // 1 / (c0² - c1² * v)
862
- return { c0: Fp6.mul(c0, t), c1: Fp6.neg(Fp6.mul(c1, t)) }; // ((C0 * T) * T) + (-C1 * T) * w
863
- },
864
- div: (lhs, rhs) => Fp12.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : Fp12.inv(rhs)),
865
- pow: (num, power) => FpPow(Fp12, num, power),
866
- invertBatch: (nums) => FpInvertBatch(Fp12, nums),
1854
+ // ((C0 * T) * T) + (-C1 * T) * w
1855
+ return Object.freeze({ c0: Fp6.mul(c0, t), c1: Fp6.neg(Fp6.mul(c1, t)) });
1856
+ }
1857
+ div(lhs, rhs) {
1858
+ const { Fp6 } = this;
1859
+ const { Fp2 } = Fp6;
1860
+ const { Fp } = Fp2;
1861
+ return this.mul(lhs, typeof rhs === 'bigint' ? Fp.inv(Fp.create(rhs)) : this.inv(rhs));
1862
+ }
1863
+ pow(num, power) {
1864
+ return FpPow(this, num, power);
1865
+ }
1866
+ invertBatch(nums) {
1867
+ return FpInvertBatch(this, nums, true);
1868
+ }
867
1869
  // Normalized
868
- add: Fp12Add,
869
- sub: Fp12Subtract,
870
- mul: Fp12Multiply,
871
- sqr: Fp12Square,
1870
+ add({ c0, c1 }, { c0: r0, c1: r1 }) {
1871
+ const { Fp6 } = this;
1872
+ return Object.freeze({
1873
+ c0: Fp6.add(c0, r0),
1874
+ c1: Fp6.add(c1, r1),
1875
+ });
1876
+ }
1877
+ sub({ c0, c1 }, { c0: r0, c1: r1 }) {
1878
+ const { Fp6 } = this;
1879
+ return Object.freeze({
1880
+ c0: Fp6.sub(c0, r0),
1881
+ c1: Fp6.sub(c1, r1),
1882
+ });
1883
+ }
1884
+ mul({ c0, c1 }, rhs) {
1885
+ const { Fp6 } = this;
1886
+ if (typeof rhs === 'bigint')
1887
+ return Object.freeze({ c0: Fp6.mul(c0, rhs), c1: Fp6.mul(c1, rhs) });
1888
+ let { c0: r0, c1: r1 } = rhs;
1889
+ let t1 = Fp6.mul(c0, r0); // c0 * r0
1890
+ let t2 = Fp6.mul(c1, r1); // c1 * r1
1891
+ return Object.freeze({
1892
+ c0: Fp6.add(t1, Fp6.mulByNonresidue(t2)), // T1 + T2 * v
1893
+ // (c0 + c1) * (r0 + r1) - (T1 + T2)
1894
+ c1: Fp6.sub(Fp6.mul(Fp6.add(c0, c1), Fp6.add(r0, r1)), Fp6.add(t1, t2)),
1895
+ });
1896
+ }
1897
+ sqr({ c0, c1 }) {
1898
+ const { Fp6 } = this;
1899
+ let ab = Fp6.mul(c0, c1); // c0 * c1
1900
+ return Object.freeze({
1901
+ // (c1 * v + c0) * (c0 + c1) - AB - AB * v
1902
+ c0: Fp6.sub(Fp6.sub(Fp6.mul(Fp6.add(Fp6.mulByNonresidue(c1), c0), Fp6.add(c0, c1)), ab), Fp6.mulByNonresidue(ab)),
1903
+ c1: Fp6.add(ab, ab),
1904
+ }); // AB + AB
1905
+ }
872
1906
  // NonNormalized stuff
873
- addN: Fp12Add,
874
- subN: Fp12Subtract,
875
- mulN: Fp12Multiply,
876
- sqrN: Fp12Square,
1907
+ addN(a, b) {
1908
+ return this.add(a, b);
1909
+ }
1910
+ subN(a, b) {
1911
+ return this.sub(a, b);
1912
+ }
1913
+ mulN(a, b) {
1914
+ return this.mul(a, b);
1915
+ }
1916
+ sqrN(a) {
1917
+ return this.sqr(a);
1918
+ }
877
1919
  // Bytes utils
878
- fromBytes: (b) => {
879
- if (b.length !== Fp12.BYTES)
880
- throw new Error(`fromBytes wrong length=${b.length}`);
881
- return {
1920
+ fromBytes(b) {
1921
+ const { Fp6 } = this;
1922
+ abytes(b);
1923
+ if (b.length !== this.BYTES)
1924
+ throw new Error('fromBytes invalid length=' + b.length);
1925
+ return this.create({
882
1926
  c0: Fp6.fromBytes(b.subarray(0, Fp6.BYTES)),
883
1927
  c1: Fp6.fromBytes(b.subarray(Fp6.BYTES)),
884
- };
885
- },
886
- toBytes: ({ c0, c1 }) => concatBytes(Fp6.toBytes(c0), Fp6.toBytes(c1)),
887
- cmov: ({ c0, c1 }, { c0: r0, c1: r1 }, c) => ({
888
- c0: Fp6.cmov(c0, r0, c),
889
- c1: Fp6.cmov(c1, r1, c),
890
- }),
1928
+ });
1929
+ }
1930
+ toBytes({ c0, c1 }) {
1931
+ const { Fp6 } = this;
1932
+ return concatBytes(Fp6.toBytes(c0), Fp6.toBytes(c1));
1933
+ }
1934
+ cmov({ c0, c1 }, { c0: r0, c1: r1 }, c) {
1935
+ const { Fp6 } = this;
1936
+ return this.create({
1937
+ c0: Fp6.cmov(c0, r0, c),
1938
+ c1: Fp6.cmov(c1, r1, c),
1939
+ });
1940
+ }
891
1941
  // Utils
892
1942
  // toString() {
893
- // return `Fp12(${this.c0} + ${this.c1} * w)`;
1943
+ // return '' + 'Fp12(' + this.c0 + this.c1 + '* w');
894
1944
  // },
895
1945
  // fromTuple(c: [Fp6, Fp6]) {
896
1946
  // return new Fp12(...c);
897
1947
  // }
898
- fromBigTwelve: (t) => ({
899
- c0: Fp6.fromBigSix(t.slice(0, 6)),
900
- c1: Fp6.fromBigSix(t.slice(6, 12)),
901
- }),
1948
+ fromBigTwelve(tuple) {
1949
+ const { Fp6 } = this;
1950
+ if (!Array.isArray(tuple) || tuple.length !== 12)
1951
+ throw new Error('invalid Fp12.fromBigTwelve');
1952
+ for (let i = 0; i < 12; i++)
1953
+ if (typeof tuple[i] !== 'bigint')
1954
+ throw new Error('invalid Fp12.fromBigTwelve');
1955
+ const t = tuple;
1956
+ return this.create({
1957
+ c0: Fp6.fromBigSix(t.slice(0, 6)),
1958
+ c1: Fp6.fromBigSix(t.slice(6, 12)),
1959
+ });
1960
+ }
902
1961
  // Raises to q**i -th power
903
1962
  frobeniusMap(lhs, power) {
904
- const r0 = Fp6.frobeniusMap(lhs.c0, power);
1963
+ const p = power % 12;
1964
+ if (p === 0)
1965
+ return Object.freeze({ c0: lhs.c0, c1: lhs.c1 });
1966
+ if (p === 6)
1967
+ return this.conjugate(lhs);
1968
+ const { Fp6 } = this;
1969
+ const { Fp2 } = Fp6;
905
1970
  const { c0, c1, c2 } = Fp6.frobeniusMap(lhs.c1, power);
906
- const coeff = FP12_FROBENIUS_COEFFICIENTS[power % 12];
907
- return {
908
- c0: r0,
909
- c1: Fp6.create({
1971
+ const coeff = this.FROBENIUS_COEFFICIENTS[p];
1972
+ return Object.freeze({
1973
+ c0: Fp6.frobeniusMap(lhs.c0, power),
1974
+ c1: Object.freeze({
910
1975
  c0: Fp2.mul(c0, coeff),
911
1976
  c1: Fp2.mul(c1, coeff),
912
1977
  c2: Fp2.mul(c2, coeff),
913
1978
  }),
914
- };
915
- },
916
- // Sparse multiplication
917
- multiplyBy014: ({ c0, c1 }, o0, o1, o4) => {
918
- let t0 = Fp6.multiplyBy01(c0, o0, o1);
919
- let t1 = Fp6.multiplyBy1(c1, o4);
920
- return {
921
- c0: Fp6.add(Fp6.mulByNonresidue(t1), t0),
922
- // (c1 + c0) * [o0, o1+o4] - T0 - T1
923
- c1: Fp6.sub(Fp6.sub(Fp6.multiplyBy01(Fp6.add(c1, c0), o0, Fp2.add(o1, o4)), t0), t1),
924
- };
925
- },
926
- multiplyByFp2: ({ c0, c1 }, rhs) => ({
927
- c0: Fp6.multiplyByFp2(c0, rhs),
928
- c1: Fp6.multiplyByFp2(c1, rhs),
929
- }),
930
- conjugate: ({ c0, c1 }) => ({ c0, c1: Fp6.neg(c1) }),
1979
+ });
1980
+ }
1981
+ mulByFp2({ c0, c1 }, rhs) {
1982
+ const { Fp6 } = this;
1983
+ return Object.freeze({
1984
+ c0: Fp6.mulByFp2(c0, rhs),
1985
+ c1: Fp6.mulByFp2(c1, rhs),
1986
+ });
1987
+ }
1988
+ conjugate({ c0, c1 }) {
1989
+ // Reuse `c0` by reference and only negate the `w` coefficient.
1990
+ return Object.freeze({ c0, c1: this.Fp6.neg(c1) });
1991
+ }
931
1992
  // A cyclotomic group is a subgroup of Fp^n defined by
932
1993
  // GΦₙ(p) = {α ∈ Fpⁿ : α^Φₙ(p) = 1}
933
1994
  // The result of any pairing is in a cyclotomic subgroup
934
1995
  // https://eprint.iacr.org/2009/565.pdf
935
- _cyclotomicSquare: ({ c0, c1 }) => {
1996
+ // https://eprint.iacr.org/2010/354.pdf
1997
+ _cyclotomicSquare({ c0, c1 }) {
1998
+ const { Fp6 } = this;
1999
+ const { Fp2 } = Fp6;
936
2000
  const { c0: c0c0, c1: c0c1, c2: c0c2 } = c0;
937
2001
  const { c0: c1c0, c1: c1c1, c2: c1c2 } = c1;
938
- const { first: t3, second: t4 } = Fp4Square(c0c0, c1c1);
939
- const { first: t5, second: t6 } = Fp4Square(c1c0, c0c2);
940
- const { first: t7, second: t8 } = Fp4Square(c0c1, c1c2);
941
- let t9 = Fp2.mulByNonresidue(t8); // T8 * (u + 1)
942
- return {
943
- c0: Fp6.create({
944
- c0: Fp2.add(Fp2.mul(Fp2.sub(t3, c0c0), _2n), t3),
945
- c1: Fp2.add(Fp2.mul(Fp2.sub(t5, c0c1), _2n), t5),
946
- c2: Fp2.add(Fp2.mul(Fp2.sub(t7, c0c2), _2n), t7),
2002
+ const { first: t3, second: t4 } = Fp2.Fp4Square(c0c0, c1c1);
2003
+ const { first: t5, second: t6 } = Fp2.Fp4Square(c1c0, c0c2);
2004
+ const { first: t7, second: t8 } = Fp2.Fp4Square(c0c1, c1c2);
2005
+ const t9 = Fp2.mulByNonresidue(t8); // T8 * (u + 1)
2006
+ return Object.freeze({
2007
+ c0: Object.freeze({
2008
+ c0: Fp2.add(Fp2.mul(Fp2.sub(t3, c0c0), _2n$1), t3), // 2 * (T3 - c0c0) + T3
2009
+ c1: Fp2.add(Fp2.mul(Fp2.sub(t5, c0c1), _2n$1), t5), // 2 * (T5 - c0c1) + T5
2010
+ c2: Fp2.add(Fp2.mul(Fp2.sub(t7, c0c2), _2n$1), t7),
2011
+ }), // 2 * (T7 - c0c2) + T7
2012
+ c1: Object.freeze({
2013
+ c0: Fp2.add(Fp2.mul(Fp2.add(t9, c1c0), _2n$1), t9), // 2 * (T9 + c1c0) + T9
2014
+ c1: Fp2.add(Fp2.mul(Fp2.add(t4, c1c1), _2n$1), t4), // 2 * (T4 + c1c1) + T4
2015
+ c2: Fp2.add(Fp2.mul(Fp2.add(t6, c1c2), _2n$1), t6),
947
2016
  }),
948
- c1: Fp6.create({
949
- c0: Fp2.add(Fp2.mul(Fp2.add(t9, c1c0), _2n), t9),
950
- c1: Fp2.add(Fp2.mul(Fp2.add(t4, c1c1), _2n), t4),
951
- c2: Fp2.add(Fp2.mul(Fp2.add(t6, c1c2), _2n), t6),
952
- }),
953
- }; // 2 * (T6 + c1c2) + T6
954
- },
2017
+ }); // 2 * (T6 + c1c2) + T6
2018
+ }
2019
+ // https://eprint.iacr.org/2009/565.pdf
955
2020
  _cyclotomicExp(num, n) {
956
- let z = Fp12.ONE;
957
- for (let i = BLS_X_LEN - 1; i >= 0; i--) {
958
- z = Fp12._cyclotomicSquare(z);
2021
+ // The loop only consumes `X_LEN` bits, so out-of-range exponents would otherwise get silently
2022
+ // truncated (or sign-extended for negatives) instead of matching the caller's requested power.
2023
+ aInRange('cyclotomic exponent', n, _0n$1, _1n$1 << BigInt(this.X_LEN));
2024
+ if (n === _0n$1)
2025
+ return this.ONE;
2026
+ let z = num;
2027
+ for (let i = bitLen(n) - 2; i >= 0; i--) {
2028
+ z = this._cyclotomicSquare(z);
959
2029
  if (bitGet(n, i))
960
- z = Fp12.mul(z, num);
2030
+ z = this.mul(z, num);
961
2031
  }
962
2032
  return z;
2033
+ }
2034
+ }
2035
+ const _FROBENIUS_COEFFICIENTS_12 = new WeakMap();
2036
+ /**
2037
+ * @param opts - Tower construction options. See {@link Tower12Opts}.
2038
+ * @returns BLS tower fields.
2039
+ * @throws If the tower options or derived Frobenius helpers are invalid. {@link Error}
2040
+ * @example
2041
+ * Construct the Fp2/Fp6/Fp12 tower used by a pairing-friendly curve.
2042
+ *
2043
+ * ```ts
2044
+ * import { tower12, type Fp2, type Fp12 } from '@noble/curves/abstract/tower.js';
2045
+ * const fields = tower12({
2046
+ * ORDER: 17n,
2047
+ * X_LEN: 4,
2048
+ * FP2_NONRESIDUE: [1n, 1n],
2049
+ * Fp2mulByB: (num: Fp2) => num,
2050
+ * Fp12finalExponentiate: (num: Fp12) => num,
2051
+ * });
2052
+ * const fp12 = fields.Fp12.ONE;
2053
+ * ```
2054
+ */
2055
+ function tower12(opts) {
2056
+ validateObject(opts, {
2057
+ ORDER: 'bigint',
2058
+ X_LEN: 'number',
2059
+ FP2_NONRESIDUE: 'object',
2060
+ Fp2mulByB: 'function',
2061
+ Fp12finalExponentiate: 'function',
2062
+ }, { NONRESIDUE: 'bigint' });
2063
+ asafenumber(opts.X_LEN, 'X_LEN');
2064
+ if (opts.X_LEN < 1)
2065
+ throw new Error('invalid X_LEN');
2066
+ const nonresidue = opts.FP2_NONRESIDUE;
2067
+ if (!Array.isArray(nonresidue) || nonresidue.length !== 2)
2068
+ throw new Error('invalid FP2_NONRESIDUE');
2069
+ if (typeof nonresidue[0] !== 'bigint' || typeof nonresidue[1] !== 'bigint')
2070
+ throw new Error('invalid FP2_NONRESIDUE');
2071
+ const Fp = Field(opts.ORDER);
2072
+ const Fp2 = new _Field2(Fp, opts);
2073
+ const Fp6 = new _Field6(Fp2);
2074
+ const Fp12 = new _Field12(Fp6, opts);
2075
+ return { Fp, Fp2, Fp6, Fp12 };
2076
+ }
2077
+
2078
+ /**
2079
+ * bls12-381 is pairing-friendly Barreto-Lynn-Scott elliptic curve construction allowing to:
2080
+
2081
+ * Construct zk-SNARKs at the ~120-bit security, as per [Barbulescu-Duquesne 2017](https://hal.science/hal-01534101/file/main.pdf)
2082
+ * Efficiently verify N aggregate signatures with 1 pairing and N ec additions:
2083
+ the Boneh-Lynn-Shacham signature scheme is orders of magnitude more efficient than Schnorr
2084
+
2085
+ BLS can mean 2 different things:
2086
+
2087
+ * Barreto-Lynn-Scott: BLS12, a Pairing Friendly Elliptic Curve
2088
+ * Boneh-Lynn-Shacham: A Signature Scheme.
2089
+
2090
+ ### Summary
2091
+
2092
+ 1. BLS Relies on expensive bilinear pairing
2093
+ 2. Secret Keys: 32 bytes
2094
+ 3. Public Keys: 48 OR 96 bytes - big-endian x coordinate of point on G1 OR G2 curve
2095
+ 4. Signatures: 96 OR 48 bytes - big-endian x coordinate of point on G2 OR G1 curve
2096
+ 5. The 12 stands for the Embedding degree.
2097
+
2098
+ Modes of operation:
2099
+
2100
+ * Long signatures: 48-byte keys + 96-byte sigs (G1 keys + G2 sigs).
2101
+ * Short signatures: 96-byte keys + 48-byte sigs (G2 keys + G1 sigs).
2102
+
2103
+ ### Formulas
2104
+
2105
+ - `P = pk x G` - public keys
2106
+ - `S = pk x H(m)` - signing, uses hash-to-curve on m
2107
+ - `e(P, H(m)) == e(G, S)` - verification using pairings
2108
+ - `e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))` - signature aggregation
2109
+
2110
+ ### Curves
2111
+
2112
+ G1 is ordinary elliptic curve. G2 is extension field curve, think "over complex numbers".
2113
+
2114
+ - G1: y² = x³ + 4
2115
+ - G2: y² = x³ + 4(u + 1) where u = √−1; r-order subgroup of E'(Fp²), M-type twist
2116
+
2117
+ ### Towers
2118
+
2119
+ Pairing G1 + G2 produces element in Fp₁₂, 12-degree polynomial.
2120
+ Fp₁₂ is usually implemented using tower of lower-degree polynomials for speed.
2121
+
2122
+ - Fp₁₂ = Fp₆² => Fp₂³
2123
+ - Fp(u) / (u² - β) where β = -1
2124
+ - Fp₂(v) / (v³ - ξ) where ξ = u + 1
2125
+ - Fp₆(w) / (w² - γ) where γ = v
2126
+ - Fp²[u] = Fp/u²+1
2127
+ - Fp⁶[v] = Fp²/v³-1-u
2128
+ - Fp¹²[w] = Fp⁶/w²-v
2129
+
2130
+ ### Params
2131
+
2132
+ * Embedding degree (k): 12
2133
+ * Seed is sometimes named x or t
2134
+ * t = -15132376222941642752
2135
+ * p = (t-1)² * (t⁴-t²+1)/3 + t
2136
+ * r = t⁴-t²+1
2137
+ * Ate loop size: X
2138
+
2139
+ To verify curve parameters, see
2140
+ [pairing-friendly-curves spec](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-11).
2141
+ Basic math is done over finite fields over p.
2142
+ More complicated math is done over polynominal extension fields.
2143
+
2144
+ ### Compatibility and notes
2145
+ 1. It is compatible with Algorand, Chia, Dfinity, Ethereum, Filecoin, ZEC.
2146
+ Filecoin uses little endian byte arrays for secret keys - make sure to reverse byte order.
2147
+ 2. Make sure to correctly select mode: "long signature" or "short signature".
2148
+ 3. Compatible with specs:
2149
+ RFC 9380,
2150
+ [cfrg-pairing-friendly-curves-11](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-11),
2151
+ [cfrg-bls-signature-05](https://datatracker.ietf.org/doc/draft-irtf-cfrg-bls-signature/).
2152
+
2153
+ *
2154
+ * @module
2155
+ */
2156
+ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2157
+ // Be friendly to bad ECMAScript parsers by not using bigint literals
2158
+ // prettier-ignore
2159
+ const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
2160
+ // To verify math:
2161
+ // https://tools.ietf.org/html/draft-irtf-cfrg-pairing-friendly-curves-11
2162
+ // The BLS parameter x (seed) for BLS12-381. The stored constant is `|x|`; call
2163
+ // sites that need the signed parameter apply the minus sign themselves.
2164
+ // x = -2^63 - 2^62 - 2^60 - 2^57 - 2^48 - 2^16
2165
+ const BLS_X = BigInt('0xd201000000010000');
2166
+ // t = x (called differently in different places)
2167
+ // const t = -BLS_X;
2168
+ const BLS_X_LEN = bitLen(BLS_X);
2169
+ // a=0, b=4
2170
+ // P is characteristic of field Fp, in which curve calculations are done.
2171
+ // p = (t-1)² * (t⁴-t²+1)/3 + t
2172
+ // bls12_381_Fp = (t-1n)**2n * (t**4n - t**2n + 1n) / 3n + t
2173
+ // r*h is curve order, amount of points on curve,
2174
+ // where r is order of prime subgroup and h is cofactor.
2175
+ // r = t⁴-t²+1
2176
+ // r = (t**4n - t**2n + 1n)
2177
+ // cofactor h of G1: (t - 1)²/3, with the signed convention `t = -x`
2178
+ // cofactorG1 = (t-1n)**2n/3n
2179
+ // x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507
2180
+ // y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569
2181
+ const bls12_381_CURVE_G1 = {
2182
+ p: BigInt('0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab'),
2183
+ n: BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001'),
2184
+ h: BigInt('0x396c8c005555e1568c00aaab0000aaab'),
2185
+ a: _0n,
2186
+ b: _4n,
2187
+ Gx: BigInt('0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb'),
2188
+ Gy: BigInt('0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1'),
2189
+ };
2190
+ // CURVE FIELDS
2191
+ // r = z⁴ − z² + 1; CURVE.n from other curves
2192
+ /**
2193
+ * bls12-381 Fr (Fn) field.
2194
+ * `fromBytes()` reduces modulo `q` instead of rejecting non-canonical encodings.
2195
+ */
2196
+ const bls12_381_Fr = Field(bls12_381_CURVE_G1.n, {
2197
+ modFromBytes: true,
2198
+ });
2199
+ // Karabina's G2345 compression for the cyclotomic subgroup. Noble stores Fp12 as
2200
+ // (c0 + c1*w), so (g0, g1, g2, g3, g4, g5) map to (c0.c0, c1.c1, c1.c0, c0.c2, c0.c1, c1.c2).
2201
+ function bls12FromCompressed(g0, g1, { g2, g3, g4, g5 }) {
2202
+ return { c0: { c0: g0, c1: g4, c2: g3 }, c1: { c0: g2, c1: g1, c2: g5 } };
2203
+ }
2204
+ function bls12Compress({ c0, c1 }) {
2205
+ return { g2: c1.c0, g3: c0.c2, g4: c0.c1, g5: c1.c2 };
2206
+ }
2207
+ function bls12CyclotomicSquareCompressed({ g2, g3, g4, g5 }) {
2208
+ const { first: h23c0, second: h23c1 } = Fp2.Fp4Square(g4, g5);
2209
+ const { first: h45c0, second: h45c1 } = Fp2.Fp4Square(g2, g3);
2210
+ const d2 = Fp2.add(g2, g2);
2211
+ const d3 = Fp2.add(g3, g3);
2212
+ const d4 = Fp2.add(g4, g4);
2213
+ const d5 = Fp2.add(g5, g5);
2214
+ return {
2215
+ g2: Fp2.add(Fp2.mul(Fp2.mulByNonresidue(h23c1), _3n), d2),
2216
+ g3: Fp2.sub(Fp2.mul(h23c0, _3n), d3),
2217
+ g4: Fp2.sub(Fp2.mul(h45c0, _3n), d4),
2218
+ g5: Fp2.add(Fp2.mul(h45c1, _3n), d5),
2219
+ };
2220
+ }
2221
+ function bls12RecoverG1Ratio({ g2, g3, g4, g5 }) {
2222
+ if (Fp2.is0(g2))
2223
+ return { num: Fp2.mul(Fp2.mul(g4, g5), _2n), den: g3 };
2224
+ return {
2225
+ num: Fp2.add(Fp2.sub(Fp2.mul(Fp2.sqr(g4), _3n), Fp2.mul(g3, _2n)), Fp2.mulByNonresidue(Fp2.sqr(g5))),
2226
+ den: Fp2.mul(g2, _4n),
2227
+ };
2228
+ }
2229
+ function bls12RecoverG0(g1, { g2, g3, g4, g5 }) {
2230
+ const g3g4 = Fp2.mul(g3, g4);
2231
+ const t = Fp2.add(Fp2.sub(Fp2.mul(Fp2.sub(Fp2.sqr(g1), g3g4), _2n), g3g4), Fp2.mul(g2, g5));
2232
+ return Fp2.add(Fp2.mulByNonresidue(t), Fp2.ONE);
2233
+ }
2234
+ function bls12CyclotomicExpCompressed(num, squarings) {
2235
+ const gs = [];
2236
+ let g = bls12Compress(num);
2237
+ for (const count of squarings) {
2238
+ for (let i = 0; i < count; i++)
2239
+ g = bls12CyclotomicSquareCompressed(g);
2240
+ gs.push(g);
2241
+ }
2242
+ // Karabina decompression is undefined at g2 = g3 = 0. Every element decompressed here lies in
2243
+ // the cyclotomic subgroup GΦ₁₂, where the only such element is the identity: unitarity
2244
+ // (z⋅z^(p⁶) = 1) forces g4² = ξ⋅g5², so g4 = g5 = 0 because ξ is a non-square in Fp2, leaving
2245
+ // z ∈ Fp4* ∩ GΦ₁₂ — trivial since gcd(p⁴−p²+1, p⁴−1) = gcd(3, p²−2) = 1 for p ≡ 1 mod 3.
2246
+ // Handle the identity explicitly instead of relying on invertBatch's passZero mapping the zero
2247
+ // denominator to 0 (which happens to reconstruct ONE, but only by coincidence of formulas).
2248
+ const isOne = gs.map(({ g2, g3 }) => Fp2.is0(g2) && Fp2.is0(g3));
2249
+ const ratios = gs.map(bls12RecoverG1Ratio);
2250
+ const invDens = Fp2.invertBatch(ratios.map(({ den }) => den));
2251
+ const elems = gs.map((compressed, i) => {
2252
+ if (isOne[i])
2253
+ return Fp12.ONE;
2254
+ const g1 = Fp2.mul(ratios[i].num, invDens[i]);
2255
+ return bls12FromCompressed(bls12RecoverG0(g1, compressed), g1, compressed);
2256
+ });
2257
+ return { result: Fp12.mul(Fp12.mul(elems[0], elems[1]), elems[2]), last: elems[2] };
2258
+ }
2259
+ function bls12CyclotomicExpX(num) {
2260
+ // BLS_X = 2^63 + 2^62 + 2^60 + 2^57 + 2^48 + 2^16.
2261
+ const { result, last } = bls12CyclotomicExpCompressed(num, [16, 32, 9]);
2262
+ let r = result;
2263
+ let s = last;
2264
+ for (let i = 0; i < 3; i++)
2265
+ s = Fp12._cyclotomicSquare(s);
2266
+ r = Fp12.mul(r, s);
2267
+ for (let i = 0; i < 2; i++)
2268
+ s = Fp12._cyclotomicSquare(s);
2269
+ r = Fp12.mul(r, s);
2270
+ s = Fp12._cyclotomicSquare(s);
2271
+ return Fp12.mul(r, s);
2272
+ }
2273
+ const { Fp, Fp2, Fp6, Fp12 } = tower12({
2274
+ ORDER: bls12_381_CURVE_G1.p,
2275
+ X_LEN: BLS_X_LEN,
2276
+ // Finite extension field over irreducible polynominal.
2277
+ // Fp(u) / (u² - β) where β = -1
2278
+ // Public `Fp2.NONRESIDUE` below is the sextic-tower value `(1, 1) = u + 1`;
2279
+ // the quadratic non-residue for the base Fp2 construction is still `-1`.
2280
+ FP2_NONRESIDUE: [_1n, _1n],
2281
+ Fp2mulByB: ({ c0, c1 }) => {
2282
+ const t0 = Fp.mul(c0, _4n);
2283
+ const t1 = Fp.mul(c1, _4n);
2284
+ // (T0-T1) + (T0+T1)*i
2285
+ return { c0: Fp.sub(t0, t1), c1: Fp.add(t0, t1) };
963
2286
  },
964
- // https://eprint.iacr.org/2010/354.pdf
965
- // https://eprint.iacr.org/2009/565.pdf
966
- finalExponentiate: (num) => {
967
- const x = BLS_X;
2287
+ Fp12finalExponentiate: (num) => {
2288
+ const powMinusX = (num) => Fp12.conjugate(bls12CyclotomicExpX(num));
968
2289
  // this^(q⁶) / this
969
2290
  const t0 = Fp12.div(Fp12.frobeniusMap(num, 6), num);
970
2291
  // t0^(q²) * t0
971
2292
  const t1 = Fp12.mul(Fp12.frobeniusMap(t0, 2), t0);
972
- const t2 = Fp12.conjugate(Fp12._cyclotomicExp(t1, x));
2293
+ const t2 = powMinusX(t1);
973
2294
  const t3 = Fp12.mul(Fp12.conjugate(Fp12._cyclotomicSquare(t1)), t2);
974
- const t4 = Fp12.conjugate(Fp12._cyclotomicExp(t3, x));
975
- const t5 = Fp12.conjugate(Fp12._cyclotomicExp(t4, x));
976
- const t6 = Fp12.mul(Fp12.conjugate(Fp12._cyclotomicExp(t5, x)), Fp12._cyclotomicSquare(t2));
977
- const t7 = Fp12.conjugate(Fp12._cyclotomicExp(t6, x));
2295
+ const t4 = powMinusX(t3);
2296
+ const t5 = powMinusX(t4);
2297
+ const t6 = Fp12.mul(powMinusX(t5), Fp12._cyclotomicSquare(t2));
2298
+ const t7 = powMinusX(t6);
978
2299
  const t2_t5_pow_q2 = Fp12.frobeniusMap(Fp12.mul(t2, t5), 2);
979
2300
  const t4_t1_pow_q3 = Fp12.frobeniusMap(Fp12.mul(t4, t1), 3);
980
2301
  const t6_t1c_pow_q1 = Fp12.frobeniusMap(Fp12.mul(t6, Fp12.conjugate(t1)), 1);
@@ -982,57 +2303,353 @@ const Fp12 = {
982
2303
  // (t2 * t5)^(q²) * (t4 * t1)^(q³) * (t6 * t1.conj)^(q^1) * t7 * t3.conj * t1
983
2304
  return Fp12.mul(Fp12.mul(Fp12.mul(t2_t5_pow_q2, t4_t1_pow_q3), t6_t1c_pow_q1), t7_t3c_t1);
984
2305
  },
2306
+ });
2307
+ // GLV endomorphism Ψ(P), for fast cofactor clearing. `Fp2.NONRESIDUE` here is
2308
+ // the tower value `u + 1`, so the Frobenius base passed to psiFrobenius is
2309
+ // `1 / (u + 1)`, and psi2 derives the published `1 / 2^((p - 1) / 3)` constant internally.
2310
+ let frob;
2311
+ const getFrob = () => frob || (frob = psiFrobenius(Fp, Fp2, Fp2.div(Fp2.ONE, Fp2.NONRESIDUE)));
2312
+ // Eager psiFrobenius setup now dominates `bls12-381.js` import, so defer it to
2313
+ // first use. After that these locals are rewritten to the direct helper refs.
2314
+ let G2psi = (c, P) => {
2315
+ const fn = getFrob().G2psi;
2316
+ G2psi = fn;
2317
+ return fn(c, P);
985
2318
  };
986
- const FP12_FROBENIUS_COEFFICIENTS = [
987
- [BigInt('0x1'), BigInt('0x0')],
988
- [
989
- BigInt('0x1904d3bf02bb0667c231beb4202c0d1f0fd603fd3cbd5f4f7b2443d784bab9c4f67ea53d63e7813d8d0775ed92235fb8'),
990
- BigInt('0x00fc3e2b36c4e03288e9e902231f9fb854a14787b6c7b36fec0c8ec971f63c5f282d5ac14d6c7ec22cf78a126ddc4af3'),
991
- ],
992
- [
993
- BigInt('0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffeffff'),
994
- BigInt('0x0'),
995
- ],
996
- [
997
- BigInt('0x135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2'),
998
- BigInt('0x06af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09'),
999
- ],
1000
- [
1001
- BigInt('0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe'),
1002
- BigInt('0x0'),
1003
- ],
1004
- [
1005
- BigInt('0x144e4211384586c16bd3ad4afa99cc9170df3560e77982d0db45f3536814f0bd5871c1908bd478cd1ee605167ff82995'),
1006
- BigInt('0x05b2cfd9013a5fd8df47fa6b48b1e045f39816240c0b8fee8beadf4d8e9c0566c63a3e6e257f87329b18fae980078116'),
1007
- ],
1008
- [
1009
- BigInt('0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa'),
1010
- BigInt('0x0'),
1011
- ],
1012
- [
1013
- BigInt('0x00fc3e2b36c4e03288e9e902231f9fb854a14787b6c7b36fec0c8ec971f63c5f282d5ac14d6c7ec22cf78a126ddc4af3'),
1014
- BigInt('0x1904d3bf02bb0667c231beb4202c0d1f0fd603fd3cbd5f4f7b2443d784bab9c4f67ea53d63e7813d8d0775ed92235fb8'),
1015
- ],
1016
- [
1017
- BigInt('0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac'),
1018
- BigInt('0x0'),
1019
- ],
1020
- [
1021
- BigInt('0x06af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09'),
1022
- BigInt('0x135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2'),
1023
- ],
1024
- [
1025
- BigInt('0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaad'),
1026
- BigInt('0x0'),
1027
- ],
1028
- [
1029
- BigInt('0x05b2cfd9013a5fd8df47fa6b48b1e045f39816240c0b8fee8beadf4d8e9c0566c63a3e6e257f87329b18fae980078116'),
1030
- BigInt('0x144e4211384586c16bd3ad4afa99cc9170df3560e77982d0db45f3536814f0bd5871c1908bd478cd1ee605167ff82995'),
1031
- ],
1032
- ].map((n) => Fp2.fromBigTuple(n));
1033
- // END OF CURVE FIELDS
1034
- // HashToCurve
2319
+ let G2psi2 = (c, P) => {
2320
+ const fn = getFrob().G2psi2;
2321
+ G2psi2 = fn;
2322
+ return fn(c, P);
2323
+ };
2324
+ /**
2325
+ * Default hash_to_field / hash-to-curve for BLS.
2326
+ * m: 1 for G1, 2 for G2
2327
+ * k: target security level in bits
2328
+ * hash: any function, e.g. BBS+ uses BLAKE2: see [github](https://github.com/hyperledger/aries-framework-go/issues/2247).
2329
+ * Field/hash parameters come from [section 8.8.2 of RFC 9380](https://www.rfc-editor.org/rfc/rfc9380#section-8.8.2),
2330
+ * but the `DST` / `encodeDST` strings below are the BLS-signature-suite override.
2331
+ */
2332
+ const hasher_opts = Object.freeze({
2333
+ DST: 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_',
2334
+ encodeDST: 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_',
2335
+ p: Fp.ORDER,
2336
+ m: 2,
2337
+ k: 128,
2338
+ expand: 'xmd',
2339
+ hash: sha256,
2340
+ });
2341
+ // a=0, b=4
2342
+ // cofactor h of G2, derived with the signed convention `t = -x`
2343
+ // (t^8 - 4t^7 + 5t^6 - 4t^4 + 6t^3 - 4t^2 - 4t + 13)/9
2344
+ // cofactorG2 = (t**8n - 4n*t**7n + 5n*t**6n - 4n*t**4n + 6n*t**3n - 4n*t**2n - 4n*t+13n)/9n
2345
+ // x = 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758*u + 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160
2346
+ // y = 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582*u + 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905
2347
+ const bls12_381_CURVE_G2 = {
2348
+ p: Fp2.ORDER,
2349
+ n: bls12_381_CURVE_G1.n,
2350
+ h: BigInt('0x5d543a95414e7f1091d50792876a202cd91de4547085abaa68a205b2e5a7ddfa628f1cb4d9e82ef21537e293a6691ae1616ec6e786f0c70cf1c38e31c7238e5'),
2351
+ a: Fp2.ZERO,
2352
+ b: Fp2.fromBigTuple([_4n, _4n]),
2353
+ Gx: Fp2.fromBigTuple([
2354
+ BigInt('0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8'),
2355
+ BigInt('0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e'),
2356
+ ]),
2357
+ Gy: Fp2.fromBigTuple([
2358
+ BigInt('0x0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801'),
2359
+ BigInt('0x0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be'),
2360
+ ]),
2361
+ };
2362
+ // Encoding utils
2363
+ const sortBit = (parts, p) => {
2364
+ for (const part of parts) {
2365
+ if (part !== _0n)
2366
+ return Boolean((part * _2n) / p);
2367
+ }
2368
+ return false;
2369
+ };
2370
+ const fp2 = {
2371
+ // Generic tower bytes use `c0 || c1`, but the BLS12-381 G2 point/signature wire encoding uses
2372
+ // `c1 || c0`, so keep this local wrapper instead of changing generic field serialization.
2373
+ encode({ c0, c1 }) {
2374
+ const { BYTES: L } = Fp;
2375
+ return concatBytes(numberToBytesBE(c1, L), numberToBytesBE(c0, L));
2376
+ },
2377
+ decode(bytes) {
2378
+ const { BYTES: L } = Fp;
2379
+ return Fp2.create({
2380
+ c0: decodeFp(bytes.subarray(L)),
2381
+ c1: decodeFp(bytes.subarray(0, L)),
2382
+ });
2383
+ },
2384
+ };
2385
+ const BaseFp = Fp;
2386
+ function decodeFp(bytes) {
2387
+ return Fp.fromBytes(bytes);
2388
+ }
2389
+ // Keep BLS12-381 point/signature codecs on one control-flow skeleton: the G1/G2
2390
+ // and point/signature variants differ only in field packing, subgroup bytes, and
2391
+ // whether uncompressed form is allowed. Copy-paste decoders were diverging.
2392
+ const coder = (name, Fp, b, encode, decode, yparts) => {
2393
+ const F = Fp;
2394
+ const enc = encode;
2395
+ const dec = decode;
2396
+ const W = F.BYTES;
2397
+ return (allowUncompressed) => ({
2398
+ encode(point, compressed = true) {
2399
+ if (!compressed && !allowUncompressed)
2400
+ throw new Error('invalid signature: expected compressed encoding');
2401
+ const infinity = point.is0();
2402
+ const { x, y } = point.toAffine();
2403
+ const bytes = compressed ? enc(x) : concatBytes(enc(x), enc(y));
2404
+ let sort;
2405
+ if (compressed && !infinity)
2406
+ sort = sortBit(yparts(y), BaseFp.ORDER);
2407
+ return setMask(bytes, { compressed, infinity, sort });
2408
+ },
2409
+ decode(bytes) {
2410
+ const raw = allowUncompressed
2411
+ ? abytes(bytes, undefined, 'point')
2412
+ : abytes(bytes, W, 'signature');
2413
+ const { compressed, infinity, sort, value } = parseMask(raw);
2414
+ if (!allowUncompressed && !compressed)
2415
+ throw new Error('invalid signature: expected compressed encoding');
2416
+ const len = compressed ? W : 2 * W;
2417
+ if (value.length !== len)
2418
+ throw new Error(`invalid ${name} point: expected ${len} bytes`);
2419
+ if (infinity) {
2420
+ // Infinity has a dedicated encoding: after the flag bits are cleared, every
2421
+ // remaining payload byte must be zero.
2422
+ for (const b of value) {
2423
+ if (b)
2424
+ throw new Error(`invalid ${name} point: non-canonical zero`);
2425
+ }
2426
+ return { x: F.ZERO, y: F.ZERO };
2427
+ }
2428
+ const x = dec(compressed ? value : value.subarray(0, W));
2429
+ let y;
2430
+ if (compressed) {
2431
+ y = F.sqrt(F.add(F.pow(x, _3n), b));
2432
+ if (!y)
2433
+ throw new Error(`invalid ${name} point: compressed`);
2434
+ if (sortBit(yparts(y), BaseFp.ORDER) !== sort)
2435
+ y = F.neg(y);
2436
+ }
2437
+ else {
2438
+ y = dec(value.subarray(W));
2439
+ }
2440
+ // The all-zero uncompressed payload must use the infinity flag instead of
2441
+ // decoding as an ordinary affine point.
2442
+ if (!compressed && F.is0(x) && F.is0(y))
2443
+ throw new Error(`invalid ${name} point: uncompressed`);
2444
+ return { x, y };
2445
+ },
2446
+ });
2447
+ };
2448
+ // Internal helper only: it copies before clearing the top flag bits. The
2449
+ // pairing-friendly-curves draft C.2 step 1 rejects 0x20 / 0x60 / 0xe0 because
2450
+ // S_bit must be zero for infinity and for all uncompressed encodings.
2451
+ function validateMask({ compressed, infinity, sort }) {
2452
+ if ((!compressed && !infinity && sort) || // 0010_0000 = 0x20
2453
+ (!compressed && infinity && sort) || // 0110_0000 = 0x60
2454
+ (compressed && infinity && sort) // 1110_0000 = 0xe0
2455
+ )
2456
+ throw new Error('invalid encoding flag');
2457
+ }
2458
+ function parseMask(bytes) {
2459
+ // Copy, so we can remove mask data.
2460
+ bytes = copyBytes(bytes);
2461
+ const mask = bytes[0] & 0b1110_0000;
2462
+ const compressed = !!((mask >> 7) & 1); // compression bit (0b1000_0000)
2463
+ const infinity = !!((mask >> 6) & 1); // point at infinity bit (0b0100_0000)
2464
+ const sort = !!((mask >> 5) & 1); // sort bit (0b0010_0000)
2465
+ validateMask({ compressed, infinity, sort });
2466
+ bytes[0] &= 0b0001_1111; // clear mask (zero first 3 bits)
2467
+ return { compressed, infinity, sort, value: bytes };
2468
+ }
2469
+ // Internal helper only: mutates a non-empty fresh buffer in place and just
2470
+ // sets bits. Keep the same invalid-flag guard as parseMask() so encoders cannot
2471
+ // manufacture states that decoders already reject.
2472
+ function setMask(bytes, mask) {
2473
+ if (bytes[0] & 0b1110_0000)
2474
+ throw new Error('setMask: non-empty mask');
2475
+ validateMask({ compressed: !!mask.compressed, infinity: !!mask.infinity, sort: !!mask.sort });
2476
+ if (mask.compressed)
2477
+ bytes[0] |= 0b1000_0000;
2478
+ if (mask.infinity)
2479
+ bytes[0] |= 0b0100_0000;
2480
+ if (mask.sort)
2481
+ bytes[0] |= 0b0010_0000;
2482
+ return bytes;
2483
+ }
2484
+ const g1coder = coder('G1', Fp, Fp.create(bls12_381_CURVE_G1.b), (x) => numberToBytesBE(x, Fp.BYTES), decodeFp, (y) => [y]);
2485
+ const g1 = { point: g1coder(true), sig: g1coder(false) };
2486
+ const signatureG1ToBytes = (point) => {
2487
+ point.assertValidity();
2488
+ return g1.sig.encode(point);
2489
+ };
2490
+ function signatureG1FromBytes(bytes) {
2491
+ const Point = bls12_381.G1.Point;
2492
+ const point = Point.fromAffine(g1.sig.decode(bytes));
2493
+ point.assertValidity();
2494
+ return point;
2495
+ }
2496
+ const g2coder = coder('G2', Fp2, bls12_381_CURVE_G2.b, fp2.encode, fp2.decode, (y) => [
2497
+ y.c1,
2498
+ y.c0,
2499
+ ]);
2500
+ const g2 = { point: g2coder(true), sig: g2coder(false) };
2501
+ const signatureG2ToBytes = (point) => {
2502
+ point.assertValidity();
2503
+ return g2.sig.encode(point);
2504
+ };
2505
+ function signatureG2FromBytes(bytes) {
2506
+ const Point = bls12_381.G2.Point;
2507
+ const point = Point.fromAffine(g2.sig.decode(bytes));
2508
+ point.assertValidity();
2509
+ return point;
2510
+ }
2511
+ const signatureCoders = {
2512
+ ShortSignature: {
2513
+ fromBytes(bytes) {
2514
+ return signatureG1FromBytes(abytes(bytes));
2515
+ },
2516
+ fromHex(hex) {
2517
+ return signatureG1FromBytes(hexToBytes(hex));
2518
+ },
2519
+ toBytes(point) {
2520
+ return signatureG1ToBytes(point);
2521
+ },
2522
+ // Historical alias: BLS signatures have a single compressed byte format here.
2523
+ toRawBytes(point) {
2524
+ return signatureG1ToBytes(point);
2525
+ },
2526
+ toHex(point) {
2527
+ return bytesToHex(signatureG1ToBytes(point));
2528
+ },
2529
+ },
2530
+ LongSignature: {
2531
+ fromBytes(bytes) {
2532
+ return signatureG2FromBytes(abytes(bytes));
2533
+ },
2534
+ fromHex(hex) {
2535
+ return signatureG2FromBytes(hexToBytes(hex));
2536
+ },
2537
+ toBytes(point) {
2538
+ return signatureG2ToBytes(point);
2539
+ },
2540
+ // Historical alias: BLS signatures have a single compressed byte format here.
2541
+ toRawBytes(point) {
2542
+ return signatureG2ToBytes(point);
2543
+ },
2544
+ toHex(point) {
2545
+ return bytesToHex(signatureG2ToBytes(point));
2546
+ },
2547
+ },
2548
+ };
2549
+ const fields = {
2550
+ Fp,
2551
+ Fp2,
2552
+ Fp6,
2553
+ Fp12,
2554
+ Fr: bls12_381_Fr,
2555
+ };
2556
+ const G1_Point = weierstrass(bls12_381_CURVE_G1, {
2557
+ // Public point APIs still accept infinity, even though the Zcash proof
2558
+ // encoding rules cited above only define nonzero point encodings.
2559
+ allowInfinityPoint: true,
2560
+ Fn: bls12_381_Fr,
2561
+ fromBytes: g1.point.decode,
2562
+ toBytes: (_c, point, isComp) => g1.point.encode(point, isComp),
2563
+ // Checks is the point resides in prime-order subgroup.
2564
+ // point.isTorsionFree() should return true for valid points
2565
+ // It returns false for shitty points.
2566
+ // https://eprint.iacr.org/2021/1130.pdf
2567
+ isTorsionFree: (c, point) => {
2568
+ // GLV endomorphism ψ(P)
2569
+ const beta = BigInt('0x5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe');
2570
+ const phi = new c(Fp.mul(point.X, beta), point.Y, point.Z);
2571
+ // TODO: unroll
2572
+ const xP = point.multiplyUnsafe(BLS_X).negate(); // [x]P
2573
+ const u2P = xP.multiplyUnsafe(BLS_X); // [u2]P
2574
+ return u2P.equals(phi);
2575
+ },
2576
+ // Clear cofactor of G1
2577
+ // https://eprint.iacr.org/2019/403
2578
+ clearCofactor: (_c, point) => {
2579
+ // return this.multiplyUnsafe(CURVE.h);
2580
+ return point.multiplyUnsafe(BLS_X).add(point); // x*P + P
2581
+ },
2582
+ });
2583
+ const G2_Point = weierstrass(bls12_381_CURVE_G2, {
2584
+ Fp: Fp2,
2585
+ // Public point APIs still accept infinity, even though the Zcash proof
2586
+ // encoding rules cited above only define nonzero point encodings.
2587
+ allowInfinityPoint: true,
2588
+ Fn: bls12_381_Fr,
2589
+ fromBytes: g2.point.decode,
2590
+ toBytes: (_c, point, isComp) => g2.point.encode(point, isComp),
2591
+ // https://eprint.iacr.org/2021/1130.pdf
2592
+ // Older version: https://eprint.iacr.org/2019/814.pdf
2593
+ isTorsionFree: (c, P) => {
2594
+ return P.multiplyUnsafe(BLS_X).negate().equals(G2psi(c, P)); // ψ(P) == [u](P)
2595
+ },
2596
+ // clear_cofactor_bls12381_g2 from RFC 9380.
2597
+ // https://eprint.iacr.org/2017/419.pdf
2598
+ // prettier-ignore
2599
+ clearCofactor: (c, P) => {
2600
+ const x = BLS_X;
2601
+ let t1 = P.multiplyUnsafe(x).negate(); // [-x]P
2602
+ let t2 = G2psi(c, P); // Ψ(P)
2603
+ let t3 = P.double(); // 2P
2604
+ t3 = G2psi2(c, t3); // Ψ²(2P)
2605
+ t3 = t3.subtract(t2); // Ψ²(2P) - Ψ(P)
2606
+ t2 = t1.add(t2); // [-x]P + Ψ(P)
2607
+ t2 = t2.multiplyUnsafe(x).negate(); // [x²]P - [x]Ψ(P)
2608
+ t3 = t3.add(t2); // Ψ²(2P) - Ψ(P) + [x²]P - [x]Ψ(P)
2609
+ t3 = t3.subtract(t1); // Ψ²(2P) - Ψ(P) + [x²]P - [x]Ψ(P) + [x]P
2610
+ const Q = t3.subtract(P); // Ψ²(2P) - Ψ(P) + [x²]P - [x]Ψ(P) + [x]P - 1P
2611
+ return Q; // [x²-x-1]P + [x-1]Ψ(P) + Ψ²(2P)
2612
+ },
2613
+ });
2614
+ const bls12_hasher_opts = {
2615
+ mapToG1: mapToG1,
2616
+ mapToG2: mapToG2,
2617
+ hasherOpts: hasher_opts,
2618
+ // RFC 9380 Appendix J defines distinct G1/G2 RO and NU suite IDs, and
2619
+ // draft-irtf-cfrg-bls-signature-06 §4.2.1 gives separate G1/G2 `_NUL_` DSTs.
2620
+ // Keep G1 encode-to-curve on the G1 domain instead of inheriting G2's `encodeDST`.
2621
+ hasherOptsG1: {
2622
+ ...hasher_opts,
2623
+ m: 1,
2624
+ DST: 'BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_',
2625
+ encodeDST: 'BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_',
2626
+ },
2627
+ hasherOptsG2: { ...hasher_opts },
2628
+ };
2629
+ const bls12_params = {
2630
+ ateLoopSize: BLS_X, // The BLS parameter x for BLS12-381
2631
+ xNegative: true,
2632
+ twistType: 'multiplicative',
2633
+ randomBytes: randomBytes,
2634
+ };
2635
+ /**
2636
+ * bls12-381 pairing-friendly curve construction.
2637
+ * Provides both longSignatures and shortSignatures.
2638
+ * @example
2639
+ * bls12-381 pairing-friendly curve construction.
2640
+ *
2641
+ * ```ts
2642
+ * const bls = bls12_381.longSignatures;
2643
+ * const { secretKey, publicKey } = bls.keygen();
2644
+ * const msg = bls.hash(new TextEncoder().encode('hello noble'));
2645
+ * const sig = bls.sign(msg, secretKey);
2646
+ * const isValid = bls.verify(sig, msg, publicKey);
2647
+ * ```
2648
+ */
2649
+ const bls12_381 = bls(fields, G1_Point, G2_Point, bls12_params, bls12_hasher_opts, signatureCoders);
1035
2650
  // 3-isogeny map from E' to E https://www.rfc-editor.org/rfc/rfc9380#appendix-E.3
2651
+ // Coefficients stay in ascending `k_(?,0)`..`k_(?,d)` order; isogenyMap()
2652
+ // reverses them internally for Horner evaluation.
1036
2653
  const isogenyMapG2 = isogenyMap(Fp2, [
1037
2654
  // xNum
1038
2655
  [
@@ -1101,7 +2718,8 @@ const isogenyMapG2 = isogenyMap(Fp2, [
1101
2718
  ['0x1', '0x0'], // LAST 1
1102
2719
  ],
1103
2720
  ].map((i) => i.map((pair) => Fp2.fromBigTuple(pair.map(BigInt)))));
1104
- // 11-isogeny map from E' to E
2721
+ // 11-isogeny map from E' to E. Coefficients stay in ascending
2722
+ // `k_(?,0)`..`k_(?,d)` order; isogenyMap() reverses them for Horner evaluation.
1105
2723
  const isogenyMapG1 = isogenyMap(Fp, [
1106
2724
  // xNum
1107
2725
  [
@@ -1171,406 +2789,43 @@ const isogenyMapG1 = isogenyMap(Fp, [
1171
2789
  '0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001', // LAST 1
1172
2790
  ],
1173
2791
  ].map((i) => i.map((j) => BigInt(j))));
1174
- // SWU Map - Fp2 to G2': y² = x³ + 240i * x + 1012 + 1012i
1175
- const G2_SWU = mapToCurveSimpleSWU(Fp2, {
1176
- A: Fp2.create({ c0: Fp.create(_0n), c1: Fp.create(BigInt(240)) }),
1177
- B: Fp2.create({ c0: Fp.create(BigInt(1012)), c1: Fp.create(BigInt(1012)) }),
1178
- Z: Fp2.create({ c0: Fp.create(BigInt(-2)), c1: Fp.create(BigInt(-1)) }), // Z: -(2 + I)
1179
- });
1180
- // Optimized SWU Map - Fp to G1
1181
- const G1_SWU = mapToCurveSimpleSWU(Fp, {
1182
- A: Fp.create(BigInt('0x144698a3b8e9433d693a02c96d4982b0ea985383ee66a8d8e8981aefd881ac98936f8da0e0f97f5cf428082d584c1d')),
1183
- B: Fp.create(BigInt('0x12e2908d11688030018b12e8753eee3b2016c1f0f24f4070a0b9c14fcef35ef55a23215a316ceaa5d1cc48e98e172be0')),
1184
- Z: Fp.create(BigInt(11)),
1185
- });
1186
- // Endomorphisms (for fast cofactor clearing)
1187
- // Ψ(P) endomorphism
1188
- const ut_root = Fp6.create({ c0: Fp2.ZERO, c1: Fp2.ONE, c2: Fp2.ZERO });
1189
- const wsq = Fp12.create({ c0: ut_root, c1: Fp6.ZERO });
1190
- const wcu = Fp12.create({ c0: Fp6.ZERO, c1: ut_root });
1191
- const [wsq_inv, wcu_inv] = Fp12.invertBatch([wsq, wcu]);
1192
- function psi(x, y) {
1193
- // Untwist Fp2->Fp12 && frobenius(1) && twist back
1194
- const x2 = Fp12.mul(Fp12.frobeniusMap(Fp12.multiplyByFp2(wsq_inv, x), 1), wsq).c0.c0;
1195
- const y2 = Fp12.mul(Fp12.frobeniusMap(Fp12.multiplyByFp2(wcu_inv, y), 1), wcu).c0.c0;
1196
- return [x2, y2];
1197
- }
1198
- // Ψ endomorphism
1199
- function G2psi(c, P) {
1200
- const affine = P.toAffine();
1201
- const p = psi(affine.x, affine.y);
1202
- return new c(p[0], p[1], Fp2.ONE);
1203
- }
1204
- // Ψ²(P) endomorphism
1205
- // 1 / F2(2)^((p-1)/3) in GF(p²)
1206
- const PSI2_C1 = BigInt('0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac');
1207
- function psi2(x, y) {
1208
- return [Fp2.mul(x, PSI2_C1), Fp2.neg(y)];
1209
- }
1210
- function G2psi2(c, P) {
1211
- const affine = P.toAffine();
1212
- const p = psi2(affine.x, affine.y);
1213
- return new c(p[0], p[1], Fp2.ONE);
2792
+ let G1_SWU;
2793
+ let G2_SWU;
2794
+ // SWU setup validates the pre-isogeny curve parameters and builds sqrt-ratio helpers.
2795
+ // Doing that eagerly adds about 10ms to `bls12-381.js` import here, so keep it lazy; after the
2796
+ // first map call the cached mapper is reused directly.
2797
+ const getG1_SWU = () => G1_SWU ||
2798
+ (G1_SWU = mapToCurveSimpleSWU(Fp, {
2799
+ A: Fp.create(BigInt('0x144698a3b8e9433d693a02c96d4982b0ea985383ee66a8d8e8981aefd881ac98936f8da0e0f97f5cf428082d584c1d')),
2800
+ B: Fp.create(BigInt('0x12e2908d11688030018b12e8753eee3b2016c1f0f24f4070a0b9c14fcef35ef55a23215a316ceaa5d1cc48e98e172be0')),
2801
+ Z: Fp.create(BigInt(11)),
2802
+ }));
2803
+ const getG2_SWU = () => G2_SWU ||
2804
+ (G2_SWU = mapToCurveSimpleSWU(Fp2, {
2805
+ // SWU map for the RFC 9380 §8.8.2 pre-isogeny G2 curve E':
2806
+ // y² = x³ + 240i * x + 1012 + 1012i
2807
+ A: Fp2.create({ c0: Fp.create(_0n), c1: Fp.create(BigInt(240)) }), // A' = 240 * I
2808
+ B: Fp2.create({ c0: Fp.create(BigInt(1012)), c1: Fp.create(BigInt(1012)) }), // B' = 1012 * (1 + I)
2809
+ Z: Fp2.create({ c0: Fp.create(BigInt(-2)), c1: Fp.create(BigInt(-1)) }), // Z: -(2 + I)
2810
+ }));
2811
+ // Internal hash-to-curve step: G1 uses `m = 1`, so only `scalars[0]` is read,
2812
+ // and the result is the isogeny image on E before the subgroup clear.
2813
+ function mapToG1(scalars) {
2814
+ const { x, y } = getG1_SWU()(Fp.create(scalars[0]));
2815
+ return isogenyMapG1(x, y);
1214
2816
  }
1215
- // Default hash_to_field options are for hash to G2.
1216
- //
1217
- // Parameter definitions are in section 5.3 of the spec unless otherwise noted.
1218
- // Parameter values come from section 8.8.2 of the spec.
1219
- // https://www.rfc-editor.org/rfc/rfc9380#section-8.8.2
1220
- //
1221
- // Base field F is GF(p^m)
1222
- // p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
1223
- // m = 2 (or 1 for G1 see section 8.8.1)
1224
- // k = 128
1225
- const htfDefaults = Object.freeze({
1226
- // DST: a domain separation tag
1227
- // defined in section 2.2.5
1228
- // Use utils.getDSTLabel(), utils.setDSTLabel(value)
1229
- DST: 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_',
1230
- encodeDST: 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_',
1231
- // p: the characteristic of F
1232
- // where F is a finite field of characteristic p and order q = p^m
1233
- p: Fp.ORDER,
1234
- // m: the extension degree of F, m >= 1
1235
- // where F is a finite field of characteristic p and order q = p^m
1236
- m: 2,
1237
- // k: the target security level for the suite in bits
1238
- // defined in section 5.1
1239
- k: 128,
1240
- // option to use a message that has already been processed by
1241
- // expand_message_xmd
1242
- expand: 'xmd',
1243
- // Hash functions for: expand_message_xmd is appropriate for use with a
1244
- // wide range of hash functions, including SHA-2, SHA-3, BLAKE2, and others.
1245
- // BBS+ uses blake2: https://github.com/hyperledger/aries-framework-go/issues/2247
1246
- hash: sha256,
1247
- });
1248
- // Encoding utils
1249
- // Point on G1 curve: (x, y)
1250
- const C_BIT_POS = Fp.BITS; // C_bit, compression bit for serialization flag
1251
- const I_BIT_POS = Fp.BITS + 1; // I_bit, point-at-infinity bit for serialization flag
1252
- const S_BIT_POS = Fp.BITS + 2; // S_bit, sign bit for serialization flag
1253
- // Compressed point of infinity
1254
- const COMPRESSED_ZERO = Fp.toBytes(bitSet(bitSet(_0n, I_BIT_POS, true), S_BIT_POS, true)); // set compressed & point-at-infinity bits
1255
- function signatureG2ToRawBytes(point) {
1256
- // NOTE: by some reasons it was missed in bls12-381, looks like bug
1257
- point.assertValidity();
1258
- const len = Fp.BYTES;
1259
- if (point.equals(bls12_381.G2.ProjectivePoint.ZERO))
1260
- return concatBytes(COMPRESSED_ZERO, numberToBytesBE(_0n, len));
1261
- const { x, y } = point.toAffine();
1262
- const { re: x0, im: x1 } = Fp2.reim(x);
1263
- const { re: y0, im: y1 } = Fp2.reim(y);
1264
- const tmp = y1 > _0n ? y1 * _2n : y0 * _2n;
1265
- const aflag1 = Boolean((tmp / Fp.ORDER) & _1n);
1266
- const z1 = bitSet(bitSet(x1, 381, aflag1), S_BIT_POS, true);
1267
- const z2 = x0;
1268
- return concatBytes(numberToBytesBE(z1, len), numberToBytesBE(z2, len));
2817
+ // Internal hash-to-curve step: G2 expects the RFC `m = 2` pair, and the result
2818
+ // is the isogeny image on E before the subgroup clear.
2819
+ function mapToG2(scalars) {
2820
+ const { x, y } = getG2_SWU()(Fp2.fromBigTuple(scalars));
2821
+ return isogenyMapG2(x, y);
1269
2822
  }
1270
- // To verify curve parameters, see pairing-friendly-curves spec:
1271
- // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-11
1272
- // Basic math is done over finite fields over p.
1273
- // More complicated math is done over polynominal extension fields.
1274
- // To simplify calculations in Fp12, we construct extension tower:
1275
- // Fp₁₂ = Fp₆² => Fp₂³
1276
- // Fp(u) / (u² - β) where β = -1
1277
- // Fp₂(v) / (v³ - ξ) where ξ = u + 1
1278
- // Fp₆(w) / (w² - γ) where γ = v
1279
- // Here goes constants && point encoding format
1280
- const bls12_381 = bls({
1281
- // Fields
1282
- fields: {
1283
- Fp,
1284
- Fp2,
1285
- Fp6,
1286
- Fp12,
1287
- Fr: Fr$1,
1288
- },
1289
- // G1 is the order-q subgroup of E1(Fp) : y² = x³ + 4, #E1(Fp) = h1q, where
1290
- // characteristic; z + (z⁴ - z² + 1)(z - 1)²/3
1291
- G1: {
1292
- Fp,
1293
- // cofactor; (z - 1)²/3
1294
- h: BigInt('0x396c8c005555e1568c00aaab0000aaab'),
1295
- // generator's coordinates
1296
- // x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507
1297
- // y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569
1298
- Gx: BigInt('0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb'),
1299
- Gy: BigInt('0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1'),
1300
- a: Fp.ZERO,
1301
- b: _4n,
1302
- htfDefaults: { ...htfDefaults, m: 1 },
1303
- wrapPrivateKey: true,
1304
- allowInfinityPoint: true,
1305
- // Checks is the point resides in prime-order subgroup.
1306
- // point.isTorsionFree() should return true for valid points
1307
- // It returns false for shitty points.
1308
- // https://eprint.iacr.org/2021/1130.pdf
1309
- isTorsionFree: (c, point) => {
1310
- // φ endomorphism
1311
- const cubicRootOfUnityModP = BigInt('0x5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe');
1312
- const phi = new c(Fp.mul(point.px, cubicRootOfUnityModP), point.py, point.pz);
1313
- // todo: unroll
1314
- const xP = point.multiplyUnsafe(bls12_381.params.x).negate(); // [x]P
1315
- const u2P = xP.multiplyUnsafe(bls12_381.params.x); // [u2]P
1316
- return u2P.equals(phi);
1317
- // https://eprint.iacr.org/2019/814.pdf
1318
- // (z² − 1)/3
1319
- // const c1 = BigInt('0x396c8c005555e1560000000055555555');
1320
- // const P = this;
1321
- // const S = P.sigma();
1322
- // const Q = S.double();
1323
- // const S2 = S.sigma();
1324
- // // [(z² − 1)/3](2σ(P) − P − σ²(P)) − σ²(P) = O
1325
- // const left = Q.subtract(P).subtract(S2).multiplyUnsafe(c1);
1326
- // const C = left.subtract(S2);
1327
- // return C.isZero();
1328
- },
1329
- // Clear cofactor of G1
1330
- // https://eprint.iacr.org/2019/403
1331
- clearCofactor: (_c, point) => {
1332
- // return this.multiplyUnsafe(CURVE.h);
1333
- return point.multiplyUnsafe(bls12_381.params.x).add(point); // x*P + P
1334
- },
1335
- mapToCurve: (scalars) => {
1336
- const { x, y } = G1_SWU(Fp.create(scalars[0]));
1337
- return isogenyMapG1(x, y);
1338
- },
1339
- fromBytes: (bytes) => {
1340
- bytes = bytes.slice();
1341
- if (bytes.length === 48) {
1342
- // TODO: Fp.bytes
1343
- const P = Fp.ORDER;
1344
- const compressedValue = bytesToNumberBE(bytes);
1345
- const bflag = bitGet(compressedValue, I_BIT_POS);
1346
- // Zero
1347
- if (bflag === _1n)
1348
- return { x: _0n, y: _0n };
1349
- const x = Fp.create(compressedValue & Fp.MASK);
1350
- const right = Fp.add(Fp.pow(x, _3n), Fp.create(bls12_381.params.G1b)); // y² = x³ + b
1351
- let y = Fp.sqrt(right);
1352
- if (!y)
1353
- throw new Error('Invalid compressed G1 point');
1354
- const aflag = bitGet(compressedValue, C_BIT_POS);
1355
- if ((y * _2n) / P !== aflag)
1356
- y = Fp.neg(y);
1357
- return { x: Fp.create(x), y: Fp.create(y) };
1358
- }
1359
- else if (bytes.length === 96) {
1360
- // Check if the infinity flag is set
1361
- if ((bytes[0] & (1 << 6)) !== 0)
1362
- return bls12_381.G1.ProjectivePoint.ZERO.toAffine();
1363
- const x = bytesToNumberBE(bytes.subarray(0, Fp.BYTES));
1364
- const y = bytesToNumberBE(bytes.subarray(Fp.BYTES));
1365
- return { x: Fp.create(x), y: Fp.create(y) };
1366
- }
1367
- else {
1368
- throw new Error('Invalid point G1, expected 48/96 bytes');
1369
- }
1370
- },
1371
- toBytes: (c, point, isCompressed) => {
1372
- const isZero = point.equals(c.ZERO);
1373
- const { x, y } = point.toAffine();
1374
- if (isCompressed) {
1375
- if (isZero)
1376
- return COMPRESSED_ZERO.slice();
1377
- const P = Fp.ORDER;
1378
- let num;
1379
- num = bitSet(x, C_BIT_POS, Boolean((y * _2n) / P)); // set aflag
1380
- num = bitSet(num, S_BIT_POS, true);
1381
- return numberToBytesBE(num, Fp.BYTES);
1382
- }
1383
- else {
1384
- if (isZero) {
1385
- // 2x PUBLIC_KEY_LENGTH
1386
- const x = concatBytes(new Uint8Array([0x40]), new Uint8Array(2 * Fp.BYTES - 1));
1387
- return x;
1388
- }
1389
- else {
1390
- return concatBytes(numberToBytesBE(x, Fp.BYTES), numberToBytesBE(y, Fp.BYTES));
1391
- }
1392
- }
1393
- },
1394
- },
1395
- // G2 is the order-q subgroup of E2(Fp²) : y² = x³+4(1+√−1),
1396
- // where Fp2 is Fp[√−1]/(x2+1). #E2(Fp2 ) = h2q, where
1397
- // G² - 1
1398
- // h2q
1399
- G2: {
1400
- Fp: Fp2,
1401
- // cofactor
1402
- h: BigInt('0x5d543a95414e7f1091d50792876a202cd91de4547085abaa68a205b2e5a7ddfa628f1cb4d9e82ef21537e293a6691ae1616ec6e786f0c70cf1c38e31c7238e5'),
1403
- Gx: Fp2.fromBigTuple([
1404
- BigInt('0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8'),
1405
- BigInt('0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e'),
1406
- ]),
1407
- // y =
1408
- // 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582,
1409
- // 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905
1410
- Gy: Fp2.fromBigTuple([
1411
- BigInt('0x0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801'),
1412
- BigInt('0x0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be'),
1413
- ]),
1414
- a: Fp2.ZERO,
1415
- b: Fp2.fromBigTuple([_4n, _4n]),
1416
- hEff: BigInt('0xbc69f08f2ee75b3584c6a0ea91b352888e2a8e9145ad7689986ff031508ffe1329c2f178731db956d82bf015d1212b02ec0ec69d7477c1ae954cbc06689f6a359894c0adebbf6b4e8020005aaa95551'),
1417
- htfDefaults: { ...htfDefaults },
1418
- wrapPrivateKey: true,
1419
- allowInfinityPoint: true,
1420
- mapToCurve: (scalars) => {
1421
- const { x, y } = G2_SWU(Fp2.fromBigTuple(scalars));
1422
- return isogenyMapG2(x, y);
1423
- },
1424
- // Checks is the point resides in prime-order subgroup.
1425
- // point.isTorsionFree() should return true for valid points
1426
- // It returns false for shitty points.
1427
- // https://eprint.iacr.org/2021/1130.pdf
1428
- isTorsionFree: (c, P) => {
1429
- return P.multiplyUnsafe(bls12_381.params.x).negate().equals(G2psi(c, P)); // ψ(P) == [u](P)
1430
- // Older version: https://eprint.iacr.org/2019/814.pdf
1431
- // Ψ²(P) => Ψ³(P) => [z]Ψ³(P) where z = -x => [z]Ψ³(P) - Ψ²(P) + P == O
1432
- // return P.psi2().psi().mulNegX().subtract(psi2).add(P).isZero();
1433
- },
1434
- // Maps the point into the prime-order subgroup G2.
1435
- // clear_cofactor_bls12381_g2 from cfrg-hash-to-curve-11
1436
- // https://eprint.iacr.org/2017/419.pdf
1437
- // prettier-ignore
1438
- clearCofactor: (c, P) => {
1439
- const x = bls12_381.params.x;
1440
- let t1 = P.multiplyUnsafe(x).negate(); // [-x]P
1441
- let t2 = G2psi(c, P); // Ψ(P)
1442
- let t3 = P.double(); // 2P
1443
- t3 = G2psi2(c, t3); // Ψ²(2P)
1444
- t3 = t3.subtract(t2); // Ψ²(2P) - Ψ(P)
1445
- t2 = t1.add(t2); // [-x]P + Ψ(P)
1446
- t2 = t2.multiplyUnsafe(x).negate(); // [x²]P - [x]Ψ(P)
1447
- t3 = t3.add(t2); // Ψ²(2P) - Ψ(P) + [x²]P - [x]Ψ(P)
1448
- t3 = t3.subtract(t1); // Ψ²(2P) - Ψ(P) + [x²]P - [x]Ψ(P) + [x]P
1449
- const Q = t3.subtract(P); // Ψ²(2P) - Ψ(P) + [x²]P - [x]Ψ(P) + [x]P - 1P
1450
- return Q; // [x²-x-1]P + [x-1]Ψ(P) + Ψ²(2P)
1451
- },
1452
- fromBytes: (bytes) => {
1453
- bytes = bytes.slice();
1454
- const m_byte = bytes[0] & 0xe0;
1455
- if (m_byte === 0x20 || m_byte === 0x60 || m_byte === 0xe0) {
1456
- throw new Error('Invalid encoding flag: ' + m_byte);
1457
- }
1458
- const bitC = m_byte & 0x80; // compression bit
1459
- const bitI = m_byte & 0x40; // point at infinity bit
1460
- const bitS = m_byte & 0x20; // sign bit
1461
- const L = Fp.BYTES;
1462
- const slc = (b, from, to) => bytesToNumberBE(b.slice(from, to));
1463
- if (bytes.length === 96 && bitC) {
1464
- const b = bls12_381.params.G2b;
1465
- const P = Fp.ORDER;
1466
- bytes[0] = bytes[0] & 0x1f; // clear flags
1467
- if (bitI) {
1468
- // check that all bytes are 0
1469
- if (bytes.reduce((p, c) => (p !== 0 ? c + 1 : c), 0) > 0) {
1470
- throw new Error('Invalid compressed G2 point');
1471
- }
1472
- return { x: Fp2.ZERO, y: Fp2.ZERO };
1473
- }
1474
- const x_1 = slc(bytes, 0, L);
1475
- const x_0 = slc(bytes, L, 2 * L);
1476
- const x = Fp2.create({ c0: Fp.create(x_0), c1: Fp.create(x_1) });
1477
- const right = Fp2.add(Fp2.pow(x, _3n), b); // y² = x³ + 4 * (u+1) = x³ + b
1478
- let y = Fp2.sqrt(right);
1479
- const Y_bit = y.c1 === _0n ? (y.c0 * _2n) / P : (y.c1 * _2n) / P ? _1n : _0n;
1480
- y = bitS > 0 && Y_bit > 0 ? y : Fp2.neg(y);
1481
- return { x, y };
1482
- }
1483
- else if (bytes.length === 192 && !bitC) {
1484
- // Check if the infinity flag is set
1485
- if ((bytes[0] & (1 << 6)) !== 0) {
1486
- return { x: Fp2.ZERO, y: Fp2.ZERO };
1487
- }
1488
- const x1 = slc(bytes, 0, L);
1489
- const x0 = slc(bytes, L, 2 * L);
1490
- const y1 = slc(bytes, 2 * L, 3 * L);
1491
- const y0 = slc(bytes, 3 * L, 4 * L);
1492
- return { x: Fp2.fromBigTuple([x0, x1]), y: Fp2.fromBigTuple([y0, y1]) };
1493
- }
1494
- else {
1495
- throw new Error('Invalid point G2, expected 96/192 bytes');
1496
- }
1497
- },
1498
- toBytes: (c, point, isCompressed) => {
1499
- const { BYTES: len, ORDER: P } = Fp;
1500
- const isZero = point.equals(c.ZERO);
1501
- const { x, y } = point.toAffine();
1502
- if (isCompressed) {
1503
- if (isZero)
1504
- return concatBytes(COMPRESSED_ZERO, numberToBytesBE(_0n, len));
1505
- const flag = Boolean(y.c1 === _0n ? (y.c0 * _2n) / P : (y.c1 * _2n) / P);
1506
- // set compressed & sign bits (looks like different offsets than for G1/Fp?)
1507
- let x_1 = bitSet(x.c1, C_BIT_POS, flag);
1508
- x_1 = bitSet(x_1, S_BIT_POS, true);
1509
- return concatBytes(numberToBytesBE(x_1, len), numberToBytesBE(x.c0, len));
1510
- }
1511
- else {
1512
- if (isZero)
1513
- return concatBytes(new Uint8Array([0x40]), new Uint8Array(4 * len - 1)); // bytes[0] |= 1 << 6;
1514
- const { re: x0, im: x1 } = Fp2.reim(x);
1515
- const { re: y0, im: y1 } = Fp2.reim(y);
1516
- return concatBytes(numberToBytesBE(x1, len), numberToBytesBE(x0, len), numberToBytesBE(y1, len), numberToBytesBE(y0, len));
1517
- }
1518
- },
1519
- Signature: {
1520
- // TODO: Optimize, it's very slow because of sqrt.
1521
- fromHex(hex) {
1522
- hex = ensureBytes('signatureHex', hex);
1523
- const P = Fp.ORDER;
1524
- const half = hex.length / 2;
1525
- if (half !== 48 && half !== 96)
1526
- throw new Error('Invalid compressed signature length, must be 96 or 192');
1527
- const z1 = bytesToNumberBE(hex.slice(0, half));
1528
- const z2 = bytesToNumberBE(hex.slice(half));
1529
- // Indicates the infinity point
1530
- const bflag1 = bitGet(z1, I_BIT_POS);
1531
- if (bflag1 === _1n)
1532
- return bls12_381.G2.ProjectivePoint.ZERO;
1533
- const x1 = Fp.create(z1 & Fp.MASK);
1534
- const x2 = Fp.create(z2);
1535
- const x = Fp2.create({ c0: x2, c1: x1 });
1536
- const y2 = Fp2.add(Fp2.pow(x, _3n), bls12_381.params.G2b); // y² = x³ + 4
1537
- // The slow part
1538
- let y = Fp2.sqrt(y2);
1539
- if (!y)
1540
- throw new Error('Failed to find a square root');
1541
- // Choose the y whose leftmost bit of the imaginary part is equal to the a_flag1
1542
- // If y1 happens to be zero, then use the bit of y0
1543
- const { re: y0, im: y1 } = Fp2.reim(y);
1544
- const aflag1 = bitGet(z1, 381);
1545
- const isGreater = y1 > _0n && (y1 * _2n) / P !== aflag1;
1546
- const isZero = y1 === _0n && (y0 * _2n) / P !== aflag1;
1547
- if (isGreater || isZero)
1548
- y = Fp2.neg(y);
1549
- const point = bls12_381.G2.ProjectivePoint.fromAffine({ x, y });
1550
- point.assertValidity();
1551
- return point;
1552
- },
1553
- toRawBytes(point) {
1554
- return signatureG2ToRawBytes(point);
1555
- },
1556
- toHex(point) {
1557
- return bytesToHex(signatureG2ToRawBytes(point));
1558
- },
1559
- },
1560
- },
1561
- params: {
1562
- x: BLS_X,
1563
- r: Fr$1.ORDER, // order; z⁴ − z² + 1; CURVE.n from other curves
1564
- },
1565
- htfDefaults,
1566
- hash: sha256,
1567
- randomBytes,
1568
- });
1569
2823
 
1570
2824
  const encoder = new TextEncoder();
1571
2825
  const Fr = bls12_381.fields.Fr;
1572
- const G1 = bls12_381.G1.ProjectivePoint;
1573
- const G2 = bls12_381.G2.ProjectivePoint;
2826
+ const G1 = bls12_381.G1.Point;
2827
+ const G2 = bls12_381.G2.Point;
2828
+ const signatures = bls12_381.longSignatures;
1574
2829
  const assertParticipant = (participant) => {
1575
2830
  if (typeof participant !== "bigint" || participant <= 0n || participant >= Fr.ORDER) {
1576
2831
  throw new TypeError("beacon participant identifiers must be non-zero field elements");
@@ -1596,7 +2851,7 @@ const evaluateCommitments = (commitments, x) => {
1596
2851
  let result = G1.ZERO;
1597
2852
  let power = 1n;
1598
2853
  for (const encoded of commitments) {
1599
- const commitment = G1.fromHex(encoded);
2854
+ const commitment = G1.fromBytes(encoded);
1600
2855
  commitment.assertValidity();
1601
2856
  result = result.add(commitment.multiply(power));
1602
2857
  power = Fr.mul(power, x);
@@ -1606,7 +2861,7 @@ const evaluateCommitments = (commitments, x) => {
1606
2861
  const sumPoints = (points) => {
1607
2862
  let result = G1.ZERO;
1608
2863
  for (const encoded of points) {
1609
- const point = G1.fromHex(encoded);
2864
+ const point = G1.fromBytes(encoded);
1610
2865
  point.assertValidity();
1611
2866
  result = result.add(point);
1612
2867
  }
@@ -1631,7 +2886,7 @@ const createBeaconContribution = (participants, threshold, randomScalar) => {
1631
2886
  return scalar;
1632
2887
  });
1633
2888
  return {
1634
- commitments: coefficients.map((coefficient) => G1.BASE.multiply(coefficient).toRawBytes(true)),
2889
+ commitments: coefficients.map((coefficient) => G1.BASE.multiply(coefficient).toBytes(true)),
1635
2890
  shares: new Map(participants.map((participant) => [participant, evaluatePolynomial(coefficients, participant)]))
1636
2891
  };
1637
2892
  };
@@ -1652,14 +2907,14 @@ const deriveBeaconPublicShare = (participant, dealerCommitments) => {
1652
2907
  assertParticipant(participant);
1653
2908
  if (dealerCommitments.length < 2) throw new Error("at least two dealer commitments are required");
1654
2909
  return sumPoints(
1655
- dealerCommitments.map((commitments) => evaluateCommitments(commitments, participant).toRawBytes(true))
1656
- ).toRawBytes(true);
2910
+ dealerCommitments.map((commitments) => evaluateCommitments(commitments, participant).toBytes(true))
2911
+ ).toBytes(true);
1657
2912
  };
1658
2913
  const deriveBeaconGroupPublicKey = (dealerCommitments) => {
1659
2914
  if (dealerCommitments.length < 2 || dealerCommitments.some((commitments) => commitments.length < 2)) {
1660
2915
  throw new Error("invalid beacon dealer commitments");
1661
2916
  }
1662
- return sumPoints(dealerCommitments.map((commitments) => commitments[0])).toRawBytes(true);
2917
+ return sumPoints(dealerCommitments.map((commitments) => commitments[0])).toBytes(true);
1663
2918
  };
1664
2919
  const beaconMessage = (network, epoch, round, previousProof) => {
1665
2920
  if (!network || epoch < 0n || round < 0n) throw new TypeError("invalid beacon round");
@@ -1669,10 +2924,10 @@ const beaconMessage = (network, epoch, round, previousProof) => {
1669
2924
  message.set(previousProof, prefix.length);
1670
2925
  return message;
1671
2926
  };
1672
- const signBeaconRound = (secretShare, message) => bls12_381.sign(message, Fr.create(secretShare));
2927
+ const signBeaconRound = (secretShare, message) => signatures.Signature.toBytes(signatures.sign(signatures.hash(message), Fr.toBytes(Fr.create(secretShare))));
1673
2928
  const verifyBeaconSignatureShare = (signature, message, publicShare) => {
1674
2929
  try {
1675
- return bls12_381.verify(signature, message, publicShare);
2930
+ return signatures.verify(signature, signatures.hash(message), publicShare);
1676
2931
  } catch {
1677
2932
  return false;
1678
2933
  }
@@ -1686,22 +2941,22 @@ const reconstructBeaconSignature = (rawShares, threshold) => {
1686
2941
  assertUniqueParticipants(participants);
1687
2942
  let signature = G2.ZERO;
1688
2943
  for (const share of selected) {
1689
- const point = bls12_381.Signature.fromHex(share.signature);
2944
+ const point = signatures.Signature.fromBytes(share.signature);
1690
2945
  point.assertValidity();
1691
2946
  signature = signature.add(point.multiply(lagrangeAtZero(share.participant, participants)));
1692
2947
  }
1693
- return bls12_381.Signature.toRawBytes(signature);
2948
+ return signatures.Signature.toBytes(signature);
1694
2949
  };
1695
2950
  const verifyBeaconProof = (proof, message, groupPublicKey) => {
1696
2951
  try {
1697
- return bls12_381.verify(proof, message, groupPublicKey);
2952
+ return signatures.verify(proof, signatures.hash(message), groupPublicKey);
1698
2953
  } catch {
1699
2954
  return false;
1700
2955
  }
1701
2956
  };
1702
2957
  const beaconRandomness = (proof) => {
1703
2958
  if (proof.length !== 96) throw new Error("invalid BLS beacon proof length");
1704
- return sha256$1(proof);
2959
+ return sha256(proof);
1705
2960
  };
1706
2961
 
1707
2962
  export { beaconMessage, beaconRandomness, combineBeaconShares, createBeaconContribution, deriveBeaconGroupPublicKey, deriveBeaconPublicShare, reconstructBeaconSignature, signBeaconRound, verifyBeaconProof, verifyBeaconShare, verifyBeaconSignatureShare };