@noble/curves 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -8
- package/lib/_shortw_utils.d.ts +11 -26
- package/lib/abstract/bls.d.ts +51 -35
- package/lib/abstract/bls.js +77 -139
- package/lib/abstract/{group.d.ts → curve.d.ts} +31 -1
- package/lib/abstract/{group.js → curve.js} +39 -2
- package/lib/abstract/edwards.d.ts +30 -81
- package/lib/abstract/edwards.js +225 -420
- package/lib/abstract/hash-to-curve.d.ts +25 -6
- package/lib/abstract/hash-to-curve.js +40 -12
- package/lib/abstract/modular.d.ts +20 -7
- package/lib/abstract/modular.js +80 -51
- package/lib/abstract/montgomery.js +3 -4
- package/lib/abstract/poseidon.d.ts +29 -0
- package/lib/abstract/poseidon.js +115 -0
- package/lib/abstract/utils.d.ts +5 -34
- package/lib/abstract/utils.js +23 -63
- package/lib/abstract/weierstrass.d.ts +56 -79
- package/lib/abstract/weierstrass.js +509 -641
- package/lib/bls12-381.d.ts +1 -0
- package/lib/bls12-381.js +75 -65
- package/lib/bn.js +1 -1
- package/lib/ed25519.d.ts +7 -5
- package/lib/ed25519.js +87 -84
- package/lib/ed448.d.ts +3 -0
- package/lib/ed448.js +88 -84
- package/lib/esm/abstract/bls.js +77 -139
- package/lib/esm/abstract/{group.js → curve.js} +37 -1
- package/lib/esm/abstract/edwards.js +223 -418
- package/lib/esm/abstract/hash-to-curve.js +38 -11
- package/lib/esm/abstract/modular.js +77 -50
- package/lib/esm/abstract/montgomery.js +4 -7
- package/lib/esm/abstract/poseidon.js +109 -0
- package/lib/esm/abstract/utils.js +21 -59
- package/lib/esm/abstract/weierstrass.js +508 -640
- package/lib/esm/bls12-381.js +86 -76
- package/lib/esm/bn.js +1 -1
- package/lib/esm/ed25519.js +85 -83
- package/lib/esm/ed448.js +86 -83
- package/lib/esm/jubjub.js +6 -5
- package/lib/esm/p256.js +11 -9
- package/lib/esm/p384.js +11 -9
- package/lib/esm/p521.js +13 -12
- package/lib/esm/secp256k1.js +118 -157
- package/lib/esm/stark.js +104 -39
- package/lib/jubjub.d.ts +3 -2
- package/lib/jubjub.js +6 -5
- package/lib/p192.d.ts +22 -52
- package/lib/p224.d.ts +22 -52
- package/lib/p256.d.ts +25 -52
- package/lib/p256.js +13 -10
- package/lib/p384.d.ts +25 -52
- package/lib/p384.js +13 -10
- package/lib/p521.d.ts +25 -52
- package/lib/p521.js +15 -13
- package/lib/secp256k1.d.ts +26 -42
- package/lib/secp256k1.js +118 -157
- package/lib/stark.d.ts +36 -21
- package/lib/stark.js +107 -39
- package/package.json +14 -9
package/lib/esm/abstract/bls.js
CHANGED
|
@@ -1,24 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import * as mod from './modular.js';
|
|
6
|
-
import { ensureBytes, numberToBytesBE, bytesToNumberBE, bitLen, bitGet } from './utils.js';
|
|
7
|
-
// Types
|
|
8
|
-
import { hexToBytes, bytesToHex } from './utils.js';
|
|
9
|
-
import { stringToBytes, hash_to_field, expand_message_xmd } from './hash-to-curve.js';
|
|
10
|
-
import { weierstrassPoints } from './weierstrass.js';
|
|
1
|
+
import { hashToPrivateScalar } from './modular.js';
|
|
2
|
+
import { bitLen, bitGet, hexToBytes, bytesToHex } from './utils.js';
|
|
3
|
+
import * as htf from './hash-to-curve.js';
|
|
4
|
+
import { weierstrassPoints, } from './weierstrass.js';
|
|
11
5
|
export function bls(CURVE) {
|
|
12
6
|
// Fields looks pretty specific for curve, so for now we need to pass them with options
|
|
13
|
-
const Fp = CURVE
|
|
14
|
-
const Fr = CURVE.Fr;
|
|
15
|
-
const Fp2 = CURVE.Fp2;
|
|
16
|
-
const Fp6 = CURVE.Fp6;
|
|
17
|
-
const Fp12 = CURVE.Fp12;
|
|
7
|
+
const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE;
|
|
18
8
|
const BLS_X_LEN = bitLen(CURVE.x);
|
|
9
|
+
const groupLen = 32; // TODO: calculate; hardcoded for now
|
|
19
10
|
// Pre-compute coefficients for sparse multiplication
|
|
20
11
|
// Point addition and point double calculations is reused for coefficients
|
|
21
|
-
function calcPairingPrecomputes(
|
|
12
|
+
function calcPairingPrecomputes(p) {
|
|
13
|
+
const { x, y } = p;
|
|
22
14
|
// prettier-ignore
|
|
23
15
|
const Qx = x, Qy = y, Qz = Fp2.ONE;
|
|
24
16
|
// prettier-ignore
|
|
@@ -26,18 +18,18 @@ export function bls(CURVE) {
|
|
|
26
18
|
let ell_coeff = [];
|
|
27
19
|
for (let i = BLS_X_LEN - 2; i >= 0; i--) {
|
|
28
20
|
// Double
|
|
29
|
-
let t0 = Fp2.
|
|
30
|
-
let t1 = Fp2.
|
|
21
|
+
let t0 = Fp2.sqr(Ry); // Ry²
|
|
22
|
+
let t1 = Fp2.sqr(Rz); // Rz²
|
|
31
23
|
let t2 = Fp2.multiplyByB(Fp2.mul(t1, 3n)); // 3 * T1 * B
|
|
32
24
|
let t3 = Fp2.mul(t2, 3n); // 3 * T2
|
|
33
|
-
let t4 = Fp2.sub(Fp2.sub(Fp2.
|
|
25
|
+
let t4 = Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(Ry, Rz)), t1), t0); // (Ry + Rz)² - T1 - T0
|
|
34
26
|
ell_coeff.push([
|
|
35
27
|
Fp2.sub(t2, t0),
|
|
36
|
-
Fp2.mul(Fp2.
|
|
37
|
-
Fp2.
|
|
28
|
+
Fp2.mul(Fp2.sqr(Rx), 3n),
|
|
29
|
+
Fp2.neg(t4), // -T4
|
|
38
30
|
]);
|
|
39
31
|
Rx = Fp2.div(Fp2.mul(Fp2.mul(Fp2.sub(t0, t3), Rx), Ry), 2n); // ((T0 - T3) * Rx * Ry) / 2
|
|
40
|
-
Ry = Fp2.sub(Fp2.
|
|
32
|
+
Ry = Fp2.sub(Fp2.sqr(Fp2.div(Fp2.add(t0, t3), 2n)), Fp2.mul(Fp2.sqr(t2), 3n)); // ((T0 + T3) / 2)² - 3 * T2²
|
|
41
33
|
Rz = Fp2.mul(t0, t4); // T0 * T4
|
|
42
34
|
if (bitGet(CURVE.x, i)) {
|
|
43
35
|
// Addition
|
|
@@ -45,13 +37,13 @@ export function bls(CURVE) {
|
|
|
45
37
|
let t1 = Fp2.sub(Rx, Fp2.mul(Qx, Rz)); // Rx - Qx * Rz
|
|
46
38
|
ell_coeff.push([
|
|
47
39
|
Fp2.sub(Fp2.mul(t0, Qx), Fp2.mul(t1, Qy)),
|
|
48
|
-
Fp2.
|
|
40
|
+
Fp2.neg(t0),
|
|
49
41
|
t1, // T1
|
|
50
42
|
]);
|
|
51
|
-
let t2 = Fp2.
|
|
43
|
+
let t2 = Fp2.sqr(t1); // T1²
|
|
52
44
|
let t3 = Fp2.mul(t2, t1); // T2 * T1
|
|
53
45
|
let t4 = Fp2.mul(t2, Rx); // T2 * Rx
|
|
54
|
-
let t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4, 2n)), Fp2.mul(Fp2.
|
|
46
|
+
let t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4, 2n)), Fp2.mul(Fp2.sqr(t0), Rz)); // T3 - 2 * T4 + T0² * Rz
|
|
55
47
|
Rx = Fp2.mul(t1, t5); // T1 * T5
|
|
56
48
|
Ry = Fp2.sub(Fp2.mul(Fp2.sub(t4, t5), t0), Fp2.mul(t3, Ry)); // (T4 - T5) * T0 - T3 * Ry
|
|
57
49
|
Rz = Fp2.mul(Rz, t3); // Rz * T3
|
|
@@ -60,175 +52,121 @@ export function bls(CURVE) {
|
|
|
60
52
|
return ell_coeff;
|
|
61
53
|
}
|
|
62
54
|
function millerLoop(ell, g1) {
|
|
55
|
+
const { x } = CURVE;
|
|
63
56
|
const Px = g1[0];
|
|
64
57
|
const Py = g1[1];
|
|
65
58
|
let f12 = Fp12.ONE;
|
|
66
59
|
for (let j = 0, i = BLS_X_LEN - 2; i >= 0; i--, j++) {
|
|
67
60
|
const E = ell[j];
|
|
68
61
|
f12 = Fp12.multiplyBy014(f12, E[0], Fp2.mul(E[1], Px), Fp2.mul(E[2], Py));
|
|
69
|
-
if (bitGet(
|
|
62
|
+
if (bitGet(x, i)) {
|
|
70
63
|
j += 1;
|
|
71
64
|
const F = ell[j];
|
|
72
65
|
f12 = Fp12.multiplyBy014(f12, F[0], Fp2.mul(F[1], Px), Fp2.mul(F[2], Py));
|
|
73
66
|
}
|
|
74
67
|
if (i !== 0)
|
|
75
|
-
f12 = Fp12.
|
|
68
|
+
f12 = Fp12.sqr(f12);
|
|
76
69
|
}
|
|
77
70
|
return Fp12.conjugate(f12);
|
|
78
71
|
}
|
|
79
|
-
// bls12-381 is a construction of two curves:
|
|
80
|
-
// 1. Fp: (x, y)
|
|
81
|
-
// 2. Fp₂: ((x₁, x₂+i), (y₁, y₂+i)) - (complex numbers)
|
|
82
|
-
//
|
|
83
|
-
// Bilinear Pairing (ate pairing) is used to combine both elements into a paired one:
|
|
84
|
-
// Fp₁₂ = e(Fp, Fp2)
|
|
85
|
-
// where Fp₁₂ = 12-degree polynomial
|
|
86
|
-
// Pairing is used to verify signatures.
|
|
87
|
-
//
|
|
88
|
-
// We are using Fp for private keys (shorter) and Fp2 for signatures (longer).
|
|
89
|
-
// Some projects may prefer to swap this relation, it is not supported for now.
|
|
90
|
-
const htfDefaults = { ...CURVE.htfDefaults };
|
|
91
|
-
function isWithinCurveOrder(num) {
|
|
92
|
-
return 0 < num && num < CURVE.r;
|
|
93
|
-
}
|
|
94
72
|
const utils = {
|
|
95
73
|
hexToBytes: hexToBytes,
|
|
96
74
|
bytesToHex: bytesToHex,
|
|
97
|
-
|
|
98
|
-
stringToBytes,
|
|
75
|
+
stringToBytes: htf.stringToBytes,
|
|
99
76
|
// TODO: do we need to export it here?
|
|
100
|
-
hashToField: (msg, count, options = {}) => hash_to_field(msg, count, { ...CURVE.htfDefaults, ...options }),
|
|
101
|
-
expandMessageXMD: (msg, DST, lenInBytes, H = CURVE.hash) => expand_message_xmd(msg, DST, lenInBytes, H),
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
* As per FIPS 186 B.1.1.
|
|
106
|
-
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
|
|
107
|
-
* @param hash hash output from sha512, or a similar function
|
|
108
|
-
* @returns valid private key
|
|
109
|
-
*/
|
|
110
|
-
hashToPrivateKey: (hash) => {
|
|
111
|
-
hash = ensureBytes(hash);
|
|
112
|
-
if (hash.length < 40 || hash.length > 1024)
|
|
113
|
-
throw new Error('Expected 40-1024 bytes of private key as per FIPS 186');
|
|
114
|
-
// hashToPrivateScalar(hash, CURVE.r)
|
|
115
|
-
// NOTE: doesn't add +/-1
|
|
116
|
-
const num = mod.mod(bytesToNumberBE(hash), CURVE.r);
|
|
117
|
-
// This should never happen
|
|
118
|
-
if (num === 0n || num === 1n)
|
|
119
|
-
throw new Error('Invalid private key');
|
|
120
|
-
return numberToBytesBE(num, 32);
|
|
121
|
-
},
|
|
122
|
-
randomBytes: (bytesLength = 32) => CURVE.randomBytes(bytesLength),
|
|
123
|
-
// NIST SP 800-56A rev 3, section 5.6.1.2.2
|
|
124
|
-
// https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
|
|
125
|
-
randomPrivateKey: () => utils.hashToPrivateKey(utils.randomBytes(40)),
|
|
126
|
-
getDSTLabel: () => htfDefaults.DST,
|
|
127
|
-
setDSTLabel(newLabel) {
|
|
128
|
-
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3.1
|
|
129
|
-
if (typeof newLabel !== 'string' || newLabel.length > 2048 || newLabel.length === 0) {
|
|
130
|
-
throw new TypeError('Invalid DST');
|
|
131
|
-
}
|
|
132
|
-
htfDefaults.DST = newLabel;
|
|
133
|
-
},
|
|
77
|
+
hashToField: (msg, count, options = {}) => htf.hash_to_field(msg, count, { ...CURVE.htfDefaults, ...options }),
|
|
78
|
+
expandMessageXMD: (msg, DST, lenInBytes, H = CURVE.hash) => htf.expand_message_xmd(msg, DST, lenInBytes, H),
|
|
79
|
+
hashToPrivateKey: (hash) => Fr.toBytes(hashToPrivateScalar(hash, CURVE.r)),
|
|
80
|
+
randomBytes: (bytesLength = groupLen) => CURVE.randomBytes(bytesLength),
|
|
81
|
+
randomPrivateKey: () => utils.hashToPrivateKey(utils.randomBytes(groupLen + 8)),
|
|
134
82
|
};
|
|
135
|
-
function normalizePrivKey(key) {
|
|
136
|
-
let int;
|
|
137
|
-
if (key instanceof Uint8Array && key.length === 32)
|
|
138
|
-
int = bytesToNumberBE(key);
|
|
139
|
-
else if (typeof key === 'string' && key.length === 64)
|
|
140
|
-
int = BigInt(`0x${key}`);
|
|
141
|
-
else if (typeof key === 'number' && key > 0 && Number.isSafeInteger(key))
|
|
142
|
-
int = BigInt(key);
|
|
143
|
-
else if (typeof key === 'bigint' && key > 0n)
|
|
144
|
-
int = key;
|
|
145
|
-
else
|
|
146
|
-
throw new TypeError('Expected valid private key');
|
|
147
|
-
int = mod.mod(int, CURVE.r);
|
|
148
|
-
if (!isWithinCurveOrder(int))
|
|
149
|
-
throw new Error('Private key must be 0 < key < CURVE.r');
|
|
150
|
-
return int;
|
|
151
|
-
}
|
|
152
83
|
// Point on G1 curve: (x, y)
|
|
153
84
|
const G1 = weierstrassPoints({
|
|
154
85
|
n: Fr.ORDER,
|
|
155
86
|
...CURVE.G1,
|
|
156
87
|
});
|
|
88
|
+
const G1HashToCurve = htf.hashToCurve(G1.ProjectivePoint, CURVE.G1.mapToCurve, {
|
|
89
|
+
...CURVE.htfDefaults,
|
|
90
|
+
...CURVE.G1.htfDefaults,
|
|
91
|
+
});
|
|
157
92
|
function pairingPrecomputes(point) {
|
|
158
93
|
const p = point;
|
|
159
94
|
if (p._PPRECOMPUTES)
|
|
160
95
|
return p._PPRECOMPUTES;
|
|
161
|
-
p._PPRECOMPUTES = calcPairingPrecomputes(
|
|
96
|
+
p._PPRECOMPUTES = calcPairingPrecomputes(point.toAffine());
|
|
162
97
|
return p._PPRECOMPUTES;
|
|
163
98
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
function millerLoopG1(Q, P) {
|
|
170
|
-
return millerLoop(pairingPrecomputes(P), [Q.x, Q.y]);
|
|
171
|
-
}
|
|
99
|
+
// TODO: export
|
|
100
|
+
// function clearPairingPrecomputes(point: G2) {
|
|
101
|
+
// const p = point as G2 & withPairingPrecomputes;
|
|
102
|
+
// p._PPRECOMPUTES = undefined;
|
|
103
|
+
// }
|
|
172
104
|
// Point on G2 curve (complex numbers): (x₁, x₂+i), (y₁, y₂+i)
|
|
173
105
|
const G2 = weierstrassPoints({
|
|
174
106
|
n: Fr.ORDER,
|
|
175
107
|
...CURVE.G2,
|
|
176
108
|
});
|
|
109
|
+
const C = G2.ProjectivePoint; // TODO: fix
|
|
110
|
+
const G2HashToCurve = htf.hashToCurve(C, CURVE.G2.mapToCurve, {
|
|
111
|
+
...CURVE.htfDefaults,
|
|
112
|
+
...CURVE.G2.htfDefaults,
|
|
113
|
+
});
|
|
177
114
|
const { Signature } = CURVE.G2;
|
|
178
115
|
// Calculates bilinear pairing
|
|
179
|
-
function pairing(
|
|
180
|
-
if (
|
|
181
|
-
throw new Error('
|
|
182
|
-
P.assertValidity();
|
|
116
|
+
function pairing(Q, P, withFinalExponent = true) {
|
|
117
|
+
if (Q.equals(G1.ProjectivePoint.ZERO) || P.equals(G2.ProjectivePoint.ZERO))
|
|
118
|
+
throw new Error('pairing is not available for ZERO point');
|
|
183
119
|
Q.assertValidity();
|
|
120
|
+
P.assertValidity();
|
|
184
121
|
// Performance: 9ms for millerLoop and ~14ms for exp.
|
|
185
|
-
const
|
|
122
|
+
const Qa = Q.toAffine();
|
|
123
|
+
const looped = millerLoop(pairingPrecomputes(P), [Qa.x, Qa.y]);
|
|
186
124
|
return withFinalExponent ? Fp12.finalExponentiate(looped) : looped;
|
|
187
125
|
}
|
|
188
126
|
function normP1(point) {
|
|
189
|
-
return point instanceof G1.
|
|
127
|
+
return point instanceof G1.ProjectivePoint ? point : G1.ProjectivePoint.fromHex(point);
|
|
190
128
|
}
|
|
191
129
|
function normP2(point) {
|
|
192
|
-
return point instanceof G2.
|
|
130
|
+
return point instanceof G2.ProjectivePoint ? point : Signature.decode(point);
|
|
193
131
|
}
|
|
194
|
-
function normP2Hash(point) {
|
|
195
|
-
return point instanceof G2.
|
|
132
|
+
function normP2Hash(point, htfOpts) {
|
|
133
|
+
return point instanceof G2.ProjectivePoint
|
|
134
|
+
? point
|
|
135
|
+
: G2HashToCurve.hashToCurve(point, htfOpts);
|
|
196
136
|
}
|
|
197
137
|
// Multiplies generator by private key.
|
|
198
138
|
// P = pk x G
|
|
199
139
|
function getPublicKey(privateKey) {
|
|
200
|
-
return G1.
|
|
140
|
+
return G1.ProjectivePoint.fromPrivateKey(privateKey).toRawBytes(true);
|
|
201
141
|
}
|
|
202
|
-
function sign(message, privateKey) {
|
|
203
|
-
const msgPoint = normP2Hash(message);
|
|
142
|
+
function sign(message, privateKey, htfOpts) {
|
|
143
|
+
const msgPoint = normP2Hash(message, htfOpts);
|
|
204
144
|
msgPoint.assertValidity();
|
|
205
|
-
const sigPoint = msgPoint.multiply(
|
|
206
|
-
if (message instanceof G2.
|
|
145
|
+
const sigPoint = msgPoint.multiply(G1.normalizePrivateKey(privateKey));
|
|
146
|
+
if (message instanceof G2.ProjectivePoint)
|
|
207
147
|
return sigPoint;
|
|
208
148
|
return Signature.encode(sigPoint);
|
|
209
149
|
}
|
|
210
150
|
// Checks if pairing of public key & hash is equal to pairing of generator & signature.
|
|
211
151
|
// e(P, H(m)) == e(G, S)
|
|
212
|
-
function verify(signature, message, publicKey) {
|
|
152
|
+
function verify(signature, message, publicKey, htfOpts) {
|
|
213
153
|
const P = normP1(publicKey);
|
|
214
|
-
const Hm = normP2Hash(message);
|
|
215
|
-
const G = G1.
|
|
154
|
+
const Hm = normP2Hash(message, htfOpts);
|
|
155
|
+
const G = G1.ProjectivePoint.BASE;
|
|
216
156
|
const S = normP2(signature);
|
|
217
157
|
// Instead of doing 2 exponentiations, we use property of billinear maps
|
|
218
158
|
// and do one exp after multiplying 2 points.
|
|
219
159
|
const ePHm = pairing(P.negate(), Hm, false);
|
|
220
160
|
const eGS = pairing(G, S, false);
|
|
221
161
|
const exp = Fp12.finalExponentiate(Fp12.mul(eGS, ePHm));
|
|
222
|
-
return Fp12.
|
|
162
|
+
return Fp12.eql(exp, Fp12.ONE);
|
|
223
163
|
}
|
|
224
164
|
function aggregatePublicKeys(publicKeys) {
|
|
225
165
|
if (!publicKeys.length)
|
|
226
166
|
throw new Error('Expected non-empty array');
|
|
227
|
-
const agg = publicKeys
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
const aggAffine = agg.toAffine();
|
|
231
|
-
if (publicKeys[0] instanceof G1.Point) {
|
|
167
|
+
const agg = publicKeys.map(normP1).reduce((sum, p) => sum.add(p), G1.ProjectivePoint.ZERO);
|
|
168
|
+
const aggAffine = agg; //.toAffine();
|
|
169
|
+
if (publicKeys[0] instanceof G1.ProjectivePoint) {
|
|
232
170
|
aggAffine.assertValidity();
|
|
233
171
|
return aggAffine;
|
|
234
172
|
}
|
|
@@ -238,11 +176,9 @@ export function bls(CURVE) {
|
|
|
238
176
|
function aggregateSignatures(signatures) {
|
|
239
177
|
if (!signatures.length)
|
|
240
178
|
throw new Error('Expected non-empty array');
|
|
241
|
-
const agg = signatures
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const aggAffine = agg.toAffine();
|
|
245
|
-
if (signatures[0] instanceof G2.Point) {
|
|
179
|
+
const agg = signatures.map(normP2).reduce((sum, s) => sum.add(s), G2.ProjectivePoint.ZERO);
|
|
180
|
+
const aggAffine = agg; //.toAffine();
|
|
181
|
+
if (signatures[0] instanceof G2.ProjectivePoint) {
|
|
246
182
|
aggAffine.assertValidity();
|
|
247
183
|
return aggAffine;
|
|
248
184
|
}
|
|
@@ -250,33 +186,34 @@ export function bls(CURVE) {
|
|
|
250
186
|
}
|
|
251
187
|
// https://ethresear.ch/t/fast-verification-of-multiple-bls-signatures/5407
|
|
252
188
|
// e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))
|
|
253
|
-
function verifyBatch(signature, messages, publicKeys) {
|
|
189
|
+
function verifyBatch(signature, messages, publicKeys, htfOpts) {
|
|
190
|
+
// @ts-ignore
|
|
191
|
+
// console.log('verifyBatch', bytesToHex(signature as any), messages, publicKeys.map(bytesToHex));
|
|
254
192
|
if (!messages.length)
|
|
255
193
|
throw new Error('Expected non-empty messages array');
|
|
256
194
|
if (publicKeys.length !== messages.length)
|
|
257
195
|
throw new Error('Pubkey count should equal msg count');
|
|
258
196
|
const sig = normP2(signature);
|
|
259
|
-
const nMessages = messages.map(normP2Hash);
|
|
197
|
+
const nMessages = messages.map((i) => normP2Hash(i, htfOpts));
|
|
260
198
|
const nPublicKeys = publicKeys.map(normP1);
|
|
261
199
|
try {
|
|
262
200
|
const paired = [];
|
|
263
201
|
for (const message of new Set(nMessages)) {
|
|
264
|
-
const groupPublicKey = nMessages.reduce((groupPublicKey, subMessage, i) => subMessage === message ? groupPublicKey.add(nPublicKeys[i]) : groupPublicKey, G1.
|
|
202
|
+
const groupPublicKey = nMessages.reduce((groupPublicKey, subMessage, i) => subMessage === message ? groupPublicKey.add(nPublicKeys[i]) : groupPublicKey, G1.ProjectivePoint.ZERO);
|
|
265
203
|
// const msg = message instanceof PointG2 ? message : await PointG2.hashToCurve(message);
|
|
266
204
|
// Possible to batch pairing for same msg with different groupPublicKey here
|
|
267
205
|
paired.push(pairing(groupPublicKey, message, false));
|
|
268
206
|
}
|
|
269
|
-
paired.push(pairing(G1.
|
|
207
|
+
paired.push(pairing(G1.ProjectivePoint.BASE.negate(), sig, false));
|
|
270
208
|
const product = paired.reduce((a, b) => Fp12.mul(a, b), Fp12.ONE);
|
|
271
209
|
const exp = Fp12.finalExponentiate(product);
|
|
272
|
-
return Fp12.
|
|
210
|
+
return Fp12.eql(exp, Fp12.ONE);
|
|
273
211
|
}
|
|
274
212
|
catch {
|
|
275
213
|
return false;
|
|
276
214
|
}
|
|
277
215
|
}
|
|
278
|
-
|
|
279
|
-
G1.Point.BASE._setWindowSize(4);
|
|
216
|
+
G1.ProjectivePoint.BASE._setWindowSize(4);
|
|
280
217
|
return {
|
|
281
218
|
CURVE,
|
|
282
219
|
Fr,
|
|
@@ -289,6 +226,7 @@ export function bls(CURVE) {
|
|
|
289
226
|
Signature,
|
|
290
227
|
millerLoop,
|
|
291
228
|
calcPairingPrecomputes,
|
|
229
|
+
hashToCurve: { G1: G1HashToCurve, G2: G2HashToCurve },
|
|
292
230
|
pairing,
|
|
293
231
|
getPublicKey,
|
|
294
232
|
sign,
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2
2
|
// Abelian group utilities
|
|
3
|
+
import { validateField, nLength } from './modular.js';
|
|
3
4
|
const _0n = BigInt(0);
|
|
4
5
|
const _1n = BigInt(1);
|
|
5
|
-
//
|
|
6
|
+
// Elliptic curve multiplication of Point by scalar. Complicated and fragile. Uses wNAF method.
|
|
7
|
+
// Windowed method is 10% faster, but takes 2x longer to generate & consumes 2x memory.
|
|
6
8
|
export function wNAF(c, bits) {
|
|
7
9
|
const constTimeNegate = (condition, item) => {
|
|
8
10
|
const neg = item.negate();
|
|
@@ -104,5 +106,39 @@ export function wNAF(c, bits) {
|
|
|
104
106
|
// which makes it less const-time: around 1 bigint multiply.
|
|
105
107
|
return { p, f };
|
|
106
108
|
},
|
|
109
|
+
wNAFCached(P, precomputesMap, n, transform) {
|
|
110
|
+
// @ts-ignore
|
|
111
|
+
const W = P._WINDOW_SIZE || 1;
|
|
112
|
+
// Calculate precomputes on a first run, reuse them after
|
|
113
|
+
let comp = precomputesMap.get(P);
|
|
114
|
+
if (!comp) {
|
|
115
|
+
comp = this.precomputeWindow(P, W);
|
|
116
|
+
if (W !== 1) {
|
|
117
|
+
precomputesMap.set(P, transform(comp));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return this.wNAF(W, comp, n);
|
|
121
|
+
},
|
|
107
122
|
};
|
|
108
123
|
}
|
|
124
|
+
export function validateAbsOpts(curve) {
|
|
125
|
+
validateField(curve.Fp);
|
|
126
|
+
for (const i of ['n', 'h']) {
|
|
127
|
+
const val = curve[i];
|
|
128
|
+
if (typeof val !== 'bigint')
|
|
129
|
+
throw new Error(`Invalid curve param ${i}=${val} (${typeof val})`);
|
|
130
|
+
}
|
|
131
|
+
if (!curve.Fp.isValid(curve.Gx))
|
|
132
|
+
throw new Error('Invalid generator X coordinate Fp element');
|
|
133
|
+
if (!curve.Fp.isValid(curve.Gy))
|
|
134
|
+
throw new Error('Invalid generator Y coordinate Fp element');
|
|
135
|
+
for (const i of ['nBitLength', 'nByteLength']) {
|
|
136
|
+
const val = curve[i];
|
|
137
|
+
if (val === undefined)
|
|
138
|
+
continue; // Optional
|
|
139
|
+
if (!Number.isSafeInteger(val))
|
|
140
|
+
throw new Error(`Invalid param ${i}=${val} (${typeof val})`);
|
|
141
|
+
}
|
|
142
|
+
// Set defaults
|
|
143
|
+
return Object.freeze({ ...nLength(curve.n, curve.nBitLength), ...curve });
|
|
144
|
+
}
|