@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.
Files changed (96) hide show
  1. package/README.md +443 -276
  2. package/abstract/bls.d.ts +17 -17
  3. package/abstract/bls.d.ts.map +1 -1
  4. package/abstract/bls.js.map +1 -1
  5. package/abstract/curve.d.ts +14 -9
  6. package/abstract/curve.d.ts.map +1 -1
  7. package/abstract/curve.js +9 -3
  8. package/abstract/curve.js.map +1 -1
  9. package/abstract/edwards.d.ts +7 -9
  10. package/abstract/edwards.d.ts.map +1 -1
  11. package/abstract/edwards.js +12 -16
  12. package/abstract/edwards.js.map +1 -1
  13. package/abstract/hash-to-curve.d.ts +32 -31
  14. package/abstract/hash-to-curve.d.ts.map +1 -1
  15. package/abstract/hash-to-curve.js +15 -14
  16. package/abstract/hash-to-curve.js.map +1 -1
  17. package/abstract/modular.d.ts.map +1 -1
  18. package/abstract/modular.js +7 -5
  19. package/abstract/modular.js.map +1 -1
  20. package/abstract/montgomery.d.ts +3 -3
  21. package/abstract/montgomery.d.ts.map +1 -1
  22. package/abstract/montgomery.js +9 -13
  23. package/abstract/montgomery.js.map +1 -1
  24. package/abstract/oprf.d.ts +4 -4
  25. package/abstract/oprf.d.ts.map +1 -1
  26. package/abstract/oprf.js +3 -3
  27. package/abstract/oprf.js.map +1 -1
  28. package/abstract/poseidon.d.ts.map +1 -1
  29. package/abstract/poseidon.js +8 -9
  30. package/abstract/poseidon.js.map +1 -1
  31. package/abstract/utils.d.ts +74 -1
  32. package/abstract/utils.d.ts.map +1 -1
  33. package/abstract/utils.js +67 -17
  34. package/abstract/utils.js.map +1 -1
  35. package/abstract/weierstrass.d.ts +66 -20
  36. package/abstract/weierstrass.d.ts.map +1 -1
  37. package/abstract/weierstrass.js +72 -68
  38. package/abstract/weierstrass.js.map +1 -1
  39. package/bls12-381.d.ts +3 -9
  40. package/bls12-381.d.ts.map +1 -1
  41. package/bls12-381.js +3 -14
  42. package/bls12-381.js.map +1 -1
  43. package/bn254.d.ts +4 -4
  44. package/bn254.d.ts.map +1 -1
  45. package/bn254.js +1 -1
  46. package/bn254.js.map +1 -1
  47. package/ed25519.d.ts +22 -18
  48. package/ed25519.d.ts.map +1 -1
  49. package/ed25519.js +59 -31
  50. package/ed25519.js.map +1 -1
  51. package/ed448.d.ts +17 -8
  52. package/ed448.d.ts.map +1 -1
  53. package/ed448.js +69 -52
  54. package/ed448.js.map +1 -1
  55. package/index.d.ts +1 -0
  56. package/index.js +20 -4
  57. package/index.js.map +1 -1
  58. package/misc.d.ts.map +1 -1
  59. package/misc.js +6 -8
  60. package/misc.js.map +1 -1
  61. package/nist.d.ts +20 -2
  62. package/nist.d.ts.map +1 -1
  63. package/nist.js +30 -10
  64. package/nist.js.map +1 -1
  65. package/package.json +34 -37
  66. package/secp256k1.d.ts +10 -7
  67. package/secp256k1.d.ts.map +1 -1
  68. package/secp256k1.js +15 -16
  69. package/secp256k1.js.map +1 -1
  70. package/src/abstract/bls.ts +22 -22
  71. package/src/abstract/curve.ts +19 -5
  72. package/src/abstract/edwards.ts +20 -23
  73. package/src/abstract/hash-to-curve.ts +50 -51
  74. package/src/abstract/modular.ts +7 -5
  75. package/src/abstract/montgomery.ts +12 -18
  76. package/src/abstract/oprf.ts +6 -6
  77. package/src/abstract/poseidon.ts +6 -8
  78. package/src/abstract/weierstrass.ts +139 -89
  79. package/src/bls12-381.ts +4 -15
  80. package/src/bn254.ts +7 -7
  81. package/src/ed25519.ts +65 -40
  82. package/src/ed448.ts +87 -69
  83. package/src/index.ts +19 -3
  84. package/src/misc.ts +7 -8
  85. package/src/nist.ts +31 -15
  86. package/src/secp256k1.ts +16 -18
  87. package/src/utils.ts +33 -83
  88. package/src/webcrypto.ts +148 -107
  89. package/utils.d.ts +5 -21
  90. package/utils.d.ts.map +1 -1
  91. package/utils.js +31 -74
  92. package/utils.js.map +1 -1
  93. package/webcrypto.d.ts +73 -21
  94. package/webcrypto.d.ts.map +1 -1
  95. package/webcrypto.js +101 -76
  96. 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
