@noble/curves 0.9.0 → 1.0.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 +197 -99
- package/abstract/bls.d.ts +43 -31
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/bls.js +37 -28
- package/abstract/bls.js.map +1 -1
- package/abstract/edwards.d.ts +6 -2
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +37 -26
- package/abstract/edwards.js.map +1 -1
- package/abstract/hash-to-curve.d.ts +1 -1
- package/abstract/hash-to-curve.d.ts.map +1 -1
- package/abstract/hash-to-curve.js +3 -2
- package/abstract/hash-to-curve.js.map +1 -1
- package/abstract/modular.js +1 -1
- package/abstract/modular.js.map +1 -1
- package/abstract/utils.d.ts.map +1 -1
- package/abstract/utils.js +2 -2
- package/abstract/utils.js.map +1 -1
- package/abstract/weierstrass.d.ts +23 -0
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +44 -31
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts.map +1 -1
- package/bls12-381.js +161 -162
- package/bls12-381.js.map +1 -1
- package/{bn.d.ts → bn254.d.ts} +1 -1
- package/bn254.d.ts.map +1 -0
- package/{bn.js → bn254.js} +1 -1
- package/bn254.js.map +1 -0
- package/ed25519.d.ts +9 -0
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +23 -9
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +9 -0
- package/ed448.d.ts.map +1 -1
- package/ed448.js +19 -18
- package/ed448.js.map +1 -1
- package/esm/abstract/bls.js +37 -28
- package/esm/abstract/bls.js.map +1 -1
- package/esm/abstract/edwards.js +37 -26
- package/esm/abstract/edwards.js.map +1 -1
- package/esm/abstract/hash-to-curve.js +3 -2
- package/esm/abstract/hash-to-curve.js.map +1 -1
- package/esm/abstract/modular.js +1 -1
- package/esm/abstract/modular.js.map +1 -1
- package/esm/abstract/utils.js +2 -2
- package/esm/abstract/utils.js.map +1 -1
- package/esm/abstract/weierstrass.js +36 -23
- package/esm/abstract/weierstrass.js.map +1 -1
- package/esm/bls12-381.js +162 -163
- package/esm/bls12-381.js.map +1 -1
- package/esm/{bn.js → bn254.js} +1 -1
- package/esm/bn254.js.map +1 -0
- package/esm/ed25519.js +21 -8
- package/esm/ed25519.js.map +1 -1
- package/esm/ed448.js +17 -17
- package/esm/ed448.js.map +1 -1
- package/esm/p256.js +5 -6
- package/esm/p256.js.map +1 -1
- package/esm/p384.js +10 -12
- package/esm/p384.js.map +1 -1
- package/esm/p521.js +22 -18
- package/esm/p521.js.map +1 -1
- package/esm/secp256k1.js +1 -1
- package/esm/secp256k1.js.map +1 -1
- package/p256.d.ts +1 -1
- package/p256.d.ts.map +1 -1
- package/p256.js +6 -7
- package/p256.js.map +1 -1
- package/p384.d.ts +1 -1
- package/p384.d.ts.map +1 -1
- package/p384.js +11 -13
- package/p384.js.map +1 -1
- package/p521.d.ts +1 -1
- package/p521.d.ts.map +1 -1
- package/p521.js +23 -19
- package/p521.js.map +1 -1
- package/package.json +5 -8
- package/secp256k1.js +1 -1
- package/secp256k1.js.map +1 -1
- package/src/abstract/bls.ts +83 -61
- package/src/abstract/edwards.ts +49 -24
- package/src/abstract/hash-to-curve.ts +4 -3
- package/src/abstract/modular.ts +1 -1
- package/src/abstract/utils.ts +2 -2
- package/src/abstract/weierstrass.ts +36 -23
- package/src/bls12-381.ts +252 -171
- package/src/ed25519.ts +23 -9
- package/src/ed448.ts +18 -17
- package/src/p256.ts +15 -19
- package/src/p384.ts +17 -21
- package/src/p521.ts +34 -22
- package/src/secp256k1.ts +1 -1
- package/bn.d.ts.map +0 -1
- package/bn.js.map +0 -1
- package/esm/bn.js.map +0 -1
- /package/src/{bn.ts → bn254.ts} +0 -0
package/src/abstract/bls.ts
CHANGED
|
@@ -24,13 +24,16 @@ import {
|
|
|
24
24
|
|
|
25
25
|
type Fp = bigint; // Can be different field?
|
|
26
26
|
|
|
27
|
+
// prettier-ignore
|
|
28
|
+
const _2n = BigInt(2), _3n = BigInt(3);
|
|
29
|
+
|
|
27
30
|
export type SignatureCoder<Fp2> = {
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
fromHex(hex: Hex): ProjPointType<Fp2>;
|
|
32
|
+
toRawBytes(point: ProjPointType<Fp2>): Uint8Array;
|
|
33
|
+
toHex(point: ProjPointType<Fp2>): string;
|
|
30
34
|
};
|
|
31
35
|
|
|
32
36
|
export type CurveType<Fp, Fp2, Fp6, Fp12> = {
|
|
33
|
-
r: bigint;
|
|
34
37
|
G1: Omit<CurvePointsType<Fp>, 'n'> & {
|
|
35
38
|
mapToCurve: htf.MapToCurve<Fp>;
|
|
36
39
|
htfDefaults: htf.Opts;
|
|
@@ -40,20 +43,25 @@ export type CurveType<Fp, Fp2, Fp6, Fp12> = {
|
|
|
40
43
|
mapToCurve: htf.MapToCurve<Fp2>;
|
|
41
44
|
htfDefaults: htf.Opts;
|
|
42
45
|
};
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
fields: {
|
|
47
|
+
Fp: IField<Fp>;
|
|
48
|
+
Fr: IField<bigint>;
|
|
49
|
+
Fp2: IField<Fp2> & {
|
|
50
|
+
reim: (num: Fp2) => { re: bigint; im: bigint };
|
|
51
|
+
multiplyByB: (num: Fp2) => Fp2;
|
|
52
|
+
frobeniusMap(num: Fp2, power: number): Fp2;
|
|
53
|
+
};
|
|
54
|
+
Fp6: IField<Fp6>;
|
|
55
|
+
Fp12: IField<Fp12> & {
|
|
56
|
+
frobeniusMap(num: Fp12, power: number): Fp12;
|
|
57
|
+
multiplyBy014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
|
|
58
|
+
conjugate(num: Fp12): Fp12;
|
|
59
|
+
finalExponentiate(num: Fp12): Fp12;
|
|
60
|
+
};
|
|
50
61
|
};
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
multiplyBy014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
|
|
55
|
-
conjugate(num: Fp12): Fp12;
|
|
56
|
-
finalExponentiate(num: Fp12): Fp12;
|
|
62
|
+
params: {
|
|
63
|
+
x: bigint;
|
|
64
|
+
r: bigint;
|
|
57
65
|
};
|
|
58
66
|
htfDefaults: htf.Opts;
|
|
59
67
|
hash: CHash; // Because we need outputLen for DRBG
|
|
@@ -61,18 +69,6 @@ export type CurveType<Fp, Fp2, Fp6, Fp12> = {
|
|
|
61
69
|
};
|
|
62
70
|
|
|
63
71
|
export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
|
|
64
|
-
CURVE: CurveType<Fp, Fp2, Fp6, Fp12>;
|
|
65
|
-
Fr: IField<bigint>;
|
|
66
|
-
Fp: IField<Fp>;
|
|
67
|
-
Fp2: IField<Fp2>;
|
|
68
|
-
Fp6: IField<Fp6>;
|
|
69
|
-
Fp12: IField<Fp12>;
|
|
70
|
-
G1: CurvePointsRes<Fp> & ReturnType<typeof htf.createHasher<Fp>>;
|
|
71
|
-
G2: CurvePointsRes<Fp2> & ReturnType<typeof htf.createHasher<Fp2>>;
|
|
72
|
-
Signature: SignatureCoder<Fp2>;
|
|
73
|
-
millerLoop: (ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]) => Fp12;
|
|
74
|
-
calcPairingPrecomputes: (p: AffinePoint<Fp2>) => [Fp2, Fp2, Fp2][];
|
|
75
|
-
pairing: (P: ProjPointType<Fp>, Q: ProjPointType<Fp2>, withFinalExponent?: boolean) => Fp12;
|
|
76
72
|
getPublicKey: (privateKey: PrivKey) => Uint8Array;
|
|
77
73
|
sign: {
|
|
78
74
|
(message: Hex, privateKey: PrivKey): Uint8Array;
|
|
@@ -83,6 +79,11 @@ export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
|
|
|
83
79
|
message: Hex | ProjPointType<Fp2>,
|
|
84
80
|
publicKey: Hex | ProjPointType<Fp>
|
|
85
81
|
) => boolean;
|
|
82
|
+
verifyBatch: (
|
|
83
|
+
signature: Hex | ProjPointType<Fp2>,
|
|
84
|
+
messages: (Hex | ProjPointType<Fp2>)[],
|
|
85
|
+
publicKeys: (Hex | ProjPointType<Fp>)[]
|
|
86
|
+
) => boolean;
|
|
86
87
|
aggregatePublicKeys: {
|
|
87
88
|
(publicKeys: Hex[]): Uint8Array;
|
|
88
89
|
(publicKeys: ProjPointType<Fp>[]): ProjPointType<Fp>;
|
|
@@ -91,22 +92,36 @@ export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
|
|
|
91
92
|
(signatures: Hex[]): Uint8Array;
|
|
92
93
|
(signatures: ProjPointType<Fp2>[]): ProjPointType<Fp2>;
|
|
93
94
|
};
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
millerLoop: (ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]) => Fp12;
|
|
96
|
+
pairing: (P: ProjPointType<Fp>, Q: ProjPointType<Fp2>, withFinalExponent?: boolean) => Fp12;
|
|
97
|
+
G1: CurvePointsRes<Fp> & ReturnType<typeof htf.createHasher<Fp>>;
|
|
98
|
+
G2: CurvePointsRes<Fp2> & ReturnType<typeof htf.createHasher<Fp2>>;
|
|
99
|
+
Signature: SignatureCoder<Fp2>;
|
|
100
|
+
params: {
|
|
101
|
+
x: bigint;
|
|
102
|
+
r: bigint;
|
|
103
|
+
G1b: bigint;
|
|
104
|
+
G2b: Fp2;
|
|
105
|
+
};
|
|
106
|
+
fields: {
|
|
107
|
+
Fp: IField<Fp>;
|
|
108
|
+
Fp2: IField<Fp2>;
|
|
109
|
+
Fp6: IField<Fp6>;
|
|
110
|
+
Fp12: IField<Fp12>;
|
|
111
|
+
Fr: IField<bigint>;
|
|
112
|
+
};
|
|
99
113
|
utils: {
|
|
100
114
|
randomPrivateKey: () => Uint8Array;
|
|
115
|
+
calcPairingPrecomputes: (p: AffinePoint<Fp2>) => [Fp2, Fp2, Fp2][];
|
|
101
116
|
};
|
|
102
117
|
};
|
|
103
118
|
|
|
104
119
|
export function bls<Fp2, Fp6, Fp12>(
|
|
105
120
|
CURVE: CurveType<Fp, Fp2, Fp6, Fp12>
|
|
106
121
|
): CurveFn<Fp, Fp2, Fp6, Fp12> {
|
|
107
|
-
// Fields
|
|
108
|
-
const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE;
|
|
109
|
-
const BLS_X_LEN = bitLen(CURVE.x);
|
|
122
|
+
// Fields are specific for curve, so for now we'll need to pass them with opts
|
|
123
|
+
const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE.fields;
|
|
124
|
+
const BLS_X_LEN = bitLen(CURVE.params.x);
|
|
110
125
|
const groupLen = 32; // TODO: calculate; hardcoded for now
|
|
111
126
|
|
|
112
127
|
// Pre-compute coefficients for sparse multiplication
|
|
@@ -122,18 +137,18 @@ export function bls<Fp2, Fp6, Fp12>(
|
|
|
122
137
|
// Double
|
|
123
138
|
let t0 = Fp2.sqr(Ry); // Ry²
|
|
124
139
|
let t1 = Fp2.sqr(Rz); // Rz²
|
|
125
|
-
let t2 = Fp2.multiplyByB(Fp2.mul(t1,
|
|
126
|
-
let t3 = Fp2.mul(t2,
|
|
140
|
+
let t2 = Fp2.multiplyByB(Fp2.mul(t1, _3n)); // 3 * T1 * B
|
|
141
|
+
let t3 = Fp2.mul(t2, _3n); // 3 * T2
|
|
127
142
|
let t4 = Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(Ry, Rz)), t1), t0); // (Ry + Rz)² - T1 - T0
|
|
128
143
|
ell_coeff.push([
|
|
129
144
|
Fp2.sub(t2, t0), // T2 - T0
|
|
130
|
-
Fp2.mul(Fp2.sqr(Rx),
|
|
145
|
+
Fp2.mul(Fp2.sqr(Rx), _3n), // 3 * Rx²
|
|
131
146
|
Fp2.neg(t4), // -T4
|
|
132
147
|
]);
|
|
133
|
-
Rx = Fp2.div(Fp2.mul(Fp2.mul(Fp2.sub(t0, t3), Rx), Ry),
|
|
134
|
-
Ry = Fp2.sub(Fp2.sqr(Fp2.div(Fp2.add(t0, t3),
|
|
148
|
+
Rx = Fp2.div(Fp2.mul(Fp2.mul(Fp2.sub(t0, t3), Rx), Ry), _2n); // ((T0 - T3) * Rx * Ry) / 2
|
|
149
|
+
Ry = Fp2.sub(Fp2.sqr(Fp2.div(Fp2.add(t0, t3), _2n)), Fp2.mul(Fp2.sqr(t2), _3n)); // ((T0 + T3) / 2)² - 3 * T2²
|
|
135
150
|
Rz = Fp2.mul(t0, t4); // T0 * T4
|
|
136
|
-
if (bitGet(CURVE.x, i)) {
|
|
151
|
+
if (bitGet(CURVE.params.x, i)) {
|
|
137
152
|
// Addition
|
|
138
153
|
let t0 = Fp2.sub(Ry, Fp2.mul(Qy, Rz)); // Ry - Qy * Rz
|
|
139
154
|
let t1 = Fp2.sub(Rx, Fp2.mul(Qx, Rz)); // Rx - Qx * Rz
|
|
@@ -145,7 +160,7 @@ export function bls<Fp2, Fp6, Fp12>(
|
|
|
145
160
|
let t2 = Fp2.sqr(t1); // T1²
|
|
146
161
|
let t3 = Fp2.mul(t2, t1); // T2 * T1
|
|
147
162
|
let t4 = Fp2.mul(t2, Rx); // T2 * Rx
|
|
148
|
-
let t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4,
|
|
163
|
+
let t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4, _2n)), Fp2.mul(Fp2.sqr(t0), Rz)); // T3 - 2 * T4 + T0² * Rz
|
|
149
164
|
Rx = Fp2.mul(t1, t5); // T1 * T5
|
|
150
165
|
Ry = Fp2.sub(Fp2.mul(Fp2.sub(t4, t5), t0), Fp2.mul(t3, Ry)); // (T4 - T5) * T0 - T3 * Ry
|
|
151
166
|
Rz = Fp2.mul(Rz, t3); // Rz * T3
|
|
@@ -155,7 +170,7 @@ export function bls<Fp2, Fp6, Fp12>(
|
|
|
155
170
|
}
|
|
156
171
|
|
|
157
172
|
function millerLoop(ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]): Fp12 {
|
|
158
|
-
const { x } = CURVE;
|
|
173
|
+
const { x } = CURVE.params;
|
|
159
174
|
const Px = g1[0];
|
|
160
175
|
const Py = g1[1];
|
|
161
176
|
let f12 = Fp12.ONE;
|
|
@@ -174,8 +189,9 @@ export function bls<Fp2, Fp6, Fp12>(
|
|
|
174
189
|
|
|
175
190
|
const utils = {
|
|
176
191
|
randomPrivateKey: (): Uint8Array => {
|
|
177
|
-
return Fr.toBytes(hashToPrivateScalar(CURVE.randomBytes(groupLen + 8), CURVE.r));
|
|
192
|
+
return Fr.toBytes(hashToPrivateScalar(CURVE.randomBytes(groupLen + 8), CURVE.params.r));
|
|
178
193
|
},
|
|
194
|
+
calcPairingPrecomputes,
|
|
179
195
|
};
|
|
180
196
|
|
|
181
197
|
// Point on G1 curve: (x, y)
|
|
@@ -236,7 +252,7 @@ export function bls<Fp2, Fp6, Fp12>(
|
|
|
236
252
|
return point instanceof G1.ProjectivePoint ? (point as G1) : G1.ProjectivePoint.fromHex(point);
|
|
237
253
|
}
|
|
238
254
|
function normP2(point: G2Hex): G2 {
|
|
239
|
-
return point instanceof G2.ProjectivePoint ? point : Signature.
|
|
255
|
+
return point instanceof G2.ProjectivePoint ? point : Signature.fromHex(point);
|
|
240
256
|
}
|
|
241
257
|
function normP2Hash(point: G2Hex, htfOpts?: htf.htfBasicOpts): G2 {
|
|
242
258
|
return point instanceof G2.ProjectivePoint
|
|
@@ -259,7 +275,7 @@ export function bls<Fp2, Fp6, Fp12>(
|
|
|
259
275
|
msgPoint.assertValidity();
|
|
260
276
|
const sigPoint = msgPoint.multiply(G1.normPrivateKeyToScalar(privateKey));
|
|
261
277
|
if (message instanceof G2.ProjectivePoint) return sigPoint;
|
|
262
|
-
return Signature.
|
|
278
|
+
return Signature.toRawBytes(sigPoint);
|
|
263
279
|
}
|
|
264
280
|
|
|
265
281
|
// Checks if pairing of public key & hash is equal to pairing of generator & signature.
|
|
@@ -309,7 +325,7 @@ export function bls<Fp2, Fp6, Fp12>(
|
|
|
309
325
|
aggAffine.assertValidity();
|
|
310
326
|
return aggAffine;
|
|
311
327
|
}
|
|
312
|
-
return Signature.
|
|
328
|
+
return Signature.toRawBytes(aggAffine);
|
|
313
329
|
}
|
|
314
330
|
|
|
315
331
|
// https://ethresear.ch/t/fast-verification-of-multiple-bls-signatures/5407
|
|
@@ -353,24 +369,30 @@ export function bls<Fp2, Fp6, Fp12>(
|
|
|
353
369
|
G1.ProjectivePoint.BASE._setWindowSize(4);
|
|
354
370
|
|
|
355
371
|
return {
|
|
356
|
-
CURVE,
|
|
357
|
-
Fr,
|
|
358
|
-
Fp,
|
|
359
|
-
Fp2,
|
|
360
|
-
Fp6,
|
|
361
|
-
Fp12,
|
|
362
|
-
G1,
|
|
363
|
-
G2,
|
|
364
|
-
Signature,
|
|
365
|
-
millerLoop,
|
|
366
|
-
calcPairingPrecomputes,
|
|
367
|
-
pairing,
|
|
368
372
|
getPublicKey,
|
|
369
373
|
sign,
|
|
370
374
|
verify,
|
|
375
|
+
verifyBatch,
|
|
371
376
|
aggregatePublicKeys,
|
|
372
377
|
aggregateSignatures,
|
|
373
|
-
|
|
378
|
+
millerLoop,
|
|
379
|
+
pairing,
|
|
380
|
+
G1,
|
|
381
|
+
G2,
|
|
382
|
+
Signature,
|
|
383
|
+
fields: {
|
|
384
|
+
Fr,
|
|
385
|
+
Fp,
|
|
386
|
+
Fp2,
|
|
387
|
+
Fp6,
|
|
388
|
+
Fp12,
|
|
389
|
+
},
|
|
390
|
+
params: {
|
|
391
|
+
x: CURVE.params.x,
|
|
392
|
+
r: CURVE.params.r,
|
|
393
|
+
G1b: CURVE.G1.b,
|
|
394
|
+
G2b: CURVE.G2.b,
|
|
395
|
+
},
|
|
374
396
|
utils,
|
|
375
397
|
};
|
|
376
398
|
}
|
package/src/abstract/edwards.ts
CHANGED
|
@@ -5,11 +5,9 @@ import * as ut from './utils.js';
|
|
|
5
5
|
import { ensureBytes, FHash, Hex } from './utils.js';
|
|
6
6
|
import { Group, GroupConstructor, wNAF, BasicCurve, validateBasic, AffinePoint } from './curve.js';
|
|
7
7
|
|
|
8
|
-
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
|
9
|
-
|
|
10
|
-
const _1n = BigInt(1);
|
|
11
|
-
const _2n = BigInt(2);
|
|
12
|
-
const _8n = BigInt(8);
|
|
8
|
+
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
|
9
|
+
// prettier-ignore
|
|
10
|
+
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _8n = BigInt(8);
|
|
13
11
|
|
|
14
12
|
// Edwards curves must declare params a & d.
|
|
15
13
|
export type CurveType = BasicCurve<bigint> & {
|
|
@@ -20,10 +18,13 @@ export type CurveType = BasicCurve<bigint> & {
|
|
|
20
18
|
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array; // clears bits to get valid field elemtn
|
|
21
19
|
domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array; // Used for hashing
|
|
22
20
|
uvRatio?: (u: bigint, v: bigint) => { isValid: boolean; value: bigint }; // Ratio √(u/v)
|
|
23
|
-
|
|
21
|
+
prehash?: FHash; // RFC 8032 pre-hashing of messages to sign() / verify()
|
|
24
22
|
mapToCurve?: (scalar: bigint[]) => AffinePoint<bigint>; // for hash-to-curve standard
|
|
25
23
|
};
|
|
26
24
|
|
|
25
|
+
// verification rule is either zip215 or rfc8032 / nist186-5. Consult fromHex:
|
|
26
|
+
const VERIFY_DEFAULT = { zip215: true };
|
|
27
|
+
|
|
27
28
|
function validateOpts(curve: CurveType) {
|
|
28
29
|
const opts = validateBasic(curve);
|
|
29
30
|
ut.validateObject(
|
|
@@ -51,6 +52,8 @@ export interface ExtPointType extends Group<ExtPointType> {
|
|
|
51
52
|
readonly ey: bigint;
|
|
52
53
|
readonly ez: bigint;
|
|
53
54
|
readonly et: bigint;
|
|
55
|
+
get x(): bigint;
|
|
56
|
+
get y(): bigint;
|
|
54
57
|
assertValidity(): void;
|
|
55
58
|
multiply(scalar: bigint): ExtPointType;
|
|
56
59
|
multiplyUnsafe(scalar: bigint): ExtPointType;
|
|
@@ -58,6 +61,8 @@ export interface ExtPointType extends Group<ExtPointType> {
|
|
|
58
61
|
isTorsionFree(): boolean;
|
|
59
62
|
clearCofactor(): ExtPointType;
|
|
60
63
|
toAffine(iz?: bigint): AffinePoint<bigint>;
|
|
64
|
+
toRawBytes(isCompressed?: boolean): Uint8Array;
|
|
65
|
+
toHex(isCompressed?: boolean): string;
|
|
61
66
|
}
|
|
62
67
|
// Static methods of Extended Point with coordinates in X, Y, Z, T
|
|
63
68
|
export interface ExtPointConstructor extends GroupConstructor<ExtPointType> {
|
|
@@ -88,7 +93,15 @@ export type CurveFn = {
|
|
|
88
93
|
// It is not generic twisted curve for now, but ed25519/ed448 generic implementation
|
|
89
94
|
export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|
90
95
|
const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>;
|
|
91
|
-
const {
|
|
96
|
+
const {
|
|
97
|
+
Fp,
|
|
98
|
+
n: CURVE_ORDER,
|
|
99
|
+
prehash: prehash,
|
|
100
|
+
hash: cHash,
|
|
101
|
+
randomBytes,
|
|
102
|
+
nByteLength,
|
|
103
|
+
h: cofactor,
|
|
104
|
+
} = CURVE;
|
|
92
105
|
const MASK = _2n ** BigInt(nByteLength * 8);
|
|
93
106
|
const modP = Fp.create; // Function overrides
|
|
94
107
|
|
|
@@ -109,7 +122,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|
|
109
122
|
if (ctx.length || phflag) throw new Error('Contexts/pre-hash are not supported');
|
|
110
123
|
return data;
|
|
111
124
|
}); // NOOP
|
|
112
|
-
const inBig = (n: bigint) => typeof n === 'bigint' &&
|
|
125
|
+
const inBig = (n: bigint) => typeof n === 'bigint' && _0n < n; // n in [1..]
|
|
113
126
|
const inRange = (n: bigint, max: bigint) => inBig(n) && inBig(max) && n < max; // n in [1..max-1]
|
|
114
127
|
const in0MaskRange = (n: bigint) => n === _0n || inRange(n, MASK); // n in [0..MASK-1]
|
|
115
128
|
function assertInRange(n: bigint, max: bigint) {
|
|
@@ -297,8 +310,9 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|
|
297
310
|
// Non-constant-time multiplication. Uses double-and-add algorithm.
|
|
298
311
|
// It's faster, but should only be used when you don't care about
|
|
299
312
|
// an exposed private key e.g. sig verification.
|
|
313
|
+
// Does NOT allow scalars higher than CURVE.n.
|
|
300
314
|
multiplyUnsafe(scalar: bigint): Point {
|
|
301
|
-
let n = assertGE0(scalar);
|
|
315
|
+
let n = assertGE0(scalar); // 0 <= scalar < CURVE.n
|
|
302
316
|
if (n === _0n) return I;
|
|
303
317
|
if (this.equals(I) || n === _1n) return this;
|
|
304
318
|
if (this.equals(G)) return this.wNAF(n).p;
|
|
@@ -341,7 +355,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|
|
341
355
|
|
|
342
356
|
// Converts hash string or Uint8Array to Point.
|
|
343
357
|
// Uses algo from RFC8032 5.1.3.
|
|
344
|
-
static fromHex(hex: Hex,
|
|
358
|
+
static fromHex(hex: Hex, zip215 = false): Point {
|
|
345
359
|
const { d, a } = CURVE;
|
|
346
360
|
const len = Fp.BYTES;
|
|
347
361
|
hex = ensureBytes('pointHex', hex, len); // copy hex to a new array
|
|
@@ -353,8 +367,8 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|
|
353
367
|
// y=0 is allowed
|
|
354
368
|
} else {
|
|
355
369
|
// RFC8032 prohibits >= p, but ZIP215 doesn't
|
|
356
|
-
if (
|
|
357
|
-
else assertInRange(y,
|
|
370
|
+
if (zip215) assertInRange(y, MASK); // zip215=true [1..P-1] (2^255-19-1 for ed25519)
|
|
371
|
+
else assertInRange(y, Fp.ORDER); // zip215=false [1..MASK-1] (2^256-1 for ed25519)
|
|
358
372
|
}
|
|
359
373
|
|
|
360
374
|
// Ed25519: x² = (y²-1)/(dy²+1) mod p. Ed448: x² = (y²-1)/(dy²-1) mod p. Generic case:
|
|
@@ -416,32 +430,43 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|
|
416
430
|
// int('LE', SHA512(dom2(F, C) || msgs)) mod N
|
|
417
431
|
function hashDomainToScalar(context: Hex = new Uint8Array(), ...msgs: Uint8Array[]) {
|
|
418
432
|
const msg = ut.concatBytes(...msgs);
|
|
419
|
-
return modN_LE(cHash(domain(msg, ensureBytes('context', context), !!
|
|
433
|
+
return modN_LE(cHash(domain(msg, ensureBytes('context', context), !!prehash)));
|
|
420
434
|
}
|
|
421
435
|
|
|
422
436
|
/** Signs message with privateKey. RFC8032 5.1.6 */
|
|
423
|
-
function sign(msg: Hex, privKey: Hex, context?: Hex): Uint8Array {
|
|
437
|
+
function sign(msg: Hex, privKey: Hex, options: { context?: Hex } = {}): Uint8Array {
|
|
424
438
|
msg = ensureBytes('message', msg);
|
|
425
|
-
if (
|
|
439
|
+
if (prehash) msg = prehash(msg); // for ed25519ph etc.
|
|
426
440
|
const { prefix, scalar, pointBytes } = getExtendedPublicKey(privKey);
|
|
427
|
-
const r = hashDomainToScalar(context, prefix, msg); // r = dom2(F, C) || prefix || PH(M)
|
|
441
|
+
const r = hashDomainToScalar(options.context, prefix, msg); // r = dom2(F, C) || prefix || PH(M)
|
|
428
442
|
const R = G.multiply(r).toRawBytes(); // R = rG
|
|
429
|
-
const k = hashDomainToScalar(context, R, pointBytes, msg); // R || A || PH(M)
|
|
443
|
+
const k = hashDomainToScalar(options.context, R, pointBytes, msg); // R || A || PH(M)
|
|
430
444
|
const s = modN(r + k * scalar); // S = (r + k * s) mod L
|
|
431
445
|
assertGE0(s); // 0 <= s < l
|
|
432
446
|
const res = ut.concatBytes(R, ut.numberToBytesLE(s, Fp.BYTES));
|
|
433
447
|
return ensureBytes('result', res, nByteLength * 2); // 64-byte signature
|
|
434
448
|
}
|
|
435
449
|
|
|
436
|
-
|
|
450
|
+
const verifyOpts: { context?: Hex; zip215?: boolean } = VERIFY_DEFAULT;
|
|
451
|
+
function verify(sig: Hex, msg: Hex, publicKey: Hex, options = verifyOpts): boolean {
|
|
452
|
+
const { context, zip215 } = options;
|
|
437
453
|
const len = Fp.BYTES; // Verifies EdDSA signature against message and public key. RFC8032 5.1.7.
|
|
438
454
|
sig = ensureBytes('signature', sig, 2 * len); // An extended group equation is checked.
|
|
439
|
-
msg = ensureBytes('message', msg);
|
|
440
|
-
if (
|
|
441
|
-
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
|
|
455
|
+
msg = ensureBytes('message', msg);
|
|
456
|
+
if (prehash) msg = prehash(msg); // for ed25519ph, etc
|
|
457
|
+
|
|
458
|
+
const s = ut.bytesToNumberLE(sig.slice(len, 2 * len));
|
|
459
|
+
// zip215: true is good for consensus-critical apps and allows points < 2^256
|
|
460
|
+
// zip215: false follows RFC8032 / NIST186-5 and restricts points to CURVE.p
|
|
461
|
+
let A, R, SB;
|
|
462
|
+
try {
|
|
463
|
+
A = Point.fromHex(publicKey, zip215);
|
|
464
|
+
R = Point.fromHex(sig.slice(0, len), zip215);
|
|
465
|
+
SB = G.multiplyUnsafe(s); // 0 <= s < l is done inside
|
|
466
|
+
} catch (error) {
|
|
467
|
+
return false;
|
|
468
|
+
}
|
|
469
|
+
|
|
445
470
|
const k = hashDomainToScalar(context, R.toRawBytes(), A.toRawBytes(), msg);
|
|
446
471
|
const RkA = R.add(A.multiplyUnsafe(k));
|
|
447
472
|
// [8][S]B = [8]R + [8][k]A'
|
|
@@ -17,7 +17,7 @@ export type Opts = {
|
|
|
17
17
|
p: bigint;
|
|
18
18
|
m: number;
|
|
19
19
|
k: number;
|
|
20
|
-
expand
|
|
20
|
+
expand: 'xmd' | 'xof';
|
|
21
21
|
hash: CHash;
|
|
22
22
|
};
|
|
23
23
|
|
|
@@ -145,10 +145,11 @@ export function hash_to_field(msg: Uint8Array, count: number, options: Opts): bi
|
|
|
145
145
|
prb = expand_message_xmd(msg, DST, len_in_bytes, hash);
|
|
146
146
|
} else if (expand === 'xof') {
|
|
147
147
|
prb = expand_message_xof(msg, DST, len_in_bytes, k, hash);
|
|
148
|
-
} else if (expand ===
|
|
148
|
+
} else if (expand === '_internal_pass') {
|
|
149
|
+
// for internal tests only
|
|
149
150
|
prb = msg;
|
|
150
151
|
} else {
|
|
151
|
-
throw new Error('expand must be "xmd"
|
|
152
|
+
throw new Error('expand must be "xmd" or "xof"');
|
|
152
153
|
}
|
|
153
154
|
const u = new Array(count);
|
|
154
155
|
for (let i = 0; i < count; i++) {
|
package/src/abstract/modular.ts
CHANGED
package/src/abstract/utils.ts
CHANGED
|
@@ -123,12 +123,12 @@ export function utf8ToBytes(str: string): Uint8Array {
|
|
|
123
123
|
// Amount of bits inside bigint (Same as n.toString(2).length)
|
|
124
124
|
export function bitLen(n: bigint) {
|
|
125
125
|
let len;
|
|
126
|
-
for (len = 0; n >
|
|
126
|
+
for (len = 0; n > _0n; n >>= _1n, len += 1);
|
|
127
127
|
return len;
|
|
128
128
|
}
|
|
129
129
|
// Gets single bit at position. NOTE: first bit position is 0 (same as arrays)
|
|
130
130
|
// Same as !!+Array.from(n.toString(2)).reverse()[pos]
|
|
131
|
-
export const bitGet = (n: bigint, pos: number) => (n >> BigInt(pos)) &
|
|
131
|
+
export const bitGet = (n: bigint, pos: number) => (n >> BigInt(pos)) & _1n;
|
|
132
132
|
// Sets single bit at position
|
|
133
133
|
export const bitSet = (n: bigint, pos: number, value: boolean) =>
|
|
134
134
|
n | ((value ? _1n : _0n) << BigInt(pos));
|
|
@@ -58,6 +58,8 @@ export interface ProjPointType<T> extends Group<ProjPointType<T>> {
|
|
|
58
58
|
readonly px: T;
|
|
59
59
|
readonly py: T;
|
|
60
60
|
readonly pz: T;
|
|
61
|
+
get x(): T;
|
|
62
|
+
get y(): T;
|
|
61
63
|
multiply(scalar: bigint): ProjPointType<T>;
|
|
62
64
|
toAffine(iz?: T): AffinePoint<T>;
|
|
63
65
|
isTorsionFree(): boolean;
|
|
@@ -129,7 +131,7 @@ export type CurvePointsRes<T> = {
|
|
|
129
131
|
|
|
130
132
|
// ASN.1 DER encoding utilities
|
|
131
133
|
const { bytesToNumberBE: b2n, hexToBytes: h2b } = ut;
|
|
132
|
-
const DER = {
|
|
134
|
+
export const DER = {
|
|
133
135
|
// asn.1 DER encoding utils
|
|
134
136
|
Err: class DERErr extends Error {
|
|
135
137
|
constructor(m = '') {
|
|
@@ -142,9 +144,13 @@ const DER = {
|
|
|
142
144
|
const len = data[1];
|
|
143
145
|
const res = data.subarray(2, len + 2);
|
|
144
146
|
if (!len || res.length !== len) throw new E('Invalid signature integer: wrong length');
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
//
|
|
147
|
+
// https://crypto.stackexchange.com/a/57734 Leftmost bit of first byte is 'negative' flag,
|
|
148
|
+
// since we always use positive integers here. It must always be empty:
|
|
149
|
+
// - add zero byte if exists
|
|
150
|
+
// - if next byte doesn't have a flag, leading zero is not allowed (minimal encoding)
|
|
151
|
+
if (res[0] & 0b10000000) throw new E('Invalid signature integer: negative');
|
|
152
|
+
if (res[0] === 0x00 && !(res[1] & 0b10000000))
|
|
153
|
+
throw new E('Invalid signature integer: unnecessary leading zero');
|
|
148
154
|
return { d: b2n(res), l: data.subarray(len + 2) }; // d is data, l is left
|
|
149
155
|
},
|
|
150
156
|
toSig(hex: string | Uint8Array): { r: bigint; s: bigint } {
|
|
@@ -161,7 +167,8 @@ const DER = {
|
|
|
161
167
|
return { r, s };
|
|
162
168
|
},
|
|
163
169
|
hexFromSig(sig: { r: bigint; s: bigint }): string {
|
|
164
|
-
|
|
170
|
+
// Add leading zero if first byte has negative bit enabled. More details in '_parseInt'
|
|
171
|
+
const slice = (s: string): string => (Number.parseInt(s[0], 16) & 0b1000 ? '00' + s : s);
|
|
165
172
|
const h = (num: number | bigint) => {
|
|
166
173
|
const hex = num.toString(16);
|
|
167
174
|
return hex.length & 1 ? `0${hex}` : hex;
|
|
@@ -176,9 +183,9 @@ const DER = {
|
|
|
176
183
|
},
|
|
177
184
|
};
|
|
178
185
|
|
|
179
|
-
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
|
180
|
-
|
|
181
|
-
const _1n = BigInt(1);
|
|
186
|
+
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
|
187
|
+
// prettier-ignore
|
|
188
|
+
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
|
|
182
189
|
|
|
183
190
|
export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|
184
191
|
const CURVE = validatePointOpts(opts);
|
|
@@ -211,6 +218,12 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|
|
211
218
|
const x3 = Fp.mul(x2, x); // x2 * x
|
|
212
219
|
return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x3 + a * x + b
|
|
213
220
|
}
|
|
221
|
+
// Validate whether the passed curve params are valid.
|
|
222
|
+
// We check if curve equation works for generator point.
|
|
223
|
+
// `assertValidity()` won't work: `isTorsionFree()` is not available at this point in bls12-381.
|
|
224
|
+
// ProjectivePoint class has not been initialized yet.
|
|
225
|
+
if (!Fp.eql(Fp.sqr(CURVE.Gy), weierstrassEquation(CURVE.Gx)))
|
|
226
|
+
throw new Error('bad generator point: equation left != right');
|
|
214
227
|
|
|
215
228
|
// Valid group elements reside in range 1..n-1
|
|
216
229
|
function isWithinCurveOrder(num: bigint): boolean {
|
|
@@ -365,7 +378,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|
|
365
378
|
// Cost: 8M + 3S + 3*a + 2*b3 + 15add.
|
|
366
379
|
double() {
|
|
367
380
|
const { a, b } = CURVE;
|
|
368
|
-
const b3 = Fp.mul(b,
|
|
381
|
+
const b3 = Fp.mul(b, _3n);
|
|
369
382
|
const { px: X1, py: Y1, pz: Z1 } = this;
|
|
370
383
|
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
|
|
371
384
|
let t0 = Fp.mul(X1, X1); // step 1
|
|
@@ -412,7 +425,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|
|
412
425
|
const { px: X2, py: Y2, pz: Z2 } = other;
|
|
413
426
|
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
|
|
414
427
|
const a = CURVE.a;
|
|
415
|
-
const b3 = Fp.mul(CURVE.b,
|
|
428
|
+
const b3 = Fp.mul(CURVE.b, _3n);
|
|
416
429
|
let t0 = Fp.mul(X1, X2); // step 1
|
|
417
430
|
let t1 = Fp.mul(Y1, Y2);
|
|
418
431
|
let t2 = Fp.mul(Z1, Z2);
|
|
@@ -589,7 +602,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|
|
589
602
|
}
|
|
590
603
|
const _bits = CURVE.nBitLength;
|
|
591
604
|
const wnaf = wNAF(Point, CURVE.endo ? Math.ceil(_bits / 2) : _bits);
|
|
592
|
-
|
|
605
|
+
// Validate if generator point is on curve
|
|
593
606
|
return {
|
|
594
607
|
CURVE,
|
|
595
608
|
ProjectivePoint: Point as ProjConstructor<T>,
|
|
@@ -1078,15 +1091,15 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|
|
1078
1091
|
export function SWUFpSqrtRatio<T>(Fp: mod.IField<T>, Z: T) {
|
|
1079
1092
|
// Generic implementation
|
|
1080
1093
|
const q = Fp.ORDER;
|
|
1081
|
-
let l =
|
|
1082
|
-
for (let o = q -
|
|
1094
|
+
let l = _0n;
|
|
1095
|
+
for (let o = q - _1n; o % _2n === _0n; o /= _2n) l += _1n;
|
|
1083
1096
|
const c1 = l; // 1. c1, the largest integer such that 2^c1 divides q - 1.
|
|
1084
|
-
const c2 = (q -
|
|
1085
|
-
const c3 = (c2 -
|
|
1086
|
-
const c4 =
|
|
1087
|
-
const c5 =
|
|
1097
|
+
const c2 = (q - _1n) / _2n ** c1; // 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
|
|
1098
|
+
const c3 = (c2 - _1n) / _2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
|
|
1099
|
+
const c4 = _2n ** c1 - _1n; // 4. c4 = 2^c1 - 1 # Integer arithmetic
|
|
1100
|
+
const c5 = _2n ** (c1 - _1n); // 5. c5 = 2^(c1 - 1) # Integer arithmetic
|
|
1088
1101
|
const c6 = Fp.pow(Z, c2); // 6. c6 = Z^c2
|
|
1089
|
-
const c7 = Fp.pow(Z, (c2 +
|
|
1102
|
+
const c7 = Fp.pow(Z, (c2 + _1n) / _2n); // 7. c7 = Z^((c2 + 1) / 2)
|
|
1090
1103
|
let sqrtRatio = (u: T, v: T): { isValid: boolean; value: T } => {
|
|
1091
1104
|
let tv1 = c6; // 1. tv1 = c6
|
|
1092
1105
|
let tv2 = Fp.pow(v, c4); // 2. tv2 = v^c4
|
|
@@ -1105,8 +1118,8 @@ export function SWUFpSqrtRatio<T>(Fp: mod.IField<T>, Z: T) {
|
|
|
1105
1118
|
tv3 = Fp.cmov(tv2, tv3, isQR); // 15. tv3 = CMOV(tv2, tv3, isQR)
|
|
1106
1119
|
tv4 = Fp.cmov(tv5, tv4, isQR); // 16. tv4 = CMOV(tv5, tv4, isQR)
|
|
1107
1120
|
// 17. for i in (c1, c1 - 1, ..., 2):
|
|
1108
|
-
for (let i = c1; i >
|
|
1109
|
-
let tv5 =
|
|
1121
|
+
for (let i = c1; i > _1n; i--) {
|
|
1122
|
+
let tv5 = _2n ** (i - _2n); // 18. tv5 = i - 2; 19. tv5 = 2^tv5
|
|
1110
1123
|
let tvv5 = Fp.pow(tv4, tv5); // 20. tv5 = tv4^tv5
|
|
1111
1124
|
const e1 = Fp.eql(tvv5, Fp.ONE); // 21. e1 = tv5 == 1
|
|
1112
1125
|
tv2 = Fp.mul(tv3, tv1); // 22. tv2 = tv3 * tv1
|
|
@@ -1117,9 +1130,9 @@ export function SWUFpSqrtRatio<T>(Fp: mod.IField<T>, Z: T) {
|
|
|
1117
1130
|
}
|
|
1118
1131
|
return { isValid: isQR, value: tv3 };
|
|
1119
1132
|
};
|
|
1120
|
-
if (Fp.ORDER %
|
|
1133
|
+
if (Fp.ORDER % _4n === _3n) {
|
|
1121
1134
|
// sqrt_ratio_3mod4(u, v)
|
|
1122
|
-
const c1 = (Fp.ORDER -
|
|
1135
|
+
const c1 = (Fp.ORDER - _3n) / _4n; // 1. c1 = (q - 3) / 4 # Integer arithmetic
|
|
1123
1136
|
const c2 = Fp.sqrt(Fp.neg(Z)); // 2. c2 = sqrt(-Z)
|
|
1124
1137
|
sqrtRatio = (u: T, v: T) => {
|
|
1125
1138
|
let tv1 = Fp.sqr(v); // 1. tv1 = v^2
|
|
@@ -1135,7 +1148,7 @@ export function SWUFpSqrtRatio<T>(Fp: mod.IField<T>, Z: T) {
|
|
|
1135
1148
|
};
|
|
1136
1149
|
}
|
|
1137
1150
|
// No curves uses that
|
|
1138
|
-
// if (Fp.ORDER %
|
|
1151
|
+
// if (Fp.ORDER % _8n === _5n) // sqrt_ratio_5mod8
|
|
1139
1152
|
return sqrtRatio;
|
|
1140
1153
|
}
|
|
1141
1154
|
// From draft-irtf-cfrg-hash-to-curve-16
|