@noble/curves 2.0.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/README.md +214 -122
  2. package/abstract/bls.d.ts +299 -16
  3. package/abstract/bls.d.ts.map +1 -1
  4. package/abstract/bls.js +82 -22
  5. package/abstract/bls.js.map +1 -1
  6. package/abstract/curve.d.ts +274 -27
  7. package/abstract/curve.d.ts.map +1 -1
  8. package/abstract/curve.js +177 -23
  9. package/abstract/curve.js.map +1 -1
  10. package/abstract/edwards.d.ts +166 -30
  11. package/abstract/edwards.d.ts.map +1 -1
  12. package/abstract/edwards.js +221 -86
  13. package/abstract/edwards.js.map +1 -1
  14. package/abstract/fft.d.ts +322 -10
  15. package/abstract/fft.d.ts.map +1 -1
  16. package/abstract/fft.js +154 -12
  17. package/abstract/fft.js.map +1 -1
  18. package/abstract/frost.d.ts +293 -0
  19. package/abstract/frost.d.ts.map +1 -0
  20. package/abstract/frost.js +704 -0
  21. package/abstract/frost.js.map +1 -0
  22. package/abstract/hash-to-curve.d.ts +173 -24
  23. package/abstract/hash-to-curve.d.ts.map +1 -1
  24. package/abstract/hash-to-curve.js +170 -31
  25. package/abstract/hash-to-curve.js.map +1 -1
  26. package/abstract/modular.d.ts +429 -37
  27. package/abstract/modular.d.ts.map +1 -1
  28. package/abstract/modular.js +414 -119
  29. package/abstract/modular.js.map +1 -1
  30. package/abstract/montgomery.d.ts +83 -12
  31. package/abstract/montgomery.d.ts.map +1 -1
  32. package/abstract/montgomery.js +32 -7
  33. package/abstract/montgomery.js.map +1 -1
  34. package/abstract/oprf.d.ts +164 -91
  35. package/abstract/oprf.d.ts.map +1 -1
  36. package/abstract/oprf.js +88 -29
  37. package/abstract/oprf.js.map +1 -1
  38. package/abstract/poseidon.d.ts +138 -7
  39. package/abstract/poseidon.d.ts.map +1 -1
  40. package/abstract/poseidon.js +178 -15
  41. package/abstract/poseidon.js.map +1 -1
  42. package/abstract/tower.d.ts +122 -3
  43. package/abstract/tower.d.ts.map +1 -1
  44. package/abstract/tower.js +323 -139
  45. package/abstract/tower.js.map +1 -1
  46. package/abstract/weierstrass.d.ts +339 -76
  47. package/abstract/weierstrass.d.ts.map +1 -1
  48. package/abstract/weierstrass.js +395 -205
  49. package/abstract/weierstrass.js.map +1 -1
  50. package/bls12-381.d.ts +16 -2
  51. package/bls12-381.d.ts.map +1 -1
  52. package/bls12-381.js +199 -209
  53. package/bls12-381.js.map +1 -1
  54. package/bn254.d.ts +11 -2
  55. package/bn254.d.ts.map +1 -1
  56. package/bn254.js +93 -38
  57. package/bn254.js.map +1 -1
  58. package/ed25519.d.ts +125 -14
  59. package/ed25519.d.ts.map +1 -1
  60. package/ed25519.js +202 -40
  61. package/ed25519.js.map +1 -1
  62. package/ed448.d.ts +108 -14
  63. package/ed448.d.ts.map +1 -1
  64. package/ed448.js +194 -42
  65. package/ed448.js.map +1 -1
  66. package/index.js +7 -1
  67. package/index.js.map +1 -1
  68. package/misc.d.ts +106 -7
  69. package/misc.d.ts.map +1 -1
  70. package/misc.js +141 -32
  71. package/misc.js.map +1 -1
  72. package/nist.d.ts +112 -11
  73. package/nist.d.ts.map +1 -1
  74. package/nist.js +139 -17
  75. package/nist.js.map +1 -1
  76. package/package.json +11 -6
  77. package/secp256k1.d.ts +92 -15
  78. package/secp256k1.d.ts.map +1 -1
  79. package/secp256k1.js +211 -28
  80. package/secp256k1.js.map +1 -1
  81. package/src/abstract/bls.ts +350 -67
  82. package/src/abstract/curve.ts +327 -44
  83. package/src/abstract/edwards.ts +367 -143
  84. package/src/abstract/fft.ts +369 -36
  85. package/src/abstract/frost.ts +1092 -0
  86. package/src/abstract/hash-to-curve.ts +255 -56
  87. package/src/abstract/modular.ts +591 -144
  88. package/src/abstract/montgomery.ts +114 -30
  89. package/src/abstract/oprf.ts +383 -194
  90. package/src/abstract/poseidon.ts +235 -35
  91. package/src/abstract/tower.ts +428 -159
  92. package/src/abstract/weierstrass.ts +710 -312
  93. package/src/bls12-381.ts +239 -236
  94. package/src/bn254.ts +107 -46
  95. package/src/ed25519.ts +227 -55
  96. package/src/ed448.ts +227 -57
  97. package/src/index.ts +7 -1
  98. package/src/misc.ts +154 -35
  99. package/src/nist.ts +143 -20
  100. package/src/secp256k1.ts +284 -41
  101. package/src/utils.ts +583 -81
  102. package/src/webcrypto.ts +302 -73
  103. package/utils.d.ts +457 -24
  104. package/utils.d.ts.map +1 -1
  105. package/utils.js +410 -53
  106. package/utils.js.map +1 -1
  107. package/webcrypto.d.ts +167 -25
  108. package/webcrypto.d.ts.map +1 -1
  109. package/webcrypto.js +165 -58
  110. package/webcrypto.js.map +1 -1