- - 🪶 36KB (gzipped) including bundled hashes, 11KB for single-curve build
13
+ - 🪶 29KB (gzipped) including bundled hashes, 11KB for single-curve build
14
14
 
15
- Curves have 4KB sister projects
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
- 4kb [secp256k1](https://github.com/paulmillr/noble-secp256k1) /
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, ed448ctx, x448 } from '@noble/curves/ed448.js';
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 { decaf448_orpf } from '@noble/curves/ed448.js';
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
- import { hexToBytes, utf8ToBytes } from '@noble/curves/utils.js';
92
-
93
- for (const curve of [secp256k1, schnorr, p256, p384, p521, ed25519, ed448]) {
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 = utf8ToBytes('hello noble');
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
- const priv2 = hexToBytes('46c930bc7bb4db7f55da20798697421b98c4175a52c630294d75a84b9c126236');
103
- const pub2 = secp256k1.getPublicKey(priv2);
128
+ import { hexToBytes } from '@noble/curves/utils.js';
129
+ const secret2 = hexToBytes('46c930bc7bb4db7f55da20798697421b98c4175a52c630294d75a84b9c126236');
130
+ const pub2 = secp256k1.getPublicKey(secret2);
104
131
  ```
105
132
 
106
- - We provide NIST P256 (same as secp256r1 / prime256v1), P384 (secp384r1) & P521 (secp521r1),
107
- their hash-to-curve methods, and OPRFs.
108
- - ECDSA signatures conform to ....
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
- import { sha512 } from '@noble/hashes/sha2.js';
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 [RFC9496](https://www.rfc-editor.org/rfc/rfc9496) more info on ristretto255 & decaf448.
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
- #### Hedged ECDSA with noise
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
- #### Consensus-friendliness vs e-voting
162
+ ECDSA `sign()` allows providing `prehash: false`, which enables using custom hashes.
142
163
 
143
- #### webcrypto: Friendly wrapper
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
- const sig2 = curve.sign(msg, secretKey, { prehash: false })
148
- const msg = new Uint8Array(32).fill(1); // message hash (not message) in ecdsa
149
- const sig = secp256k1.sign(msg, priv); // `{prehash: true}` option is available
150
- const isValid = secp256k1.verify(sig, msg, pub) === true;
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
- const isValid = ed25519.verify(sig, msg, pub); // Default mode: follows ZIP215
188
+ ECDSA `sign()` allows providing `extraEntropy`, which switches sig generation to hedged mode.
153
189
 
154
- // SBS / e-voting / RFC8032 / FIPS 186-5
155
- const isValidRfc = ed25519.verify(sig, msg, pub, { zip215: false });
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
- // Variants from RFC8032: with context, prehashed
158
- import { ed25519ctx, ed25519ph } from '@noble/curves/ed25519.js';
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
- import { secp256k1 } from '@noble/curves/secp256k1.js';
162
- // random entropy
163
- const sigNoisy = secp256k1.sign(msg, priv, { extraEntropy: true });
164
- // set custom entropy
165
- const ent = new Uint8Array(32).fill(3);
166
- const sigNoisy2 = secp256k1.sign(msg, priv, { extraEntropy: ent });
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
- - Hedged ECDSA is add-on, providing improved protection against fault attacks.
170
- It adds noise to signatures.
171
- The technique is used by default in BIP340; we also implement them optionally for ECDSA.
172
- Check out blog post [Deterministic signatures are not your friends](https://paulmillr.com/posts/deterministic-signatures/)
173
- and [cfrg-det-sigs-with-noise draft](https://datatracker.ietf.org/doc/draft-irtf-cfrg-det-sigs-with-noise/).
174
- - In ed25519 & ed448, default `verify` behavior follows ZIP215 and
175
- can be used in consensus-critical applications.
176
- If you need SBS (Strongly Binding Signatures) and FIPS 186-5 compliance,
177
- use `zip215: false`. Check out [Edwards Signatures section for more info](#edwards-twisted-edwards-curve).
178
- Both options have SUF-CMA (strong unforgeability under chosen message attacks).
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.getSharedSecret(priv, pub) === x25519.scalarMult(priv, pub); // aliases
196
- x25519.getPublicKey(priv) === x25519.scalarMultBase(priv);
197
- // ed25519 => x25519 conversion
198
- import { edwardsToMontgomeryPub, edwardsToMontgomeryPriv } from '@noble/curves/ed25519.js';
199
- edwardsToMontgomeryPub(ed25519.getPublicKey(ed25519.utils.randomPrivateKey()));
200
- edwardsToMontgomeryPriv(ed25519.utils.randomPrivateKey());
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
- - X25519 aka ECDH on Curve25519 from [RFC7748](https://www.rfc-editor.org/rfc/rfc7748).
204
- - X448 aka ECDH on Curve448 from [RFC7748](https://www.rfc-editor.org/rfc/rfc7748)
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
- > [!NOTE]
207
- > In Weierstrass curves, shared secrets include y-parity bytes. To strip it, use `key.slice(1)`.
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
- > In Weierstrass curves, shared secrets are not hashed: use hashing or KDF on top, like `sha256(shared)` or `hkdf(shared)`.
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
- // private keys are 32 bytes
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.getPublicKey(privateKey);
226
- // Sign msg with custom (Ethereum) DST
227
- const msg = new TextEncoder().encode('hello');
228
- const DST = 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_';
229
- const msgp = blsl.hash(msg, DST);
230
- const signature = blsl.sign(msgp, privateKey);
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
- // Short signatures (G1), long public keys (G2)
347
+ // G1 sigs, G2 pubkeys
235
348
  const blss = bls12_381.shortSignatures;
236
- const publicKey2 = blss.getPublicKey(privateKey);
237
- const msgp2 = blss.hash(new TextEncoder().encode('hello'), 'BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_')
238
- const signature2 = blss.sign(msgp2, privateKey);
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.randomPrivateKey(),
245
- bls12_381.utils.randomPrivateKey(),
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.ProjectivePoint.BASE, bls.G2.ProjectivePoint.BASE;
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
- We don't implement Point methods toBytes.
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
- For example usage, check out [the implementation of bn254 EVM precompiles](https://github.com/paulmillr/noble-curves/blob/3ed792f8ad9932765b84d1064afea8663a255457/test/bn254.test.js#L697).
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
- ### Low-level math: EC Point, modular
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 { info, Point } = curve;
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 math
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
- ECDSA signatures:
350
-
351
- - Are represented by `Signature` instances with `r, s` and optional `recovery` properties
352
- - Have `recoverPublicKey()`, `toBytes()` with optional `format: 'compact' | 'der'`
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
- `mod.Field` is always **field over prime number**. Non-prime fields aren't supported for now.
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
- #### Custom Edwards curve
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
- #### Custom ECDSA, requires curve and hash
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 p192 = ecdsa(p192_Point, sha256);
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
- - Server cannot calculate Output by itself: it doesn't know Input
480
- - Client cannot calculate Output by itself: it doesn't know server secretKey
481
- - An attacker interception the communication can't restore Input/Output/serverSecretKey and can't
482
- link Input to some value.
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 => curves v2
770
-
771
- WIP. Changelog of v2, when upgrading from curves v1.
772
-
773
- ### noble-secp256k1 v1 => curves v1
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 4kb implementations of cryptography,
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 => curves v1
977
+ ### noble-ed25519 v1 to curves v1
811
978
 
812
- Upgrading from [@noble/ed25519](https://github.com/paulmillr/noble-ed25519) 1.7:
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 => curves v1
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