@leofcoin/chain 1.10.15 → 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.
- package/exports/beacon-envelope.js +6 -6
- package/exports/beacon-epoch.js +1 -1
- package/exports/beacon-lifecycle.js +1 -1
- package/exports/beacon-round.js +1 -1
- package/exports/beacon.js +15 -14
- package/exports/browser/_commonjsHelpers-B85MJLTf.js +5 -0
- package/exports/browser/beacon-envelope.js +61 -150
- package/exports/browser/beacon-epoch.js +2 -2
- package/exports/browser/beacon-lifecycle.js +3 -3
- package/exports/browser/beacon-round.js +2 -2
- package/exports/browser/beacon-wire.js +3 -3
- package/exports/browser/beacon.js +2483 -1228
- package/exports/browser/{browser-DvU1xNFS-DmmNUdE-.js → browser-CCfR9KOM-CpStHgTj.js} +4 -4
- package/exports/browser/{browser-crypto-B9N9KlL4.js → browser-crypto-CH0ua22L.js} +284 -159
- package/exports/browser/chain.js +30 -16
- package/exports/browser/{client-DDC8C510-3uaB70KL.js → client-C_pBoi1g-_pfKzCvX.js} +256 -80
- package/exports/browser/{connection-monitor-GPztgSSJ.js → connection-monitor-D-krNiRH.js} +4 -4
- package/exports/browser/{hkdf-CnZvwpZ9.js → hkdf-BUHINPFj.js} +2 -94
- package/exports/browser/{index-Ccn5sExe.js → index-DmuI-29u.js} +55 -52
- package/exports/browser/{index-Bxr5Iztg-WbFIosyf.js → index-dTQVgkpf-BbdshWB0.js} +4 -4
- package/exports/browser/index.browser-CFfJpgKV.js +857 -0
- package/exports/browser/{messages-DH0tgNM9-C2CtyLsf.js → messages-BGHaihSi-eHZVl0y9.js} +10 -5
- package/exports/browser/{node-browser-BYi7D_oa.js → node-browser-BNe-en1l.js} +950 -482
- package/exports/browser/node-browser.js +4 -4
- package/exports/browser/{sha2-piimb6xL.js → sha2-BKcEJzgd.js} +144 -3
- package/exports/browser/weierstrass-BVfhtHbl.js +3596 -0
- package/exports/browser/workers/block-worker.js +5 -3
- package/exports/browser/workers/machine-worker.js +50 -22
- package/exports/{workers/worker-DMCj1e6z-CTYVa2yX.js → browser/workers/worker-BDTWOCzS-COrAszCt.js} +304 -168
- package/exports/chain.js +10 -5
- package/exports/{constants-CMYKv-Rt.js → constants-Dy9i9S_o.js} +1 -1
- package/exports/hash-format-state.js +17 -0
- package/exports/node.js +1 -1
- package/exports/workers/block-worker.js +5 -3
- package/exports/workers/machine-worker.js +50 -22
- package/exports/{browser/workers/worker-DMCj1e6z-CTYVa2yX.js → workers/worker-BDTWOCzS-COrAszCt.js} +304 -168
- package/package.json +14 -13
- package/exports/browser/weierstrass-Q0QND1ZT.js +0 -2156
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { s as
|
|
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
|
-
|
|
5
|
-
|
|
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
|
-
|
|
16
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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(
|
|
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('
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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(
|
|
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(
|
|
141
|
+
DST = H.create({ dkLen }).update(asciiToBytes('H2C-OVERSIZE-DST-')).update(DST).digest();
|
|
80
142
|
}
|
|
81
|
-
if
|
|
82
|
-
|
|
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
|
|
95
|
-
* @param count
|
|
96
|
-
* @param options
|
|
97
|
-
* @returns [u_0, ..., u_(count - 1)]
|
|
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: '
|
|
104
|
-
k: '
|
|
105
|
-
hash: '
|
|
182
|
+
m: 'number',
|
|
183
|
+
k: 'number',
|
|
184
|
+
hash: 'function',
|
|
106
185
|
});
|
|
107
|
-
const { p, k, m, hash, expand, DST
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
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 [
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
|
158
|
-
const
|
|
159
|
-
const
|
|
160
|
-
const
|
|
161
|
-
|
|
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
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
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
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
|
|
184
|
-
|
|
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,
|
|
774
|
+
const Qx = x, Qy = y, negQy = Fp2.neg(y);
|
|
187
775
|
// prettier-ignore
|
|
188
|
-
let Rx = Qx, Ry = Qy, Rz =
|
|
189
|
-
|
|
190
|
-
for (
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
-
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
|
|
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
|
|
278
|
-
throw new
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
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
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
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),
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
//
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
const
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
//
|
|
458
|
-
|
|
459
|
-
let
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
//
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
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
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
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
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
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
|
|
504
|
-
|
|
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
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
const
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
const
|
|
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
|
|
550
|
-
const { re: x0, im: x1 } =
|
|
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
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
return {
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
const
|
|
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
|
-
|
|
634
|
-
|
|
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
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
};
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
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
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
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
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
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
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
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
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
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
|
-
|
|
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
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
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
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
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:
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
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
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
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
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
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
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
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
|
|
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
|
|
899
|
-
|
|
900
|
-
|
|
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
|
|
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 =
|
|
907
|
-
return {
|
|
908
|
-
c0:
|
|
909
|
-
c1:
|
|
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
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
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
|
-
|
|
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
|
-
|
|
942
|
-
return {
|
|
943
|
-
c0:
|
|
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
|
-
|
|
949
|
-
|
|
950
|
-
|
|
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
|
-
|
|
957
|
-
|
|
958
|
-
|
|
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 =
|
|
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
|
-
|
|
965
|
-
|
|
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 =
|
|
2293
|
+
const t2 = powMinusX(t1);
|
|
973
2294
|
const t3 = Fp12.mul(Fp12.conjugate(Fp12._cyclotomicSquare(t1)), t2);
|
|
974
|
-
const t4 =
|
|
975
|
-
const t5 =
|
|
976
|
-
const t6 = Fp12.mul(
|
|
977
|
-
const t7 =
|
|
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
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
],
|
|
1020
|
-
[
|
|
1021
|
-
BigInt('
|
|
1022
|
-
BigInt('
|
|
1023
|
-
],
|
|
1024
|
-
[
|
|
1025
|
-
BigInt('
|
|
1026
|
-
BigInt('
|
|
1027
|
-
],
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
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
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
//
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
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
|
-
//
|
|
1216
|
-
//
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
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.
|
|
1573
|
-
const G2 = bls12_381.G2.
|
|
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.
|
|
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.
|
|
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).
|
|
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).
|
|
1656
|
-
).
|
|
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])).
|
|
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) =>
|
|
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
|
|
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 =
|
|
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
|
|
2948
|
+
return signatures.Signature.toBytes(signature);
|
|
1694
2949
|
};
|
|
1695
2950
|
const verifyBeaconProof = (proof, message, groupPublicKey) => {
|
|
1696
2951
|
try {
|
|
1697
|
-
return
|
|
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
|
|
2959
|
+
return sha256(proof);
|
|
1705
2960
|
};
|
|
1706
2961
|
|
|
1707
2962
|
export { beaconMessage, beaconRandomness, combineBeaconShares, createBeaconContribution, deriveBeaconGroupPublicKey, deriveBeaconPublicShare, reconstructBeaconSignature, signBeaconRound, verifyBeaconProof, verifyBeaconShare, verifyBeaconSignatureShare };
|