@noble/curves 1.4.2 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +135 -123
- package/_shortw_utils.d.ts.map +1 -1
- package/abstract/bls.d.ts +37 -34
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/bls.js +167 -115
- package/abstract/bls.js.map +1 -1
- package/abstract/curve.d.ts +2 -1
- package/abstract/curve.d.ts.map +1 -1
- package/abstract/curve.js +22 -7
- package/abstract/curve.js.map +1 -1
- package/abstract/edwards.d.ts +11 -0
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +79 -75
- package/abstract/edwards.js.map +1 -1
- package/abstract/modular.d.ts +4 -0
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +13 -2
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +4 -9
- package/abstract/montgomery.js.map +1 -1
- package/abstract/tower.d.ts +106 -0
- package/abstract/tower.d.ts.map +1 -0
- package/abstract/tower.js +497 -0
- package/abstract/tower.js.map +1 -0
- package/abstract/utils.d.ts +17 -0
- package/abstract/utils.d.ts.map +1 -1
- package/abstract/utils.js +50 -1
- package/abstract/utils.js.map +1 -1
- package/abstract/weierstrass.d.ts +7 -0
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +88 -72
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts +1 -65
- package/bls12-381.d.ts.map +1 -1
- package/bls12-381.js +48 -575
- package/bls12-381.js.map +1 -1
- package/bn254.d.ts +10 -6
- package/bn254.d.ts.map +1 -1
- package/bn254.js +207 -10
- package/bn254.js.map +1 -1
- package/ed25519.d.ts +7 -4
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +3 -0
- package/ed25519.js.map +1 -1
- package/esm/_shortw_utils.d.ts.map +1 -1
- package/esm/abstract/bls.d.ts +37 -34
- package/esm/abstract/bls.d.ts.map +1 -1
- package/esm/abstract/bls.js +168 -116
- package/esm/abstract/bls.js.map +1 -1
- package/esm/abstract/curve.d.ts +2 -1
- package/esm/abstract/curve.d.ts.map +1 -1
- package/esm/abstract/curve.js +22 -7
- package/esm/abstract/curve.js.map +1 -1
- package/esm/abstract/edwards.d.ts +11 -0
- package/esm/abstract/edwards.d.ts.map +1 -1
- package/esm/abstract/edwards.js +80 -76
- package/esm/abstract/edwards.js.map +1 -1
- package/esm/abstract/modular.d.ts +4 -0
- package/esm/abstract/modular.d.ts.map +1 -1
- package/esm/abstract/modular.js +12 -2
- package/esm/abstract/modular.js.map +1 -1
- package/esm/abstract/montgomery.d.ts.map +1 -1
- package/esm/abstract/montgomery.js +5 -10
- package/esm/abstract/montgomery.js.map +1 -1
- package/esm/abstract/tower.d.ts +106 -0
- package/esm/abstract/tower.d.ts.map +1 -0
- package/esm/abstract/tower.js +493 -0
- package/esm/abstract/tower.js.map +1 -0
- package/esm/abstract/utils.d.ts +17 -0
- package/esm/abstract/utils.d.ts.map +1 -1
- package/esm/abstract/utils.js +44 -0
- package/esm/abstract/utils.js.map +1 -1
- package/esm/abstract/weierstrass.d.ts +7 -0
- package/esm/abstract/weierstrass.d.ts.map +1 -1
- package/esm/abstract/weierstrass.js +89 -73
- package/esm/abstract/weierstrass.js.map +1 -1
- package/esm/bls12-381.d.ts +1 -65
- package/esm/bls12-381.d.ts.map +1 -1
- package/esm/bls12-381.js +50 -577
- package/esm/bls12-381.js.map +1 -1
- package/esm/bn254.d.ts +10 -6
- package/esm/bn254.d.ts.map +1 -1
- package/esm/bn254.js +206 -9
- package/esm/bn254.js.map +1 -1
- package/esm/ed25519.d.ts +7 -4
- package/esm/ed25519.d.ts.map +1 -1
- package/esm/ed25519.js +3 -0
- package/esm/ed25519.js.map +1 -1
- package/esm/p256.d.ts.map +1 -1
- package/esm/p384.d.ts.map +1 -1
- package/esm/p521.d.ts.map +1 -1
- package/esm/secp256k1.d.ts +6 -0
- package/esm/secp256k1.d.ts.map +1 -1
- package/esm/secp256k1.js +17 -13
- package/esm/secp256k1.js.map +1 -1
- package/p256.d.ts.map +1 -1
- package/p384.d.ts.map +1 -1
- package/p521.d.ts.map +1 -1
- package/package.json +2 -1
- package/secp256k1.d.ts +6 -0
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +16 -12
- package/secp256k1.js.map +1 -1
- package/src/abstract/bls.ts +222 -168
- package/src/abstract/curve.ts +23 -7
- package/src/abstract/edwards.ts +81 -68
- package/src/abstract/modular.ts +13 -3
- package/src/abstract/montgomery.ts +11 -10
- package/src/abstract/tower.ts +604 -0
- package/src/abstract/utils.ts +49 -0
- package/src/abstract/weierstrass.ts +85 -68
- package/src/bls12-381.ts +53 -707
- package/src/bn254.ts +224 -9
- package/src/ed25519.ts +5 -2
- package/src/secp256k1.ts +24 -12
package/src/bn254.ts
CHANGED
|
@@ -1,20 +1,235 @@
|
|
|
1
1
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2
2
|
import { sha256 } from '@noble/hashes/sha256';
|
|
3
3
|
import { getHash } from './_shortw_utils.js';
|
|
4
|
-
import { Field } from './abstract/modular.js';
|
|
5
4
|
import { weierstrass } from './abstract/weierstrass.js';
|
|
5
|
+
import { randomBytes } from '@noble/hashes/utils';
|
|
6
|
+
import { bls, CurveFn } from './abstract/bls.js';
|
|
7
|
+
import { Field } from './abstract/modular.js';
|
|
8
|
+
import { bitGet, bitLen, notImplemented } from './abstract/utils.js';
|
|
9
|
+
import { tower12, psiFrobenius } from './abstract/tower.js';
|
|
10
|
+
// Types
|
|
11
|
+
import type { Fp, Fp2, Fp6, Fp12 } from './abstract/tower.js';
|
|
12
|
+
// prettier-ignore
|
|
13
|
+
const _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
|
|
14
|
+
// prettier-ignore
|
|
15
|
+
const _6n = BigInt(6);
|
|
16
|
+
|
|
17
|
+
/*
|
|
18
|
+
bn254, previously known as alt_bn_128, when it had 128-bit security.
|
|
19
|
+
Barbulescu-Duquesne 2017 shown it's weaker: just about 100 bits,
|
|
20
|
+
so the naming has been adjusted to its prime bit count:
|
|
21
|
+
https://hal.science/hal-01534101/file/main.pdf
|
|
22
|
+
|
|
23
|
+
There are huge compatibility issues in the ecosystem:
|
|
24
|
+
|
|
25
|
+
1. Different libraries call it in different ways: "bn254", "bn256", "alt_bn128", "bn128".
|
|
26
|
+
2. libff has bn128, but it's a different curve with different G2:
|
|
27
|
+
https://github.com/scipr-lab/libff/blob/a44f482e18b8ac04d034c193bd9d7df7817ad73f/libff/algebra/curves/bn128/bn128_init.cpp#L166-L169
|
|
28
|
+
3. halo2curves bn256 is also incompatible and returns different outputs
|
|
29
|
+
|
|
30
|
+
The goal of our implementation is to support "Ethereum" variant of the curve,
|
|
31
|
+
because it at least has specs:
|
|
32
|
+
|
|
33
|
+
- EIP196 (https://eips.ethereum.org/EIPS/eip-196) describes bn254 ECADD and ECMUL opcodes for EVM
|
|
34
|
+
- EIP197 (https://eips.ethereum.org/EIPS/eip-197) describes bn254 pairings
|
|
35
|
+
- It's hard: EIPs don't have proper tests. EIP-197 returns boolean output instead of Fp12
|
|
36
|
+
- The existing implementations are bad. Some are deprecated:
|
|
37
|
+
- https://github.com/paritytech/bn (old version)
|
|
38
|
+
- https://github.com/ewasm/ethereum-bn128.rs (uses paritytech/bn)
|
|
39
|
+
- https://github.com/zcash-hackworks/bn
|
|
40
|
+
- https://github.com/arkworks-rs/curves/blob/master/bn254/src/lib.rs
|
|
41
|
+
- Python implementations use different towers and produce different Fp12 outputs:
|
|
42
|
+
- https://github.com/ethereum/py_pairing
|
|
43
|
+
- https://github.com/ethereum/execution-specs/blob/master/src/ethereum/crypto/alt_bn128.py
|
|
44
|
+
- Points are encoded differently in different implementations
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
/*
|
|
48
|
+
Seed (X): 4965661367192848881
|
|
49
|
+
Fr: (36x⁴+36x³+18x²+6x+1)
|
|
50
|
+
Fp: (36x⁴+36x³+24x²+6x+1)
|
|
51
|
+
(E / Fp ): Y² = X³+3
|
|
52
|
+
(Et / Fp²): Y² = X³+3/(u+9) (D-type twist)
|
|
53
|
+
Ate loop size: 6x+2
|
|
54
|
+
|
|
55
|
+
Towers:
|
|
56
|
+
- Fp²[u] = Fp/u²+1
|
|
57
|
+
- Fp⁶[v] = Fp²/v³-9-u
|
|
58
|
+
- Fp¹²[w] = Fp⁶/w²-v
|
|
59
|
+
*/
|
|
60
|
+
const BN_X = BigInt('4965661367192848881');
|
|
61
|
+
const BN_X_LEN = bitLen(BN_X);
|
|
62
|
+
const SIX_X_SQUARED = _6n * BN_X ** _2n;
|
|
63
|
+
|
|
64
|
+
// Finite field over r. It's for convenience and is not used in the code below.
|
|
65
|
+
const Fr = Field(
|
|
66
|
+
BigInt('21888242871839275222246405745257275088548364400416034343698204186575808495617')
|
|
67
|
+
);
|
|
68
|
+
// Fp2.div(Fp2.mul(Fp2.ONE, _3n), Fp2.NONRESIDUE)
|
|
69
|
+
const Fp2B = {
|
|
70
|
+
c0: BigInt('19485874751759354771024239261021720505790618469301721065564631296452457478373'),
|
|
71
|
+
c1: BigInt('266929791119991161246907387137283842545076965332900288569378510910307636690'),
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const { Fp, Fp2, Fp6, Fp4Square, Fp12 } = tower12({
|
|
75
|
+
ORDER: BigInt('21888242871839275222246405745257275088696311157297823662689037894645226208583'),
|
|
76
|
+
FP2_NONRESIDUE: [BigInt(9), _1n],
|
|
77
|
+
Fp2mulByB: (num) => Fp2.mul(num, Fp2B),
|
|
78
|
+
// The result of any pairing is in a cyclotomic subgroup
|
|
79
|
+
// https://eprint.iacr.org/2009/565.pdf
|
|
80
|
+
Fp12cyclotomicSquare: ({ c0, c1 }): Fp12 => {
|
|
81
|
+
const { c0: c0c0, c1: c0c1, c2: c0c2 } = c0;
|
|
82
|
+
const { c0: c1c0, c1: c1c1, c2: c1c2 } = c1;
|
|
83
|
+
const { first: t3, second: t4 } = Fp4Square(c0c0, c1c1);
|
|
84
|
+
const { first: t5, second: t6 } = Fp4Square(c1c0, c0c2);
|
|
85
|
+
const { first: t7, second: t8 } = Fp4Square(c0c1, c1c2);
|
|
86
|
+
let t9 = Fp2.mulByNonresidue(t8); // T8 * (u + 1)
|
|
87
|
+
return {
|
|
88
|
+
c0: Fp6.create({
|
|
89
|
+
c0: Fp2.add(Fp2.mul(Fp2.sub(t3, c0c0), _2n), t3), // 2 * (T3 - c0c0) + T3
|
|
90
|
+
c1: Fp2.add(Fp2.mul(Fp2.sub(t5, c0c1), _2n), t5), // 2 * (T5 - c0c1) + T5
|
|
91
|
+
c2: Fp2.add(Fp2.mul(Fp2.sub(t7, c0c2), _2n), t7),
|
|
92
|
+
}), // 2 * (T7 - c0c2) + T7
|
|
93
|
+
c1: Fp6.create({
|
|
94
|
+
c0: Fp2.add(Fp2.mul(Fp2.add(t9, c1c0), _2n), t9), // 2 * (T9 + c1c0) + T9
|
|
95
|
+
c1: Fp2.add(Fp2.mul(Fp2.add(t4, c1c1), _2n), t4), // 2 * (T4 + c1c1) + T4
|
|
96
|
+
c2: Fp2.add(Fp2.mul(Fp2.add(t6, c1c2), _2n), t6),
|
|
97
|
+
}),
|
|
98
|
+
}; // 2 * (T6 + c1c2) + T6
|
|
99
|
+
},
|
|
100
|
+
Fp12cyclotomicExp(num, n) {
|
|
101
|
+
let z = Fp12.ONE;
|
|
102
|
+
for (let i = BN_X_LEN - 1; i >= 0; i--) {
|
|
103
|
+
z = Fp12._cyclotomicSquare(z);
|
|
104
|
+
if (bitGet(n, i)) z = Fp12.mul(z, num);
|
|
105
|
+
}
|
|
106
|
+
return z;
|
|
107
|
+
},
|
|
108
|
+
// https://eprint.iacr.org/2010/354.pdf
|
|
109
|
+
// https://eprint.iacr.org/2009/565.pdf
|
|
110
|
+
Fp12finalExponentiate: (num) => {
|
|
111
|
+
const powMinusX = (num: Fp12) => Fp12.conjugate(Fp12._cyclotomicExp(num, BN_X));
|
|
112
|
+
const r0 = Fp12.mul(Fp12.conjugate(num), Fp12.inv(num));
|
|
113
|
+
const r = Fp12.mul(Fp12.frobeniusMap(r0, 2), r0);
|
|
114
|
+
const y1 = Fp12._cyclotomicSquare(powMinusX(r));
|
|
115
|
+
const y2 = Fp12.mul(Fp12._cyclotomicSquare(y1), y1);
|
|
116
|
+
const y4 = powMinusX(y2);
|
|
117
|
+
const y6 = powMinusX(Fp12._cyclotomicSquare(y4));
|
|
118
|
+
const y8 = Fp12.mul(Fp12.mul(Fp12.conjugate(y6), y4), Fp12.conjugate(y2));
|
|
119
|
+
const y9 = Fp12.mul(y8, y1);
|
|
120
|
+
return Fp12.mul(
|
|
121
|
+
Fp12.frobeniusMap(Fp12.mul(Fp12.conjugate(r), y9), 3),
|
|
122
|
+
Fp12.mul(
|
|
123
|
+
Fp12.frobeniusMap(y8, 2),
|
|
124
|
+
Fp12.mul(Fp12.frobeniusMap(y9, 1), Fp12.mul(Fp12.mul(y8, y4), r))
|
|
125
|
+
)
|
|
126
|
+
);
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// END OF CURVE FIELDS
|
|
131
|
+
const { G2psi, psi } = psiFrobenius(Fp, Fp2, Fp2.NONRESIDUE);
|
|
132
|
+
|
|
133
|
+
/*
|
|
134
|
+
No hashToCurve for now (and signatures):
|
|
135
|
+
|
|
136
|
+
- RFC 9380 doesn't mention bn254 and doesn't provide test vectors
|
|
137
|
+
- Overall seems like nobody is using BLS signatures on top of bn254
|
|
138
|
+
- Seems like it can utilize SVDW, which is not implemented yet
|
|
139
|
+
*/
|
|
140
|
+
const htfDefaults = Object.freeze({
|
|
141
|
+
// DST: a domain separation tag defined in section 2.2.5
|
|
142
|
+
DST: 'BN254G2_XMD:SHA-256_SVDW_RO_',
|
|
143
|
+
encodeDST: 'BN254G2_XMD:SHA-256_SVDW_RO_',
|
|
144
|
+
p: Fp.ORDER,
|
|
145
|
+
m: 2,
|
|
146
|
+
k: 128,
|
|
147
|
+
expand: 'xmd',
|
|
148
|
+
hash: sha256,
|
|
149
|
+
} as const);
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* bn254 (a.k.a. alt_bn128) pairing-friendly curve.
|
|
153
|
+
* Contains G1 / G2 operations and pairings.
|
|
154
|
+
*/
|
|
155
|
+
export const bn254: CurveFn = bls({
|
|
156
|
+
// Fields
|
|
157
|
+
fields: { Fp, Fp2, Fp6, Fp12, Fr },
|
|
158
|
+
G1: {
|
|
159
|
+
Fp,
|
|
160
|
+
h: BigInt(1),
|
|
161
|
+
Gx: BigInt(1),
|
|
162
|
+
Gy: BigInt(2),
|
|
163
|
+
a: Fp.ZERO,
|
|
164
|
+
b: _3n,
|
|
165
|
+
htfDefaults: { ...htfDefaults, m: 1, DST: 'BN254G2_XMD:SHA-256_SVDW_RO_' },
|
|
166
|
+
wrapPrivateKey: true,
|
|
167
|
+
allowInfinityPoint: true,
|
|
168
|
+
mapToCurve: notImplemented,
|
|
169
|
+
fromBytes: notImplemented,
|
|
170
|
+
toBytes: notImplemented,
|
|
171
|
+
ShortSignature: {
|
|
172
|
+
fromHex: notImplemented,
|
|
173
|
+
toRawBytes: notImplemented,
|
|
174
|
+
toHex: notImplemented,
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
G2: {
|
|
178
|
+
Fp: Fp2,
|
|
179
|
+
// cofactor: (36 * X^4) + (36 * X^3) + (30 * X^2) + 6*X + 1
|
|
180
|
+
h: BigInt('21888242871839275222246405745257275088844257914179612981679871602714643921549'),
|
|
181
|
+
Gx: Fp2.fromBigTuple([
|
|
182
|
+
BigInt('10857046999023057135944570762232829481370756359578518086990519993285655852781'),
|
|
183
|
+
BigInt('11559732032986387107991004021392285783925812861821192530917403151452391805634'),
|
|
184
|
+
]),
|
|
185
|
+
Gy: Fp2.fromBigTuple([
|
|
186
|
+
BigInt('8495653923123431417604973247489272438418190587263600148770280649306958101930'),
|
|
187
|
+
BigInt('4082367875863433681332203403145435568316851327593401208105741076214120093531'),
|
|
188
|
+
]),
|
|
189
|
+
a: Fp2.ZERO,
|
|
190
|
+
b: Fp2B,
|
|
191
|
+
hEff: BigInt('21888242871839275222246405745257275088844257914179612981679871602714643921549'),
|
|
192
|
+
htfDefaults: { ...htfDefaults },
|
|
193
|
+
wrapPrivateKey: true,
|
|
194
|
+
allowInfinityPoint: true,
|
|
195
|
+
isTorsionFree: (c, P) => P.multiplyUnsafe(SIX_X_SQUARED).equals(G2psi(c, P)), // [p]P = [6X^2]P
|
|
196
|
+
mapToCurve: notImplemented,
|
|
197
|
+
fromBytes: notImplemented,
|
|
198
|
+
toBytes: notImplemented,
|
|
199
|
+
Signature: {
|
|
200
|
+
fromHex: notImplemented,
|
|
201
|
+
toRawBytes: notImplemented,
|
|
202
|
+
toHex: notImplemented,
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
params: {
|
|
206
|
+
ateLoopSize: BN_X * _6n + _2n,
|
|
207
|
+
r: Fr.ORDER,
|
|
208
|
+
xNegative: false,
|
|
209
|
+
twistType: 'divisive',
|
|
210
|
+
},
|
|
211
|
+
htfDefaults,
|
|
212
|
+
hash: sha256,
|
|
213
|
+
randomBytes,
|
|
214
|
+
|
|
215
|
+
postPrecompute: (Rx, Ry, Rz, Qx, Qy, pointAdd) => {
|
|
216
|
+
const q = psi(Qx, Qy);
|
|
217
|
+
({ Rx, Ry, Rz } = pointAdd(Rx, Ry, Rz, q[0], q[1]));
|
|
218
|
+
const q2 = psi(q[0], q[1]);
|
|
219
|
+
pointAdd(Rx, Ry, Rz, q2[0], Fp2.neg(q2[1]));
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
|
|
6
223
|
/**
|
|
7
|
-
* bn254
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* so the naming has been adjusted to its prime bit count
|
|
11
|
-
* https://hal.science/hal-01534101/file/main.pdf
|
|
224
|
+
* bn254 weierstrass curve with ECDSA.
|
|
225
|
+
* This is very rare and probably not used anywhere.
|
|
226
|
+
* Instead, you should use G1 / G2, defined above.
|
|
12
227
|
*/
|
|
13
|
-
export const
|
|
228
|
+
export const bn254_weierstrass = weierstrass({
|
|
14
229
|
a: BigInt(0),
|
|
15
230
|
b: BigInt(3),
|
|
16
|
-
Fp
|
|
17
|
-
n: BigInt('
|
|
231
|
+
Fp,
|
|
232
|
+
n: BigInt('21888242871839275222246405745257275088548364400416034343698204186575808495617'),
|
|
18
233
|
Gx: BigInt(1),
|
|
19
234
|
Gy: BigInt(2),
|
|
20
235
|
h: BigInt(1),
|
package/src/ed25519.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { sha512 } from '@noble/hashes/sha512';
|
|
3
3
|
import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils';
|
|
4
4
|
import { AffinePoint, Group } from './abstract/curve.js';
|
|
5
|
-
import { ExtPointType, twistedEdwards } from './abstract/edwards.js';
|
|
5
|
+
import { CurveFn, ExtPointType, twistedEdwards } from './abstract/edwards.js';
|
|
6
6
|
import { createHasher, expand_message_xmd, htfBasicOpts } from './abstract/hash-to-curve.js';
|
|
7
7
|
import { Field, FpSqrtEven, isNegativeLE, mod, pow2 } from './abstract/modular.js';
|
|
8
8
|
import { montgomery } from './abstract/montgomery.js';
|
|
@@ -126,7 +126,10 @@ const ed25519Defaults = /* @__PURE__ */ (() =>
|
|
|
126
126
|
uvRatio,
|
|
127
127
|
}) as const)();
|
|
128
128
|
|
|
129
|
-
|
|
129
|
+
/**
|
|
130
|
+
* ed25519 curve with EdDSA signatures.
|
|
131
|
+
*/
|
|
132
|
+
export const ed25519: CurveFn = /* @__PURE__ */ (() => twistedEdwards(ed25519Defaults))();
|
|
130
133
|
|
|
131
134
|
function ed25519_domain(data: Uint8Array, ctx: Uint8Array, phflag: boolean) {
|
|
132
135
|
if (ctx.length > 255) throw new Error('Context is too big');
|
package/src/secp256k1.ts
CHANGED
|
@@ -5,7 +5,14 @@ import { createCurve } from './_shortw_utils.js';
|
|
|
5
5
|
import { createHasher, isogenyMap } from './abstract/hash-to-curve.js';
|
|
6
6
|
import { Field, mod, pow2 } from './abstract/modular.js';
|
|
7
7
|
import type { Hex, PrivKey } from './abstract/utils.js';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
inRange,
|
|
10
|
+
aInRange,
|
|
11
|
+
bytesToNumberBE,
|
|
12
|
+
concatBytes,
|
|
13
|
+
ensureBytes,
|
|
14
|
+
numberToBytesBE,
|
|
15
|
+
} from './abstract/utils.js';
|
|
9
16
|
import { ProjPointType as PointType, mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
|
10
17
|
|
|
11
18
|
const secp256k1P = BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f');
|
|
@@ -44,6 +51,9 @@ function sqrtMod(y: bigint): bigint {
|
|
|
44
51
|
|
|
45
52
|
const Fp = Field(secp256k1P, undefined, undefined, { sqrt: sqrtMod });
|
|
46
53
|
|
|
54
|
+
/**
|
|
55
|
+
* secp256k1 short weierstrass curve and ECDSA signatures over it.
|
|
56
|
+
*/
|
|
47
57
|
export const secp256k1 = createCurve(
|
|
48
58
|
{
|
|
49
59
|
a: BigInt(0), // equation params: a, b
|
|
@@ -92,8 +102,6 @@ export const secp256k1 = createCurve(
|
|
|
92
102
|
// Schnorr signatures are superior to ECDSA from above. Below is Schnorr-specific BIP0340 code.
|
|
93
103
|
// https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
|
|
94
104
|
const _0n = BigInt(0);
|
|
95
|
-
const fe = (x: bigint) => typeof x === 'bigint' && _0n < x && x < secp256k1P;
|
|
96
|
-
const ge = (x: bigint) => typeof x === 'bigint' && _0n < x && x < secp256k1N;
|
|
97
105
|
/** An object mapping tags to their tagged hash prefix of [SHA256(tag) | SHA256(tag)] */
|
|
98
106
|
const TAGGED_HASH_PREFIXES: { [tag: string]: Uint8Array } = {};
|
|
99
107
|
function taggedHash(tag: string, ...messages: Uint8Array[]): Uint8Array {
|
|
@@ -127,7 +135,7 @@ function schnorrGetExtPubKey(priv: PrivKey) {
|
|
|
127
135
|
* @returns valid point checked for being on-curve
|
|
128
136
|
*/
|
|
129
137
|
function lift_x(x: bigint): PointType<bigint> {
|
|
130
|
-
|
|
138
|
+
aInRange('x', x, _1n, secp256k1P); // Fail if x ≥ p.
|
|
131
139
|
const xx = modP(x * x);
|
|
132
140
|
const c = modP(xx * x + BigInt(7)); // Let c = x³ + 7 mod p.
|
|
133
141
|
let y = sqrtMod(c); // Let y = c^(p+1)/4 mod p.
|
|
@@ -136,11 +144,12 @@ function lift_x(x: bigint): PointType<bigint> {
|
|
|
136
144
|
p.assertValidity();
|
|
137
145
|
return p;
|
|
138
146
|
}
|
|
147
|
+
const num = bytesToNumberBE;
|
|
139
148
|
/**
|
|
140
149
|
* Create tagged hash, convert it to bigint, reduce modulo-n.
|
|
141
150
|
*/
|
|
142
151
|
function challenge(...args: Uint8Array[]): bigint {
|
|
143
|
-
return modN(
|
|
152
|
+
return modN(num(taggedHash('BIP0340/challenge', ...args)));
|
|
144
153
|
}
|
|
145
154
|
|
|
146
155
|
/**
|
|
@@ -162,9 +171,9 @@ function schnorrSign(
|
|
|
162
171
|
const m = ensureBytes('message', message);
|
|
163
172
|
const { bytes: px, scalar: d } = schnorrGetExtPubKey(privateKey); // checks for isWithinCurveOrder
|
|
164
173
|
const a = ensureBytes('auxRand', auxRand, 32); // Auxiliary random data a: a 32-byte array
|
|
165
|
-
const t = numTo32b(d ^
|
|
174
|
+
const t = numTo32b(d ^ num(taggedHash('BIP0340/aux', a))); // Let t be the byte-wise xor of bytes(d) and hash/aux(a)
|
|
166
175
|
const rand = taggedHash('BIP0340/nonce', t, px, m); // Let rand = hash/nonce(t || bytes(P) || m)
|
|
167
|
-
const k_ = modN(
|
|
176
|
+
const k_ = modN(num(rand)); // Let k' = int(rand) mod n
|
|
168
177
|
if (k_ === _0n) throw new Error('sign failed: k is zero'); // Fail if k' = 0.
|
|
169
178
|
const { bytes: rx, scalar: k } = schnorrGetExtPubKey(k_); // Let R = k'⋅G.
|
|
170
179
|
const e = challenge(rx, px, m); // Let e = int(hash/challenge(bytes(R) || bytes(P) || m)) mod n.
|
|
@@ -185,11 +194,11 @@ function schnorrVerify(signature: Hex, message: Hex, publicKey: Hex): boolean {
|
|
|
185
194
|
const m = ensureBytes('message', message);
|
|
186
195
|
const pub = ensureBytes('publicKey', publicKey, 32);
|
|
187
196
|
try {
|
|
188
|
-
const P = lift_x(
|
|
189
|
-
const r =
|
|
190
|
-
if (!
|
|
191
|
-
const s =
|
|
192
|
-
if (!
|
|
197
|
+
const P = lift_x(num(pub)); // P = lift_x(int(pk)); fail if that fails
|
|
198
|
+
const r = num(sig.subarray(0, 32)); // Let r = int(sig[0:32]); fail if r ≥ p.
|
|
199
|
+
if (!inRange(r, _1n, secp256k1P)) return false;
|
|
200
|
+
const s = num(sig.subarray(32, 64)); // Let s = int(sig[32:64]); fail if s ≥ n.
|
|
201
|
+
if (!inRange(s, _1n, secp256k1N)) return false;
|
|
193
202
|
const e = challenge(numTo32b(r), pointToBytes(P), m); // int(challenge(bytes(r)||bytes(P)||m))%n
|
|
194
203
|
const R = GmulAdd(P, s, modN(-e)); // R = s⋅G - e⋅P
|
|
195
204
|
if (!R || !R.hasEvenY() || R.toAffine().x !== r) return false; // -eP == (n-e)P
|
|
@@ -199,6 +208,9 @@ function schnorrVerify(signature: Hex, message: Hex, publicKey: Hex): boolean {
|
|
|
199
208
|
}
|
|
200
209
|
}
|
|
201
210
|
|
|
211
|
+
/**
|
|
212
|
+
* Schnorr signatures over secp256k1.
|
|
213
|
+
*/
|
|
202
214
|
export const schnorr = /* @__PURE__ */ (() => ({
|
|
203
215
|
getPublicKey: schnorrGetPublicKey,
|
|
204
216
|
sign: schnorrSign,
|