@noble/curves 1.9.7 → 2.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +520 -505
- package/abstract/bls.d.ts +58 -120
- package/abstract/bls.d.ts.map +1 -1
- package/abstract/bls.js +108 -152
- package/abstract/bls.js.map +1 -1
- package/abstract/curve.d.ts +18 -54
- package/abstract/curve.d.ts.map +1 -1
- package/abstract/curve.js +30 -49
- package/abstract/curve.js.map +1 -1
- package/abstract/edwards.d.ts +18 -77
- package/abstract/edwards.d.ts.map +1 -1
- package/abstract/edwards.js +68 -144
- package/abstract/edwards.js.map +1 -1
- package/abstract/fft.js +14 -27
- package/abstract/fft.js.map +1 -1
- package/abstract/hash-to-curve.d.ts +35 -47
- package/abstract/hash-to-curve.d.ts.map +1 -1
- package/abstract/hash-to-curve.js +42 -46
- package/abstract/hash-to-curve.js.map +1 -1
- package/abstract/modular.d.ts +5 -17
- package/abstract/modular.d.ts.map +1 -1
- package/abstract/modular.js +170 -169
- package/abstract/modular.js.map +1 -1
- package/abstract/montgomery.d.ts +7 -12
- package/abstract/montgomery.d.ts.map +1 -1
- package/abstract/montgomery.js +22 -29
- package/abstract/montgomery.js.map +1 -1
- package/abstract/oprf.d.ts +282 -0
- package/abstract/oprf.d.ts.map +1 -0
- package/abstract/oprf.js +297 -0
- package/abstract/oprf.js.map +1 -0
- package/abstract/poseidon.d.ts.map +1 -1
- package/abstract/poseidon.js +26 -31
- package/abstract/poseidon.js.map +1 -1
- package/abstract/tower.d.ts.map +1 -1
- package/abstract/tower.js +43 -19
- package/abstract/tower.js.map +1 -1
- package/abstract/weierstrass.d.ts +77 -168
- package/abstract/weierstrass.d.ts.map +1 -1
- package/abstract/weierstrass.js +184 -389
- package/abstract/weierstrass.js.map +1 -1
- package/bls12-381.d.ts +5 -11
- package/bls12-381.d.ts.map +1 -1
- package/bls12-381.js +161 -181
- package/bls12-381.js.map +1 -1
- package/bn254.d.ts +59 -11
- package/bn254.d.ts.map +1 -1
- package/bn254.js +69 -97
- package/bn254.js.map +1 -1
- package/ed25519.d.ts +33 -48
- package/ed25519.d.ts.map +1 -1
- package/ed25519.js +147 -161
- package/ed25519.js.map +1 -1
- package/ed448.d.ts +27 -36
- package/ed448.d.ts.map +1 -1
- package/ed448.js +143 -164
- 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 +10 -14
- package/misc.d.ts.map +1 -1
- package/misc.js +53 -62
- package/misc.js.map +1 -1
- package/nist.d.ts +31 -16
- package/nist.d.ts.map +1 -1
- package/nist.js +75 -64
- package/nist.js.map +1 -1
- package/package.json +20 -234
- package/secp256k1.d.ts +17 -30
- package/secp256k1.d.ts.map +1 -1
- package/secp256k1.js +59 -73
- package/secp256k1.js.map +1 -1
- package/src/abstract/bls.ts +207 -354
- package/src/abstract/curve.ts +25 -84
- package/src/abstract/edwards.ts +68 -193
- package/src/abstract/hash-to-curve.ts +71 -85
- package/src/abstract/modular.ts +150 -134
- package/src/abstract/montgomery.ts +28 -35
- package/src/abstract/oprf.ts +600 -0
- package/src/abstract/poseidon.ts +6 -8
- package/src/abstract/tower.ts +0 -3
- package/src/abstract/weierstrass.ts +203 -525
- package/src/bls12-381.ts +133 -139
- package/src/bn254.ts +69 -93
- package/src/ed25519.ts +106 -133
- package/src/ed448.ts +111 -138
- package/src/index.ts +19 -3
- package/src/misc.ts +68 -51
- package/src/nist.ts +77 -70
- package/src/secp256k1.ts +46 -81
- package/src/utils.ts +67 -137
- package/src/webcrypto.ts +403 -0
- package/utils.d.ts +31 -38
- package/utils.d.ts.map +1 -1
- package/utils.js +66 -185
- package/utils.js.map +1 -1
- package/webcrypto.d.ts +99 -0
- package/webcrypto.d.ts.map +1 -0
- package/webcrypto.js +256 -0
- package/webcrypto.js.map +1 -0
- package/_shortw_utils.d.ts +0 -19
- package/_shortw_utils.d.ts.map +0 -1
- package/_shortw_utils.js +0 -20
- package/_shortw_utils.js.map +0 -1
- package/abstract/utils.d.ts +0 -78
- package/abstract/utils.d.ts.map +0 -1
- package/abstract/utils.js +0 -73
- package/abstract/utils.js.map +0 -1
- package/esm/_shortw_utils.d.ts +0 -19
- package/esm/_shortw_utils.d.ts.map +0 -1
- package/esm/_shortw_utils.js +0 -16
- package/esm/_shortw_utils.js.map +0 -1
- package/esm/abstract/bls.d.ts +0 -190
- package/esm/abstract/bls.d.ts.map +0 -1
- package/esm/abstract/bls.js +0 -408
- package/esm/abstract/bls.js.map +0 -1
- package/esm/abstract/curve.d.ts +0 -231
- package/esm/abstract/curve.d.ts.map +0 -1
- package/esm/abstract/curve.js +0 -465
- package/esm/abstract/curve.js.map +0 -1
- package/esm/abstract/edwards.d.ts +0 -243
- package/esm/abstract/edwards.d.ts.map +0 -1
- package/esm/abstract/edwards.js +0 -627
- package/esm/abstract/edwards.js.map +0 -1
- package/esm/abstract/fft.d.ts +0 -122
- package/esm/abstract/fft.d.ts.map +0 -1
- package/esm/abstract/fft.js +0 -425
- package/esm/abstract/fft.js.map +0 -1
- package/esm/abstract/hash-to-curve.d.ts +0 -102
- package/esm/abstract/hash-to-curve.d.ts.map +0 -1
- package/esm/abstract/hash-to-curve.js +0 -203
- package/esm/abstract/hash-to-curve.js.map +0 -1
- package/esm/abstract/modular.d.ts +0 -171
- package/esm/abstract/modular.d.ts.map +0 -1
- package/esm/abstract/modular.js +0 -530
- package/esm/abstract/modular.js.map +0 -1
- package/esm/abstract/montgomery.d.ts +0 -30
- package/esm/abstract/montgomery.d.ts.map +0 -1
- package/esm/abstract/montgomery.js +0 -157
- package/esm/abstract/montgomery.js.map +0 -1
- package/esm/abstract/poseidon.d.ts +0 -68
- package/esm/abstract/poseidon.d.ts.map +0 -1
- package/esm/abstract/poseidon.js +0 -296
- package/esm/abstract/poseidon.js.map +0 -1
- package/esm/abstract/tower.d.ts +0 -95
- package/esm/abstract/tower.d.ts.map +0 -1
- package/esm/abstract/tower.js +0 -714
- package/esm/abstract/tower.js.map +0 -1
- package/esm/abstract/utils.d.ts +0 -78
- package/esm/abstract/utils.d.ts.map +0 -1
- package/esm/abstract/utils.js +0 -70
- package/esm/abstract/utils.js.map +0 -1
- package/esm/abstract/weierstrass.d.ts +0 -416
- package/esm/abstract/weierstrass.d.ts.map +0 -1
- package/esm/abstract/weierstrass.js +0 -1413
- package/esm/abstract/weierstrass.js.map +0 -1
- package/esm/bls12-381.d.ts +0 -16
- package/esm/bls12-381.d.ts.map +0 -1
- package/esm/bls12-381.js +0 -705
- package/esm/bls12-381.js.map +0 -1
- package/esm/bn254.d.ts +0 -18
- package/esm/bn254.d.ts.map +0 -1
- package/esm/bn254.js +0 -214
- package/esm/bn254.js.map +0 -1
- package/esm/ed25519.d.ts +0 -106
- package/esm/ed25519.d.ts.map +0 -1
- package/esm/ed25519.js +0 -467
- package/esm/ed25519.js.map +0 -1
- package/esm/ed448.d.ts +0 -100
- package/esm/ed448.d.ts.map +0 -1
- package/esm/ed448.js +0 -459
- package/esm/ed448.js.map +0 -1
- package/esm/index.d.ts +0 -2
- package/esm/index.d.ts.map +0 -1
- package/esm/index.js +0 -17
- package/esm/index.js.map +0 -1
- package/esm/jubjub.d.ts +0 -12
- package/esm/jubjub.d.ts.map +0 -1
- package/esm/jubjub.js +0 -12
- package/esm/jubjub.js.map +0 -1
- package/esm/misc.d.ts +0 -19
- package/esm/misc.d.ts.map +0 -1
- package/esm/misc.js +0 -109
- package/esm/misc.js.map +0 -1
- package/esm/nist.d.ts +0 -21
- package/esm/nist.d.ts.map +0 -1
- package/esm/nist.js +0 -132
- package/esm/nist.js.map +0 -1
- package/esm/p256.d.ts +0 -16
- package/esm/p256.d.ts.map +0 -1
- package/esm/p256.js +0 -16
- package/esm/p256.js.map +0 -1
- package/esm/p384.d.ts +0 -16
- package/esm/p384.d.ts.map +0 -1
- package/esm/p384.js +0 -16
- package/esm/p384.js.map +0 -1
- package/esm/p521.d.ts +0 -16
- package/esm/p521.d.ts.map +0 -1
- package/esm/p521.js +0 -16
- package/esm/p521.js.map +0 -1
- package/esm/package.json +0 -4
- package/esm/pasta.d.ts +0 -10
- package/esm/pasta.d.ts.map +0 -1
- package/esm/pasta.js +0 -10
- package/esm/pasta.js.map +0 -1
- package/esm/secp256k1.d.ts +0 -89
- package/esm/secp256k1.d.ts.map +0 -1
- package/esm/secp256k1.js +0 -294
- package/esm/secp256k1.js.map +0 -1
- package/esm/utils.d.ts +0 -110
- package/esm/utils.d.ts.map +0 -1
- package/esm/utils.js +0 -322
- package/esm/utils.js.map +0 -1
- package/jubjub.d.ts +0 -12
- package/jubjub.d.ts.map +0 -1
- package/jubjub.js +0 -15
- package/jubjub.js.map +0 -1
- package/p256.d.ts +0 -16
- package/p256.d.ts.map +0 -1
- package/p256.js +0 -13
- package/p256.js.map +0 -1
- package/p384.d.ts +0 -16
- package/p384.d.ts.map +0 -1
- package/p384.js +0 -13
- package/p384.js.map +0 -1
- package/p521.d.ts +0 -16
- package/p521.d.ts.map +0 -1
- package/p521.js +0 -13
- package/p521.js.map +0 -1
- package/pasta.d.ts +0 -10
- package/pasta.d.ts.map +0 -1
- package/pasta.js +0 -13
- package/pasta.js.map +0 -1
- package/src/_shortw_utils.ts +0 -21
- package/src/abstract/utils.ts +0 -80
- package/src/jubjub.ts +0 -12
- package/src/p256.ts +0 -15
- package/src/p384.ts +0 -15
- package/src/p521.ts +0 -15
- package/src/package.json +0 -3
- package/src/pasta.ts +0 -9
package/README.md
CHANGED
|
@@ -5,14 +5,14 @@ Audited & minimal JS implementation of elliptic curve cryptography.
|
|
|
5
5
|
- 🔒 [**Audited**](#security) by independent security firms
|
|
6
6
|
- 🔻 Tree-shakeable: unused code is excluded from your builds
|
|
7
7
|
- 🏎 Fast: hand-optimized for caveats of JS engines
|
|
8
|
-
- 🔍 Reliable:
|
|
8
|
+
- 🔍 Reliable: cross-library / wycheproof tests and fuzzing ensure correctness
|
|
9
9
|
- ➰ Weierstrass, Edwards, Montgomery curves; ECDSA, EdDSA, Schnorr, BLS signatures
|
|
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
|
- 🪶 36KB (gzipped) including bundled hashes, 11KB for single-curve build
|
|
14
14
|
|
|
15
|
-
Curves have
|
|
15
|
+
Curves have 5kb sister projects
|
|
16
16
|
[secp256k1](https://github.com/paulmillr/noble-secp256k1) & [ed25519](https://github.com/paulmillr/noble-ed25519).
|
|
17
17
|
They have smaller attack surface, but less features.
|
|
18
18
|
|
|
@@ -30,7 +30,7 @@ Take a glance at [GitHub Discussions](https://github.com/paulmillr/noble-curves/
|
|
|
30
30
|
[curves](https://github.com/paulmillr/noble-curves),
|
|
31
31
|
[hashes](https://github.com/paulmillr/noble-hashes),
|
|
32
32
|
[post-quantum](https://github.com/paulmillr/noble-post-quantum),
|
|
33
|
-
|
|
33
|
+
5kb [secp256k1](https://github.com/paulmillr/noble-secp256k1) /
|
|
34
34
|
[ed25519](https://github.com/paulmillr/noble-ed25519)
|
|
35
35
|
- [Check out homepage](https://paulmillr.com/noble/)
|
|
36
36
|
for reading resources, documentation and apps built with noble
|
|
@@ -50,251 +50,314 @@ A standalone file [noble-curves.js](https://github.com/paulmillr/noble-curves/re
|
|
|
50
50
|
```ts
|
|
51
51
|
// import * from '@noble/curves'; // Error: use sub-imports, to ensure small app size
|
|
52
52
|
import { secp256k1, schnorr } from '@noble/curves/secp256k1.js';
|
|
53
|
-
import { ed25519, ed25519ph, ed25519ctx, x25519 } from '@noble/curves/ed25519.js';
|
|
54
|
-
import { ed448, ed448ph,
|
|
53
|
+
import { ed25519, ed25519ph, ed25519ctx, x25519, ristretto255 } from '@noble/curves/ed25519.js';
|
|
54
|
+
import { ed448, ed448ph, x448, decaf448 } from '@noble/curves/ed448.js';
|
|
55
55
|
import { p256, p384, p521 } from '@noble/curves/nist.js';
|
|
56
56
|
import { bls12_381 } from '@noble/curves/bls12-381.js';
|
|
57
57
|
import { bn254 } from '@noble/curves/bn254.js';
|
|
58
|
-
import { jubjub, babyjubjub } from '@noble/curves/misc.js';
|
|
59
|
-
|
|
58
|
+
import { jubjub, babyjubjub, brainpoolP256r1, brainpoolP384r1, brainpoolP512r1 } from '@noble/curves/misc.js';
|
|
59
|
+
|
|
60
|
+
// hash-to-curve
|
|
61
|
+
import { secp256k1_hasher } from '@noble/curves/secp256k1.js';
|
|
62
|
+
import { p256_hasher, p384_hasher, p521_hasher } from '@noble/curves/nist.js';
|
|
63
|
+
import { ristretto255_hasher } from '@noble/curves/ed25519.js';
|
|
64
|
+
import { decaf448_hasher } from '@noble/curves/ed448.js';
|
|
65
|
+
|
|
66
|
+
// OPRFs
|
|
67
|
+
import { p256_oprf, p384_oprf, p521_oprf } from '@noble/curves/nist.js';
|
|
68
|
+
import { ristretto255_oprf } from '@noble/curves/ed25519.js';
|
|
69
|
+
import { decaf448_oprf } from '@noble/curves/ed448.js';
|
|
70
|
+
|
|
71
|
+
// utils
|
|
72
|
+
import { bytesToHex, hexToBytes, concatBytes } from '@noble/curves/abstract/utils.js';
|
|
73
|
+
import { Field } from '@noble/curves/abstract/modular.js';
|
|
74
|
+
import { weierstrass, ecdsa } from '@noble/curves/abstract/weierstrass.js';
|
|
75
|
+
import { edwards, eddsa } from '@noble/curves/abstract/edwards.js';
|
|
76
|
+
import { poseidon, poseidonSponge } from '@noble/curves/abstract/poseidon.js';
|
|
77
|
+
import { FFT, poly } from '@noble/curves/abstract/fft.js';
|
|
60
78
|
```
|
|
61
79
|
|
|
62
|
-
-
|
|
63
|
-
- [
|
|
64
|
-
- [
|
|
65
|
-
- [
|
|
66
|
-
- [
|
|
67
|
-
- [
|
|
68
|
-
- [
|
|
69
|
-
- [
|
|
70
|
-
- [
|
|
71
|
-
- [
|
|
72
|
-
- [
|
|
73
|
-
- [
|
|
74
|
-
- [
|
|
75
|
-
- [
|
|
76
|
-
- [
|
|
77
|
-
|
|
78
|
-
- [
|
|
79
|
-
- [modular](#modular-modular-arithmetics-
|
|
80
|
-
- [
|
|
81
|
-
- [
|
|
82
|
-
- [
|
|
80
|
+
- Examples
|
|
81
|
+
- [ECDSA, EdDSA, Schnorr signatures](#ecdsa-eddsa-schnorr-signatures)
|
|
82
|
+
- [secp256k1, p256, p384, p521, ed25519, ed448, brainpool](#secp256k1-p256-p384-p521-ed25519-ed448-brainpool)
|
|
83
|
+
- [ristretto255, decaf448](#ristretto255-decaf448)
|
|
84
|
+
- [Prehashed signing](#prehashed-signing)
|
|
85
|
+
- [Hedged ECDSA with noise](#hedged-ecdsa-with-noise)
|
|
86
|
+
- [Consensus-friendliness vs e-voting](#consensus-friendliness-vs-e-voting)
|
|
87
|
+
- [ECDH: Diffie-Hellman shared secrets](#ecdh-diffie-hellman-shared-secrets)
|
|
88
|
+
- [webcrypto: Friendly wrapper](#webcrypto-friendly-wrapper)
|
|
89
|
+
- [BLS signatures, bls12-381, bn254 aka alt\_bn128](#bls-signatures-bls12-381-bn254-aka-alt_bn128)
|
|
90
|
+
- [Hashing to curve points](#hash-to-curve-hashing-to-curve-points)
|
|
91
|
+
- [OPRFs](#oprfs)
|
|
92
|
+
- [Poseidon hash](#poseidon-poseidon-hash)
|
|
93
|
+
- [Fast Fourier Transform](#fft-fast-fourier-transform)
|
|
94
|
+
- [utils](#utils-byte-shuffling-conversion)
|
|
95
|
+
- [Internals](#internals)
|
|
96
|
+
- [Elliptic curve Point math](#elliptic-curve-point-math)
|
|
97
|
+
- [modular: Modular arithmetics \& finite fields](#modular-modular-arithmetics--finite-fields)
|
|
98
|
+
- [weierstrass: Custom Weierstrass curve](#weierstrass-custom-weierstrass-curve)
|
|
99
|
+
- [edwards: Custom Edwards curve](#edwards-custom-edwards-curve)
|
|
100
|
+
- [Custom ECDSA instance](#custom-ecdsa-instance)
|
|
83
101
|
- [Security](#security)
|
|
84
102
|
- [Speed](#speed)
|
|
85
|
-
- [Upgrading](#upgrading)
|
|
86
103
|
- [Contributing & testing](#contributing--testing)
|
|
87
|
-
- [
|
|
104
|
+
- [Upgrading](#upgrading)
|
|
88
105
|
|
|
89
|
-
###
|
|
106
|
+
### ECDSA, EdDSA, Schnorr signatures
|
|
90
107
|
|
|
91
|
-
####
|
|
108
|
+
#### secp256k1, p256, p384, p521, ed25519, ed448, brainpool
|
|
92
109
|
|
|
93
|
-
```
|
|
94
|
-
import { secp256k1 } from '@noble/curves/secp256k1.js';
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
110
|
+
```js
|
|
111
|
+
import { secp256k1, schnorr } from '@noble/curves/secp256k1.js';
|
|
112
|
+
import { p256, p384, p521 } from '@noble/curves/nist.js';
|
|
113
|
+
import { ed25519 } from '@noble/curves/ed25519.js';
|
|
114
|
+
import { ed448 } from '@noble/curves/ed448.js';
|
|
115
|
+
import { brainpoolP256r1, brainpoolP384r1, brainpoolP512r1 } from '@noble/curves/misc.js';
|
|
116
|
+
for (const curve of [
|
|
117
|
+
secp256k1, schnorr,
|
|
118
|
+
p256, p384, p521,
|
|
119
|
+
ed25519, ed448,
|
|
120
|
+
brainpoolP256r1, brainpoolP384r1, brainpoolP512r1
|
|
121
|
+
]) {
|
|
122
|
+
const { secretKey, publicKey } = curve.keygen();
|
|
123
|
+
const msg = new TextEncoder().encode('hello noble');
|
|
124
|
+
const sig = curve.sign(msg, secretKey);
|
|
125
|
+
const isValid = curve.verify(sig, msg, publicKey);
|
|
126
|
+
console.log(curve, secretKey, publicKey, sig, isValid);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Specific private key
|
|
130
|
+
import { hexToBytes } from '@noble/curves/utils.js';
|
|
131
|
+
const secret2 = hexToBytes('46c930bc7bb4db7f55da20798697421b98c4175a52c630294d75a84b9c126236');
|
|
132
|
+
const pub2 = secp256k1.getPublicKey(secret2);
|
|
111
133
|
```
|
|
112
134
|
|
|
113
|
-
|
|
135
|
+
ECDSA signatures use deterministic k, conforming to [RFC 6979](https://www.rfc-editor.org/rfc/rfc6979).
|
|
136
|
+
EdDSA conforms to [RFC 8032](https://www.rfc-editor.org/rfc/rfc8032).
|
|
137
|
+
Schnorr (secp256k1-only) conforms to [BIP 340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki).
|
|
114
138
|
|
|
115
|
-
####
|
|
139
|
+
#### ristretto255, decaf448
|
|
116
140
|
|
|
117
141
|
```ts
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
142
|
+
import { ristretto255, ristretto255_hasher, ristretto255_oprf } from '@noble/curves/ed25519.js';
|
|
143
|
+
import { decaf448, decaf448_hasher, decaf448_oprf } from '@noble/curves/ed448.js';
|
|
144
|
+
|
|
145
|
+
console.log(ristretto255.Point, decaf448.Point);
|
|
121
146
|
```
|
|
122
147
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
optionally for ECDSA. Check out blog post
|
|
126
|
-
[Deterministic signatures are not your friends](https://paulmillr.com/posts/deterministic-signatures/)
|
|
127
|
-
and [spec draft](https://datatracker.ietf.org/doc/draft-irtf-cfrg-det-sigs-with-noise/).
|
|
148
|
+
Check out [RFC 9496](https://www.rfc-editor.org/rfc/rfc9496) more info on ristretto255 & decaf448.
|
|
149
|
+
Check out separate documentation for [Point](#elliptic-curve-point-math), [hasher](#hash-to-curve-hashing-to-curve-points) and [oprf](#oprfs).
|
|
128
150
|
|
|
129
|
-
####
|
|
151
|
+
#### Prehashed signing
|
|
130
152
|
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
//
|
|
153
|
+
```js
|
|
154
|
+
import { secp256k1 } from '@noble/curves/secp256k1.js';
|
|
155
|
+
import { keccak256 } from '@noble/hashes/sha3.js';
|
|
156
|
+
const { secretKey } = curve.keygen();
|
|
157
|
+
const msg = new TextEncoder().encode('hello noble');
|
|
158
|
+
// prehash: true (default) - hash using secp256k1.hash (sha256)
|
|
159
|
+
const sig = secp256k1.sign(msg, secretKey);
|
|
160
|
+
// prehash: false - hash using custom hash
|
|
161
|
+
const sigKeccak = secp256k1.sign(keccak256(msg), secretKey, { prehash: false });
|
|
137
162
|
```
|
|
138
163
|
|
|
139
|
-
|
|
164
|
+
ECDSA `sign()` allows providing `prehash: false`, which enables using custom hashes.
|
|
140
165
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
166
|
+
A ECDSA signature is not just "math over elliptic curve points".
|
|
167
|
+
It's actually math + hashing: p256 is in fact p256 point + sha256 hash.
|
|
168
|
+
By default, we hash messages. To use custom hash methods,
|
|
169
|
+
make sure to disable prehashing.
|
|
170
|
+
|
|
171
|
+
> [!NOTE]
|
|
172
|
+
> Previously, in noble-curves v1, `prehash: false` was the default.
|
|
173
|
+
> Some other libraries (like libsecp256k1) have no prehashing.
|
|
174
|
+
|
|
175
|
+
#### Hedged ECDSA with noise
|
|
176
|
+
|
|
177
|
+
```js
|
|
178
|
+
import { secp256k1 } from '@noble/curves/secp256k1.js';
|
|
179
|
+
const { secretKey } = curve.keygen();
|
|
180
|
+
const msg = new TextEncoder().encode('hello noble');
|
|
181
|
+
// extraEntropy: false - default, hedging disabled
|
|
182
|
+
const sigNoisy = secp256k1.sign(msg, secretKey);
|
|
183
|
+
// extraEntropy: true - fetch 32 random bytes from CSPRNG
|
|
184
|
+
const sigNoisy = secp256k1.sign(msg, secretKey, { extraEntropy: true });
|
|
185
|
+
// extraEntropy: bytes - specific extra entropy
|
|
186
|
+
const ent = Uint8Array.from([0xca, 0xfe, 0x01, 0x23]);
|
|
187
|
+
const sigNoisy2 = secp256k1.sign(msg, secretKey, { extraEntropy: ent });
|
|
148
188
|
```
|
|
149
189
|
|
|
150
|
-
|
|
190
|
+
ECDSA `sign()` allows providing `extraEntropy`, which switches sig generation to hedged mode.
|
|
151
191
|
|
|
152
|
-
|
|
192
|
+
By default, ECDSA signatures are generated deterministically,
|
|
193
|
+
following [RFC 6979](https://www.rfc-editor.org/rfc/rfc6979).
|
|
194
|
+
However, purely deterministic signatures are vulnerable to fault attacks.
|
|
195
|
+
Newer signature schemes, such as BIP340 schnorr, switched to hedged signatures because of this.
|
|
196
|
+
Hedging is basically incorporating some randomness into sig generation process.
|
|
197
|
+
|
|
198
|
+
For more info, check out
|
|
199
|
+
[Deterministic signatures are not your friends](https://paulmillr.com/posts/deterministic-signatures/),
|
|
200
|
+
[RFC 6979](https://www.rfc-editor.org/rfc/rfc6979) section 3.6,
|
|
201
|
+
and [cfrg-det-sigs-with-noise draft](https://datatracker.ietf.org/doc/draft-irtf-cfrg-det-sigs-with-noise/).
|
|
202
|
+
|
|
203
|
+
#### Consensus-friendliness vs e-voting
|
|
204
|
+
|
|
205
|
+
```js
|
|
153
206
|
import { ed25519 } from '@noble/curves/ed25519.js';
|
|
154
|
-
const
|
|
155
|
-
const
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
ed25519.verify(sig, msg, pub);
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
// Variants from RFC8032: with context, prehashed
|
|
162
|
-
import { ed25519ctx, ed25519ph } from '@noble/curves/ed25519.js';
|
|
207
|
+
const { secretKey, publicKey } = ed25519.keygen();
|
|
208
|
+
const msg = new TextEncoder().encode('hello noble');
|
|
209
|
+
const sig = ed25519.sign(msg, secretKey);
|
|
210
|
+
// zip215: true
|
|
211
|
+
const isValid = ed25519.verify(sig, msg, pub);
|
|
212
|
+
// SBS / e-voting / RFC8032 / FIPS 186-5
|
|
213
|
+
const isValidRfc = ed25519.verify(sig, msg, pub, { zip215: false });
|
|
163
214
|
```
|
|
164
215
|
|
|
165
|
-
|
|
166
|
-
can be used in consensus-critical applications.
|
|
167
|
-
If you need SBS (Strongly Binding Signatures) and FIPS 186-5 compliance,
|
|
168
|
-
use `zip215: false`. Check out [Edwards Signatures section for more info](#edwards-twisted-edwards-curve).
|
|
169
|
-
Both options have SUF-CMA (strong unforgeability under chosen message attacks).
|
|
216
|
+
In ed25519, there is an ability to choose between consensus-friendliness vs e-voting mode.
|
|
170
217
|
|
|
171
|
-
|
|
218
|
+
- `zip215: true` is default behavior. It has slightly looser verification logic
|
|
219
|
+
to be [consensus-friendly](https://hdevalence.ca/blog/2020-10-04-its-25519am), following [ZIP215](https://zips.z.cash/zip-0215) rules
|
|
220
|
+
- `zip215: false` switches verification criteria to strict
|
|
221
|
+
[RFC 8032](https://www.rfc-editor.org/rfc/rfc8032) / [FIPS 186-5](https://csrc.nist.gov/publications/detail/fips/186/5/final)
|
|
222
|
+
and additionally provides [non-repudiation with SBS](https://eprint.iacr.org/2020/1244),
|
|
223
|
+
which is useful for:
|
|
224
|
+
- Contract Signing: if A signed an agreement with B using key that allows repudiation, it can later claim that it signed a different contract
|
|
225
|
+
- E-voting: malicious voters may pick keys that allow repudiation in order to deny results
|
|
226
|
+
- Blockchains: transaction of amount X might also be valid for a different amount Y
|
|
172
227
|
|
|
173
|
-
|
|
174
|
-
|
|
228
|
+
Both modes have SUF-CMA (strong unforgeability under chosen message attacks).
|
|
229
|
+
|
|
230
|
+
### ECDH: Diffie-Hellman shared secrets
|
|
231
|
+
|
|
232
|
+
```js
|
|
233
|
+
import { secp256k1 } from '@noble/curves/secp256k1.js';
|
|
175
234
|
import { x25519 } from '@noble/curves/ed25519.js';
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
x25519.getSharedSecret(priv, pub) === x25519.scalarMult(priv, pub); // aliases
|
|
179
|
-
x25519.getPublicKey(priv) === x25519.scalarMultBase(priv);
|
|
180
|
-
x25519.getPublicKey(x25519.utils.randomPrivateKey());
|
|
181
|
-
|
|
182
|
-
// ed25519 => x25519 conversion
|
|
183
|
-
import { edwardsToMontgomeryPub, edwardsToMontgomeryPriv } from '@noble/curves/ed25519.js';
|
|
184
|
-
edwardsToMontgomeryPub(ed25519.getPublicKey(ed25519.utils.randomPrivateKey()));
|
|
185
|
-
edwardsToMontgomeryPriv(ed25519.utils.randomPrivateKey());
|
|
186
|
-
```
|
|
235
|
+
import { x448 } from '@noble/curves/ed448.js';
|
|
236
|
+
import { p256, p384, p521 } from '@noble/curves/nist.js';
|
|
187
237
|
|
|
188
|
-
|
|
238
|
+
for (const curve of [secp256k1, schnorr, x25519, x448, p256, p384, p521]) {
|
|
239
|
+
const alice = curve.keygen();
|
|
240
|
+
const bob = curve.keygen();
|
|
241
|
+
const sharedKey = curve.getSharedSecret(alice.secretKey, bob.publicKey);
|
|
242
|
+
console.log('alice', alice, 'bob', bob, 'shared', sharedKey);
|
|
243
|
+
}
|
|
189
244
|
|
|
190
|
-
|
|
191
|
-
import {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
} from '@noble/curves/ed25519.js';
|
|
198
|
-
|
|
199
|
-
const msg = new TextEncoder().encode('Ristretto is traditionally a short shot of espresso coffee');
|
|
200
|
-
hashToCurve(msg);
|
|
201
|
-
|
|
202
|
-
const rp = RistrettoPoint.fromHex(
|
|
203
|
-
'6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919'
|
|
204
|
-
);
|
|
205
|
-
RistrettoPoint.BASE.multiply(2n).add(rp).subtract(RistrettoPoint.BASE).toRawBytes();
|
|
206
|
-
RistrettoPoint.ZERO.equals(dp) === false;
|
|
207
|
-
// pre-hashed hash-to-curve
|
|
208
|
-
RistrettoPoint.hashToCurve(sha512(msg));
|
|
209
|
-
// full hash-to-curve including domain separation tag
|
|
210
|
-
hashToRistretto255(msg, { DST: 'ristretto255_XMD:SHA-512_R255MAP_RO_' });
|
|
245
|
+
// x25519 & x448 specific methods
|
|
246
|
+
import { ed25519 } from '@noble/curves/ed25519.js';
|
|
247
|
+
const alice = ed25519.keygen();
|
|
248
|
+
const bob = ed25519.keygen();
|
|
249
|
+
const aliceSecX = ed25519.utils.toMontgomerySecret(alice.secretKey);
|
|
250
|
+
const bobPubX = ed25519.utils.toMontgomery(bob.publicKey);
|
|
251
|
+
const sharedKey = x25519.getSharedSecret(aliceSecX, bobPubX);
|
|
211
252
|
```
|
|
212
253
|
|
|
213
|
-
|
|
254
|
+
We provide ECDH over all Weierstrass curves, and over 2 Montgomery curves
|
|
255
|
+
X25519 (Curve25519) & X448 (Curve448), conforming to [RFC 7748](https://www.rfc-editor.org/rfc/rfc7748).
|
|
214
256
|
|
|
215
|
-
|
|
257
|
+
In Weierstrass curves, shared secrets:
|
|
216
258
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const priv = ed448.utils.randomPrivateKey();
|
|
220
|
-
const pub = ed448.getPublicKey(priv);
|
|
221
|
-
const msg = new TextEncoder().encode('whatsup');
|
|
222
|
-
const sig = ed448.sign(msg, priv);
|
|
223
|
-
ed448.verify(sig, msg, pub);
|
|
224
|
-
|
|
225
|
-
// Variants from RFC8032: prehashed
|
|
226
|
-
import { ed448ph } from '@noble/curves/ed448.js';
|
|
227
|
-
```
|
|
259
|
+
- Include y-parity bytes: use `key.slice(1)` to strip it
|
|
260
|
+
- Are not hashed: use hashing or KDF on top, like `sha256(shared)` or `hkdf(shared)`
|
|
228
261
|
|
|
229
|
-
####
|
|
262
|
+
#### webcrypto: Friendly wrapper
|
|
230
263
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
x448.getPublicKey(priv) === x448.scalarMultBase(priv);
|
|
264
|
+
> [!NOTE]
|
|
265
|
+
> Webcrypto methods are always async.
|
|
266
|
+
|
|
267
|
+
##### webcrypto signatures
|
|
236
268
|
|
|
237
|
-
|
|
238
|
-
import {
|
|
239
|
-
|
|
269
|
+
```js
|
|
270
|
+
import { ed25519, ed448, p256, p384, p521 } from './src/webcrypto.ts';
|
|
271
|
+
|
|
272
|
+
(async () => {
|
|
273
|
+
for (let [name, curve] of Object.entries({ p256, p384, p521, ed25519, ed448 })) {
|
|
274
|
+
console.log('curve', name);
|
|
275
|
+
if (!await curve.isSupported()) {
|
|
276
|
+
console.log('is not supported, skipping');
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
const keys = await curve.keygen();
|
|
280
|
+
const msg = new TextEncoder().encode('hello noble');
|
|
281
|
+
const sig = await curve.sign(msg, keys.secretKey);
|
|
282
|
+
const isValid = await curve.verify(sig, msg, keys.publicKey);
|
|
283
|
+
console.log({
|
|
284
|
+
keys, msg, sig, isValid
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
})();
|
|
240
288
|
```
|
|
241
289
|
|
|
242
|
-
|
|
290
|
+
##### webcrypto ecdh
|
|
243
291
|
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
);
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
292
|
+
```js
|
|
293
|
+
import { p256, p384, p521, x25519, x448 } from './src/webcrypto.ts';
|
|
294
|
+
|
|
295
|
+
(async () => {
|
|
296
|
+
for (let [name, curve] of Object.entries({ p256, p384, p521, x25519, x448 })) {
|
|
297
|
+
console.log('curve', name);
|
|
298
|
+
if (!await curve.isSupported()) {
|
|
299
|
+
console.log('is not supported, skipping');
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
const alice = await curve.keygen();
|
|
303
|
+
const bob = await curve.keygen();
|
|
304
|
+
const shared = await curve.getSharedSecret(alice.secretKey, bob.publicKey);
|
|
305
|
+
const shared2 = await curve.getSharedSecret(bob.secretKey, alice.publicKey);
|
|
306
|
+
console.log({shared});
|
|
307
|
+
}
|
|
308
|
+
})();
|
|
261
309
|
```
|
|
262
310
|
|
|
263
|
-
|
|
311
|
+
##### Key conversion from noble to webcrypto and back
|
|
264
312
|
|
|
265
|
-
|
|
313
|
+
```js
|
|
314
|
+
import { p256 as p256n } from './src/nist.ts';
|
|
315
|
+
import { p256 } from './src/webcrypto.ts';
|
|
316
|
+
(async () => {
|
|
317
|
+
const nobleKeys = p256n.keygen();
|
|
318
|
+
// convert noble keys to webcrypto
|
|
319
|
+
const webKeys = {
|
|
320
|
+
secretKey: await p256.utils.convertSecretKey(nobleKeys.secretKey, 'raw', 'pkcs8'),
|
|
321
|
+
publicKey: await p256.utils.convertPublicKey(nobleKeys.publicKey, 'raw', 'spki')
|
|
322
|
+
};
|
|
323
|
+
// convert webcrypto keys to noble
|
|
324
|
+
const nobleKeys2 = {
|
|
325
|
+
secretKey: await p256.utils.convertSecretKey(webKeys.secretKey, 'pkcs8', 'raw'),
|
|
326
|
+
publicKey: await p256.utils.convertPublicKey(webKeys.publicKey, 'spki', 'raw')
|
|
327
|
+
};
|
|
328
|
+
})();
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### BLS signatures, bls12-381, bn254 aka alt_bn128
|
|
266
332
|
|
|
267
333
|
```ts
|
|
268
334
|
import { bls12_381 } from '@noble/curves/bls12-381.js';
|
|
269
|
-
import { hexToBytes } from '@noble/curves/abstract/utils.js';
|
|
270
|
-
|
|
271
|
-
// private keys are 32 bytes
|
|
272
|
-
const privKey = hexToBytes('67d53f170b908cabb9eb326c3c337762d59289a8fec79f7bc9254b584b73265c');
|
|
273
|
-
// const privKey = bls12_381.utils.randomPrivateKey();
|
|
274
335
|
|
|
275
|
-
//
|
|
336
|
+
// G1 pubkeys, G2 sigs
|
|
276
337
|
const blsl = bls12_381.longSignatures;
|
|
277
|
-
const publicKey = blsl.
|
|
278
|
-
//
|
|
279
|
-
const msg = new TextEncoder().encode('hello');
|
|
280
|
-
|
|
281
|
-
const msgp = blsl.hash(msg
|
|
282
|
-
|
|
338
|
+
const { secretKey, publicKey } = blsl.keygen();
|
|
339
|
+
// const publicKey = blsl.getPublicKey(secretKey);
|
|
340
|
+
const msg = new TextEncoder().encode('hello noble');
|
|
341
|
+
// default DST
|
|
342
|
+
const msgp = blsl.hash(msg);
|
|
343
|
+
// custom DST (Ethereum)
|
|
344
|
+
const msgpd = blsl.hash(msg, 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_');
|
|
345
|
+
const signature = blsl.sign(msgp, secretKey);
|
|
283
346
|
const isValid = blsl.verify(signature, msgp, publicKey);
|
|
284
|
-
console.log({ publicKey, signature, isValid });
|
|
347
|
+
console.log('long', { publicKey, signature, isValid });
|
|
285
348
|
|
|
286
|
-
//
|
|
349
|
+
// G1 sigs, G2 pubkeys
|
|
287
350
|
const blss = bls12_381.shortSignatures;
|
|
288
|
-
const publicKey2 = blss.getPublicKey(
|
|
289
|
-
const msgp2 = blss.hash(
|
|
290
|
-
const signature2 = blss.sign(msgp2,
|
|
351
|
+
const publicKey2 = blss.getPublicKey(secretKey);
|
|
352
|
+
const msgp2 = blss.hash(msg, 'BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_');
|
|
353
|
+
const signature2 = blss.sign(msgp2, secretKey);
|
|
291
354
|
const isValid2 = blss.verify(signature2, msgp2, publicKey);
|
|
292
355
|
console.log({ publicKey2, signature2, isValid2 });
|
|
293
356
|
|
|
294
357
|
// Aggregation
|
|
295
358
|
const aggregatedKey = bls12_381.longSignatures.aggregatePublicKeys([
|
|
296
|
-
bls12_381.utils.
|
|
297
|
-
bls12_381.utils.
|
|
359
|
+
bls12_381.utils.randomSecretKey(),
|
|
360
|
+
bls12_381.utils.randomSecretKey(),
|
|
298
361
|
]);
|
|
299
362
|
// const aggregatedSig = bls.aggregateSignatures(sigs)
|
|
300
363
|
|
|
@@ -304,278 +367,88 @@ const aggregatedKey = bls12_381.longSignatures.aggregatePublicKeys([
|
|
|
304
367
|
// bls.fields.Fp12.finalExponentiate(bls.fields.Fp12.mul(PointG1, PointG2));
|
|
305
368
|
|
|
306
369
|
// Others
|
|
307
|
-
// bls.G1.
|
|
370
|
+
// bls.G1.Point.BASE, bls.G2.Point.BASE;
|
|
308
371
|
// bls.fields.Fp, bls.fields.Fp2, bls.fields.Fp12, bls.fields.Fr;
|
|
309
372
|
```
|
|
310
373
|
|
|
311
374
|
See [abstract/bls](#bls-barreto-lynn-scott-curves).
|
|
312
375
|
For example usage, check out [the implementation of BLS EVM precompiles](https://github.com/ethereumjs/ethereumjs-monorepo/blob/361f4edbc239e795a411ac2da7e5567298b9e7e5/packages/evm/src/precompiles/bls12_381/noble.ts).
|
|
313
376
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
```ts
|
|
317
|
-
import { bn254 } from '@noble/curves/bn254.js';
|
|
318
|
-
|
|
319
|
-
console.log(bn254.G1, bn254.G2, bn254.pairing);
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
The API mirrors [BLS](#bls12-381). The curve was previously called alt_bn128.
|
|
377
|
+
The BN254 API mirrors [BLS](#bls12-381). The curve was previously called alt_bn128.
|
|
323
378
|
The implementation is compatible with [EIP-196](https://eips.ethereum.org/EIPS/eip-196) and
|
|
324
379
|
[EIP-197](https://eips.ethereum.org/EIPS/eip-197).
|
|
325
380
|
|
|
326
|
-
|
|
327
|
-
To work around this limitation, has to initialize points on their own from BigInts.
|
|
328
|
-
Reason it's not implemented is because [there is no standard](https://github.com/privacy-scaling-explorations/halo2curves/issues/109).
|
|
381
|
+
For BN254 usage, check out [the implementation of bn254 EVM precompiles](https://github.com/paulmillr/noble-curves/blob/3ed792f8ad9932765b84d1064afea8663a255457/test/bn254.test.js#L697).
|
|
382
|
+
We don't implement Point methods toBytes. To work around this limitation, has to initialize points on their own from BigInts. Reason it's not implemented is because [there is no standard](https://github.com/privacy-scaling-explorations/halo2curves/issues/109).
|
|
329
383
|
Points of divergence:
|
|
330
384
|
|
|
331
385
|
- Endianness: LE vs BE (byte-swapped)
|
|
332
386
|
- Flags as first hex bits (similar to BLS) vs no-flags
|
|
333
387
|
- Imaginary part last in G2 vs first (c0, c1 vs c1, c0)
|
|
334
388
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
#### misc curves
|
|
389
|
+
### hash-to-curve: hashing to curve points
|
|
338
390
|
|
|
339
391
|
```ts
|
|
340
|
-
import {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
It's always possible to use different hashing library.
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
### weierstrass: Short Weierstrass curve
|
|
359
|
-
|
|
360
|
-
```js
|
|
361
|
-
import { weierstrass } from '@noble/curves/abstract/weierstrass.js';
|
|
362
|
-
// NIST secp192r1 aka p192. https://www.secg.org/sec2-v2.pdf
|
|
363
|
-
const p192_CURVE = {
|
|
364
|
-
p: 0xfffffffffffffffffffffffffffffffeffffffffffffffffn,
|
|
365
|
-
n: 0xffffffffffffffffffffffff99def836146bc9b1b4d22831n,
|
|
366
|
-
h: 1n,
|
|
367
|
-
a: 0xfffffffffffffffffffffffffffffffefffffffffffffffcn,
|
|
368
|
-
b: 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1n,
|
|
369
|
-
Gx: 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012n,
|
|
370
|
-
Gy: 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811n,
|
|
392
|
+
import { bls12_381 } from './src/bls12-381.ts';
|
|
393
|
+
import { ed25519_hasher, ristretto255_hasher } from './src/ed25519.ts';
|
|
394
|
+
import { decaf448_hasher, ed448_hasher } from './src/ed448.ts';
|
|
395
|
+
import { p256_hasher, p384_hasher, p521_hasher } from './src/nist.ts';
|
|
396
|
+
import { secp256k1_hasher } from './src/secp256k1.ts';
|
|
397
|
+
|
|
398
|
+
const h = {
|
|
399
|
+
secp256k1_hasher,
|
|
400
|
+
p256_hasher, p384_hasher, p521_hasher,
|
|
401
|
+
ed25519_hasher,
|
|
402
|
+
ed448_hasher,
|
|
403
|
+
ristretto255_hasher,
|
|
404
|
+
decaf448_hasher,
|
|
405
|
+
bls_G1: bls12_381.G1,
|
|
406
|
+
bls_G2: bls12_381.G2
|
|
371
407
|
};
|
|
372
|
-
const p192_Point = weierstrass(p192_CURVE);
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
Short Weierstrass curve's formula is `y² = x³ + ax + b`. `weierstrass`
|
|
376
|
-
expects arguments `a`, `b`, field characteristic `p`, curve order `n`,
|
|
377
|
-
cofactor `h` and coordinates `Gx`, `Gy` of generator point.
|
|
378
|
-
|
|
379
|
-
#### Projective Weierstrass Point
|
|
380
408
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
// Multi-scalar-multiplication (MSM) is basically `(Pa + Qb + Rc + ...)`.
|
|
400
|
-
// It's 10-30x faster vs naive addition for large amount of points.
|
|
401
|
-
// Pippenger algorithm is used underneath.
|
|
402
|
-
const points = [Point.BASE, Point.BASE.multiply(2n), Point.BASE.multiply(4n), Point.BASE.multiply(8n)];
|
|
403
|
-
Point.msm(points, [3n, 5n, 7n, 11n]).equals(Point.BASE.multiply(129n)); // 129*G
|
|
409
|
+
const msg = Uint8Array.from([0xca, 0xfe, 0x01, 0x23]);
|
|
410
|
+
console.log('msg', msg);
|
|
411
|
+
for (let [name, c] of Object.entries(h)) {
|
|
412
|
+
const hashToCurve = c.hashToCurve(msg).toHex();
|
|
413
|
+
const hashToCurve_customDST = c.hashToCurve(msg, { DST: 'hello noble' }).toHex();
|
|
414
|
+
const encodeToCurve = 'encodeToCurve' in c ? c.encodeToCurve(msg).toHex() : undefined;
|
|
415
|
+
// ristretto255, decaf448 only
|
|
416
|
+
const deriveToCurve = 'deriveToCurve' in c ?
|
|
417
|
+
c.deriveToCurve!(new Uint8Array(c.Point.Fp.BYTES * 2)).toHex() : undefined;
|
|
418
|
+
const hashToScalar = c.hashToScalar(msg);
|
|
419
|
+
console.log({
|
|
420
|
+
name, hashToCurve, hashToCurve_customDST, encodeToCurve, deriveToCurve, hashToScalar
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// abstract methods
|
|
425
|
+
import { expand_message_xmd, expand_message_xof, hash_to_field } from '@noble/curves/abstract/hash-to-curve.js';
|
|
404
426
|
```
|
|
405
427
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
```js
|
|
409
|
-
import { ecdsa } from '@noble/curves/abstract/weierstrass.js';
|
|
410
|
-
import { sha256 } from '@noble/hashes/sha2.js';
|
|
411
|
-
const p192 = ecdsa(p192_Point, sha256);
|
|
412
|
-
const priv = p192.utils.randomPrivateKey();
|
|
413
|
-
const pub = p192.getPublicKey(priv);
|
|
414
|
-
const msg = sha256(new TextEncoder().encode('custom curve'));
|
|
415
|
-
const sig = p192.sign(msg);
|
|
416
|
-
const isValid = p192.verify(sig, msg, pub);
|
|
417
|
-
```
|
|
418
|
-
|
|
419
|
-
ECDSA signatures:
|
|
420
|
-
|
|
421
|
-
- Are represented by `Signature` instances with `r, s` and optional `recovery` properties
|
|
422
|
-
- Have `recoverPublicKey()`, `toBytes()` with optional `format: 'compact' | 'der'`
|
|
423
|
-
- Can be prehashed, or non-prehashed:
|
|
424
|
-
- `sign(msgHash, privKey)` (default, prehash: false) - you did hashing before
|
|
425
|
-
- `sign(msg, privKey, {prehash: true})` - curves will do hashing for you
|
|
426
|
-
- Are generated deterministically, following [RFC6979](https://www.rfc-editor.org/rfc/rfc6979).
|
|
427
|
-
- Consider [hedged ECDSA with noise](#hedged-ecdsa-with-noise) for adding randomness into
|
|
428
|
-
for signatures, to get improved security against fault attacks.
|
|
429
|
-
|
|
430
|
-
### edwards: Twisted Edwards curve
|
|
431
|
-
|
|
432
|
-
```ts
|
|
433
|
-
import { edwards } from '@noble/curves/abstract/edwards.js';
|
|
434
|
-
const ed25519_CURVE = {
|
|
435
|
-
p: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffedn,
|
|
436
|
-
n: 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3edn,
|
|
437
|
-
h: 8n,
|
|
438
|
-
a: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffecn,
|
|
439
|
-
d: 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3n,
|
|
440
|
-
Gx: 0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51an,
|
|
441
|
-
Gy: 0x6666666666666666666666666666666666666666666666666666666666666658n,
|
|
442
|
-
};
|
|
443
|
-
const ed25519_Point = edwards(ed25519_CURVE);
|
|
444
|
-
```
|
|
445
|
-
|
|
446
|
-
Twisted Edwards curve's formula is `ax² + y² = 1 + dx²y²`.
|
|
447
|
-
You must specify `a`, `d`, field characteristic `p`, curve order `n` (sometimes named as `L`),
|
|
448
|
-
cofactor `h` and coordinates `Gx`, `Gy` of generator point.
|
|
449
|
-
|
|
450
|
-
#### Extended Edwards Point
|
|
428
|
+
The module allows to hash arbitrary strings to elliptic curve points. Implements [RFC 9380](https://www.rfc-editor.org/rfc/rfc9380).
|
|
451
429
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
const p = Point.BASE;
|
|
458
|
-
// arithmetics
|
|
459
|
-
p.add(p).equals(p.double());
|
|
460
|
-
p.subtract(p).equals(Point.ZERO);
|
|
461
|
-
p.negate();
|
|
462
|
-
p.multiply(31415n);
|
|
463
|
-
|
|
464
|
-
// decoding, encoding
|
|
465
|
-
const b = p.toBytes();
|
|
466
|
-
const p2 = Point.fromBytes(b);
|
|
467
|
-
// on-curve test
|
|
468
|
-
p.assertValidity();
|
|
469
|
-
// affine conversion
|
|
470
|
-
const { x, y } = p.toAffine();
|
|
471
|
-
const p3 = Point.fromAffine({ x, y });
|
|
472
|
-
// misc
|
|
473
|
-
const pcl = p.clearCofactor();
|
|
474
|
-
console.log(p.isTorsionFree(), p.isSmallOrder());
|
|
475
|
-
```
|
|
430
|
+
> [!NOTE]
|
|
431
|
+
> Why is `p256_hasher` separate from `p256`?
|
|
432
|
+
> The methods reside in separate _hasher namespace for tree-shaking:
|
|
433
|
+
> this way users who don't need hash-to-curve, won't have it in their builds.
|
|
476
434
|
|
|
477
|
-
|
|
435
|
+
### OPRFs
|
|
478
436
|
|
|
479
437
|
```js
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
// ed25519.verify();
|
|
438
|
+
import { p256_oprf, p384_oprf, p521_oprf } from '@noble/curves/nist.js';
|
|
439
|
+
import { ristretto255_oprf } from '@noble/curves/ed25519.js';
|
|
440
|
+
import { decaf448_orpf } from '@noble/curves/ed448.js';
|
|
484
441
|
```
|
|
485
442
|
|
|
486
|
-
We
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
For EdDSA signatures:
|
|
490
|
-
|
|
491
|
-
- `zip215: true` is default behavior. It has slightly looser verification logic
|
|
492
|
-
to be [consensus-friendly](https://hdevalence.ca/blog/2020-10-04-its-25519am), following [ZIP215](https://zips.z.cash/zip-0215) rules
|
|
493
|
-
- `zip215: false` switches verification criteria to strict
|
|
494
|
-
[RFC8032](https://www.rfc-editor.org/rfc/rfc8032) / [FIPS 186-5](https://csrc.nist.gov/publications/detail/fips/186/5/final)
|
|
495
|
-
and additionally provides [non-repudiation with SBS](https://eprint.iacr.org/2020/1244),
|
|
496
|
-
which is useful for:
|
|
497
|
-
- Contract Signing: if A signed an agreement with B using key that allows repudiation, it can later claim that it signed a different contract
|
|
498
|
-
- E-voting: malicious voters may pick keys that allow repudiation in order to deny results
|
|
499
|
-
- Blockchains: transaction of amount X might also be valid for a different amount Y
|
|
500
|
-
- Both modes have SUF-CMA (strong unforgeability under chosen message attacks).
|
|
501
|
-
|
|
502
|
-
### montgomery: Montgomery curve
|
|
503
|
-
|
|
504
|
-
The module contains methods for x-only ECDH on Curve25519 / Curve448 from RFC7748.
|
|
505
|
-
Proper Elliptic Curve Points are not implemented yet.
|
|
506
|
-
|
|
507
|
-
### bls: Barreto-Lynn-Scott curves
|
|
508
|
-
|
|
509
|
-
The module abstracts BLS (Barreto-Lynn-Scott) pairing-friendly elliptic curve construction.
|
|
510
|
-
They allow to construct [zk-SNARKs](https://z.cash/technology/zksnarks/) and
|
|
511
|
-
use aggregated, batch-verifiable
|
|
512
|
-
[threshold signatures](https://medium.com/snigirev.stepan/bls-signatures-better-than-schnorr-5a7fe30ea716),
|
|
513
|
-
using Boneh-Lynn-Shacham signature scheme.
|
|
514
|
-
|
|
515
|
-
The module doesn't expose `CURVE` property: use `G1.CURVE`, `G2.CURVE` instead.
|
|
516
|
-
Only BLS12-381 is currently implemented.
|
|
517
|
-
Defining BLS12-377 and BLS24 should be straightforward.
|
|
518
|
-
|
|
519
|
-
The default BLS uses short public keys (with public keys in G1 and signatures in G2).
|
|
520
|
-
Short signatures (public keys in G2 and signatures in G1) are also supported.
|
|
521
|
-
|
|
522
|
-
### hash-to-curve: Hashing strings to curve points
|
|
523
|
-
|
|
524
|
-
The module allows to hash arbitrary strings to elliptic curve points. Implements [RFC 9380](https://www.rfc-editor.org/rfc/rfc9380).
|
|
525
|
-
|
|
526
|
-
Every curve has exported `hashToCurve` and `encodeToCurve` methods. You should always prefer `hashToCurve` for security:
|
|
527
|
-
|
|
528
|
-
```ts
|
|
529
|
-
import { hashToCurve, encodeToCurve } from '@noble/curves/secp256k1.js';
|
|
530
|
-
import { randomBytes } from '@noble/hashes/utils.js';
|
|
531
|
-
hashToCurve('0102abcd');
|
|
532
|
-
console.log(hashToCurve(randomBytes()));
|
|
533
|
-
console.log(encodeToCurve(randomBytes()));
|
|
534
|
-
|
|
535
|
-
import { bls12_381 } from '@noble/curves/bls12-381.js';
|
|
536
|
-
bls12_381.G1.hashToCurve(randomBytes(), { DST: 'another' });
|
|
537
|
-
bls12_381.G2.hashToCurve(randomBytes(), { DST: 'custom' });
|
|
538
|
-
```
|
|
443
|
+
We provide OPRFs (oblivious pseudorandom functions),
|
|
444
|
+
conforming to [RFC 9497](https://www.rfc-editor.org/rfc/rfc9497).
|
|
539
445
|
|
|
540
|
-
|
|
446
|
+
OPRF allows to interactively create an `Output = PRF(Input, serverSecretKey)`:
|
|
541
447
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
DST: Uint8Array,
|
|
547
|
-
lenInBytes: number,
|
|
548
|
-
H: CHash // For CHash see abstract/weierstrass docs section
|
|
549
|
-
): Uint8Array;
|
|
550
|
-
// produces a uniformly random byte string using an extendable-output function (XOF) H.
|
|
551
|
-
function expand_message_xof(
|
|
552
|
-
msg: Uint8Array,
|
|
553
|
-
DST: Uint8Array,
|
|
554
|
-
lenInBytes: number,
|
|
555
|
-
k: number,
|
|
556
|
-
H: CHash
|
|
557
|
-
): Uint8Array;
|
|
558
|
-
// Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F
|
|
559
|
-
function hash_to_field(msg: Uint8Array, count: number, options: Opts): bigint[][];
|
|
560
|
-
|
|
561
|
-
/**
|
|
562
|
-
* * `DST` is a domain separation tag, defined in section 2.2.5
|
|
563
|
-
* * `p` characteristic of F, where F is a finite field of characteristic p and order q = p^m
|
|
564
|
-
* * `m` is extension degree (1 for prime fields)
|
|
565
|
-
* * `k` is the target security target in bits (e.g. 128), from section 5.1
|
|
566
|
-
* * `expand` is `xmd` (SHA2, SHA3, BLAKE) or `xof` (SHAKE, BLAKE-XOF)
|
|
567
|
-
* * `hash` conforming to `utils.CHash` interface, with `outputLen` / `blockLen` props
|
|
568
|
-
*/
|
|
569
|
-
type UnicodeOrBytes = string | Uint8Array;
|
|
570
|
-
type Opts = {
|
|
571
|
-
DST: UnicodeOrBytes;
|
|
572
|
-
p: bigint;
|
|
573
|
-
m: number;
|
|
574
|
-
k: number;
|
|
575
|
-
expand?: 'xmd' | 'xof';
|
|
576
|
-
hash: CHash;
|
|
577
|
-
};
|
|
578
|
-
```
|
|
448
|
+
- Server cannot calculate Output by itself: it doesn't know Input
|
|
449
|
+
- Client cannot calculate Output by itself: it doesn't know server secretKey
|
|
450
|
+
- An attacker interception the communication can't restore Input/Output/serverSecretKey and can't
|
|
451
|
+
link Input to some value.
|
|
579
452
|
|
|
580
453
|
### poseidon: Poseidon hash
|
|
581
454
|
|
|
@@ -611,13 +484,93 @@ const permutation = poseidon.poseidon(opts);
|
|
|
611
484
|
const sponge = poseidon.poseidonSponge(opts); // use carefully, not specced
|
|
612
485
|
```
|
|
613
486
|
|
|
614
|
-
###
|
|
487
|
+
### fft: Fast Fourier Transform
|
|
488
|
+
|
|
489
|
+
```ts
|
|
490
|
+
import * as fft from '@noble/curves/abstract/fft.js';
|
|
491
|
+
import { bls12_381 } from '@noble/curves/bls12-381.js';
|
|
492
|
+
const Fr = bls12_381.fields.Fr;
|
|
493
|
+
const roots = fft.rootsOfUnity(Fr, 7n);
|
|
494
|
+
const fftFr = fft.FFT(roots, Fr);
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
Experimental implementation of NTT / FFT (Fast Fourier Transform) over finite fields.
|
|
498
|
+
API may change at any time. The code has not been audited. Feature requests are welcome.
|
|
499
|
+
|
|
500
|
+
### utils: byte shuffling, conversion
|
|
615
501
|
|
|
616
502
|
```ts
|
|
617
|
-
import
|
|
503
|
+
import { bytesToHex, concatBytes, equalBytes, hexToBytes } from '@noble/curves/abstract/utils.js';
|
|
504
|
+
|
|
505
|
+
bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23]));
|
|
506
|
+
hexToBytes('cafe0123');
|
|
507
|
+
concatBytes(Uint8Array.from([0xca, 0xfe]), Uint8Array.from([0x01, 0x23]));
|
|
508
|
+
equalBytes(Uint8Array.of(0xca), Uint8Array.of(0xca));
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### Internals
|
|
512
|
+
|
|
513
|
+
#### Elliptic curve Point math
|
|
514
|
+
|
|
515
|
+
```js
|
|
516
|
+
import { secp256k1, schnorr } from '@noble/curves/secp256k1.js';
|
|
517
|
+
import { p256, p384, p521 } from '@noble/curves/nist.js';
|
|
518
|
+
import { ed25519, ristretto255 } from '@noble/curves/ed25519.js';
|
|
519
|
+
import { ed448, decaf448 } from '@noble/curves/ed448.js';
|
|
520
|
+
import { bls12_381 } from '@noble/curves/bls12-381.js'
|
|
521
|
+
import { bn254 } from '@noble/curves/bn254.js';
|
|
522
|
+
import { jubjub, babyjubjub } from '@noble/curves/misc.js';
|
|
523
|
+
|
|
524
|
+
const curves = [
|
|
525
|
+
secp256k1, schnorr, p256, p384, p521, ed25519, ed448,
|
|
526
|
+
ristretto255, decaf448,
|
|
527
|
+
bls12_381.G1, bls12_381.G2, bn254.G1, bn254.G2,
|
|
528
|
+
jubjub, babyjubjub
|
|
529
|
+
];
|
|
530
|
+
for (const curve of curves) {
|
|
531
|
+
const { Point } = curve;
|
|
532
|
+
const { BASE, ZERO, Fp, Fn } = Point;
|
|
533
|
+
const p = BASE.multiply(2n);
|
|
534
|
+
|
|
535
|
+
// Initialization
|
|
536
|
+
if (info.type === 'weierstrass') {
|
|
537
|
+
// projective (homogeneous) coordinates: (X, Y, Z) ∋ (x=X/Z, y=Y/Z)
|
|
538
|
+
const p_ = new Point(BASE.X, BASE.Y, BASE.Z);
|
|
539
|
+
} else if (info.type === 'edwards') {
|
|
540
|
+
// extended coordinates: (X, Y, Z, T) ∋ (x=X/Z, y=Y/Z)
|
|
541
|
+
const p_ = new Point(BASE.X, BASE.Y, BASE.Z, BASE.T);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// Math
|
|
545
|
+
const p1 = p.add(p);
|
|
546
|
+
const p2 = p.double();
|
|
547
|
+
const p3 = p.subtract(p);
|
|
548
|
+
const p4 = p.negate();
|
|
549
|
+
const p5 = p.multiply(451n);
|
|
550
|
+
|
|
551
|
+
// MSM (multi-scalar multiplication)
|
|
552
|
+
const pa = [BASE, BASE.multiply(2n), BASE.multiply(4n), BASE.multiply(8n)];
|
|
553
|
+
const p6 = Point.msm(pa, [3n, 5n, 7n, 11n]);
|
|
554
|
+
const _true3 = p6.equals(BASE.multiply(129n)); // 129*G
|
|
555
|
+
|
|
556
|
+
const pcl = p.clearCofactor();
|
|
557
|
+
console.log(p.isTorsionFree(), p.isSmallOrder());
|
|
558
|
+
|
|
559
|
+
const r1 = p.toBytes();
|
|
560
|
+
const r1_ = Point.fromBytes(r1);
|
|
561
|
+
const r2 = p.toAffine();
|
|
562
|
+
const { x, y } = r2;
|
|
563
|
+
const r2_ = Point.fromAffine(r2);
|
|
564
|
+
}
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
#### modular: Modular arithmetics & finite fields
|
|
568
|
+
|
|
569
|
+
```js
|
|
570
|
+
import { mod, invert, Field } from '@noble/curves/abstract/modular.js';
|
|
618
571
|
|
|
619
572
|
// Finite Field utils
|
|
620
|
-
const fp =
|
|
573
|
+
const fp = Field(2n ** 255n - 19n); // Finite field over 2^255-19
|
|
621
574
|
fp.mul(591n, 932n); // multiplication
|
|
622
575
|
fp.pow(481n, 11024858120n); // exponentiation
|
|
623
576
|
fp.div(5n, 17n); // division: 5/17 mod 2^255-19 == 5 * invert(17)
|
|
@@ -625,79 +578,73 @@ fp.inv(5n); // modular inverse
|
|
|
625
578
|
fp.sqrt(21n); // square root
|
|
626
579
|
|
|
627
580
|
// Non-Field generic utils are also available
|
|
628
|
-
mod
|
|
629
|
-
|
|
630
|
-
mod.invertBatch([1n, 2n, 4n], 21n); // => [1n, 11n, 16n] in one inversion
|
|
581
|
+
mod(21n, 10n); // 21 mod 10 == 1n; fixed version of 21 % 10
|
|
582
|
+
invert(17n, 10n); // invert(17) mod 10; modular multiplicative inverse
|
|
631
583
|
```
|
|
632
584
|
|
|
633
|
-
|
|
585
|
+
All arithmetics is done with JS bigints over finite fields,
|
|
586
|
+
which is defined from `modular` sub-module.
|
|
587
|
+
|
|
588
|
+
Field operations are not constant-time: see [security](#security).
|
|
634
589
|
The fact is mostly irrelevant, but the important method to keep in mind is `pow`,
|
|
635
590
|
which may leak exponent bits, when used naïvely.
|
|
636
591
|
|
|
637
|
-
|
|
638
|
-
We don't test for prime-ness for speed and because algorithms are probabilistic anyway.
|
|
639
|
-
Initializing a non-prime field could make your app suspectible to
|
|
640
|
-
DoS (infilite loop) on Tonelli-Shanks square root calculation.
|
|
641
|
-
|
|
642
|
-
Unlike `mod.inv`, `mod.invertBatch` won't throw on `0`: make sure to throw an error yourself.
|
|
643
|
-
|
|
644
|
-
### fft: Fast Fourier Transform
|
|
645
|
-
|
|
646
|
-
Experimental implementation of NTT / FFT (Fast Fourier Transform) over finite fields.
|
|
647
|
-
API may change at any time. The code has not been audited. Feature requests are welcome.
|
|
592
|
+
#### weierstrass: Custom Weierstrass curve
|
|
648
593
|
|
|
649
|
-
```
|
|
650
|
-
import
|
|
594
|
+
```js
|
|
595
|
+
import { weierstrass } from '@noble/curves/abstract/weierstrass.js';
|
|
596
|
+
// NIST secp192r1 aka p192. https://www.secg.org/sec2-v2.pdf
|
|
597
|
+
const p192_CURVE = {
|
|
598
|
+
p: 0xfffffffffffffffffffffffffffffffeffffffffffffffffn,
|
|
599
|
+
n: 0xffffffffffffffffffffffff99def836146bc9b1b4d22831n,
|
|
600
|
+
h: 1n,
|
|
601
|
+
a: 0xfffffffffffffffffffffffffffffffefffffffffffffffcn,
|
|
602
|
+
b: 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1n,
|
|
603
|
+
Gx: 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012n,
|
|
604
|
+
Gy: 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811n,
|
|
605
|
+
};
|
|
606
|
+
const p192_Point = weierstrass(p192_CURVE);
|
|
651
607
|
```
|
|
652
608
|
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
Doing so will make the key [biased](https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/).
|
|
657
|
-
|
|
658
|
-
To make the bias negligible, we follow [FIPS 186-5 A.2](https://csrc.nist.gov/publications/detail/fips/186/5/final)
|
|
659
|
-
and [RFC 9380](https://www.rfc-editor.org/rfc/rfc9380#section-5.2).
|
|
660
|
-
This means, for 32-byte key, we would need 48-byte hash to get 2^-128 bias, which matches curve security level.
|
|
609
|
+
Short Weierstrass curve's formula is `y² = x³ + ax + b`. `weierstrass`
|
|
610
|
+
expects arguments `a`, `b`, field characteristic `p`, curve order `n`,
|
|
611
|
+
cofactor `h` and coordinates `Gx`, `Gy` of generator point.
|
|
661
612
|
|
|
662
|
-
|
|
663
|
-
Use [abstract/hash-to-curve](#hash-to-curve-hashing-strings-to-curve-points)
|
|
664
|
-
if you need to hash to **public key**.
|
|
613
|
+
#### edwards: Custom Edwards curve
|
|
665
614
|
|
|
666
|
-
```
|
|
667
|
-
import {
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
615
|
+
```js
|
|
616
|
+
import { edwards } from '@noble/curves/abstract/edwards.js';
|
|
617
|
+
const ed25519_CURVE = {
|
|
618
|
+
p: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffedn,
|
|
619
|
+
n: 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3edn,
|
|
620
|
+
h: 8n,
|
|
621
|
+
a: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffecn,
|
|
622
|
+
d: 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3n,
|
|
623
|
+
Gx: 0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51an,
|
|
624
|
+
Gy: 0x6666666666666666666666666666666666666666666666666666666666666658n,
|
|
625
|
+
};
|
|
626
|
+
const ed25519_Point = edwards(ed25519_CURVE);
|
|
674
627
|
```
|
|
675
628
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
import * as utils from '@noble/curves/abstract/utils.js';
|
|
680
|
-
|
|
681
|
-
utils.bytesToHex(Uint8Array.from([0xde, 0xad, 0xbe, 0xef]));
|
|
682
|
-
utils.hexToBytes('deadbeef');
|
|
683
|
-
utils.numberToHexUnpadded(123n);
|
|
684
|
-
utils.hexToNumber();
|
|
629
|
+
Twisted Edwards curve's formula is `ax² + y² = 1 + dx²y²`.
|
|
630
|
+
You must specify `a`, `d`, field characteristic `p`, curve order `n` (sometimes named as `L`),
|
|
631
|
+
cofactor `h` and coordinates `Gx`, `Gy` of generator point.
|
|
685
632
|
|
|
686
|
-
|
|
687
|
-
utils.bytesToNumberLE(Uint8Array.from([0xde, 0xad, 0xbe, 0xef]));
|
|
688
|
-
utils.numberToBytesBE(123n, 32);
|
|
689
|
-
utils.numberToBytesLE(123n, 64);
|
|
633
|
+
#### Custom ECDSA instance
|
|
690
634
|
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
635
|
+
```js
|
|
636
|
+
import { ecdsa } from '@noble/curves/abstract/weierstrass.js';
|
|
637
|
+
import { sha256 } from '@noble/hashes/sha2.js';
|
|
638
|
+
const p192_sha256 = ecdsa(p192_Point, sha256);
|
|
639
|
+
// or
|
|
640
|
+
const p192_sha224 = ecdsa(p192.Point, sha224);
|
|
641
|
+
|
|
642
|
+
const keys = p192_sha256.keygen();
|
|
643
|
+
const msg = new TextEncoder().encode('custom curve');
|
|
644
|
+
const sig = p192_sha256.sign(msg, keys.secretKey);
|
|
645
|
+
const isValid = p192_sha256.verify(sig, msg, keys.publicKey);
|
|
694
646
|
```
|
|
695
647
|
|
|
696
|
-
### Unreleased bits
|
|
697
|
-
|
|
698
|
-
- `test/unreleased-xeddsa.ts` contains implementation of XEd25519, defined by Signal
|
|
699
|
-
- `test/misc/endomorphism.js` contains tool for generation of endomorphism params for Koblitz curves
|
|
700
|
-
|
|
701
648
|
## Security
|
|
702
649
|
|
|
703
650
|
The library has been independently audited:
|
|
@@ -723,7 +670,7 @@ The library has been independently audited:
|
|
|
723
670
|
- The audit has been funded by [Ryan Shea](https://www.shea.io)
|
|
724
671
|
|
|
725
672
|
It is tested against property-based, cross-library and Wycheproof vectors,
|
|
726
|
-
and is being fuzzed in [the separate repo](https://github.com/paulmillr/
|
|
673
|
+
and is being fuzzed in [the separate repo](https://github.com/paulmillr/integration-tests).
|
|
727
674
|
|
|
728
675
|
If you see anything unusual: investigate and report.
|
|
729
676
|
|
|
@@ -820,8 +767,8 @@ init 10ms
|
|
|
820
767
|
getPublicKey x 9,099 ops/sec @ 109μs/op
|
|
821
768
|
sign x 7,182 ops/sec @ 139μs/op
|
|
822
769
|
verify x 1,188 ops/sec @ 841μs/op
|
|
823
|
-
getSharedSecret x 735 ops/sec @ 1ms/op
|
|
824
770
|
recoverPublicKey x 1,265 ops/sec @ 790μs/op
|
|
771
|
+
getSharedSecret x 735 ops/sec @ 1ms/op
|
|
825
772
|
schnorr.sign x 957 ops/sec @ 1ms/op
|
|
826
773
|
schnorr.verify x 1,210 ops/sec @ 825μs/op
|
|
827
774
|
|
|
@@ -917,14 +864,82 @@ aggregateSignatures/2048 x 0 ops/sec @ 2823ms/op
|
|
|
917
864
|
|
|
918
865
|
Supported node.js versions:
|
|
919
866
|
|
|
920
|
-
- v2: v20.19+ (ESM-only)
|
|
921
|
-
- v1: v14.21+ (ESM & CJS)
|
|
922
|
-
|
|
923
|
-
### curves v1
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
867
|
+
- v2 (2025-08): v20.19+ (ESM-only)
|
|
868
|
+
- v1 (2023-04): v14.21+ (ESM & CJS)
|
|
869
|
+
|
|
870
|
+
### curves v1 to curves v2
|
|
871
|
+
|
|
872
|
+
v2 massively simplifies internals, improves security, reduces bundle size and lays path for the future.
|
|
873
|
+
We tried to keep v2 as much backwards-compatible as possible.
|
|
874
|
+
|
|
875
|
+
Submodule paths now require `.js` extension e.g. `curves/ed25519` => `curves/ed25519.js`.
|
|
876
|
+
This allows usage in bundler-less environments without source maps
|
|
877
|
+
|
|
878
|
+
**Logic** changes:
|
|
879
|
+
|
|
880
|
+
- Most methods now expect Uint8Array, string hex inputs are prohibited
|
|
881
|
+
- The change simplifies reasoning, improves security and reduces malleability
|
|
882
|
+
- `Point.fromHex` now expects string-only hex inputs, use `Point.fromBytes` for Uint8Array
|
|
883
|
+
- Breaking changes of ECDSA (secp256k1, p256, p384...):
|
|
884
|
+
- sign, verify: Switch to **prehashed messages**. Instead of
|
|
885
|
+
messageHash, the methods now expect unhashed message.
|
|
886
|
+
To bring back old behavior, use option `{prehash: false}`
|
|
887
|
+
- sign, verify: Switch to **lowS signatures** by default.
|
|
888
|
+
This change doesn't affect secp256k1, which has been using lowS since beginning.
|
|
889
|
+
To bring back old behavior, use option `{lowS: true}`
|
|
890
|
+
- sign, verify: Switch to **Uint8Array signatures** (format: 'compact') by default.
|
|
891
|
+
- verify: **der format must be explicitly specified** in `{format: 'der'}`.
|
|
892
|
+
This reduces malleability
|
|
893
|
+
- verify: **prohibit Signature-instance** signature. User must now always do
|
|
894
|
+
`signature.toBytes()`
|
|
895
|
+
- Breaking changes of BLS signatures (bls12-381, bn254):
|
|
896
|
+
- Move getPublicKey, sign, verify, signShortSignature etc into two new namespaces:
|
|
897
|
+
bls.longSignatures (G1 pubkeys, G2 sigs) and bls.shortSignatures (G1 sigs, G2 pubkeys).
|
|
898
|
+
- verifyBatch now expects array of inputs `{message: ..., publicKey: ...}[]`
|
|
899
|
+
- Curve changes:
|
|
900
|
+
- Massively simplify curve creation, split it into point creation & sig generator creation
|
|
901
|
+
- New methods are `weierstrass() + ecdsa()` / `edwards() + eddsa()`
|
|
902
|
+
- weierstrass / edwards expect simplified curve params (Fp became p)
|
|
903
|
+
- ecdsa / eddsa expect Point class and hash
|
|
904
|
+
- Remove unnecessary Fn argument in `pippenger`
|
|
905
|
+
- modular changes:
|
|
906
|
+
- Field#fromBytes() now validates elements to be in 0..order-1 range
|
|
907
|
+
- Massive type renamings and improvements
|
|
908
|
+
|
|
909
|
+
**Renamings:**
|
|
910
|
+
|
|
911
|
+
- Module changes
|
|
912
|
+
- `p256`, `p384`, `p521` modules have been moved into `nist`
|
|
913
|
+
- `jubjub` module has been moved into `misc`
|
|
914
|
+
- Point changes
|
|
915
|
+
- ExtendedPoint, ProjectivePoint => Point
|
|
916
|
+
- Point coordinates (projective / extended) from px/ex, py/ey, pz/ez, et => X, Y, Z, T
|
|
917
|
+
- Point.normalizeZ, Point.msm => separate methods in `abstract/curve.js` submodule
|
|
918
|
+
- Point.fromPrivateKey() got removed, use `Point.BASE.multiply()` and `Point.Fn.fromBytes(secretKey)`
|
|
919
|
+
- toRawBytes, fromRawBytes => toBytes, fromBytes
|
|
920
|
+
- RistrettoPoint => ristretto255.Point, DecafPoiont => decaf448.Point
|
|
921
|
+
- Signature (ECDSA) changes
|
|
922
|
+
- toCompactRawBytes, toDERRawBytes => toBytes('compact'), toBytes('der')
|
|
923
|
+
- toCompactHex, toDERHex => toHex('compact'), toHex('der')
|
|
924
|
+
- fromCompact, fromDER => fromBytes(format), fromHex(format)
|
|
925
|
+
- utils changes
|
|
926
|
+
- randomPrivateKey => randomSecretKey
|
|
927
|
+
- utils.precompute, Point#_setWindowSize => Point#precompute
|
|
928
|
+
- edwardsToMontgomery => utils.toMontgomery
|
|
929
|
+
- edwardsToMontgomeryPriv => utils.toMontgomerySecret
|
|
930
|
+
- Rename all curve-specific hash-to-curve methods to `*curve*_hasher`.
|
|
931
|
+
Example: `secp256k1.hashToCurve` => `secp256k1_hasher.hashToCurve()`
|
|
932
|
+
|
|
933
|
+
**Removed features:**
|
|
934
|
+
|
|
935
|
+
- Point#multiplyAndAddUnsafe, Point#hasEvenY
|
|
936
|
+
- CURVE property with all kinds of random stuff. Point.CURVE() now replaces it, but only provides
|
|
937
|
+
curve parameters
|
|
938
|
+
- Remove `pasta`, `bn254_weierstrass` (NOT pairing-based bn254) curves
|
|
939
|
+
- Field.MASK
|
|
940
|
+
- utils.normPrivateKeyToScalar
|
|
941
|
+
|
|
942
|
+
### noble-secp256k1 v1 to curves v1
|
|
928
943
|
|
|
929
944
|
Previously, the library was split into single-feature packages
|
|
930
945
|
[noble-secp256k1](https://github.com/paulmillr/noble-secp256k1),
|
|
@@ -932,7 +947,7 @@ Previously, the library was split into single-feature packages
|
|
|
932
947
|
[noble-bls12-381](https://github.com/paulmillr/noble-bls12-381).
|
|
933
948
|
|
|
934
949
|
Curves continue their original work. The single-feature packages changed their
|
|
935
|
-
direction towards providing minimal
|
|
950
|
+
direction towards providing minimal 5kb implementations of cryptography,
|
|
936
951
|
which means they have less features.
|
|
937
952
|
|
|
938
953
|
- `getPublicKey`
|
|
@@ -961,9 +976,9 @@ which means they have less features.
|
|
|
961
976
|
- `utils` were split into `utils` (same api as in noble-curves) and
|
|
962
977
|
`etc` (`hmacSha256Sync` and others)
|
|
963
978
|
|
|
964
|
-
### noble-ed25519 v1
|
|
979
|
+
### noble-ed25519 v1 to curves v1
|
|
965
980
|
|
|
966
|
-
Upgrading from [@noble/ed25519](https://github.com/paulmillr/noble-ed25519)
|
|
981
|
+
Upgrading from [@noble/ed25519](https://github.com/paulmillr/noble-ed25519):
|
|
967
982
|
|
|
968
983
|
- Methods are now sync by default
|
|
969
984
|
- `bigint` is no longer allowed in `getPublicKey`, `sign`, `verify`. Reason: ed25519 is LE, can lead to bugs
|
|
@@ -974,7 +989,7 @@ Upgrading from [@noble/ed25519](https://github.com/paulmillr/noble-ed25519) 1.7:
|
|
|
974
989
|
- `getSharedSecret` was moved to `x25519` module
|
|
975
990
|
- `toX25519` has been moved to `edwardsToMontgomeryPub` and `edwardsToMontgomeryPriv` methods
|
|
976
991
|
|
|
977
|
-
### noble-bls12-381
|
|
992
|
+
### noble-bls12-381 to curves v1
|
|
978
993
|
|
|
979
994
|
Upgrading from [@noble/bls12-381](https://github.com/paulmillr/noble-bls12-381):
|
|
980
995
|
|