package/README.md CHANGED
@@ -7,10 +7,10 @@ Audited & minimal JS implementation of elliptic curve cryptography.
7
7
  - 🏎 Fast: hand-optimized for caveats of JS engines
8
8
  - 🔍 Reliable: cross-library / wycheproof tests and fuzzing ensure correctness
9
9
  - ➰ Weierstrass, Edwards, Montgomery curves; ECDSA, EdDSA, Schnorr, BLS signatures
10
- - ✍️ ECDH, hash-to-curve, OPRF, Poseidon ZK-friendly hash
10
+ - ✍️ ECDH, hash-to-curve, OPRF, FROST, Poseidon hash, FFT
11
11
  - 🔖 Non-repudiation (SUF-CMA, SBS) & consensus-friendliness (ZIP215) in ed25519, ed448
12
12
  - 🥈 Optional, friendly wrapper over native WebCrypto
13
- - 🪶 29KB (gzipped) including bundled hashes, 11KB for single-curve build
13
+ - 🪶 32KB (gzipped) including bundled hashes, 11KB for single-curve build
14
14
 
15
15
  Curves have 5kb sister projects
16
16
  [secp256k1](https://github.com/paulmillr/noble-secp256k1) & [ed25519](https://github.com/paulmillr/noble-ed25519).
@@ -32,8 +32,8 @@ Take a glance at [GitHub Discussions](https://github.com/paulmillr/noble-curves/
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
- - [Check out homepage](https://paulmillr.com/noble/)
36
- for reading resources, documentation and apps built with noble
35
+ - [Check out the homepage](https://paulmillr.com/noble/)
36
+ for reading resources, documentation, and apps built with noble
37
37
 
38
38
  ## Usage
39
39
 
@@ -67,7 +67,7 @@ import { ristretto255_oprf } from '@noble/curves/ed25519.js';
67
67
  import { decaf448_oprf } from '@noble/curves/ed448.js';
68
68
 
69
69
  // utils
70
- import { bytesToHex, hexToBytes, concatBytes } from '@noble/curves/abstract/utils.js';
70
+ import { bytesToHex, hexToBytes, concatBytes } from '@noble/curves/utils.js';
71
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';
@@ -80,6 +80,7 @@ import { FFT, poly } from '@noble/curves/abstract/fft.js';
80
80
  - [secp256k1, p256, p384, p521, ed25519, ed448, brainpool](#secp256k1-p256-p384-p521-ed25519-ed448-brainpool)
81
81
  - [ristretto255, decaf448](#ristretto255-decaf448)
82
82
  - [Prehashed signing](#prehashed-signing)
83
+ - [Recovering public keys from signatures](#recovering-public-keys-from-signatures)
83
84
  - [Hedged ECDSA with noise](#hedged-ecdsa-with-noise)
84
85
  - [Consensus-friendliness vs e-voting](#consensus-friendliness-vs-e-voting)
85
86
  - [ECDH: Diffie-Hellman shared secrets](#ecdh-diffie-hellman-shared-secrets)
@@ -87,6 +88,7 @@ import { FFT, poly } from '@noble/curves/abstract/fft.js';
87
88
  - [BLS signatures, bls12-381, bn254 aka alt\_bn128](#bls-signatures-bls12-381-bn254-aka-alt_bn128)
88
89
  - [Hashing to curve points](#hash-to-curve-hashing-to-curve-points)
89
90
  - [OPRFs](#oprfs)
91
+ - [FROST threshold signatures](#frost-threshold-signatures)
90
92
  - [Poseidon hash](#poseidon-poseidon-hash)
91
93
  - [Fast Fourier Transform](#fft-fast-fourier-transform)
92
94
  - [utils](#utils-byte-shuffling-conversion)
@@ -134,6 +136,8 @@ ECDSA signatures use deterministic k, conforming to [RFC 6979](https://www.rfc-e
134
136
  EdDSA conforms to [RFC 8032](https://www.rfc-editor.org/rfc/rfc8032).
135
137
  Schnorr (secp256k1-only) conforms to [BIP 340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki).
136
138
 
139
+ Messages are always hashed first.
140
+
137
141
  #### ristretto255, decaf448
138
142
 
139
143
  ```ts
@@ -150,36 +154,60 @@ Check out separate documentation for [Point](#elliptic-curve-point-math), [hashe
150
154
 
151
155
  ```js
152
156
  import { secp256k1 } from '@noble/curves/secp256k1.js';
153
- import { keccak256 } from '@noble/hashes/sha3.js';
154
- const { secretKey } = curve.keygen();
157
+ import { keccak_256 } from '@noble/hashes/sha3.js';
158
+ const { secretKey } = secp256k1.keygen();
155
159
  const msg = new TextEncoder().encode('hello noble');
156
160
  // prehash: true (default) - hash using secp256k1.hash (sha256)
157
161
  const sig = secp256k1.sign(msg, secretKey);
158
162
  // prehash: false - hash using custom hash
159
- const sigKeccak = secp256k1.sign(keccak256(msg), secretKey, { prehash: false });
163
+ const sigKeccak = secp256k1.sign(keccak_256(msg), secretKey, { prehash: false });
160
164
  ```
161
165
 
162
- ECDSA `sign()` allows providing `prehash: false`, which enables using custom hashes.
166
+ Default sign() and verify() behavior (`prehash: true`) applies built-in hash function to message first.
167
+ For secp256k1 that's sha256, for p521 that's sha512.
163
168
 
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.
169
+ Providing `prehash: false` allows user to specify their own hash function (e.g. use secp256k1 + keccak_256).
168
170
 
169
171
  > [!NOTE]
170
172
  > Previously, in noble-curves v1, `prehash: false` was the default.
171
173
  > Some other libraries (like libsecp256k1) have no prehashing.
172
174
 
175
+ #### Recovering public keys from signatures
176
+
177
+ ```js
178
+ import { secp256k1 } from '@noble/curves/secp256k1.js';
179
+ const { secretKey, publicKey } = secp256k1.keygen();
180
+ const msg = new TextEncoder().encode('hello noble');
181
+ const sigRec = secp256k1.sign(msg, secretKey, { format: 'recovered' });
182
+ const publicKey_ = secp256k1.recoverPublicKey(sigRec, msg); // == publicKey
183
+
184
+ // recovered sig is compact sig with an extra byte
185
+ const sigNoRec = secp256k1.sign(msg, secretKey, { format: 'compact' });
186
+ // sigNoRec == sigRec.slice(1)
187
+
188
+ // Signature instance
189
+ const sigInstance = secp256k1.Signature.fromBytes(sigRec, 'recovered');
190
+ ```
191
+
192
+ Public key recovery - only supported with ECDSA.
193
+
194
+ > [!NOTE]
195
+ > Key recovery is a simple math operation.
196
+ > There are no guarantees the signing was actually done.
197
+ > It's possible to forge signature and msg hash (r, s, h), which would
198
+ > recover into a random public key, but it's not feasible
199
+ > to find m which would lead to this specific forged h.
200
+
173
201
  #### Hedged ECDSA with noise
174
202
 
175
203
  ```js
176
204
  import { secp256k1 } from '@noble/curves/secp256k1.js';
177
- const { secretKey } = curve.keygen();
205
+ const { secretKey } = secp256k1.keygen();
178
206
  const msg = new TextEncoder().encode('hello noble');
179
207
  // extraEntropy: false - default, hedging disabled
180
208
  const sigNoisy = secp256k1.sign(msg, secretKey);
181
209
  // extraEntropy: true - fetch 32 random bytes from CSPRNG
182
- const sigNoisy = secp256k1.sign(msg, secretKey, { extraEntropy: true });
210
+ const sigNoisyA = secp256k1.sign(msg, secretKey, { extraEntropy: true });
183
211
  // extraEntropy: bytes - specific extra entropy
184
212
  const ent = Uint8Array.from([0xca, 0xfe, 0x01, 0x23]);
185
213
  const sigNoisy2 = secp256k1.sign(msg, secretKey, { extraEntropy: ent });
@@ -206,24 +234,24 @@ const { secretKey, publicKey } = ed25519.keygen();
206
234
  const msg = new TextEncoder().encode('hello noble');
207
235
  const sig = ed25519.sign(msg, secretKey);
208
236
  // zip215: true
209
- const isValid = ed25519.verify(sig, msg, pub);
237
+ const isValid = ed25519.verify(sig, msg, publicKey);
210
238
  // SBS / e-voting / RFC8032 / FIPS 186-5
211
- const isValidRfc = ed25519.verify(sig, msg, pub, { zip215: false });
239
+ const isValidRfc = ed25519.verify(sig, msg, publicKey, { zip215: false });
212
240
  ```
213
241
 
242
+ > [!NOTE]
243
+ > Most other libraries don't have SUF-CMA & SBS - less optimal choice for their security.
244
+
214
245
  In ed25519, there is an ability to choose between consensus-friendliness vs e-voting mode.
215
246
 
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
247
+ * `zip215: true` (default) uses the more permissive, [consensus-friendly](https://hdevalence.ca/blog/2020-10-04-its-25519am) verification rules defined in [ZIP215](https://zips.z.cash/zip-0215).
248
+ * `zip215: false` enforces strict [RFC 8032](https://www.rfc-editor.org/rfc/rfc8032) / [FIPS 186-5](https://csrc.nist.gov/publications/detail/fips/186/5/final) verification and adds SBS-based non-repudiation, which is useful for:
249
+ * **Contract signing:** prevents a signer from later claiming they signed a different document
250
+ * **E-voting:** stops voters from choosing keys that let them repudiate their vote
251
+ * **Blockchains:** avoids signatures valid for multiple transactions (e.g., amount X also validating amount Y)
225
252
 
226
253
  Both modes have SUF-CMA (strong unforgeability under chosen message attacks).
254
+ See [Taming the many EdDSAs](https://eprint.iacr.org/2020/1244) for more info.
227
255
 
228
256
  ### ECDH: Diffie-Hellman shared secrets
229
257
 
@@ -233,7 +261,7 @@ import { x25519 } from '@noble/curves/ed25519.js';
233
261
  import { x448 } from '@noble/curves/ed448.js';
234
262
  import { p256, p384, p521 } from '@noble/curves/nist.js';
235
263
 
236
- for (const curve of [secp256k1, schnorr, x25519, x448, p256, p384, p521]) {
264
+ for (const curve of [secp256k1, x25519, x448, p256, p384, p521]) {
237
265
  const alice = curve.keygen();
238
266
  const bob = curve.keygen();
239
267
  const sharedKey = curve.getSharedSecret(alice.secretKey, bob.publicKey);
@@ -265,7 +293,7 @@ In Weierstrass curves, shared secrets:
265
293
  ##### webcrypto signatures
266
294
 
267
295
  ```js
268
- import { ed25519, ed448, p256, p384, p521 } from './src/webcrypto.ts';
296
+ import { ed25519, ed448, p256, p384, p521 } from '@noble/curves/webcrypto.js';
269
297
 
270
298
  (async () => {
271
299
  for (let [name, curve] of Object.entries({ p256, p384, p521, ed25519, ed448 })) {
@@ -288,7 +316,7 @@ import { ed25519, ed448, p256, p384, p521 } from './src/webcrypto.ts';
288
316
  ##### webcrypto ecdh
289
317
 
290
318
  ```js
291
- import { p256, p384, p521, x25519, x448 } from './src/webcrypto.ts';
319
+ import { p256, p384, p521, x25519, x448 } from '@noble/curves/webcrypto.js';
292
320
 
293
321
  (async () => {
294
322
  for (let [name, curve] of Object.entries({ p256, p384, p521, x25519, x448 })) {
@@ -309,8 +337,8 @@ import { p256, p384, p521, x25519, x448 } from './src/webcrypto.ts';
309
337
  ##### Key conversion from noble to webcrypto and back
310
338
 
311
339
  ```js
312
- import { p256 as p256n } from './src/nist.ts';
313
- import { p256 } from './src/webcrypto.ts';
340
+ import { p256 as p256n } from '@noble/curves/nist.js';
341
+ import { p256 } from '@noble/curves/webcrypto.js';
314
342
  (async () => {
315
343
  const nobleKeys = p256n.keygen();
316
344
  // convert noble keys to webcrypto
@@ -352,13 +380,13 @@ const blss = bls12_381.shortSignatures;
352
380
  const publicKey2 = blss.getPublicKey(secretKey);
353
381
  const msgp2 = blss.hash(msg, 'BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_');
354
382
  const signature2 = blss.sign(msgp2, secretKey);
355
- const isValid2 = blss.verify(signature2, msgp2, publicKey);
383
+ const isValid2 = blss.verify(signature2, msgp2, publicKey2);
356
384
  console.log({ publicKey2, signature2, isValid2 });
357
385
 
358
386
  // Aggregation
359
387
  const aggregatedKey = bls12_381.longSignatures.aggregatePublicKeys([
360
- bls12_381.utils.randomSecretKey(),
361
- bls12_381.utils.randomSecretKey(),
388
+ blsl.getPublicKey(bls12_381.utils.randomSecretKey()),
389
+ blsl.getPublicKey(bls12_381.utils.randomSecretKey()),
362
390
  ]);
363
391
  // const aggregatedSig = bls.aggregateSignatures(sigs)
364
392
 
@@ -390,11 +418,11 @@ Points of divergence:
390
418
  ### hash-to-curve: hashing to curve points
391
419
 
392
420
  ```ts
393
- import { bls12_381 } from './src/bls12-381.ts';
394
- import { ed25519_hasher, ristretto255_hasher } from './src/ed25519.ts';
395
- import { decaf448_hasher, ed448_hasher } from './src/ed448.ts';
396
- import { p256_hasher, p384_hasher, p521_hasher } from './src/nist.ts';
397
- import { secp256k1_hasher } from './src/secp256k1.ts';
421
+ import { bls12_381 } from '@noble/curves/bls12-381.js';
422
+ import { ed25519_hasher, ristretto255_hasher } from '@noble/curves/ed25519.js';
423
+ import { decaf448_hasher, ed448_hasher } from '@noble/curves/ed448.js';
424
+ import { p256_hasher, p384_hasher, p521_hasher } from '@noble/curves/nist.js';
425
+ import { secp256k1_hasher } from '@noble/curves/secp256k1.js';
398
426
 
399
427
  const h = {
400
428
  secp256k1_hasher,
@@ -438,7 +466,7 @@ The module allows to hash arbitrary strings to elliptic curve points. Implements
438
466
  ```js
439
467
  import { p256_oprf, p384_oprf, p521_oprf } from '@noble/curves/nist.js';
440
468
  import { ristretto255_oprf } from '@noble/curves/ed25519.js';
441
- import { decaf448_orpf } from '@noble/curves/ed448.js';
469
+ import { decaf448_oprf } from '@noble/curves/ed448.js';
442
470
  ```
443
471
 
444
472
  We provide OPRFs (oblivious pseudorandom functions),
@@ -451,6 +479,84 @@ OPRF allows to interactively create an `Output = PRF(Input, serverSecretKey)`:
451
479
  - An attacker interception the communication can't restore Input/Output/serverSecretKey and can't
452
480
  link Input to some value.
453
481
 
482
+ ### FROST threshold signatures
483
+
484
+ FROST implements [RFC 9591](https://www.rfc-editor.org/rfc/rfc9591) threshold Schnorr signing.
485
+ It is similar to multisig from the application point of view: any `min` of `max` participants
486
+ can jointly produce one Schnorr signature under a shared public key.
487
+ Supported ciphersuites are `p256_FROST`, `ed25519_FROST`, `ed448_FROST`, `ristretto255_FROST`,
488
+ `secp256k1_FROST`, and `schnorr_FROST` (Taproot-compatible secp256k1).
489
+ Signing has two rounds: selected signers commit first, then produce signature shares.
490
+
491
+ > [!WARNING]
492
+ > The FROST code is new and has not been audited yet.
493
+ > It passes the imported `frost-rs` vectors/examples and local regression tests.
494
+
495
+ ```js
496
+ import { p256_FROST } from '@noble/curves/nist.js';
497
+
498
+ const signers = { min: 2, max: 3 };
499
+ const alice = p256_FROST.Identifier.derive('alice@example.com');
500
+ const bob = p256_FROST.Identifier.derive('bob@example.com');
501
+ const carol = p256_FROST.Identifier.derive('carol@example.com');
502
+ // trusted dealer
503
+ const deal = p256_FROST.trustedDealer(signers, [alice, bob, carol]);
504
+ for (const id of [alice, bob, carol]) p256_FROST.validateSecret(deal.secretShares[id], deal.public);
505
+
506
+ const msg = new TextEncoder().encode('hello threshold');
507
+ // round 1: selected signers commit
508
+ const aliceRound1 = p256_FROST.commit(deal.secretShares[alice]);
509
+ const bobRound1 = p256_FROST.commit(deal.secretShares[bob]);
510
+ const commitmentList = [aliceRound1.commitments, bobRound1.commitments];
511
+ // round 2: signers produce signature shares
512
+ const sigShares = {
513
+ [alice]: p256_FROST.signShare(
514
+ deal.secretShares[alice],
515
+ deal.public,
516
+ aliceRound1.nonces,
517
+ commitmentList,
518
+ msg
519
+ ),
520
+ [bob]: p256_FROST.signShare(
521
+ deal.secretShares[bob],
522
+ deal.public,
523
+ bobRound1.nonces,
524
+ commitmentList,
525
+ msg
526
+ ),
527
+ };
528
+ const sig = p256_FROST.aggregate(deal.public, commitmentList, msg, sigShares);
529
+ const isValid = p256_FROST.verify(sig, msg, deal.public.commitments[0]);
530
+ ```
531
+
532
+ Key generation can be done with a trusted dealer or with DKG.
533
+ DKG has three rounds: participants commit to key generation, exchange private shares, then derive final participant keys.
534
+
535
+ ```js
536
+ import { p256_FROST } from '@noble/curves/nist.js';
537
+
538
+ const signers = { min: 2, max: 3 };
539
+ const alice = p256_FROST.DKG.round1(p256_FROST.Identifier.fromNumber(1), signers);
540
+ const bob = p256_FROST.DKG.round1(p256_FROST.Identifier.fromNumber(2), signers);
541
+ const carol = p256_FROST.DKG.round1(p256_FROST.Identifier.fromNumber(3), signers);
542
+
543
+ // round 1: participants commit to key generation
544
+ const aliceRound2 = p256_FROST.DKG.round2(alice.secret, [bob.public, carol.public]);
545
+ const bobRound2 = p256_FROST.DKG.round2(bob.secret, [alice.public, carol.public]);
546
+ const carolRound2 = p256_FROST.DKG.round2(carol.secret, [alice.public, bob.public]);
547
+
548
+ // round 2: private shares for each recipient
549
+ const aliceKey = p256_FROST.DKG.round3(alice.secret, [bob.public, carol.public], [
550
+ bobRound2[p256_FROST.Identifier.fromNumber(1)],
551
+ carolRound2[p256_FROST.Identifier.fromNumber(1)],
552
+ ]);
553
+ // round 3: final participant key package
554
+ ```
555
+
556
+ DKG helpers are intended for interoperable testing and practical key generation.
557
+ The library implements the cryptographic steps, not the surrounding application protocol:
558
+ callers still need authenticated communication, coordination, retries, session handling, and policy.
559
+
454
560
  ### poseidon: Poseidon hash
455
561
 
456
562
  Implements [Poseidon](https://www.poseidon-hash.info) ZK-friendly hash:
@@ -461,11 +567,13 @@ We don't provide them: you should construct them manually.
461
567
  Check out [scure-starknet](https://github.com/paulmillr/scure-starknet) package for a proper example.
462
568
 
463
569
  ```ts
464
- import { poseidon, poseidonSponge } from '@noble/curves/abstract/poseidon.js';
570
+ import { bn254 } from '@noble/curves/bn254.js';
571
+ import { grainGenConstants, poseidon, poseidonSponge } from '@noble/curves/abstract/poseidon.js';
465
572
 
466
573
  const rate = 2;
467
574
  const capacity = 1;
468
- const { mds, roundConstants } = poseidon.grainGenConstants({
575
+ const Fp = bn254.fields.Fr;
576
+ const { mds, roundConstants } = grainGenConstants({
469
577
  Fp,
470
578
  t: rate + capacity,
471
579
  roundsFull: 8,
@@ -481,8 +589,8 @@ const opts = {
481
589
  roundsFull: 8,
482
590
  roundsPartial: 31,
483
591
  };
484
- const permutation = poseidon.poseidon(opts);
485
- const sponge = poseidon.poseidonSponge(opts); // use carefully, not specced
592
+ const permutation = poseidon({ ...opts, t: rate + capacity });
593
+ const sponge = poseidonSponge(opts); // use carefully, not specced
486
594
  ```
487
595
 
488
596
  ### fft: Fast Fourier Transform
@@ -501,7 +609,7 @@ API may change at any time. The code has not been audited. Feature requests are
501
609
  ### utils: byte shuffling, conversion
502
610
 
503
611
  ```ts
504
- import { bytesToHex, concatBytes, equalBytes, hexToBytes } from '@noble/curves/abstract/utils.js';
612
+ import { bytesToHex, concatBytes, equalBytes, hexToBytes } from '@noble/curves/utils.js';
505
613
 
506
614
  bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23]));
507
615
  hexToBytes('cafe0123');
@@ -514,30 +622,32 @@ equalBytes(Uint8Array.of(0xca), Uint8Array.of(0xca));
514
622
  #### Elliptic curve Point math
515
623
 
516
624
  ```js
625
+ import { pippenger } from '@noble/curves/abstract/curve.js';
517
626
  import { secp256k1, schnorr } from '@noble/curves/secp256k1.js';
518
627
  import { p256, p384, p521 } from '@noble/curves/nist.js';
519
628
  import { ed25519, ristretto255 } from '@noble/curves/ed25519.js';
520
629
  import { ed448, decaf448 } from '@noble/curves/ed448.js';
521
- import { bls12_381 } from '@noble/curves/bls12-381.js'
630
+ import { bls12_381 } from '@noble/curves/bls12-381.js';
522
631
  import { bn254 } from '@noble/curves/bn254.js';
523
632
  import { jubjub, babyjubjub } from '@noble/curves/misc.js';
524
633
 
525
634
  const curves = [
526
635
  secp256k1, schnorr, p256, p384, p521, ed25519, ed448,
527
636
  ristretto255, decaf448,
528
- bls12_381.G1, bls12_381.G2, bn254.G1, bn254.G2,
637
+ bls12_381.G1, bls12_381.G2, bn254.G1,
529
638
  jubjub, babyjubjub
530
639
  ];
531
640
  for (const curve of curves) {
532
641
  const { Point } = curve;
533
642
  const { BASE, ZERO, Fp, Fn } = Point;
643
+ const info = Point.CURVE?.();
534
644
  const p = BASE.multiply(2n);
535
645
 
536
646
  // Initialization
537
- if (info.type === 'weierstrass') {
647
+ if (info?.type === 'weierstrass') {
538
648
  // projective (homogeneous) coordinates: (X, Y, Z) ∋ (x=X/Z, y=Y/Z)
539
649
  const p_ = new Point(BASE.X, BASE.Y, BASE.Z);
540
- } else if (info.type === 'edwards') {
650
+ } else if (info?.type === 'edwards') {
541
651
  // extended coordinates: (X, Y, Z, T) ∋ (x=X/Z, y=Y/Z)
542
652
  const p_ = new Point(BASE.X, BASE.Y, BASE.Z, BASE.T);
543
653
  }
@@ -551,11 +661,11 @@ for (const curve of curves) {
551
661
 
552
662
  // MSM (multi-scalar multiplication)
553
663
  const pa = [BASE, BASE.multiply(2n), BASE.multiply(4n), BASE.multiply(8n)];
554
- const p6 = Point.msm(pa, [3n, 5n, 7n, 11n]);
664
+ const p6 = pippenger(Point, pa, [3n, 5n, 7n, 11n]);
555
665
  const _true3 = p6.equals(BASE.multiply(129n)); // 129*G
556
666
 
557
667
  const pcl = p.clearCofactor();
558
- console.log(p.isTorsionFree(), p.isSmallOrder());
668
+ const isTorsionFree = p.isTorsionFree();
559
669
 
560
670
  const r1 = p.toBytes();
561
671
  const r1_ = Point.fromBytes(r1);
@@ -576,7 +686,7 @@ fp.mul(591n, 932n); // multiplication
576
686
  fp.pow(481n, 11024858120n); // exponentiation
577
687
  fp.div(5n, 17n); // division: 5/17 mod 2^255-19 == 5 * invert(17)
578
688
  fp.inv(5n); // modular inverse
579
- fp.sqrt(21n); // square root
689
+ fp.sqrt(4n); // square root
580
690
 
581
691
  // Non-Field generic utils are also available
582
692
  mod(21n, 10n); // 21 mod 10 == 1n; fixed version of 21 % 10
@@ -634,11 +744,21 @@ cofactor `h` and coordinates `Gx`, `Gy` of generator point.
634
744
  #### Custom ECDSA instance
635
745
 
636
746
  ```js
637
- import { ecdsa } from '@noble/curves/abstract/weierstrass.js';
638
- import { sha256 } from '@noble/hashes/sha2.js';
747
+ import { weierstrass, ecdsa } from '@noble/curves/abstract/weierstrass.js';
748
+ import { sha224, sha256 } from '@noble/hashes/sha2.js';
749
+ const p192_CURVE = {
750
+ p: 0xfffffffffffffffffffffffffffffffeffffffffffffffffn,
751
+ n: 0xffffffffffffffffffffffff99def836146bc9b1b4d22831n,
752
+ h: 1n,
753
+ a: 0xfffffffffffffffffffffffffffffffefffffffffffffffcn,
754
+ b: 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1n,
755
+ Gx: 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012n,
756
+ Gy: 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811n,
757
+ };
758
+ const p192_Point = weierstrass(p192_CURVE);
639
759
  const p192_sha256 = ecdsa(p192_Point, sha256);
640
760
  // or
641
- const p192_sha224 = ecdsa(p192.Point, sha224);
761
+ const p192_sha224 = ecdsa(p192_Point, sha224);
642
762
 
643
763
  const keys = p192_sha256.keygen();
644
764
  const msg = new TextEncoder().encode('custom curve');
@@ -648,21 +768,24 @@ const isValid = p192_sha256.verify(sig, msg, keys.publicKey);
648
768
 
649
769
  ## Security
650
770
 
651
- The library has been independently audited:
771
+ The library has been audited:
652
772
 
653
- - at version 1.6.0, in Sep 2024, by [Cure53](https://cure53.de)
773
+ - at version 2.2.0, in Apr 2026, by ourselves (self-audited)
774
+ - Scope: everything
775
+ - [Changes since audit](https://github.com/paulmillr/noble-curves/compare/2.2.0..main)
776
+ - at version 1.6.0, in Sep 2024, independently, by [Cure53](https://cure53.de)
654
777
  - PDFs: [website](https://cure53.de/audit-report_noble-crypto-libs.pdf), [in-repo](./audit/2024-09-cure53-audit-nbl4.pdf)
655
778
  - [Changes since audit](https://github.com/paulmillr/noble-curves/compare/1.6.0..main)
656
779
  - Scope: ed25519, ed448, their add-ons, bls12-381, bn254,
657
780
  hash-to-curve, low-level primitives bls, tower, edwards, montgomery.
658
781
  - The audit has been funded by [OpenSats](https://opensats.org)
659
- - at version 1.2.0, in Sep 2023, by [Kudelski Security](https://kudelskisecurity.com)
782
+ - at version 1.2.0, in Sep 2023, independently, by [Kudelski Security](https://kudelskisecurity.com)
660
783
  - PDFs: [in-repo](./audit/2023-09-kudelski-audit-starknet.pdf)
661
784
  - [Changes since audit](https://github.com/paulmillr/noble-curves/compare/1.2.0..main)
662
785
  - Scope: [scure-starknet](https://github.com/paulmillr/scure-starknet) and its related
663
786
  abstract modules of noble-curves: `curve`, `modular`, `poseidon`, `weierstrass`
664
787
  - The audit has been funded by [Starkware](https://starkware.co)
665
- - at version 0.7.3, in Feb 2023, by [Trail of Bits](https://www.trailofbits.com)
788
+ - at version 0.7.3, in Feb 2023, independently, by [Trail of Bits](https://www.trailofbits.com)
666
789
  - PDFs: [website](https://github.com/trailofbits/publications/blob/master/reviews/2023-01-ryanshea-noblecurveslibrary-securityreview.pdf),
667
790
  [in-repo](./audit/2023-01-trailofbits-audit-curves.pdf)
668
791
  - [Changes since audit](https://github.com/paulmillr/noble-curves/compare/0.7.3..main)
@@ -711,31 +834,27 @@ test-suite in the actual application that consumes the library.
711
834
 
712
835
  ### Supply chain security
713
836
 
714
- - **Commits** are signed with PGP keys, to prevent forgery. Make sure to verify commit signatures
715
- - **Releases** are transparent and built on GitHub CI. Make sure to verify [provenance](https://docs.npmjs.com/generating-provenance-statements) logs
716
- - Use GitHub CLI to verify single-file builds:
717
- `gh attestation verify --owner paulmillr noble-curves.js`
718
- - **Rare releasing** is followed to ensure less re-audit need for end-users
719
- - **Dependencies** are minimized and locked-down: any dependency could get hacked and users will be downloading malware with every install.
720
- - We make sure to use as few dependencies as possible
721
- - Automatic dep updates are prevented by locking-down version ranges; diffs are checked with `npm-diff`
722
- - **Dev Dependencies** are disabled for end-users; they are only used to develop / build the source code
837
+ - **Commits** are signed with PGP keys to prevent forgery. Be sure to verify the commit signatures
838
+ - **Releases** are made transparently through token-less GitHub CI and Trusted Publishing. Be sure to verify the [provenance logs](https://docs.npmjs.com/generating-provenance-statements) for authenticity.
839
+ - **Rare releasing** is practiced to minimize the need for re-audits by end-users.
840
+ - **Dependencies** are minimized and strictly pinned to reduce supply-chain risk.
841
+ - We use as few dependencies as possible.
842
+ - Version ranges are locked, and changes are checked with npm-diff.
843
+ - **Dev dependencies** are excluded from end-user installs; they’re only used for development and build steps.
723
844
 
724
845
  For this package, there is 1 dependency; and a few dev dependencies:
725
846
 
726
847
  - [noble-hashes](https://github.com/paulmillr/noble-hashes) provides cryptographic hashing functionality
727
- - micro-bmark, micro-should and jsbt are used for benchmarking / testing / build tooling and developed by the same author
728
- - prettier, fast-check and typescript are used for code quality / test generation / ts compilation. It's hard to audit their source code thoroughly and fully because of their size
848
+ - jsbt is used for benchmarking / testing / build tooling and developed by the same author
849
+ - prettier, fast-check and typescript are used for code quality / test generation / ts compilation
729
850
 
730
851
  ### Randomness
731
852
 
732
- We're deferring to built-in
733
- [crypto.getRandomValues](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues)
734
- which is considered cryptographically secure (CSPRNG).
853
+ We rely on the built-in
854
+ [`crypto.getRandomValues`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues),
855
+ which is considered a cryptographically secure PRNG.
735
856
 
736
- In the past, browsers had bugs that made it weak: it may happen again.
737
- Implementing a userspace CSPRNG to get resilient to the weakness
738
- is even worse: there is no reliable userspace source of quality entropy.
857
+ Browsers have had weaknesses in the past - and could again - but implementing a userspace CSPRNG is even worse, as there’s no reliable userspace source of high-quality entropy.
739
858
 
740
859
  ### Quantum computers
741
860
 
@@ -895,13 +1014,15 @@ Changes:
895
1014
  - Most methods now expect Uint8Array, string hex inputs are prohibited
896
1015
  - The change simplifies reasoning, improves security and reduces malleability
897
1016
  - `Point.fromHex` now expects string-only hex inputs, use `Point.fromBytes` for Uint8Array
1017
+ - Many methods were renamed, upgrade to curves v1.9 first to highlight deprecated old names
898
1018
  - Breaking changes of ECDSA (secp256k1, p256, p384...):
1019
+ - To bring back old behavior, pass `{ prehash: false, lowS: false }` to sign / verify
899
1020
  - sign, verify: Switch to **prehashed messages**. Instead of
900
1021
  messageHash, the methods now expect unhashed message.
901
1022
  To bring back old behavior, use option `{prehash: false}`
902
1023
  - sign, verify: Switch to **lowS signatures** by default.
903
1024
  This change doesn't affect secp256k1, which has been using lowS since beginning.
904
- To bring back old behavior, use option `{lowS: true}`
1025
+ To bring back old behavior, use option `{lowS: false}`
905
1026
  - sign, verify: Switch to **Uint8Array signatures** (format: 'compact') by default.
906
1027
  - verify: **der format must be explicitly specified** in `{format: 'der'}`.
907
1028
  This reduces malleability
@@ -949,13 +1070,13 @@ Renamings:
949
1070
  Removed features:
950
1071
 
951
1072
  - Point#multiplyAndAddUnsafe, Point#hasEvenY
952
- - CURVE property with all kinds of random stuff. Point.CURVE() now replaces it, but only provides
1073
+ - `CURVE` property with all kinds of random stuff. Point.CURVE() now replaces it, but only provides
953
1074
  curve parameters
954
1075
  - Remove `pasta`, `bn254_weierstrass` (NOT pairing-based bn254) curves
1076
+ - utils.normPrivateKeyToScalar - use `Point.Fn.fromBytes`
955
1077
  - Field.MASK
956
- - utils.normPrivateKeyToScalar
957
1078
 
958
- ### noble-secp256k1 v1 to curves v1
1079
+ ### secp256k1 v1, ed25519 v1, bls12-381 v1 to curves v1
959
1080
 
960
1081
  Previously, the library was split into single-feature packages
961
1082
  [noble-secp256k1](https://github.com/paulmillr/noble-secp256k1),
@@ -964,55 +1085,26 @@ Previously, the library was split into single-feature packages
964
1085
 
965
1086
  Curves continue their original work. The single-feature packages changed their
966
1087
  direction towards providing minimal 5kb implementations of cryptography,
967
- which means they have less features.
968
-
969
- - `getPublicKey`
970
- - now produce 33-byte compressed signatures by default
971
- - to use old behavior, which produced 65-byte uncompressed keys, set
972
- argument `isCompressed` to `false`: `getPublicKey(priv, false)`
973
- - `sign`
974
- - is now sync
975
- - now returns `Signature` instance with `{ r, s, recovery }` properties
976
- - `canonical` option was renamed to `lowS`
977
- - `recovered` option has been removed because recovery bit is always returned now
978
- - `der` option has been removed. There are 2 options:
979
- 1. Use compact encoding: `fromCompact`, `toCompactRawBytes`, `toCompactHex`.
980
- Compact encoding is simply a concatenation of 32-byte r and 32-byte s.
981
- 2. If you must use DER encoding, switch to noble-curves (see above).
982
- - `verify`
983
- - is now sync
984
- - `strict` option was renamed to `lowS`
985
- - `getSharedSecret`
986
- - now produce 33-byte compressed signatures by default
987
- - to use old behavior, which produced 65-byte uncompressed keys, set
988
- argument `isCompressed` to `false`: `getSharedSecret(a, b, false)`
1088
+ which means they have less features. Separate bls package had been deprecated.
1089
+
1090
+ secp256k1:
1091
+
1092
+ - `getPublicKey`: defaults to compressed sigs (use second argument to adjust)
1093
+ - `sign`: renamed options `canonical` => `lowS`; `der` => `format: 'der'`
1094
+ - `verify`: renamed option `strict` => `lowS`
1095
+ - `getSharedSecret`: defaults to compressed sigs (use third argument to adjust)
989
1096
  - `recoverPublicKey(msg, sig, rec)` was changed to `sig.recoverPublicKey(msg)`
990
- - `number` type for private keys have been removed: use `bigint` instead
991
1097
  - `Point` (2d xy) has been changed to `ProjectivePoint` (3d xyz)
992
- - `utils` were split into `utils` (same api as in noble-curves) and
993
- `etc` (`hmacSha256Sync` and others)
994
-
995
- ### noble-ed25519 v1 to curves v1
996
1098
 
997
- Upgrading from [@noble/ed25519](https://github.com/paulmillr/noble-ed25519):
1099
+ ed25519:
998
1100
 
999
- - Methods are now sync by default
1000
- - `bigint` is no longer allowed in `getPublicKey`, `sign`, `verify`. Reason: ed25519 is LE, can lead to bugs
1001
- - `Point` (2d xy) has been changed to `ExtendedPoint` (xyzt)
1002
- - `Signature` was removed: just use raw bytes or hex now
1003
- - `utils` were split into `utils` (same api as in noble-curves) and
1004
- `etc` (`sha512Sync` and others)
1101
+ - `Signature` was removed in favor of raw bytes
1005
1102
  - `getSharedSecret` was moved to `x25519` module
1006
- - `toX25519` has been moved to `edwardsToMontgomeryPub` and `edwardsToMontgomeryPriv` methods
1007
-
1008
- ### noble-bls12-381 to curves v1
1009
1103
 
1010
- Upgrading from [@noble/bls12-381](https://github.com/paulmillr/noble-bls12-381):
1104
+ bls12-381:
1011
1105
 
1012
- - Methods and classes were renamed:
1013
- - PointG1 -> G1.Point, PointG2 -> G2.Point
1014
- - PointG2.fromSignature -> Signature.decode, PointG2.toSignature -> Signature.encode
1015
- - Fp2 ORDER was corrected
1106
+ - Renamed PointG1 -> G1.Point, PointG2 -> G2.Point
1107
+ - Renamed PointG2.fromSignature -> Signature.decode, PointG2.toSignature -> Signature.encode
1016
1108
 
1017
1109
  ## Contributing & testing
1018
1110