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