@noble/curves 2.0.0-beta.1 → 2.0.0-beta.2
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 +442 -273
- package/abstract/bls.d.ts +17 -17
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/bls.js.map +1 -1
- package/abstract/curve.d.ts +14 -9
- package/abstract/curve.d.ts.map +1 -1
- package/abstract/curve.js +9 -3
- package/abstract/curve.js.map +1 -1
- package/abstract/edwards.d.ts +7 -9
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +12 -16
- package/abstract/edwards.js.map +1 -1
- package/abstract/hash-to-curve.d.ts +32 -31
- package/abstract/hash-to-curve.d.ts.map +1 -1
- package/abstract/hash-to-curve.js +15 -14
- package/abstract/hash-to-curve.js.map +1 -1
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +7 -5
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts +3 -3
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +9 -13
- package/abstract/montgomery.js.map +1 -1
- package/abstract/oprf.d.ts +4 -4
- package/abstract/oprf.d.ts.map +1 -1
- package/abstract/oprf.js +2 -2
- package/abstract/oprf.js.map +1 -1
- package/abstract/poseidon.d.ts.map +1 -1
- package/abstract/poseidon.js +8 -9
- package/abstract/poseidon.js.map +1 -1
- package/abstract/weierstrass.d.ts +66 -20
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +72 -68
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts +3 -9
- package/bls12-381.d.ts.map +1 -1
- package/bls12-381.js +3 -14
- package/bls12-381.js.map +1 -1
- package/bn254.d.ts +3 -3
- package/bn254.d.ts.map +1 -1
- package/bn254.js.map +1 -1
- package/ed25519.d.ts +22 -18
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +59 -31
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +17 -8
- package/ed448.d.ts.map +1 -1
- package/ed448.js +69 -52
- package/ed448.js.map +1 -1
- package/index.d.ts +1 -0
- package/index.js +20 -4
- package/index.js.map +1 -1
- package/misc.js +2 -2
- package/misc.js.map +1 -1
- package/nist.d.ts +20 -2
- package/nist.d.ts.map +1 -1
- package/nist.js +30 -10
- package/nist.js.map +1 -1
- package/package.json +14 -13
- package/secp256k1.d.ts +10 -7
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +15 -16
- package/secp256k1.js.map +1 -1
- package/src/abstract/bls.ts +22 -22
- package/src/abstract/curve.ts +19 -5
- package/src/abstract/edwards.ts +20 -23
- package/src/abstract/hash-to-curve.ts +50 -51
- package/src/abstract/modular.ts +7 -5
- package/src/abstract/montgomery.ts +12 -18
- package/src/abstract/oprf.ts +5 -5
- package/src/abstract/poseidon.ts +6 -8
- package/src/abstract/weierstrass.ts +139 -89
- package/src/bls12-381.ts +4 -15
- package/src/bn254.ts +6 -6
- package/src/ed25519.ts +65 -40
- package/src/ed448.ts +87 -69
- package/src/index.ts +19 -3
- package/src/misc.ts +2 -2
- package/src/nist.ts +31 -15
- package/src/secp256k1.ts +16 -18
- package/src/utils.ts +33 -83
- package/src/webcrypto.ts +148 -107
- package/utils.d.ts +4 -20
- package/utils.d.ts.map +1 -1
- package/utils.js +30 -73
- package/utils.js.map +1 -1
- package/webcrypto.d.ts +73 -21
- package/webcrypto.d.ts.map +1 -1
- package/webcrypto.js +101 -76
- package/webcrypto.js.map +1 -1
- package/_shortw_utils.d.ts +0 -19
- package/_shortw_utils.d.ts.map +0 -1
- package/_shortw_utils.js +0 -20
- package/_shortw_utils.js.map +0 -1
- package/abstract/utils.d.ts +0 -5
- package/abstract/utils.d.ts.map +0 -1
- package/abstract/utils.js +0 -23
- package/abstract/utils.js.map +0 -1
- package/jubjub.d.ts +0 -12
- package/jubjub.d.ts.map +0 -1
- package/jubjub.js +0 -15
- package/jubjub.js.map +0 -1
- package/p256.d.ts +0 -16
- package/p256.d.ts.map +0 -1
- package/p256.js +0 -13
- package/p256.js.map +0 -1
- package/p384.d.ts +0 -16
- package/p384.d.ts.map +0 -1
- package/p384.js +0 -13
- package/p384.js.map +0 -1
- package/p521.d.ts +0 -16
- package/p521.d.ts.map +0 -1
- package/p521.js +0 -13
- package/p521.js.map +0 -1
- package/pasta.d.ts +0 -10
- package/pasta.d.ts.map +0 -1
- package/pasta.js +0 -13
- package/pasta.js.map +0 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ Audited & minimal JS implementation of elliptic curve cryptography.
|
|
|
12
12
|
- 🥈 Optional, friendly wrapper over native WebCrypto
|
|
13
13
|
- 🪶 36KB (gzipped) including bundled hashes, 11KB for single-curve build
|
|
14
14
|
|
|
15
|
-
Curves have
|
|
15
|
+
Curves have 5kb sister projects
|
|
16
16
|
[secp256k1](https://github.com/paulmillr/noble-secp256k1) & [ed25519](https://github.com/paulmillr/noble-ed25519).
|
|
17
17
|
They have smaller attack surface, but less features.
|
|
18
18
|
|
|
@@ -30,7 +30,7 @@ Take a glance at [GitHub Discussions](https://github.com/paulmillr/noble-curves/
|
|
|
30
30
|
[curves](https://github.com/paulmillr/noble-curves),
|
|
31
31
|
[hashes](https://github.com/paulmillr/noble-hashes),
|
|
32
32
|
[post-quantum](https://github.com/paulmillr/noble-post-quantum),
|
|
33
|
-
|
|
33
|
+
5kb [secp256k1](https://github.com/paulmillr/noble-secp256k1) /
|
|
34
34
|
[ed25519](https://github.com/paulmillr/noble-ed25519)
|
|
35
35
|
- [Check out homepage](https://paulmillr.com/noble/)
|
|
36
36
|
for reading resources, documentation and apps built with noble
|
|
@@ -50,12 +50,12 @@ A standalone file [noble-curves.js](https://github.com/paulmillr/noble-curves/re
|
|
|
50
50
|
```ts
|
|
51
51
|
// import * from '@noble/curves'; // Error: use sub-imports, to ensure small app size
|
|
52
52
|
import { secp256k1, schnorr } from '@noble/curves/secp256k1.js';
|
|
53
|
-
import { ed25519, ed25519ph, ed25519ctx, x25519 } from '@noble/curves/ed25519.js';
|
|
54
|
-
import { ed448, ed448ph,
|
|
53
|
+
import { ed25519, ed25519ph, ed25519ctx, x25519, ristretto255 } from '@noble/curves/ed25519.js';
|
|
54
|
+
import { ed448, ed448ph, x448, decaf448 } from '@noble/curves/ed448.js';
|
|
55
55
|
import { p256, p384, p521 } from '@noble/curves/nist.js';
|
|
56
56
|
import { bls12_381 } from '@noble/curves/bls12-381.js';
|
|
57
57
|
import { bn254 } from '@noble/curves/bn254.js';
|
|
58
|
-
import { jubjub, babyjubjub } from '@noble/curves/misc.js';
|
|
58
|
+
import { jubjub, babyjubjub, brainpoolP256r1, brainpoolP384r1, brainpoolP512r1 } from '@noble/curves/misc.js';
|
|
59
59
|
|
|
60
60
|
// hash-to-curve
|
|
61
61
|
import { secp256k1_hasher } from '@noble/curves/secp256k1.js';
|
|
@@ -66,48 +66,75 @@ import { decaf448_hasher } from '@noble/curves/ed448.js';
|
|
|
66
66
|
// OPRFs
|
|
67
67
|
import { p256_oprf, p384_oprf, p521_oprf } from '@noble/curves/nist.js';
|
|
68
68
|
import { ristretto255_oprf } from '@noble/curves/ed25519.js';
|
|
69
|
-
import {
|
|
69
|
+
import { decaf448_oprf } from '@noble/curves/ed448.js';
|
|
70
70
|
|
|
71
71
|
// utils
|
|
72
|
+
import { bytesToHex, hexToBytes, concatBytes } from '@noble/curves/abstract/utils.js';
|
|
73
|
+
import { Field } from '@noble/curves/abstract/modular.js';
|
|
72
74
|
import { weierstrass, ecdsa } from '@noble/curves/abstract/weierstrass.js';
|
|
73
75
|
import { edwards, eddsa } from '@noble/curves/abstract/edwards.js';
|
|
74
76
|
import { poseidon, poseidonSponge } from '@noble/curves/abstract/poseidon.js';
|
|
75
|
-
import { Field, mod, pow } from '@noble/curves/abstract/modular.js';
|
|
76
77
|
import { FFT, poly } from '@noble/curves/abstract/fft.js';
|
|
77
|
-
import { bytesToHex, hexToBytes, concatBytes, utf8ToBytes } from '@noble/curves/abstract/utils.js';
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
+
- Examples
|
|
81
|
+
- [ECDSA, EdDSA, Schnorr signatures](#ecdsa-eddsa-schnorr-signatures)
|
|
82
|
+
- [secp256k1, p256, p384, p521, ed25519, ed448, brainpool](#secp256k1-p256-p384-p521-ed25519-ed448-brainpool)
|
|
83
|
+
- [ristretto255, decaf448](#ristretto255-decaf448)
|
|
84
|
+
- [Prehashed signing](#prehashed-signing)
|
|
85
|
+
- [Hedged ECDSA with noise](#hedged-ecdsa-with-noise)
|
|
86
|
+
- [Consensus-friendliness vs e-voting](#consensus-friendliness-vs-e-voting)
|
|
87
|
+
- [ECDH: Diffie-Hellman shared secrets](#ecdh-diffie-hellman-shared-secrets)
|
|
88
|
+
- [webcrypto: Friendly wrapper](#webcrypto-friendly-wrapper)
|
|
89
|
+
- [BLS signatures, bls12-381, bn254 aka alt\_bn128](#bls-signatures-bls12-381-bn254-aka-alt_bn128)
|
|
90
|
+
- [Hashing to curve points](#hash-to-curve-hashing-to-curve-points)
|
|
91
|
+
- [OPRFs](#oprfs)
|
|
92
|
+
- [Poseidon hash](#poseidon-poseidon-hash)
|
|
93
|
+
- [Fast Fourier Transform](#fft-fast-fourier-transform)
|
|
94
|
+
- [utils](#utils-byte-shuffling-conversion)
|
|
95
|
+
- [Internals](#internals)
|
|
96
|
+
- [Elliptic curve Point math](#elliptic-curve-point-math)
|
|
97
|
+
- [modular: Modular arithmetics \& finite fields](#modular-modular-arithmetics--finite-fields)
|
|
98
|
+
- [weierstrass: Custom Weierstrass curve](#weierstrass-custom-weierstrass-curve)
|
|
99
|
+
- [edwards: Custom Edwards curve](#edwards-custom-edwards-curve)
|
|
100
|
+
- [Custom ECDSA instance](#custom-ecdsa-instance)
|
|
101
|
+
- [Security](#security)
|
|
102
|
+
- [Speed](#speed)
|
|
103
|
+
- [Contributing & testing](#contributing--testing)
|
|
104
|
+
- [Upgrading](#upgrading)
|
|
80
105
|
|
|
81
106
|
### ECDSA, EdDSA, Schnorr signatures
|
|
82
107
|
|
|
83
|
-
#### secp256k1, p256, p384, p521, ed25519, ed448
|
|
108
|
+
#### secp256k1, p256, p384, p521, ed25519, ed448, brainpool
|
|
84
109
|
|
|
85
110
|
```js
|
|
86
111
|
import { secp256k1, schnorr } from '@noble/curves/secp256k1.js';
|
|
87
112
|
import { p256, p384, p521 } from '@noble/curves/nist.js';
|
|
88
113
|
import { ed25519 } from '@noble/curves/ed25519.js';
|
|
89
114
|
import { ed448 } from '@noble/curves/ed448.js';
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
115
|
+
import { brainpoolP256r1, brainpoolP384r1, brainpoolP512r1 } from '@noble/curves/misc.js';
|
|
116
|
+
for (const curve of [
|
|
117
|
+
secp256k1, schnorr,
|
|
118
|
+
p256, p384, p521,
|
|
119
|
+
ed25519, ed448,
|
|
120
|
+
brainpoolP256r1, brainpoolP384r1, brainpoolP512r1
|
|
121
|
+
]) {
|
|
94
122
|
const { secretKey, publicKey } = curve.keygen();
|
|
95
|
-
const msg =
|
|
123
|
+
const msg = new TextEncoder().encode('hello noble');
|
|
96
124
|
const sig = curve.sign(msg, secretKey);
|
|
97
125
|
const isValid = curve.verify(sig, msg, publicKey);
|
|
98
|
-
console.log(curve, secretKey, publicKey, sig);
|
|
126
|
+
console.log(curve, secretKey, publicKey, sig, isValid);
|
|
99
127
|
}
|
|
100
128
|
|
|
101
129
|
// Specific private key
|
|
102
|
-
|
|
103
|
-
const
|
|
130
|
+
import { hexToBytes } from '@noble/curves/utils.js';
|
|
131
|
+
const secret2 = hexToBytes('46c930bc7bb4db7f55da20798697421b98c4175a52c630294d75a84b9c126236');
|
|
132
|
+
const pub2 = secp256k1.getPublicKey(secret2);
|
|
104
133
|
```
|
|
105
134
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
-
|
|
109
|
-
- EdDSA conform to RFC8032.
|
|
110
|
-
- Schnorr is only available for secp256k1 and conforms to BIP340.
|
|
135
|
+
ECDSA signatures use deterministic k, conforming to [RFC 6979](https://www.rfc-editor.org/rfc/rfc6979).
|
|
136
|
+
EdDSA conforms to [RFC 8032](https://www.rfc-editor.org/rfc/rfc8032).
|
|
137
|
+
Schnorr (secp256k1-only) conforms to [BIP 340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki).
|
|
111
138
|
|
|
112
139
|
#### ristretto255, decaf448
|
|
113
140
|
|
|
@@ -115,67 +142,90 @@ their hash-to-curve methods, and OPRFs.
|
|
|
115
142
|
import { ristretto255, ristretto255_hasher, ristretto255_oprf } from '@noble/curves/ed25519.js';
|
|
116
143
|
import { decaf448, decaf448_hasher, decaf448_oprf } from '@noble/curves/ed448.js';
|
|
117
144
|
|
|
118
|
-
|
|
119
|
-
import { shake256 } from '@noble/hashes/sha3.js';
|
|
120
|
-
|
|
121
|
-
const msg = new TextEncoder().encode('Ristretto is traditionally a short shot of espresso coffee');
|
|
122
|
-
hashToCurve(msg);
|
|
123
|
-
|
|
124
|
-
const dp = DecafPoint.fromHex(
|
|
125
|
-
'c898eb4f87f97c564c6fd61fc7e49689314a1f818ec85eeb3bd5514ac816d38778f69ef347a89fca817e66defdedce178c7cc709b2116e75'
|
|
126
|
-
);
|
|
127
|
-
DecafPoint.BASE.multiply(2n).add(dp).subtract(DecafPoint.BASE).toBytes();
|
|
128
|
-
DecafPoint.ZERO.equals(dp) === false;
|
|
129
|
-
// pre-hashed hash-to-curve
|
|
130
|
-
DecafPoint.hashToCurve(shake256(msg, { dkLen: 112 }));
|
|
131
|
-
// full hash-to-curve including domain separation tag
|
|
132
|
-
hashToDecaf448(msg, { DST: 'decaf448_XOF:SHAKE256_D448MAP_RO_' });
|
|
145
|
+
console.log(ristretto255.Point, decaf448.Point);
|
|
133
146
|
```
|
|
134
147
|
|
|
135
|
-
Check out [
|
|
148
|
+
Check out [RFC 9496](https://www.rfc-editor.org/rfc/rfc9496) more info on ristretto255 & decaf448.
|
|
149
|
+
Check out separate documentation for [Point](#elliptic-curve-point-math), [hasher](#hash-to-curve-hashing-to-curve-points) and [oprf](#oprfs).
|
|
136
150
|
|
|
137
151
|
#### Prehashed signing
|
|
138
152
|
|
|
139
|
-
|
|
153
|
+
```js
|
|
154
|
+
import { secp256k1 } from '@noble/curves/secp256k1.js';
|
|
155
|
+
import { keccak256 } from '@noble/hashes/sha3.js';
|
|
156
|
+
const { secretKey } = curve.keygen();
|
|
157
|
+
const msg = new TextEncoder().encode('hello noble');
|
|
158
|
+
// prehash: true (default) - hash using secp256k1.hash (sha256)
|
|
159
|
+
const sig = secp256k1.sign(msg, secretKey);
|
|
160
|
+
// prehash: false - hash using custom hash
|
|
161
|
+
const sigKeccak = secp256k1.sign(keccak256(msg), secretKey, { prehash: false });
|
|
162
|
+
```
|
|
140
163
|
|
|
141
|
-
|
|
164
|
+
ECDSA `sign()` allows providing `prehash: false`, which enables using custom hashes.
|
|
142
165
|
|
|
143
|
-
|
|
166
|
+
A ECDSA signature is not just "math over elliptic curve points".
|
|
167
|
+
It's actually math + hashing: p256 is in fact p256 point + sha256 hash.
|
|
168
|
+
By default, we hash messages. To use custom hash methods,
|
|
169
|
+
make sure to disable prehashing.
|
|
170
|
+
|
|
171
|
+
> [!NOTE]
|
|
172
|
+
> Previously, in noble-curves v1, `prehash: false` was the default.
|
|
173
|
+
> Some other libraries (like libsecp256k1) have no prehashing.
|
|
144
174
|
|
|
175
|
+
#### Hedged ECDSA with noise
|
|
145
176
|
|
|
146
177
|
```js
|
|
147
|
-
|
|
148
|
-
const
|
|
149
|
-
const
|
|
150
|
-
|
|
178
|
+
import { secp256k1 } from '@noble/curves/secp256k1.js';
|
|
179
|
+
const { secretKey } = curve.keygen();
|
|
180
|
+
const msg = new TextEncoder().encode('hello noble');
|
|
181
|
+
// extraEntropy: false - default, hedging disabled
|
|
182
|
+
const sigNoisy = secp256k1.sign(msg, secretKey);
|
|
183
|
+
// extraEntropy: true - fetch 32 random bytes from CSPRNG
|
|
184
|
+
const sigNoisy = secp256k1.sign(msg, secretKey, { extraEntropy: true });
|
|
185
|
+
// extraEntropy: bytes - specific extra entropy
|
|
186
|
+
const ent = Uint8Array.from([0xca, 0xfe, 0x01, 0x23]);
|
|
187
|
+
const sigNoisy2 = secp256k1.sign(msg, secretKey, { extraEntropy: ent });
|
|
188
|
+
```
|
|
151
189
|
|
|
152
|
-
|
|
190
|
+
ECDSA `sign()` allows providing `extraEntropy`, which switches sig generation to hedged mode.
|
|
153
191
|
|
|
154
|
-
|
|
155
|
-
|
|
192
|
+
By default, ECDSA signatures are generated deterministically,
|
|
193
|
+
following [RFC 6979](https://www.rfc-editor.org/rfc/rfc6979).
|
|
194
|
+
However, purely deterministic signatures are vulnerable to fault attacks.
|
|
195
|
+
Newer signature schemes, such as BIP340 schnorr, switched to hedged signatures because of this.
|
|
196
|
+
Hedging is basically incorporating some randomness into sig generation process.
|
|
156
197
|
|
|
157
|
-
|
|
158
|
-
|
|
198
|
+
For more info, check out
|
|
199
|
+
[Deterministic signatures are not your friends](https://paulmillr.com/posts/deterministic-signatures/),
|
|
200
|
+
[RFC 6979](https://www.rfc-editor.org/rfc/rfc6979) section 3.6,
|
|
201
|
+
and [cfrg-det-sigs-with-noise draft](https://datatracker.ietf.org/doc/draft-irtf-cfrg-det-sigs-with-noise/).
|
|
159
202
|
|
|
203
|
+
#### Consensus-friendliness vs e-voting
|
|
160
204
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
const
|
|
166
|
-
|
|
205
|
+
```js
|
|
206
|
+
import { ed25519 } from '@noble/curves/ed25519.js';
|
|
207
|
+
const { secretKey, publicKey } = ed25519.keygen();
|
|
208
|
+
const msg = new TextEncoder().encode('hello noble');
|
|
209
|
+
const sig = ed25519.sign(msg, secretKey);
|
|
210
|
+
// zip215: true
|
|
211
|
+
const isValid = ed25519.verify(sig, msg, pub);
|
|
212
|
+
// SBS / e-voting / RFC8032 / FIPS 186-5
|
|
213
|
+
const isValidRfc = ed25519.verify(sig, msg, pub, { zip215: false });
|
|
167
214
|
```
|
|
168
215
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
216
|
+
In ed25519, there is an ability to choose between consensus-friendliness vs e-voting mode.
|
|
217
|
+
|
|
218
|
+
- `zip215: true` is default behavior. It has slightly looser verification logic
|
|
219
|
+
to be [consensus-friendly](https://hdevalence.ca/blog/2020-10-04-its-25519am), following [ZIP215](https://zips.z.cash/zip-0215) rules
|
|
220
|
+
- `zip215: false` switches verification criteria to strict
|
|
221
|
+
[RFC 8032](https://www.rfc-editor.org/rfc/rfc8032) / [FIPS 186-5](https://csrc.nist.gov/publications/detail/fips/186/5/final)
|
|
222
|
+
and additionally provides [non-repudiation with SBS](https://eprint.iacr.org/2020/1244),
|
|
223
|
+
which is useful for:
|
|
224
|
+
- Contract Signing: if A signed an agreement with B using key that allows repudiation, it can later claim that it signed a different contract
|
|
225
|
+
- E-voting: malicious voters may pick keys that allow repudiation in order to deny results
|
|
226
|
+
- Blockchains: transaction of amount X might also be valid for a different amount Y
|
|
227
|
+
|
|
228
|
+
Both modes have SUF-CMA (strong unforgeability under chosen message attacks).
|
|
179
229
|
|
|
180
230
|
### ECDH: Diffie-Hellman shared secrets
|
|
181
231
|
|
|
@@ -192,57 +242,122 @@ for (const curve of [secp256k1, schnorr, x25519, x448, p256, p384, p521]) {
|
|
|
192
242
|
console.log('alice', alice, 'bob', bob, 'shared', sharedKey);
|
|
193
243
|
}
|
|
194
244
|
|
|
195
|
-
x25519
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
245
|
+
// x25519 & x448 specific methods
|
|
246
|
+
import { ed25519 } from '@noble/curves/ed25519.js';
|
|
247
|
+
const alice = ed25519.keygen();
|
|
248
|
+
const bob = ed25519.keygen();
|
|
249
|
+
const aliceSecX = ed25519.utils.toMontgomerySecret(alice.secretKey);
|
|
250
|
+
const bobPubX = ed25519.utils.toMontgomery(bob.publicKey);
|
|
251
|
+
const sharedKey = x25519.getSharedSecret(aliceSecX, bobPubX);
|
|
201
252
|
```
|
|
202
253
|
|
|
203
|
-
|
|
204
|
-
|
|
254
|
+
We provide ECDH over all Weierstrass curves, and over 2 Montgomery curves
|
|
255
|
+
X25519 (Curve25519) & X448 (Curve448), conforming to [RFC 7748](https://www.rfc-editor.org/rfc/rfc7748).
|
|
205
256
|
|
|
206
|
-
|
|
207
|
-
|
|
257
|
+
In Weierstrass curves, shared secrets:
|
|
258
|
+
|
|
259
|
+
- Include y-parity bytes: use `key.slice(1)` to strip it
|
|
260
|
+
- Are not hashed: use hashing or KDF on top, like `sha256(shared)` or `hkdf(shared)`
|
|
261
|
+
|
|
262
|
+
#### webcrypto: Friendly wrapper
|
|
208
263
|
|
|
209
264
|
> [!NOTE]
|
|
210
|
-
>
|
|
265
|
+
> Webcrypto methods are always async.
|
|
266
|
+
|
|
267
|
+
##### webcrypto signatures
|
|
268
|
+
|
|
269
|
+
```js
|
|
270
|
+
import { ed25519, ed448, p256, p384, p521 } from './src/webcrypto.ts';
|
|
271
|
+
|
|
272
|
+
(async () => {
|
|
273
|
+
for (let [name, curve] of Object.entries({ p256, p384, p521, ed25519, ed448 })) {
|
|
274
|
+
console.log('curve', name);
|
|
275
|
+
if (!await curve.isSupported()) {
|
|
276
|
+
console.log('is not supported, skipping');
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
const keys = await curve.keygen();
|
|
280
|
+
const msg = new TextEncoder().encode('hello noble');
|
|
281
|
+
const sig = await curve.sign(msg, keys.secretKey);
|
|
282
|
+
const isValid = await curve.verify(sig, msg, keys.publicKey);
|
|
283
|
+
console.log({
|
|
284
|
+
keys, msg, sig, isValid
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
})();
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
##### webcrypto ecdh
|
|
291
|
+
|
|
292
|
+
```js
|
|
293
|
+
import { p256, p384, p521, x25519, x448 } from './src/webcrypto.ts';
|
|
294
|
+
|
|
295
|
+
(async () => {
|
|
296
|
+
for (let [name, curve] of Object.entries({ p256, p384, p521, x25519, x448 })) {
|
|
297
|
+
console.log('curve', name);
|
|
298
|
+
if (!await curve.isSupported()) {
|
|
299
|
+
console.log('is not supported, skipping');
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
const alice = await curve.keygen();
|
|
303
|
+
const bob = await curve.keygen();
|
|
304
|
+
const shared = await curve.getSharedSecret(alice.secretKey, bob.publicKey);
|
|
305
|
+
const shared2 = await curve.getSharedSecret(bob.secretKey, alice.publicKey);
|
|
306
|
+
console.log({shared});
|
|
307
|
+
}
|
|
308
|
+
})();
|
|
309
|
+
```
|
|
211
310
|
|
|
311
|
+
##### Key conversion from noble to webcrypto and back
|
|
312
|
+
|
|
313
|
+
```js
|
|
314
|
+
import { p256 as p256n } from './src/nist.ts';
|
|
315
|
+
import { p256 } from './src/webcrypto.ts';
|
|
316
|
+
(async () => {
|
|
317
|
+
const nobleKeys = p256n.keygen();
|
|
318
|
+
// convert noble keys to webcrypto
|
|
319
|
+
const webKeys = {
|
|
320
|
+
secretKey: await p256.utils.convertSecretKey(nobleKeys.secretKey, 'raw', 'pkcs8'),
|
|
321
|
+
publicKey: await p256.utils.convertPublicKey(nobleKeys.publicKey, 'raw', 'spki')
|
|
322
|
+
};
|
|
323
|
+
// convert webcrypto keys to noble
|
|
324
|
+
const nobleKeys2 = {
|
|
325
|
+
secretKey: await p256.utils.convertSecretKey(webKeys.secretKey, 'pkcs8', 'raw'),
|
|
326
|
+
publicKey: await p256.utils.convertPublicKey(webKeys.publicKey, 'spki', 'raw')
|
|
327
|
+
};
|
|
328
|
+
})();
|
|
329
|
+
```
|
|
212
330
|
|
|
213
331
|
### BLS signatures, bls12-381, bn254 aka alt_bn128
|
|
214
332
|
|
|
215
333
|
```ts
|
|
216
334
|
import { bls12_381 } from '@noble/curves/bls12-381.js';
|
|
217
|
-
import { hexToBytes } from '@noble/curves/abstract/utils.js';
|
|
218
|
-
|
|
219
|
-
// private keys are 32 bytes
|
|
220
|
-
const privKey = hexToBytes('67d53f170b908cabb9eb326c3c337762d59289a8fec79f7bc9254b584b73265c');
|
|
221
|
-
// const privKey = bls12_381.utils.randomPrivateKey();
|
|
222
335
|
|
|
223
|
-
//
|
|
336
|
+
// G1 pubkeys, G2 sigs
|
|
224
337
|
const blsl = bls12_381.longSignatures;
|
|
225
|
-
const publicKey = blsl.
|
|
226
|
-
//
|
|
227
|
-
const msg = new TextEncoder().encode('hello');
|
|
228
|
-
|
|
229
|
-
const msgp = blsl.hash(msg
|
|
230
|
-
|
|
338
|
+
const { secretKey, publicKey } = blsl.keygen();
|
|
339
|
+
// const publicKey = blsl.getPublicKey(secretKey);
|
|
340
|
+
const msg = new TextEncoder().encode('hello noble');
|
|
341
|
+
// default DST
|
|
342
|
+
const msgp = blsl.hash(msg);
|
|
343
|
+
// custom DST (Ethereum)
|
|
344
|
+
const msgpd = blsl.hash(msg, 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_');
|
|
345
|
+
const signature = blsl.sign(msgp, secretKey);
|
|
231
346
|
const isValid = blsl.verify(signature, msgp, publicKey);
|
|
232
|
-
console.log({ publicKey, signature, isValid });
|
|
347
|
+
console.log('long', { publicKey, signature, isValid });
|
|
233
348
|
|
|
234
|
-
//
|
|
349
|
+
// G1 sigs, G2 pubkeys
|
|
235
350
|
const blss = bls12_381.shortSignatures;
|
|
236
|
-
const publicKey2 = blss.getPublicKey(
|
|
237
|
-
const msgp2 = blss.hash(
|
|
238
|
-
const signature2 = blss.sign(msgp2,
|
|
351
|
+
const publicKey2 = blss.getPublicKey(secretKey);
|
|
352
|
+
const msgp2 = blss.hash(msg, 'BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_');
|
|
353
|
+
const signature2 = blss.sign(msgp2, secretKey);
|
|
239
354
|
const isValid2 = blss.verify(signature2, msgp2, publicKey);
|
|
240
355
|
console.log({ publicKey2, signature2, isValid2 });
|
|
241
356
|
|
|
242
357
|
// Aggregation
|
|
243
358
|
const aggregatedKey = bls12_381.longSignatures.aggregatePublicKeys([
|
|
244
|
-
bls12_381.utils.
|
|
245
|
-
bls12_381.utils.
|
|
359
|
+
bls12_381.utils.randomSecretKey(),
|
|
360
|
+
bls12_381.utils.randomSecretKey(),
|
|
246
361
|
]);
|
|
247
362
|
// const aggregatedSig = bls.aggregateSignatures(sigs)
|
|
248
363
|
|
|
@@ -252,7 +367,7 @@ const aggregatedKey = bls12_381.longSignatures.aggregatePublicKeys([
|
|
|
252
367
|
// bls.fields.Fp12.finalExponentiate(bls.fields.Fp12.mul(PointG1, PointG2));
|
|
253
368
|
|
|
254
369
|
// Others
|
|
255
|
-
// bls.G1.
|
|
370
|
+
// bls.G1.Point.BASE, bls.G2.Point.BASE;
|
|
256
371
|
// bls.fields.Fp, bls.fields.Fp2, bls.fields.Fp12, bls.fields.Fr;
|
|
257
372
|
```
|
|
258
373
|
|
|
@@ -263,18 +378,139 @@ The BN254 API mirrors [BLS](#bls12-381). The curve was previously called alt_bn1
|
|
|
263
378
|
The implementation is compatible with [EIP-196](https://eips.ethereum.org/EIPS/eip-196) and
|
|
264
379
|
[EIP-197](https://eips.ethereum.org/EIPS/eip-197).
|
|
265
380
|
|
|
266
|
-
|
|
267
|
-
To work around this limitation, has to initialize points on their own from BigInts.
|
|
268
|
-
Reason it's not implemented is because [there is no standard](https://github.com/privacy-scaling-explorations/halo2curves/issues/109).
|
|
381
|
+
For BN254 usage, check out [the implementation of bn254 EVM precompiles](https://github.com/paulmillr/noble-curves/blob/3ed792f8ad9932765b84d1064afea8663a255457/test/bn254.test.js#L697).
|
|
382
|
+
We don't implement Point methods toBytes. To work around this limitation, has to initialize points on their own from BigInts. Reason it's not implemented is because [there is no standard](https://github.com/privacy-scaling-explorations/halo2curves/issues/109).
|
|
269
383
|
Points of divergence:
|
|
270
384
|
|
|
271
385
|
- Endianness: LE vs BE (byte-swapped)
|
|
272
386
|
- Flags as first hex bits (similar to BLS) vs no-flags
|
|
273
387
|
- Imaginary part last in G2 vs first (c0, c1 vs c1, c0)
|
|
274
388
|
|
|
275
|
-
|
|
389
|
+
### hash-to-curve: hashing to curve points
|
|
390
|
+
|
|
391
|
+
```ts
|
|
392
|
+
import { bls12_381 } from './src/bls12-381.ts';
|
|
393
|
+
import { ed25519_hasher, ristretto255_hasher } from './src/ed25519.ts';
|
|
394
|
+
import { decaf448_hasher, ed448_hasher } from './src/ed448.ts';
|
|
395
|
+
import { p256_hasher, p384_hasher, p521_hasher } from './src/nist.ts';
|
|
396
|
+
import { secp256k1_hasher } from './src/secp256k1.ts';
|
|
397
|
+
|
|
398
|
+
const h = {
|
|
399
|
+
secp256k1_hasher,
|
|
400
|
+
p256_hasher, p384_hasher, p521_hasher,
|
|
401
|
+
ed25519_hasher,
|
|
402
|
+
ed448_hasher,
|
|
403
|
+
ristretto255_hasher,
|
|
404
|
+
decaf448_hasher,
|
|
405
|
+
bls_G1: bls12_381.G1,
|
|
406
|
+
bls_G2: bls12_381.G2
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
const msg = Uint8Array.from([0xca, 0xfe, 0x01, 0x23]);
|
|
410
|
+
console.log('msg', msg);
|
|
411
|
+
for (let [name, c] of Object.entries(h)) {
|
|
412
|
+
const hashToCurve = c.hashToCurve(msg).toHex();
|
|
413
|
+
const hashToCurve_customDST = c.hashToCurve(msg, { DST: 'hello noble' }).toHex();
|
|
414
|
+
const encodeToCurve = 'encodeToCurve' in c ? c.encodeToCurve(msg).toHex() : undefined;
|
|
415
|
+
// ristretto255, decaf448 only
|
|
416
|
+
const deriveToCurve = 'deriveToCurve' in c ?
|
|
417
|
+
c.deriveToCurve!(new Uint8Array(c.Point.Fp.BYTES * 2)).toHex() : undefined;
|
|
418
|
+
const hashToScalar = c.hashToScalar(msg);
|
|
419
|
+
console.log({
|
|
420
|
+
name, hashToCurve, hashToCurve_customDST, encodeToCurve, deriveToCurve, hashToScalar
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// abstract methods
|
|
425
|
+
import { expand_message_xmd, expand_message_xof, hash_to_field } from '@noble/curves/abstract/hash-to-curve.js';
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
The module allows to hash arbitrary strings to elliptic curve points. Implements [RFC 9380](https://www.rfc-editor.org/rfc/rfc9380).
|
|
429
|
+
|
|
430
|
+
> [!NOTE]
|
|
431
|
+
> Why is `p256_hasher` separate from `p256`?
|
|
432
|
+
> The methods reside in separate _hasher namespace for tree-shaking:
|
|
433
|
+
> this way users who don't need hash-to-curve, won't have it in their builds.
|
|
434
|
+
|
|
435
|
+
### OPRFs
|
|
436
|
+
|
|
437
|
+
```js
|
|
438
|
+
import { p256_oprf, p384_oprf, p521_oprf } from '@noble/curves/nist.js';
|
|
439
|
+
import { ristretto255_oprf } from '@noble/curves/ed25519.js';
|
|
440
|
+
import { decaf448_orpf } from '@noble/curves/ed448.js';
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
We provide OPRFs (oblivious pseudorandom functions),
|
|
444
|
+
conforming to [RFC 9497](https://www.rfc-editor.org/rfc/rfc9497).
|
|
445
|
+
|
|
446
|
+
OPRF allows to interactively create an `Output = PRF(Input, serverSecretKey)`:
|
|
447
|
+
|
|
448
|
+
- Server cannot calculate Output by itself: it doesn't know Input
|
|
449
|
+
- Client cannot calculate Output by itself: it doesn't know server secretKey
|
|
450
|
+
- An attacker interception the communication can't restore Input/Output/serverSecretKey and can't
|
|
451
|
+
link Input to some value.
|
|
452
|
+
|
|
453
|
+
### poseidon: Poseidon hash
|
|
454
|
+
|
|
455
|
+
Implements [Poseidon](https://www.poseidon-hash.info) ZK-friendly hash:
|
|
456
|
+
permutation and sponge.
|
|
457
|
+
|
|
458
|
+
There are many poseidon variants with different constants.
|
|
459
|
+
We don't provide them: you should construct them manually.
|
|
460
|
+
Check out [scure-starknet](https://github.com/paulmillr/scure-starknet) package for a proper example.
|
|
461
|
+
|
|
462
|
+
```ts
|
|
463
|
+
import { poseidon, poseidonSponge } from '@noble/curves/abstract/poseidon.js';
|
|
464
|
+
|
|
465
|
+
const rate = 2;
|
|
466
|
+
const capacity = 1;
|
|
467
|
+
const { mds, roundConstants } = poseidon.grainGenConstants({
|
|
468
|
+
Fp,
|
|
469
|
+
t: rate + capacity,
|
|
470
|
+
roundsFull: 8,
|
|
471
|
+
roundsPartial: 31,
|
|
472
|
+
});
|
|
473
|
+
const opts = {
|
|
474
|
+
Fp,
|
|
475
|
+
rate,
|
|
476
|
+
capacity,
|
|
477
|
+
sboxPower: 17,
|
|
478
|
+
mds,
|
|
479
|
+
roundConstants,
|
|
480
|
+
roundsFull: 8,
|
|
481
|
+
roundsPartial: 31,
|
|
482
|
+
};
|
|
483
|
+
const permutation = poseidon.poseidon(opts);
|
|
484
|
+
const sponge = poseidon.poseidonSponge(opts); // use carefully, not specced
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
### fft: Fast Fourier Transform
|
|
488
|
+
|
|
489
|
+
```ts
|
|
490
|
+
import * as fft from '@noble/curves/abstract/fft.js';
|
|
491
|
+
import { bls12_381 } from '@noble/curves/bls12-381.js';
|
|
492
|
+
const Fr = bls12_381.fields.Fr;
|
|
493
|
+
const roots = fft.rootsOfUnity(Fr, 7n);
|
|
494
|
+
const fftFr = fft.FFT(roots, Fr);
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
Experimental implementation of NTT / FFT (Fast Fourier Transform) over finite fields.
|
|
498
|
+
API may change at any time. The code has not been audited. Feature requests are welcome.
|
|
499
|
+
|
|
500
|
+
### utils: byte shuffling, conversion
|
|
501
|
+
|
|
502
|
+
```ts
|
|
503
|
+
import { bytesToHex, concatBytes, equalBytes, hexToBytes } from '@noble/curves/abstract/utils.js';
|
|
504
|
+
|
|
505
|
+
bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23]));
|
|
506
|
+
hexToBytes('cafe0123');
|
|
507
|
+
concatBytes(Uint8Array.from([0xca, 0xfe]), Uint8Array.from([0x01, 0x23]));
|
|
508
|
+
equalBytes(Uint8Array.of(0xca), Uint8Array.of(0xca));
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### Internals
|
|
276
512
|
|
|
277
|
-
|
|
513
|
+
#### Elliptic curve Point math
|
|
278
514
|
|
|
279
515
|
```js
|
|
280
516
|
import { secp256k1, schnorr } from '@noble/curves/secp256k1.js';
|
|
@@ -292,7 +528,7 @@ const curves = [
|
|
|
292
528
|
jubjub, babyjubjub
|
|
293
529
|
];
|
|
294
530
|
for (const curve of curves) {
|
|
295
|
-
const {
|
|
531
|
+
const { Point } = curve;
|
|
296
532
|
const { BASE, ZERO, Fp, Fn } = Point;
|
|
297
533
|
const p = BASE.multiply(2n);
|
|
298
534
|
|
|
@@ -328,7 +564,7 @@ for (const curve of curves) {
|
|
|
328
564
|
}
|
|
329
565
|
```
|
|
330
566
|
|
|
331
|
-
#### Modular
|
|
567
|
+
#### modular: Modular arithmetics & finite fields
|
|
332
568
|
|
|
333
569
|
```js
|
|
334
570
|
import { mod, invert, Field } from '@noble/curves/abstract/modular.js';
|
|
@@ -346,59 +582,14 @@ mod(21n, 10n); // 21 mod 10 == 1n; fixed version of 21 % 10
|
|
|
346
582
|
invert(17n, 10n); // invert(17) mod 10; modular multiplicative inverse
|
|
347
583
|
```
|
|
348
584
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
- Can be prehashed, or non-prehashed:
|
|
354
|
-
- `sign(msgHash, privKey)` (default, prehash: false) - you did hashing before
|
|
355
|
-
- `sign(msg, privKey, {prehash: true})` - curves will do hashing for you
|
|
356
|
-
- Are generated deterministically, following [RFC6979](https://www.rfc-editor.org/rfc/rfc6979).
|
|
357
|
-
- Consider [hedged ECDSA with noise](#hedged-ecdsa-with-noise) for adding randomness into
|
|
358
|
-
for signatures, to get improved security against fault attacks.
|
|
359
|
-
|
|
360
|
-
All arithmetics is done with JS
|
|
361
|
-
bigints over finite fields, which is defined from `modular` sub-module.
|
|
362
|
-
For scalar multiplication, we use
|
|
363
|
-
[precomputed tables with w-ary non-adjacent form (wNAF)](https://paulmillr.com/posts/noble-secp256k1-fast-ecc/).
|
|
364
|
-
Precomputes are enabled for weierstrass and edwards BASE points of a curve.
|
|
365
|
-
Field operations are not constant-time: they are using JS bigints, see [security](#security).
|
|
585
|
+
All arithmetics is done with JS bigints over finite fields,
|
|
586
|
+
which is defined from `modular` sub-module.
|
|
587
|
+
|
|
588
|
+
Field operations are not constant-time: see [security](#security).
|
|
366
589
|
The fact is mostly irrelevant, but the important method to keep in mind is `pow`,
|
|
367
590
|
which may leak exponent bits, when used naïvely.
|
|
368
591
|
|
|
369
|
-
|
|
370
|
-
We don't test for prime-ness for speed and because algorithms are probabilistic anyway.
|
|
371
|
-
Initializing a non-prime field could make your app suspectible to
|
|
372
|
-
DoS (infilite loop) on Tonelli-Shanks square root calculation.
|
|
373
|
-
|
|
374
|
-
Unlike `mod.inv`, `mod.invertBatch` won't throw on `0`: make sure to throw an error yourself.
|
|
375
|
-
|
|
376
|
-
We define ed25519, ed448; user can use custom curves with EdDSA,
|
|
377
|
-
but EdDSA in general is not defined. Check out `edwards.ts` source code.
|
|
378
|
-
|
|
379
|
-
For EdDSA signatures:
|
|
380
|
-
|
|
381
|
-
- `zip215: true` is default behavior. It has slightly looser verification logic
|
|
382
|
-
to be [consensus-friendly](https://hdevalence.ca/blog/2020-10-04-its-25519am), following [ZIP215](https://zips.z.cash/zip-0215) rules
|
|
383
|
-
- `zip215: false` switches verification criteria to strict
|
|
384
|
-
[RFC8032](https://www.rfc-editor.org/rfc/rfc8032) / [FIPS 186-5](https://csrc.nist.gov/publications/detail/fips/186/5/final)
|
|
385
|
-
and additionally provides [non-repudiation with SBS](https://eprint.iacr.org/2020/1244),
|
|
386
|
-
which is useful for:
|
|
387
|
-
- Contract Signing: if A signed an agreement with B using key that allows repudiation, it can later claim that it signed a different contract
|
|
388
|
-
- E-voting: malicious voters may pick keys that allow repudiation in order to deny results
|
|
389
|
-
- Blockchains: transaction of amount X might also be valid for a different amount Y
|
|
390
|
-
- Both modes have SUF-CMA (strong unforgeability under chosen message attacks).
|
|
391
|
-
|
|
392
|
-
### Making custom curves
|
|
393
|
-
|
|
394
|
-
- Short Weierstrass curve's formula is `y² = x³ + ax + b`. `weierstrass`
|
|
395
|
-
expects arguments `a`, `b`, field characteristic `p`, curve order `n`,
|
|
396
|
-
cofactor `h` and coordinates `Gx`, `Gy` of generator point.
|
|
397
|
-
- Twisted Edwards curve's formula is `ax² + y² = 1 + dx²y²`.
|
|
398
|
-
You must specify `a`, `d`, field characteristic `p`, curve order `n` (sometimes named as `L`),
|
|
399
|
-
cofactor `h` and coordinates `Gx`, `Gy` of generator point.
|
|
400
|
-
|
|
401
|
-
#### Custom Weierstrass curve
|
|
592
|
+
#### weierstrass: Custom Weierstrass curve
|
|
402
593
|
|
|
403
594
|
```js
|
|
404
595
|
import { weierstrass } from '@noble/curves/abstract/weierstrass.js';
|
|
@@ -415,7 +606,11 @@ const p192_CURVE = {
|
|
|
415
606
|
const p192_Point = weierstrass(p192_CURVE);
|
|
416
607
|
```
|
|
417
608
|
|
|
418
|
-
|
|
609
|
+
Short Weierstrass curve's formula is `y² = x³ + ax + b`. `weierstrass`
|
|
610
|
+
expects arguments `a`, `b`, field characteristic `p`, curve order `n`,
|
|
611
|
+
cofactor `h` and coordinates `Gx`, `Gy` of generator point.
|
|
612
|
+
|
|
613
|
+
#### edwards: Custom Edwards curve
|
|
419
614
|
|
|
420
615
|
```js
|
|
421
616
|
import { edwards } from '@noble/curves/abstract/edwards.js';
|
|
@@ -431,117 +626,23 @@ const ed25519_CURVE = {
|
|
|
431
626
|
const ed25519_Point = edwards(ed25519_CURVE);
|
|
432
627
|
```
|
|
433
628
|
|
|
434
|
-
|
|
629
|
+
Twisted Edwards curve's formula is `ax² + y² = 1 + dx²y²`.
|
|
630
|
+
You must specify `a`, `d`, field characteristic `p`, curve order `n` (sometimes named as `L`),
|
|
631
|
+
cofactor `h` and coordinates `Gx`, `Gy` of generator point.
|
|
632
|
+
|
|
633
|
+
#### Custom ECDSA instance
|
|
435
634
|
|
|
436
635
|
```js
|
|
437
636
|
import { ecdsa } from '@noble/curves/abstract/weierstrass.js';
|
|
438
637
|
import { sha256 } from '@noble/hashes/sha2.js';
|
|
439
|
-
const
|
|
638
|
+
const p192_sha256 = ecdsa(p192_Point, sha256);
|
|
639
|
+
// or
|
|
440
640
|
const p192_sha224 = ecdsa(p192.Point, sha224);
|
|
441
|
-
const keys = p192.keygen();
|
|
442
|
-
const msg = new TextEncoder().encode('custom curve');
|
|
443
|
-
const sig = p192.sign(msg, keys.secretKey);
|
|
444
|
-
const isValid = p192.verify(sig, msg, keys.publicKey);
|
|
445
|
-
// const ed25519 = eddsa(ed25519_Point, { hash: sha512 });
|
|
446
|
-
```
|
|
447
|
-
|
|
448
|
-
### hash-to-curve: hashing to curve points
|
|
449
|
-
|
|
450
|
-
```ts
|
|
451
|
-
import { secp256k1_hasher } from '@noble/curves/secp256k1.js';
|
|
452
|
-
import { p256_hasher, p384_hasher, p521_hasher } from '@noble/curves/nist.js';
|
|
453
|
-
import { ristretto255_hasher } from '@noble/curves/ed25519.js';
|
|
454
|
-
import { decaf448_hasher } from '@noble/curves/ed448.js';
|
|
455
|
-
|
|
456
|
-
import { bls12_381 } from '@noble/curves/bls12-381.js';
|
|
457
|
-
bls12_381.G1.hashToCurve(randomBytes(), { DST: 'another' });
|
|
458
|
-
bls12_381.G2.hashToCurve(randomBytes(), { DST: 'custom' });
|
|
459
|
-
|
|
460
|
-
import { expand_message_xmd, expand_message_xof, hash_to_field } from '@noble/curves/abstract/hash-to-curve.js';
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
The module allows to hash arbitrary strings to elliptic curve points. Implements [RFC 9380](https://www.rfc-editor.org/rfc/rfc9380).
|
|
464
|
-
|
|
465
|
-
Every curve has exported `hashToCurve` and `encodeToCurve` methods. You should always prefer `hashToCurve` for security:
|
|
466
|
-
|
|
467
|
-
### oprf: oblivious pseudorandom functions
|
|
468
|
-
|
|
469
|
-
```js
|
|
470
|
-
import { p256_oprf, p384_oprf, p521_oprf } from '@noble/curves/nist.js';
|
|
471
|
-
import { ristretto255_oprf } from '@noble/curves/ed25519.js';
|
|
472
|
-
import { decaf448_orpf } from '@noble/curves/ed448.js';
|
|
473
|
-
```
|
|
474
|
-
|
|
475
|
-
We provide OPRFs, conforming to [RFC 9497](https://www.rfc-editor.org/rfc/rfc9497).
|
|
476
|
-
|
|
477
|
-
OPRF allows to interactively create an `Output = PRF(Input, serverSecretKey)`:
|
|
478
641
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
### poseidon: Poseidon hash
|
|
485
|
-
|
|
486
|
-
Implements [Poseidon](https://www.poseidon-hash.info) ZK-friendly hash:
|
|
487
|
-
permutation and sponge.
|
|
488
|
-
|
|
489
|
-
There are many poseidon variants with different constants.
|
|
490
|
-
We don't provide them: you should construct them manually.
|
|
491
|
-
Check out [scure-starknet](https://github.com/paulmillr/scure-starknet) package for a proper example.
|
|
492
|
-
|
|
493
|
-
```ts
|
|
494
|
-
import { poseidon, poseidonSponge } from '@noble/curves/abstract/poseidon.js';
|
|
495
|
-
|
|
496
|
-
const rate = 2;
|
|
497
|
-
const capacity = 1;
|
|
498
|
-
const { mds, roundConstants } = poseidon.grainGenConstants({
|
|
499
|
-
Fp,
|
|
500
|
-
t: rate + capacity,
|
|
501
|
-
roundsFull: 8,
|
|
502
|
-
roundsPartial: 31,
|
|
503
|
-
});
|
|
504
|
-
const opts = {
|
|
505
|
-
Fp,
|
|
506
|
-
rate,
|
|
507
|
-
capacity,
|
|
508
|
-
sboxPower: 17,
|
|
509
|
-
mds,
|
|
510
|
-
roundConstants,
|
|
511
|
-
roundsFull: 8,
|
|
512
|
-
roundsPartial: 31,
|
|
513
|
-
};
|
|
514
|
-
const permutation = poseidon.poseidon(opts);
|
|
515
|
-
const sponge = poseidon.poseidonSponge(opts); // use carefully, not specced
|
|
516
|
-
```
|
|
517
|
-
|
|
518
|
-
### fft: Fast Fourier Transform
|
|
519
|
-
|
|
520
|
-
Experimental implementation of NTT / FFT (Fast Fourier Transform) over finite fields.
|
|
521
|
-
API may change at any time. The code has not been audited. Feature requests are welcome.
|
|
522
|
-
|
|
523
|
-
```ts
|
|
524
|
-
import * as fft from '@noble/curves/abstract/fft.js';
|
|
525
|
-
```
|
|
526
|
-
|
|
527
|
-
### utils: byte shuffling, conversion
|
|
528
|
-
|
|
529
|
-
```ts
|
|
530
|
-
import * as utils from '@noble/curves/abstract/utils.js';
|
|
531
|
-
|
|
532
|
-
utils.bytesToHex(Uint8Array.from([0xde, 0xad, 0xbe, 0xef]));
|
|
533
|
-
utils.hexToBytes('deadbeef');
|
|
534
|
-
utils.numberToHexUnpadded(123n);
|
|
535
|
-
utils.hexToNumber();
|
|
536
|
-
|
|
537
|
-
utils.bytesToNumberBE(Uint8Array.from([0xde, 0xad, 0xbe, 0xef]));
|
|
538
|
-
utils.bytesToNumberLE(Uint8Array.from([0xde, 0xad, 0xbe, 0xef]));
|
|
539
|
-
utils.numberToBytesBE(123n, 32);
|
|
540
|
-
utils.numberToBytesLE(123n, 64);
|
|
541
|
-
|
|
542
|
-
utils.concatBytes(Uint8Array.from([0xde, 0xad]), Uint8Array.from([0xbe, 0xef]));
|
|
543
|
-
utils.nLength(255n);
|
|
544
|
-
utils.equalBytes(Uint8Array.from([0xde]), Uint8Array.from([0xde]));
|
|
642
|
+
const keys = p192_sha256.keygen();
|
|
643
|
+
const msg = new TextEncoder().encode('custom curve');
|
|
644
|
+
const sig = p192_sha256.sign(msg, keys.secretKey);
|
|
645
|
+
const isValid = p192_sha256.verify(sig, msg, keys.publicKey);
|
|
545
646
|
```
|
|
546
647
|
|
|
547
648
|
## Security
|
|
@@ -666,8 +767,8 @@ init 10ms
|
|
|
666
767
|
getPublicKey x 9,099 ops/sec @ 109μs/op
|
|
667
768
|
sign x 7,182 ops/sec @ 139μs/op
|
|
668
769
|
verify x 1,188 ops/sec @ 841μs/op
|
|
669
|
-
getSharedSecret x 735 ops/sec @ 1ms/op
|
|
670
770
|
recoverPublicKey x 1,265 ops/sec @ 790μs/op
|
|
771
|
+
getSharedSecret x 735 ops/sec @ 1ms/op
|
|
671
772
|
schnorr.sign x 957 ops/sec @ 1ms/op
|
|
672
773
|
schnorr.verify x 1,210 ops/sec @ 825μs/op
|
|
673
774
|
|
|
@@ -763,14 +864,82 @@ aggregateSignatures/2048 x 0 ops/sec @ 2823ms/op
|
|
|
763
864
|
|
|
764
865
|
Supported node.js versions:
|
|
765
866
|
|
|
766
|
-
- v2: v20.19+ (ESM-only)
|
|
767
|
-
- v1: v14.21+ (ESM & CJS)
|
|
768
|
-
|
|
769
|
-
### curves v1
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
867
|
+
- v2 (2025-08): v20.19+ (ESM-only)
|
|
868
|
+
- v1 (2023-04): v14.21+ (ESM & CJS)
|
|
869
|
+
|
|
870
|
+
### curves v1 to curves v2
|
|
871
|
+
|
|
872
|
+
v2 massively simplifies internals, improves security, reduces bundle size and lays path for the future.
|
|
873
|
+
We tried to keep v2 as much backwards-compatible as possible.
|
|
874
|
+
|
|
875
|
+
Submodule paths now require `.js` extension e.g. `curves/ed25519` => `curves/ed25519.js`.
|
|
876
|
+
This allows usage in bundler-less environments without source maps
|
|
877
|
+
|
|
878
|
+
**Logic** changes:
|
|
879
|
+
|
|
880
|
+
- Most methods now expect Uint8Array, string hex inputs are prohibited
|
|
881
|
+
- The change simplifies reasoning, improves security and reduces malleability
|
|
882
|
+
- `Point.fromHex` now expects string-only hex inputs, use `Point.fromBytes` for Uint8Array
|
|
883
|
+
- Breaking changes of ECDSA (secp256k1, p256, p384...):
|
|
884
|
+
- sign, verify: Switch to **prehashed messages**. Instead of
|
|
885
|
+
messageHash, the methods now expect unhashed message.
|
|
886
|
+
To bring back old behavior, use option `{prehash: false}`
|
|
887
|
+
- sign, verify: Switch to **lowS signatures** by default.
|
|
888
|
+
This change doesn't affect secp256k1, which has been using lowS since beginning.
|
|
889
|
+
To bring back old behavior, use option `{lowS: true}`
|
|
890
|
+
- sign, verify: Switch to **Uint8Array signatures** (format: 'compact') by default.
|
|
891
|
+
- verify: **der format must be explicitly specified** in `{format: 'der'}`.
|
|
892
|
+
This reduces malleability
|
|
893
|
+
- verify: **prohibit Signature-instance** signature. User must now always do
|
|
894
|
+
`signature.toBytes()`
|
|
895
|
+
- Breaking changes of BLS signatures (bls12-381, bn254):
|
|
896
|
+
- Move getPublicKey, sign, verify, signShortSignature etc into two new namespaces:
|
|
897
|
+
bls.longSignatures (G1 pubkeys, G2 sigs) and bls.shortSignatures (G1 sigs, G2 pubkeys).
|
|
898
|
+
- verifyBatch now expects array of inputs `{message: ..., publicKey: ...}[]`
|
|
899
|
+
- Curve changes:
|
|
900
|
+
- Massively simplify curve creation, split it into point creation & sig generator creation
|
|
901
|
+
- New methods are `weierstrass() + ecdsa()` / `edwards() + eddsa()`
|
|
902
|
+
- weierstrass / edwards expect simplified curve params (Fp became p)
|
|
903
|
+
- ecdsa / eddsa expect Point class and hash
|
|
904
|
+
- Remove unnecessary Fn argument in `pippenger`
|
|
905
|
+
- modular changes:
|
|
906
|
+
- Field#fromBytes() now validates elements to be in 0..order-1 range
|
|
907
|
+
- Massive type renamings and improvements
|
|
908
|
+
|
|
909
|
+
**Renamings:**
|
|
910
|
+
|
|
911
|
+
- Module changes
|
|
912
|
+
- `p256`, `p384`, `p521` modules have been moved into `nist`
|
|
913
|
+
- `jubjub` module has been moved into `misc`
|
|
914
|
+
- Point changes
|
|
915
|
+
- ExtendedPoint, ProjectivePoint => Point
|
|
916
|
+
- Point coordinates (projective / extended) from px/ex, py/ey, pz/ez, et => X, Y, Z, T
|
|
917
|
+
- Point.normalizeZ, Point.msm => separate methods in `abstract/curve.js` submodule
|
|
918
|
+
- Point.fromPrivateKey() got removed, use `Point.BASE.multiply()` and `Point.Fn.fromBytes(secretKey)`
|
|
919
|
+
- toRawBytes, fromRawBytes => toBytes, fromBytes
|
|
920
|
+
- RistrettoPoint => ristretto255.Point, DecafPoiont => decaf448.Point
|
|
921
|
+
- Signature (ECDSA) changes
|
|
922
|
+
- toCompactRawBytes, toDERRawBytes => toBytes('compact'), toBytes('der')
|
|
923
|
+
- toCompactHex, toDERHex => toHex('compact'), toHex('der')
|
|
924
|
+
- fromCompact, fromDER => fromBytes(format), fromHex(format)
|
|
925
|
+
- utils changes
|
|
926
|
+
- randomPrivateKey => randomSecretKey
|
|
927
|
+
- utils.precompute, Point#_setWindowSize => Point#precompute
|
|
928
|
+
- edwardsToMontgomery => utils.toMontgomery
|
|
929
|
+
- edwardsToMontgomeryPriv => utils.toMontgomerySecret
|
|
930
|
+
- Rename all curve-specific hash-to-curve methods to `*curve*_hasher`.
|
|
931
|
+
Example: `secp256k1.hashToCurve` => `secp256k1_hasher.hashToCurve()`
|
|
932
|
+
|
|
933
|
+
**Removed features:**
|
|
934
|
+
|
|
935
|
+
- Point#multiplyAndAddUnsafe, Point#hasEvenY
|
|
936
|
+
- CURVE property with all kinds of random stuff. Point.CURVE() now replaces it, but only provides
|
|
937
|
+
curve parameters
|
|
938
|
+
- Remove `pasta`, `bn254_weierstrass` (NOT pairing-based bn254) curves
|
|
939
|
+
- Field.MASK
|
|
940
|
+
- utils.normPrivateKeyToScalar
|
|
941
|
+
|
|
942
|
+
### noble-secp256k1 v1 to curves v1
|
|
774
943
|
|
|
775
944
|
Previously, the library was split into single-feature packages
|
|
776
945
|
[noble-secp256k1](https://github.com/paulmillr/noble-secp256k1),
|
|
@@ -778,7 +947,7 @@ Previously, the library was split into single-feature packages
|
|
|
778
947
|
[noble-bls12-381](https://github.com/paulmillr/noble-bls12-381).
|
|
779
948
|
|
|
780
949
|
Curves continue their original work. The single-feature packages changed their
|
|
781
|
-
direction towards providing minimal
|
|
950
|
+
direction towards providing minimal 5kb implementations of cryptography,
|
|
782
951
|
which means they have less features.
|
|
783
952
|
|
|
784
953
|
- `getPublicKey`
|
|
@@ -807,9 +976,9 @@ which means they have less features.
|
|
|
807
976
|
- `utils` were split into `utils` (same api as in noble-curves) and
|
|
808
977
|
`etc` (`hmacSha256Sync` and others)
|
|
809
978
|
|
|
810
|
-
### noble-ed25519 v1
|
|
979
|
+
### noble-ed25519 v1 to curves v1
|
|
811
980
|
|
|
812
|
-
Upgrading from [@noble/ed25519](https://github.com/paulmillr/noble-ed25519)
|
|
981
|
+
Upgrading from [@noble/ed25519](https://github.com/paulmillr/noble-ed25519):
|
|
813
982
|
|
|
814
983
|
- Methods are now sync by default
|
|
815
984
|
- `bigint` is no longer allowed in `getPublicKey`, `sign`, `verify`. Reason: ed25519 is LE, can lead to bugs
|
|
@@ -820,7 +989,7 @@ Upgrading from [@noble/ed25519](https://github.com/paulmillr/noble-ed25519) 1.7:
|
|
|
820
989
|
- `getSharedSecret` was moved to `x25519` module
|
|
821
990
|
- `toX25519` has been moved to `edwardsToMontgomeryPub` and `edwardsToMontgomeryPriv` methods
|
|
822
991
|
|
|
823
|
-
### noble-bls12-381
|
|
992
|
+
### noble-bls12-381 to curves v1
|
|
824
993
|
|
|
825
994
|
Upgrading from [@noble/bls12-381](https://github.com/paulmillr/noble-bls12-381):
|
|
826
995
|
|