@noble/curves 0.6.4 → 0.7.1
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 +486 -298
- package/{lib/_shortw_utils.d.ts → _shortw_utils.d.ts} +1 -1
- package/_shortw_utils.d.ts.map +1 -0
- package/{lib/_shortw_utils.js → _shortw_utils.js} +2 -0
- package/_shortw_utils.js.map +1 -0
- package/{lib/abstract → abstract}/bls.d.ts +4 -9
- package/abstract/bls.d.ts.map +1 -0
- package/{lib/abstract → abstract}/bls.js +13 -26
- package/abstract/bls.js.map +1 -0
- package/{lib/abstract → abstract}/curve.d.ts +1 -0
- package/abstract/curve.d.ts.map +1 -0
- package/{lib/abstract → abstract}/curve.js +1 -0
- package/abstract/curve.js.map +1 -0
- package/{lib/abstract → abstract}/edwards.d.ts +1 -0
- package/abstract/edwards.d.ts.map +1 -0
- package/{lib/abstract → abstract}/edwards.js +9 -15
- package/abstract/edwards.js.map +1 -0
- package/{lib/abstract → abstract}/hash-to-curve.d.ts +5 -5
- package/abstract/hash-to-curve.d.ts.map +1 -0
- package/{lib/abstract → abstract}/hash-to-curve.js +41 -38
- package/abstract/hash-to-curve.js.map +1 -0
- package/{lib/abstract → abstract}/modular.d.ts +1 -0
- package/abstract/modular.d.ts.map +1 -0
- package/{lib/abstract → abstract}/modular.js +2 -1
- package/abstract/modular.js.map +1 -0
- package/{lib/abstract → abstract}/montgomery.d.ts +4 -3
- package/abstract/montgomery.d.ts.map +1 -0
- package/{lib/abstract → abstract}/montgomery.js +12 -9
- package/abstract/montgomery.js.map +1 -0
- package/{lib/abstract → abstract}/poseidon.d.ts +1 -0
- package/abstract/poseidon.d.ts.map +1 -0
- package/{lib/abstract → abstract}/poseidon.js +1 -0
- package/abstract/poseidon.js.map +1 -0
- package/{lib/abstract → abstract}/utils.d.ts +12 -1
- package/abstract/utils.d.ts.map +1 -0
- package/{lib/abstract → abstract}/utils.js +96 -10
- package/abstract/utils.js.map +1 -0
- package/{lib/abstract → abstract}/weierstrass.d.ts +6 -6
- package/abstract/weierstrass.d.ts.map +1 -0
- package/{lib/abstract → abstract}/weierstrass.js +74 -115
- package/abstract/weierstrass.js.map +1 -0
- package/{lib/bls12-381.d.ts → bls12-381.d.ts} +1 -0
- package/bls12-381.d.ts.map +1 -0
- package/{lib/bls12-381.js → bls12-381.js} +41 -7
- package/bls12-381.js.map +1 -0
- package/{lib/bn.d.ts → bn.d.ts} +1 -0
- package/bn.d.ts.map +1 -0
- package/{lib/bn.js → bn.js} +1 -0
- package/bn.js.map +1 -0
- package/{lib/ed25519.d.ts → ed25519.d.ts} +2 -1
- package/ed25519.d.ts.map +1 -0
- package/{lib/ed25519.js → ed25519.js} +6 -5
- package/ed25519.js.map +1 -0
- package/{lib/ed448.d.ts → ed448.d.ts} +2 -1
- package/ed448.d.ts.map +1 -0
- package/{lib/ed448.js → ed448.js} +4 -3
- package/ed448.js.map +1 -0
- package/{lib/esm → esm}/_shortw_utils.js +2 -0
- package/esm/_shortw_utils.js.map +1 -0
- package/{lib/esm → esm}/abstract/bls.js +14 -27
- package/esm/abstract/bls.js.map +1 -0
- package/{lib/esm → esm}/abstract/curve.js +1 -0
- package/esm/abstract/curve.js.map +1 -0
- package/{lib/esm → esm}/abstract/edwards.js +9 -15
- package/esm/abstract/edwards.js.map +1 -0
- package/{lib/esm → esm}/abstract/hash-to-curve.js +40 -36
- package/esm/abstract/hash-to-curve.js.map +1 -0
- package/{lib/esm → esm}/abstract/modular.js +2 -1
- package/esm/abstract/modular.js.map +1 -0
- package/{lib/esm → esm}/abstract/montgomery.js +12 -9
- package/esm/abstract/montgomery.js.map +1 -0
- package/{lib/esm → esm}/abstract/poseidon.js +1 -0
- package/esm/abstract/poseidon.js.map +1 -0
- package/{lib/esm → esm}/abstract/utils.js +93 -9
- package/esm/abstract/utils.js.map +1 -0
- package/{lib/esm → esm}/abstract/weierstrass.js +74 -115
- package/esm/abstract/weierstrass.js.map +1 -0
- package/{lib/esm → esm}/bls12-381.js +41 -7
- package/esm/bls12-381.js.map +1 -0
- package/{lib/esm → esm}/bn.js +1 -0
- package/esm/bn.js.map +1 -0
- package/{lib/esm → esm}/ed25519.js +7 -6
- package/esm/ed25519.js.map +1 -0
- package/{lib/esm → esm}/ed448.js +4 -3
- package/esm/ed448.js.map +1 -0
- package/{lib → esm}/index.js +1 -0
- package/esm/index.js.map +1 -0
- package/{lib/esm → esm}/jubjub.js +1 -0
- package/esm/jubjub.js.map +1 -0
- package/{lib/esm → esm}/p192.js +1 -0
- package/esm/p192.js.map +1 -0
- package/{lib/esm → esm}/p224.js +1 -0
- package/esm/p224.js.map +1 -0
- package/{lib/esm → esm}/p256.js +2 -1
- package/esm/p256.js.map +1 -0
- package/{lib/esm → esm}/p384.js +2 -1
- package/esm/p384.js.map +1 -0
- package/{lib/esm → esm}/p521.js +2 -1
- package/esm/p521.js.map +1 -0
- package/{lib/esm → esm}/package.json +0 -0
- package/{lib/esm → esm}/pasta.js +1 -0
- package/esm/pasta.js.map +1 -0
- package/{lib/esm → esm}/secp256k1.js +51 -50
- package/esm/secp256k1.js.map +1 -0
- package/{lib/esm → esm}/stark.js +5 -4
- package/esm/stark.js.map +1 -0
- package/index.d.ts +1 -0
- package/index.d.ts.map +1 -0
- package/index.js +3 -0
- package/index.js.map +1 -0
- package/{lib/jubjub.d.ts → jubjub.d.ts} +1 -0
- package/jubjub.d.ts.map +1 -0
- package/{lib/jubjub.js → jubjub.js} +1 -0
- package/jubjub.js.map +1 -0
- package/{lib/p192.d.ts → p192.d.ts} +1 -2
- package/p192.d.ts.map +1 -0
- package/{lib/p192.js → p192.js} +1 -0
- package/p192.js.map +1 -0
- package/{lib/p224.d.ts → p224.d.ts} +1 -2
- package/p224.d.ts.map +1 -0
- package/{lib/p224.js → p224.js} +1 -0
- package/p224.js.map +1 -0
- package/{lib/p256.d.ts → p256.d.ts} +2 -3
- package/p256.d.ts.map +1 -0
- package/{lib/p256.js → p256.js} +2 -1
- package/p256.js.map +1 -0
- package/{lib/p384.d.ts → p384.d.ts} +2 -3
- package/p384.d.ts.map +1 -0
- package/{lib/p384.js → p384.js} +2 -1
- package/p384.js.map +1 -0
- package/{lib/p521.d.ts → p521.d.ts} +2 -3
- package/p521.d.ts.map +1 -0
- package/{lib/p521.js → p521.js} +2 -1
- package/p521.js.map +1 -0
- package/package.json +84 -79
- package/{lib/pasta.d.ts → pasta.d.ts} +1 -0
- package/pasta.d.ts.map +1 -0
- package/{lib/pasta.js → pasta.js} +1 -0
- package/pasta.js.map +1 -0
- package/{lib/secp256k1.d.ts → secp256k1.d.ts} +20 -6
- package/secp256k1.d.ts.map +1 -0
- package/{lib/secp256k1.js → secp256k1.js} +48 -47
- package/secp256k1.js.map +1 -0
- package/src/_shortw_utils.ts +20 -0
- package/src/abstract/bls.ts +376 -0
- package/src/abstract/curve.ts +199 -0
- package/src/abstract/edwards.ts +479 -0
- package/src/abstract/hash-to-curve.ts +220 -0
- package/src/abstract/modular.ts +417 -0
- package/src/abstract/montgomery.ts +186 -0
- package/src/abstract/poseidon.ts +119 -0
- package/src/abstract/utils.ts +246 -0
- package/src/abstract/weierstrass.ts +1177 -0
- package/src/bls12-381.ts +1274 -0
- package/src/bn.ts +21 -0
- package/src/ed25519.ts +428 -0
- package/src/ed448.ts +241 -0
- package/{lib/esm/index.js → src/index.ts} +0 -1
- package/src/jubjub.ts +58 -0
- package/src/p192.ts +25 -0
- package/src/p224.ts +25 -0
- package/src/p256.ts +53 -0
- package/src/p384.ts +57 -0
- package/src/p521.ts +57 -0
- package/src/pasta.ts +31 -0
- package/src/secp256k1.ts +270 -0
- package/src/stark.ts +356 -0
- package/{lib/stark.d.ts → stark.d.ts} +1 -1
- package/stark.d.ts.map +1 -0
- package/{lib/stark.js → stark.js} +5 -4
- package/stark.js.map +1 -0
- package/lib/index.d.ts +0 -0
package/README.md
CHANGED
|
@@ -1,36 +1,35 @@
|
|
|
1
1
|
# noble-curves
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Audited & minimal JS implementation of elliptic curve cryptography.
|
|
4
4
|
|
|
5
|
+
- **noble** family, zero dependencies
|
|
5
6
|
- Short Weierstrass, Edwards, Montgomery curves
|
|
6
7
|
- ECDSA, EdDSA, Schnorr, BLS signature schemes, ECDH key agreement
|
|
7
|
-
- [hash to curve](https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/)
|
|
8
|
-
for encoding or hashing an arbitrary string to
|
|
9
|
-
- [Poseidon](https://www.poseidon-hash.info) ZK-friendly hash
|
|
8
|
+
- #️⃣ [hash to curve](https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/)
|
|
9
|
+
for encoding or hashing an arbitrary string to an elliptic curve point
|
|
10
|
+
- 🧜♂️ [Poseidon](https://www.poseidon-hash.info) ZK-friendly hash
|
|
10
11
|
- 🏎 [Ultra-fast](#speed), hand-optimized for caveats of JS engines
|
|
11
12
|
- 🔍 Unique tests ensure correctness. Wycheproof vectors included
|
|
12
13
|
- 🔻 Tree-shaking-friendly: there is no entry point, which ensures small size of your app
|
|
13
14
|
|
|
14
15
|
Package consists of two parts:
|
|
15
16
|
|
|
16
|
-
1.
|
|
17
|
-
2.
|
|
17
|
+
1. [Abstract](#abstract-api), zero-dependency EC algorithms
|
|
18
|
+
2. [Implementations](#implementations), utilizing one dependency `@noble/hashes`, providing ready-to-use:
|
|
18
19
|
- NIST curves secp192r1/P192, secp224r1/P224, secp256r1/P256, secp384r1/P384, secp521r1/P521
|
|
19
20
|
- SECG curve secp256k1
|
|
21
|
+
- ed25519/curve25519/x25519/ristretto255, edwards448/curve448/x448 RFC7748 / RFC8032 / ZIP215 stuff
|
|
20
22
|
- pairing-friendly curves bls12-381, bn254
|
|
21
|
-
- ed25519/curve25519/x25519/ristretto, edwards448/curve448/x448 RFC7748 / RFC8032 / ZIP215 stuff
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
([secp256k1](https://github.com/paulmillr/noble-secp256k1),
|
|
25
|
-
[
|
|
26
|
-
which had security audits and were developed from 2019 to 2022.
|
|
27
|
-
Check out [Upgrading](#upgrading) section if you've used them before.
|
|
24
|
+
Check out [Upgrading](#upgrading) if you've previously used single-feature noble packages
|
|
25
|
+
([secp256k1](https://github.com/paulmillr/noble-secp256k1), [ed25519](https://github.com/paulmillr/noble-ed25519)).
|
|
26
|
+
See [Resources](#resouces) for articles and real-world software that uses curves.
|
|
28
27
|
|
|
29
28
|
### This library belongs to _noble_ crypto
|
|
30
29
|
|
|
31
30
|
> **noble-crypto** — high-security, easily auditable set of contained cryptographic libraries and tools.
|
|
32
31
|
|
|
33
|
-
-
|
|
32
|
+
- No dependencies, protection against supply chain attacks
|
|
34
33
|
- Easily auditable TypeScript/JS code
|
|
35
34
|
- Supported in all major browsers and stable node.js versions
|
|
36
35
|
- All releases are signed with PGP keys
|
|
@@ -42,15 +41,41 @@ Check out [Upgrading](#upgrading) section if you've used them before.
|
|
|
42
41
|
|
|
43
42
|
## Usage
|
|
44
43
|
|
|
45
|
-
Use NPM
|
|
46
|
-
[GitHub's releases page](https://github.com/paulmillr/noble-curves/releases):
|
|
44
|
+
Use NPM for browser / node.js:
|
|
47
45
|
|
|
48
46
|
> npm install @noble/curves
|
|
49
47
|
|
|
50
|
-
|
|
48
|
+
For [Deno](https://deno.land), use it with [npm specifier](https://deno.land/manual@v1.28.0/node/npm_specifiers). In browser, you could also include the single file from
|
|
49
|
+
[GitHub's releases page](https://github.com/paulmillr/noble-curves/releases).
|
|
50
|
+
|
|
51
|
+
The library is tree-shaking-friendly and does not expose root entry point as `import * from '@noble/curves'`.
|
|
52
|
+
Instead, you need to import specific primitives. This is done to ensure small size of your apps.
|
|
53
|
+
|
|
54
|
+
### Implementations
|
|
55
|
+
|
|
56
|
+
Each curve can be used in the following way:
|
|
51
57
|
|
|
52
58
|
```ts
|
|
53
|
-
import { secp256k1 } from '@noble/curves/secp256k1';
|
|
59
|
+
import { secp256k1 } from '@noble/curves/secp256k1'; // ECMAScript Modules (ESM) and Common.js
|
|
60
|
+
// import { secp256k1 } from 'npm:@noble/curves@1.2.0/secp256k1'; // Deno
|
|
61
|
+
const priv = secp256k1.utils.randomPrivateKey();
|
|
62
|
+
const pub = secp256k1.getPublicKey(priv);
|
|
63
|
+
const msg = new Uint8Array(32).fill(1);
|
|
64
|
+
const sig = secp256k1.sign(msg, priv);
|
|
65
|
+
secp256k1.verify(sig, msg, pub) === true;
|
|
66
|
+
|
|
67
|
+
const privHex = '46c930bc7bb4db7f55da20798697421b98c4175a52c630294d75a84b9c126236';
|
|
68
|
+
const pub2 = secp256k1.getPublicKey(privHex); // keys & other inputs can be Uint8Array-s or hex strings
|
|
69
|
+
|
|
70
|
+
// Follows hash-to-curve specification to encode arbitrary hashes to EC points
|
|
71
|
+
import { hashToCurve, encodeToCurve } from '@noble/curves/secp256k1';
|
|
72
|
+
hashToCurve('0102abcd');
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
All curves:
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { secp256k1, schnorr } from '@noble/curves/secp256k1';
|
|
54
79
|
import { ed25519, ed25519ph, ed25519ctx, x25519, RistrettoPoint } from '@noble/curves/ed25519';
|
|
55
80
|
import { ed448, ed448ph, ed448ctx, x448 } from '@noble/curves/ed448';
|
|
56
81
|
import { p256 } from '@noble/curves/p256';
|
|
@@ -63,63 +88,214 @@ import { bn254 } from '@noble/curves/bn';
|
|
|
63
88
|
import { jubjub } from '@noble/curves/jubjub';
|
|
64
89
|
```
|
|
65
90
|
|
|
66
|
-
|
|
91
|
+
Weierstrass curves feature recovering public keys from signatures and ECDH key agreement:
|
|
67
92
|
|
|
68
93
|
```ts
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const pub = secp256k1.getPublicKey(key);
|
|
73
|
-
const msg = new Uint8Array(32).fill(1);
|
|
74
|
-
const sig = secp256k1.sign(msg, key);
|
|
75
|
-
// weierstrass curves should use extraEntropy: https://moderncrypto.org/mail-archive/curves/2017/000925.html
|
|
76
|
-
const sigImprovedSecurity = secp256k1.sign(msg, key, { extraEntropy: true });
|
|
77
|
-
secp256k1.verify(sig, msg, pub) === true;
|
|
78
|
-
// secp, p*, pasta curves allow pub recovery
|
|
79
|
-
sig.recoverPublicKey(msg) === pub;
|
|
94
|
+
// extraEntropy https://moderncrypto.org/mail-archive/curves/2017/000925.html
|
|
95
|
+
const sigImprovedSecurity = secp256k1.sign(msg, priv, { extraEntropy: true });
|
|
96
|
+
sig.recoverPublicKey(msg) === pub; // public key recovery
|
|
80
97
|
const someonesPub = secp256k1.getPublicKey(secp256k1.utils.randomPrivateKey());
|
|
81
|
-
const shared = secp256k1.getSharedSecret(
|
|
98
|
+
const shared = secp256k1.getSharedSecret(priv, someonesPub); // ECDH (elliptic curve diffie-hellman)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
secp256k1 has schnorr signature implementation which follows
|
|
102
|
+
[BIP340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki):
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
import { schnorr } from '@noble/curves/secp256k1';
|
|
106
|
+
const priv = schnorr.utils.randomPrivateKey();
|
|
107
|
+
const pub = schnorr.getPublicKey(priv);
|
|
108
|
+
const msg = new TextEncoder().encode('hello');
|
|
109
|
+
const sig = schnorr.sign(msg, priv);
|
|
110
|
+
const isValid = schnorr.verify(sig, msg, pub);
|
|
111
|
+
console.log(isValid);
|
|
82
112
|
```
|
|
83
113
|
|
|
84
|
-
|
|
114
|
+
ed25519 module has ed25519ctx / ed25519ph variants,
|
|
115
|
+
x25519 ECDH and [ristretto255](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448).
|
|
116
|
+
It follows [ZIP215](https://zips.z.cash/zip-0215) and [can be used in consensus-critical applications](https://hdevalence.ca/blog/2020-10-04-its-25519am):
|
|
85
117
|
|
|
86
|
-
|
|
118
|
+
```ts
|
|
119
|
+
import { ed25519 } from '@noble/curves/ed25519';
|
|
120
|
+
|
|
121
|
+
// Variants from RFC8032: with context, prehashed
|
|
122
|
+
import { ed25519ctx, ed25519ph } from '@noble/curves/ed25519';
|
|
123
|
+
|
|
124
|
+
// ECDH using curve25519 aka x25519
|
|
125
|
+
import { x25519 } from '@noble/curves/ed25519';
|
|
126
|
+
const priv = 'a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4';
|
|
127
|
+
const pub = 'e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c';
|
|
128
|
+
x25519.getSharedSecret(priv, pub) === x25519.scalarMult(priv, pub); // aliases
|
|
129
|
+
x25519.getPublicKey(priv) === x25519.scalarMultBase(priv);
|
|
130
|
+
|
|
131
|
+
// hash-to-curve
|
|
132
|
+
import { hashToCurve, encodeToCurve } from '@noble/curves/ed25519';
|
|
133
|
+
|
|
134
|
+
import { RistrettoPoint } from '@noble/curves/ed25519';
|
|
135
|
+
const rp = RistrettoPoint.fromHex(
|
|
136
|
+
'6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919'
|
|
137
|
+
);
|
|
138
|
+
RistrettoPoint.hashToCurve('Ristretto is traditionally a short shot of espresso coffee');
|
|
139
|
+
// also has add(), equals(), multiply(), toRawBytes() methods
|
|
140
|
+
```
|
|
87
141
|
|
|
88
|
-
|
|
142
|
+
ed448 module is basically the same:
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
import { ed448, ed448ph, ed448ctx, x448 } from '@noble/curves/ed448';
|
|
146
|
+
import { hashToCurve, encodeToCurve } from '@noble/curves/ed448';
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
BLS12-381 pairing-friendly Barreto-Lynn-Scott elliptic curve construction allows to
|
|
150
|
+
construct [zk-SNARKs](https://z.cash/technology/zksnarks/) at the 128-bit security
|
|
151
|
+
and use aggregated, batch-verifiable
|
|
152
|
+
[threshold signatures](https://medium.com/snigirev.stepan/bls-signatures-better-than-schnorr-5a7fe30ea716),
|
|
153
|
+
using Boneh-Lynn-Shacham signature scheme.
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
import { bls12_381 as bls } from '@noble/curves/bls12-381';
|
|
157
|
+
const privateKey = '67d53f170b908cabb9eb326c3c337762d59289a8fec79f7bc9254b584b73265c';
|
|
158
|
+
const message = '64726e3da8';
|
|
159
|
+
const publicKey = bls.getPublicKey(privateKey);
|
|
160
|
+
const signature = bls.sign(message, privateKey);
|
|
161
|
+
const isValid = bls.verify(signature, message, publicKey);
|
|
162
|
+
console.log({ publicKey, signature, isValid });
|
|
163
|
+
|
|
164
|
+
// Sign 1 msg with 3 keys
|
|
165
|
+
const privateKeys = [
|
|
166
|
+
'18f020b98eb798752a50ed0563b079c125b0db5dd0b1060d1c1b47d4a193e1e4',
|
|
167
|
+
'ed69a8c50cf8c9836be3b67c7eeff416612d45ba39a5c099d48fa668bf558c9c',
|
|
168
|
+
'16ae669f3be7a2121e17d0c68c05a8f3d6bef21ec0f2315f1d7aec12484e4cf5',
|
|
169
|
+
];
|
|
170
|
+
const messages = ['d2', '0d98', '05caf3'];
|
|
171
|
+
const publicKeys = privateKeys.map(bls.getPublicKey);
|
|
172
|
+
const signatures2 = privateKeys.map((p) => bls.sign(message, p));
|
|
173
|
+
const aggPubKey2 = bls.aggregatePublicKeys(publicKeys);
|
|
174
|
+
const aggSignature2 = bls.aggregateSignatures(signatures2);
|
|
175
|
+
const isValid2 = bls.verify(aggSignature2, message, aggPubKey2);
|
|
176
|
+
console.log({ signatures2, aggSignature2, isValid2 });
|
|
177
|
+
|
|
178
|
+
// Sign 3 msgs with 3 keys
|
|
179
|
+
const signatures3 = privateKeys.map((p, i) => bls.sign(messages[i], p));
|
|
180
|
+
const aggSignature3 = bls.aggregateSignatures(signatures3);
|
|
181
|
+
const isValid3 = bls.verifyBatch(aggSignature3, messages, publicKeys);
|
|
182
|
+
console.log({ publicKeys, signatures3, aggSignature3, isValid3 });
|
|
183
|
+
|
|
184
|
+
// Pairings
|
|
185
|
+
// bls.pairing(PointG1, PointG2)
|
|
186
|
+
// Also, check out hash-to-curve examples below.
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Abstract API
|
|
190
|
+
|
|
191
|
+
Abstract API allows to define custom curves. All arithmetics is done with JS bigints over finite fields,
|
|
192
|
+
which is defined from `modular` sub-module. For scalar multiplication, we use [precomputed tables with w-ary non-adjacent form (wNAF)](https://paulmillr.com/posts/noble-secp256k1-fast-ecc/).
|
|
193
|
+
Precomputes are enabled for weierstrass and edwards BASE points of a curve. You could precompute any
|
|
194
|
+
other point (e.g. for ECDH) using `utils.precompute()` method: check out examples.
|
|
195
|
+
|
|
196
|
+
There are following zero-dependency algorithms:
|
|
197
|
+
|
|
198
|
+
- [abstract/weierstrass: Short Weierstrass curve](#abstractweierstrass-short-weierstrass-curve)
|
|
89
199
|
- [abstract/edwards: Twisted Edwards curve](#abstractedwards-twisted-edwards-curve)
|
|
90
200
|
- [abstract/montgomery: Montgomery curve](#abstractmontgomery-montgomery-curve)
|
|
91
|
-
- [abstract/weierstrass: Short Weierstrass curve](#abstractweierstrass-short-weierstrass-curve)
|
|
92
201
|
- [abstract/hash-to-curve: Hashing strings to curve points](#abstracthash-to-curve-hashing-strings-to-curve-points)
|
|
93
202
|
- [abstract/poseidon: Poseidon hash](#abstractposeidon-poseidon-hash)
|
|
94
|
-
- [abstract/modular](#abstractmodular)
|
|
95
|
-
- [abstract/utils](#abstractutils)
|
|
96
|
-
|
|
97
|
-
### Overview
|
|
203
|
+
- [abstract/modular: Modular arithmetics utilities](#abstractmodular-modular-arithmetics-utilities)
|
|
204
|
+
- [abstract/utils: General utilities](#abstractutils-general-utilities)
|
|
98
205
|
|
|
99
|
-
|
|
206
|
+
### abstract/weierstrass: Short Weierstrass curve
|
|
100
207
|
|
|
101
208
|
```ts
|
|
102
|
-
import { bls } from '@noble/curves/abstract/bls';
|
|
103
|
-
import { twistedEdwards } from '@noble/curves/abstract/edwards';
|
|
104
|
-
import { montgomery } from '@noble/curves/abstract/montgomery';
|
|
105
209
|
import { weierstrass } from '@noble/curves/abstract/weierstrass';
|
|
106
|
-
import * as mod from '@noble/curves/abstract/modular';
|
|
107
|
-
import * as utils from '@noble/curves/abstract/utils';
|
|
108
210
|
```
|
|
109
211
|
|
|
110
|
-
|
|
212
|
+
Short Weierstrass curve's formula is `y² = x³ + ax + b`. `weierstrass` expects arguments `a`, `b`, field `Fp`, curve order `n`, cofactor `h`
|
|
213
|
+
and coordinates `Gx`, `Gy` of generator point.
|
|
214
|
+
|
|
215
|
+
**`k` generation** is done deterministically, following [RFC6979](https://www.rfc-editor.org/rfc/rfc6979).
|
|
216
|
+
For this you will need `hmac` & `hash`, which in our implementations is provided by noble-hashes.
|
|
217
|
+
If you're using different hashing library, make sure to wrap it in the following interface:
|
|
111
218
|
|
|
112
219
|
```ts
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
220
|
+
type CHash = {
|
|
221
|
+
(message: Uint8Array): Uint8Array;
|
|
222
|
+
blockLen: number;
|
|
223
|
+
outputLen: number;
|
|
224
|
+
create(): any;
|
|
225
|
+
};
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
**Weierstrass points:**
|
|
229
|
+
|
|
230
|
+
1. Exported as `ProjectivePoint`
|
|
231
|
+
2. Represented in projective (homogeneous) coordinates: (x, y, z) ∋ (x=x/z, y=y/z)
|
|
232
|
+
3. Use complete exception-free formulas for addition and doubling
|
|
233
|
+
4. Can be decoded/encoded from/to Uint8Array / hex strings using `ProjectivePoint.fromHex` and `ProjectivePoint#toRawBytes()`
|
|
234
|
+
5. Have `assertValidity()` which checks for being on-curve
|
|
235
|
+
6. Have `toAffine()` and `x` / `y` getters which convert to 2d xy affine coordinates
|
|
118
236
|
|
|
119
|
-
|
|
120
|
-
//
|
|
121
|
-
|
|
237
|
+
```ts
|
|
238
|
+
// T is usually bigint, but can be something else like complex numbers in BLS curves
|
|
239
|
+
interface ProjPointType<T> extends Group<ProjPointType<T>> {
|
|
240
|
+
readonly px: T;
|
|
241
|
+
readonly py: T;
|
|
242
|
+
readonly pz: T;
|
|
243
|
+
multiply(scalar: bigint): ProjPointType<T>;
|
|
244
|
+
multiplyUnsafe(scalar: bigint): ProjPointType<T>;
|
|
245
|
+
multiplyAndAddUnsafe(Q: ProjPointType<T>, a: bigint, b: bigint): ProjPointType<T> | undefined;
|
|
246
|
+
toAffine(iz?: T): AffinePoint<T>;
|
|
247
|
+
isTorsionFree(): boolean;
|
|
248
|
+
clearCofactor(): ProjPointType<T>;
|
|
249
|
+
assertValidity(): void;
|
|
250
|
+
hasEvenY(): boolean;
|
|
251
|
+
toRawBytes(isCompressed?: boolean): Uint8Array;
|
|
252
|
+
toHex(isCompressed?: boolean): string;
|
|
253
|
+
}
|
|
254
|
+
// Static methods for 3d XYZ points
|
|
255
|
+
interface ProjConstructor<T> extends GroupConstructor<ProjPointType<T>> {
|
|
256
|
+
new (x: T, y: T, z: T): ProjPointType<T>;
|
|
257
|
+
fromAffine(p: AffinePoint<T>): ProjPointType<T>;
|
|
258
|
+
fromHex(hex: Hex): ProjPointType<T>;
|
|
259
|
+
fromPrivateKey(privateKey: PrivKey): ProjPointType<T>;
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
**ECDSA signatures** are represented by `Signature` instances and can be described by the interface:
|
|
264
|
+
|
|
265
|
+
```ts
|
|
266
|
+
interface SignatureType {
|
|
267
|
+
readonly r: bigint;
|
|
268
|
+
readonly s: bigint;
|
|
269
|
+
readonly recovery?: number;
|
|
270
|
+
assertValidity(): void;
|
|
271
|
+
addRecoveryBit(recovery: number): SignatureType;
|
|
272
|
+
hasHighS(): boolean;
|
|
273
|
+
normalizeS(): SignatureType;
|
|
274
|
+
recoverPublicKey(msgHash: Hex): ProjPointType<bigint>;
|
|
275
|
+
toCompactRawBytes(): Uint8Array;
|
|
276
|
+
toCompactHex(): string;
|
|
277
|
+
// DER-encoded
|
|
278
|
+
toDERRawBytes(): Uint8Array;
|
|
279
|
+
toDERHex(): string;
|
|
280
|
+
}
|
|
281
|
+
type SignatureConstructor = {
|
|
282
|
+
new (r: bigint, s: bigint): SignatureType;
|
|
283
|
+
fromCompact(hex: Hex): SignatureType;
|
|
284
|
+
fromDER(hex: Hex): SignatureType;
|
|
285
|
+
};
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Example implementing [secq256k1](https://personaelabs.org/posts/spartan-ecdsa) (NOT secp256k1)
|
|
289
|
+
[cycle](https://zcash.github.io/halo2/background/curves.html#cycles-of-curves) of secp256k1 with Fp/N flipped.
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
import { weierstrass } from '@noble/curves/abstract/weierstrass';
|
|
293
|
+
import { Field } from '@noble/curves/abstract/modular'; // finite field, mod arithmetics done over it
|
|
294
|
+
import { sha256 } from '@noble/hashes/sha256'; // 3rd-party sha256() of type utils.CHash, with blockLen/outputLen
|
|
295
|
+
import { hmac } from '@noble/hashes/hmac'; // 3rd-party hmac() that will accept sha256()
|
|
296
|
+
import { concatBytes, randomBytes } from '@noble/hashes/utils'; // 3rd-party utilities
|
|
122
297
|
const secq256k1 = weierstrass({
|
|
298
|
+
// secq256k1: cycle of secp256k1 with Fp/N flipped.
|
|
123
299
|
a: 0n,
|
|
124
300
|
b: 7n,
|
|
125
301
|
Fp: Field(2n ** 256n - 432420386565659656852420866394968145599n),
|
|
@@ -130,50 +306,110 @@ const secq256k1 = weierstrass({
|
|
|
130
306
|
hmac: (key: Uint8Array, ...msgs: Uint8Array[]) => hmac(sha256, key, concatBytes(...msgs)),
|
|
131
307
|
randomBytes,
|
|
132
308
|
});
|
|
309
|
+
|
|
310
|
+
// All curves expose same generic interface.
|
|
311
|
+
const priv = secq256k1.utils.randomPrivateKey();
|
|
312
|
+
secq256k1.getPublicKey(priv); // Convert private key to public.
|
|
313
|
+
const sig = secq256k1.sign(msg, priv); // Sign msg with private key.
|
|
314
|
+
secq256k1.verify(sig, msg, priv); // Verify if sig is correct.
|
|
315
|
+
|
|
316
|
+
const Point = secq256k1.ProjectivePoint;
|
|
317
|
+
const point = Point.BASE; // Elliptic curve Point class and BASE point static var.
|
|
318
|
+
point.add(point).equals(point.double()); // add(), equals(), double() methods
|
|
319
|
+
point.subtract(point).equals(Point.ZERO); // subtract() method, ZERO static var
|
|
320
|
+
point.negate(); // Flips point over x/y coordinate.
|
|
321
|
+
point.multiply(31415n); // Multiplication of Point by scalar.
|
|
322
|
+
|
|
323
|
+
point.assertValidity(); // Checks for being on-curve
|
|
324
|
+
point.toAffine(); // Converts to 2d affine xy coordinates
|
|
325
|
+
|
|
326
|
+
secq256k1.CURVE.n;
|
|
327
|
+
secq256k1.CURVE.Fp.mod();
|
|
328
|
+
secq256k1.CURVE.hash();
|
|
329
|
+
|
|
330
|
+
// precomputes
|
|
331
|
+
const fast = secq256k1.utils.precompute(8, Point.fromHex(someonesPubKey));
|
|
332
|
+
fast.multiply(privKey); // much faster ECDH now
|
|
133
333
|
```
|
|
134
334
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
335
|
+
`weierstrass()` returns `CurveFn`:
|
|
336
|
+
|
|
337
|
+
```ts
|
|
338
|
+
type SignOpts = { lowS?: boolean; prehash?: boolean; extraEntropy: boolean | Uint8Array };
|
|
339
|
+
type CurveFn = {
|
|
340
|
+
CURVE: ReturnType<typeof validateOpts>;
|
|
341
|
+
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
|
|
342
|
+
getSharedSecret: (privateA: PrivKey, publicB: Hex, isCompressed?: boolean) => Uint8Array;
|
|
343
|
+
sign: (msgHash: Hex, privKey: PrivKey, opts?: SignOpts) => SignatureType;
|
|
344
|
+
verify: (
|
|
345
|
+
signature: Hex | SignatureType,
|
|
346
|
+
msgHash: Hex,
|
|
347
|
+
publicKey: Hex,
|
|
348
|
+
opts?: { lowS?: boolean; prehash?: boolean }
|
|
349
|
+
) => boolean;
|
|
350
|
+
ProjectivePoint: ProjectivePointConstructor;
|
|
351
|
+
Signature: SignatureConstructor;
|
|
352
|
+
utils: {
|
|
353
|
+
normPrivateKeyToScalar: (key: PrivKey) => bigint;
|
|
354
|
+
isValidPrivateKey(key: PrivKey): boolean;
|
|
355
|
+
randomPrivateKey: () => Uint8Array;
|
|
356
|
+
precompute: (windowSize?: number, point?: ProjPointType<bigint>) => ProjPointType<bigint>;
|
|
150
357
|
};
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
It is possible to enable precomputes for edwards & weierstrass curves.
|
|
154
|
-
Precomputes are calculated once (takes ~20-40ms), after that most `G` base point multiplications:
|
|
155
|
-
for example, `getPublicKey()`, `sign()` and similar methods - would be much faster.
|
|
156
|
-
Use `curve.utils.precompute()` to adjust precomputation window size
|
|
157
|
-
- You could use optional special params to tune performance:
|
|
158
|
-
- `Fp({sqrt})` square root calculation, used for point decompression
|
|
159
|
-
- `endo` endomorphism options for Koblitz curves
|
|
358
|
+
};
|
|
359
|
+
```
|
|
160
360
|
|
|
161
361
|
### abstract/edwards: Twisted Edwards curve
|
|
162
362
|
|
|
163
|
-
Twisted Edwards curve's formula is
|
|
363
|
+
Twisted Edwards curve's formula is `ax² + y² = 1 + dx²y²`. You must specify `a`, `d`, field `Fp`, order `n`, cofactor `h`
|
|
364
|
+
and coordinates `Gx`, `Gy` of generator point.
|
|
164
365
|
|
|
165
|
-
|
|
166
|
-
- For EdDSA signatures, params `hash` is also required. `adjustScalarBytes` which instructs how to change private scalars could be specified
|
|
366
|
+
For EdDSA signatures, `hash` param required. `adjustScalarBytes` which instructs how to change private scalars could be specified.
|
|
167
367
|
|
|
168
|
-
|
|
368
|
+
**Edwards points:**
|
|
369
|
+
|
|
370
|
+
1. Exported as `ExtendedPoint`
|
|
371
|
+
2. Represented in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z)
|
|
372
|
+
3. Use complete exception-free formulas for addition and doubling
|
|
373
|
+
4. Can be decoded/encoded from/to Uint8Array / hex strings using `ExtendedPoint.fromHex` and `ExtendedPoint#toRawBytes()`
|
|
374
|
+
5. Have `assertValidity()` which checks for being on-curve
|
|
375
|
+
6. Have `toAffine()` and `x` / `y` getters which convert to 2d xy affine coordinates
|
|
376
|
+
7. Have `isTorsionFree()`, `clearCofactor()` and `isSmallOrder()` utilities to handle torsions
|
|
377
|
+
|
|
378
|
+
```ts
|
|
379
|
+
interface ExtPointType extends Group<ExtPointType> {
|
|
380
|
+
readonly ex: bigint;
|
|
381
|
+
readonly ey: bigint;
|
|
382
|
+
readonly ez: bigint;
|
|
383
|
+
readonly et: bigint;
|
|
384
|
+
assertValidity(): void;
|
|
385
|
+
multiply(scalar: bigint): ExtPointType;
|
|
386
|
+
multiplyUnsafe(scalar: bigint): ExtPointType;
|
|
387
|
+
isSmallOrder(): boolean;
|
|
388
|
+
isTorsionFree(): boolean;
|
|
389
|
+
clearCofactor(): ExtPointType;
|
|
390
|
+
toAffine(iz?: bigint): AffinePoint<bigint>;
|
|
391
|
+
}
|
|
392
|
+
// Static methods of Extended Point with coordinates in X, Y, Z, T
|
|
393
|
+
interface ExtPointConstructor extends GroupConstructor<ExtPointType> {
|
|
394
|
+
new (x: bigint, y: bigint, z: bigint, t: bigint): ExtPointType;
|
|
395
|
+
fromAffine(p: AffinePoint<bigint>): ExtPointType;
|
|
396
|
+
fromHex(hex: Hex): ExtPointType;
|
|
397
|
+
fromPrivateKey(privateKey: Hex): ExtPointType;
|
|
398
|
+
}
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
Example implementing edwards25519:
|
|
402
|
+
|
|
403
|
+
```ts
|
|
169
404
|
import { twistedEdwards } from '@noble/curves/abstract/edwards';
|
|
170
|
-
import { div } from '@noble/curves/abstract/modular';
|
|
405
|
+
import { Field, div } from '@noble/curves/abstract/modular';
|
|
171
406
|
import { sha512 } from '@noble/hashes/sha512';
|
|
172
407
|
|
|
408
|
+
const Fp = Field(2n ** 255n - 19n);
|
|
173
409
|
const ed25519 = twistedEdwards({
|
|
174
410
|
a: -1n,
|
|
175
|
-
d: div(-121665n, 121666n
|
|
176
|
-
|
|
411
|
+
d: Fp.div(-121665n, 121666n), // -121665n/121666n mod p
|
|
412
|
+
Fp,
|
|
177
413
|
n: 2n ** 252n + 27742317777372353535851937790883648493n,
|
|
178
414
|
h: 8n,
|
|
179
415
|
Gx: 15112221349535400772501151409588531511454012693041857206046113283949847762202n,
|
|
@@ -181,31 +417,24 @@ const ed25519 = twistedEdwards({
|
|
|
181
417
|
hash: sha512,
|
|
182
418
|
randomBytes,
|
|
183
419
|
adjustScalarBytes(bytes) {
|
|
184
|
-
// optional
|
|
420
|
+
// optional; but mandatory in ed25519
|
|
185
421
|
bytes[0] &= 248;
|
|
186
422
|
bytes[31] &= 127;
|
|
187
423
|
bytes[31] |= 64;
|
|
188
424
|
return bytes;
|
|
189
425
|
},
|
|
190
426
|
} as const);
|
|
191
|
-
const key = ed25519.utils.randomPrivateKey();
|
|
192
|
-
const pub = ed25519.getPublicKey(key);
|
|
193
|
-
const msg = new TextEncoder().encode('hello world'); // strings not accepted, must be Uint8Array
|
|
194
|
-
const sig = ed25519.sign(msg, key);
|
|
195
|
-
ed25519.verify(sig, msg, pub) === true;
|
|
196
427
|
```
|
|
197
428
|
|
|
198
429
|
`twistedEdwards()` returns `CurveFn` of following type:
|
|
199
430
|
|
|
200
431
|
```ts
|
|
201
|
-
|
|
432
|
+
type CurveFn = {
|
|
202
433
|
CURVE: ReturnType<typeof validateOpts>;
|
|
203
|
-
getPublicKey: (privateKey:
|
|
204
|
-
sign: (message: Hex, privateKey: Hex) => Uint8Array;
|
|
205
|
-
verify: (sig: SigType, message: Hex, publicKey:
|
|
206
|
-
|
|
207
|
-
ExtendedPoint: ExtendedPointConstructor;
|
|
208
|
-
Signature: SignatureConstructor;
|
|
434
|
+
getPublicKey: (privateKey: Hex) => Uint8Array;
|
|
435
|
+
sign: (message: Hex, privateKey: Hex, context?: Hex) => Uint8Array;
|
|
436
|
+
verify: (sig: SigType, message: Hex, publicKey: Hex, context?: Hex) => boolean;
|
|
437
|
+
ExtendedPoint: ExtPointConstructor;
|
|
209
438
|
utils: {
|
|
210
439
|
randomPrivateKey: () => Uint8Array;
|
|
211
440
|
getExtendedPublicKey: (key: PrivKey) => {
|
|
@@ -221,26 +450,20 @@ export type CurveFn = {
|
|
|
221
450
|
|
|
222
451
|
### abstract/montgomery: Montgomery curve
|
|
223
452
|
|
|
224
|
-
|
|
453
|
+
The module contains methods for x-only ECDH on Curve25519 / Curve448 from RFC7748. Proper Elliptic Curve Points are not implemented yet.
|
|
225
454
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
You must specify curve field, `a24` special variable, `montgomeryBits`, `nByteLength`, and coordinate `u` of generator point.
|
|
455
|
+
You must specify curve params `Fp`, `a`, `Gu` coordinate of u, `montgomeryBits` and `nByteLength`.
|
|
229
456
|
|
|
230
457
|
```typescript
|
|
231
458
|
import { montgomery } from '@noble/curves/abstract/montgomery';
|
|
232
459
|
|
|
233
460
|
const x25519 = montgomery({
|
|
234
|
-
|
|
235
|
-
|
|
461
|
+
Fp: Field(2n ** 255n - 19n),
|
|
462
|
+
a: 486662n,
|
|
463
|
+
Gu: 9n,
|
|
236
464
|
montgomeryBits: 255,
|
|
237
465
|
nByteLength: 32,
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
// Optional params
|
|
241
|
-
powPminus2: (x: bigint): bigint => {
|
|
242
|
-
return mod.pow(x, P - 2, P);
|
|
243
|
-
},
|
|
466
|
+
// Optional param
|
|
244
467
|
adjustScalarBytes(bytes) {
|
|
245
468
|
bytes[0] &= 248;
|
|
246
469
|
bytes[31] &= 127;
|
|
@@ -250,145 +473,61 @@ const x25519 = montgomery({
|
|
|
250
473
|
});
|
|
251
474
|
```
|
|
252
475
|
|
|
253
|
-
### abstract/
|
|
254
|
-
|
|
255
|
-
Short Weierstrass curve's formula is: y² = x³ + ax + b. Uses deterministic ECDSA from RFC6979. You can also specify `extraEntropy` in `sign()`.
|
|
256
|
-
|
|
257
|
-
- You must specify curve params: `a`, `b`, field `Fp`, order `n`, cofactor `h` and coordinates `Gx`, `Gy` of generator point
|
|
258
|
-
- For ECDSA, you must specify `hash`, `hmac`. It is also possible to recover keys from signatures
|
|
259
|
-
- For ECDH, use `getSharedSecret(privKeyA, pubKeyB)`
|
|
260
|
-
- Optional params are `lowS` (default value) and `endo` (endomorphism)
|
|
476
|
+
### abstract/hash-to-curve: Hashing strings to curve points
|
|
261
477
|
|
|
262
|
-
|
|
263
|
-
import { Fp } from '@noble/curves/abstract/modular';
|
|
264
|
-
import { weierstrass } from '@noble/curves/abstract/weierstrass'; // Short Weierstrass curve
|
|
265
|
-
import { sha256 } from '@noble/hashes/sha256';
|
|
266
|
-
import { hmac } from '@noble/hashes/hmac';
|
|
267
|
-
import { concatBytes, randomBytes } from '@noble/hashes/utils';
|
|
478
|
+
The module allows to hash arbitrary strings to elliptic curve points. Implements [hash-to-curve v11](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11).
|
|
268
479
|
|
|
269
|
-
|
|
270
|
-
a: 0n,
|
|
271
|
-
b: 7n,
|
|
272
|
-
Fp: Fp(2n ** 256n - 2n ** 32n - 2n ** 9n - 2n ** 8n - 2n ** 7n - 2n ** 6n - 2n ** 4n - 1n),
|
|
273
|
-
n: 2n ** 256n - 432420386565659656852420866394968145599n,
|
|
274
|
-
Gx: 55066263022277343669578718895168534326250603453777594175500187360389116729240n,
|
|
275
|
-
Gy: 32670510020758816978083085130507043184471273380659243275938904335757337482424n,
|
|
276
|
-
hash: sha256,
|
|
277
|
-
hmac: (k: Uint8Array, ...msgs: Uint8Array[]) => hmac(sha256, key, concatBytes(...msgs)),
|
|
278
|
-
randomBytes,
|
|
480
|
+
Every curve has exported `hashToCurve` and `encodeToCurve` methods:
|
|
279
481
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
// Beta param
|
|
286
|
-
beta: 0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501een,
|
|
287
|
-
// Split scalar k into k1, k2
|
|
288
|
-
splitScalar: (k: bigint) => {
|
|
289
|
-
// return { k1neg: true, k1: 512n, k2neg: false, k2: 448n };
|
|
290
|
-
},
|
|
291
|
-
},
|
|
292
|
-
});
|
|
482
|
+
```ts
|
|
483
|
+
import { hashToCurve, encodeToCurve } from '@noble/curves/secp256k1';
|
|
484
|
+
import { randomBytes } from '@noble/hashes/utils';
|
|
485
|
+
console.log(hashToCurve(randomBytes()));
|
|
486
|
+
console.log(encodeToCurve(randomBytes()));
|
|
293
487
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
const msg = randomBytes(32);
|
|
298
|
-
const sig = secp256k1.sign(msg, key);
|
|
299
|
-
secp256k1.verify(sig, msg, pub); // true
|
|
300
|
-
sig.recoverPublicKey(msg); // == pub
|
|
301
|
-
const someonesPubkey = secp256k1.getPublicKey(secp256k1.utils.randomPrivateKey());
|
|
302
|
-
const shared = secp256k1.getSharedSecret(key, someonesPubkey);
|
|
488
|
+
import { bls12_381 } from '@noble/curves/bls12-381';
|
|
489
|
+
bls12_381.G1.hashToCurve(randomBytes(), { DST: 'another' });
|
|
490
|
+
bls12_381.G2.hashToCurve(randomBytes(), { DST: 'custom' });
|
|
303
491
|
```
|
|
304
492
|
|
|
305
|
-
|
|
493
|
+
If you need low-level methods from spec:
|
|
494
|
+
|
|
495
|
+
`expand_message_xmd` [(spec)](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.4.1) produces a uniformly random byte string using a cryptographic hash function H that outputs b bits.
|
|
306
496
|
|
|
307
497
|
```ts
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
Signature: SignatureConstructor;
|
|
322
|
-
utils: {
|
|
323
|
-
isValidPrivateKey(privateKey: PrivKey): boolean;
|
|
324
|
-
hashToPrivateKey: (hash: Hex) => Uint8Array;
|
|
325
|
-
randomPrivateKey: () => Uint8Array;
|
|
326
|
-
};
|
|
327
|
-
};
|
|
498
|
+
function expand_message_xmd(
|
|
499
|
+
msg: Uint8Array,
|
|
500
|
+
DST: Uint8Array,
|
|
501
|
+
lenInBytes: number,
|
|
502
|
+
H: CHash
|
|
503
|
+
): Uint8Array;
|
|
504
|
+
function expand_message_xof(
|
|
505
|
+
msg: Uint8Array,
|
|
506
|
+
DST: Uint8Array,
|
|
507
|
+
lenInBytes: number,
|
|
508
|
+
k: number,
|
|
509
|
+
H: CHash
|
|
510
|
+
): Uint8Array;
|
|
328
511
|
```
|
|
329
512
|
|
|
330
|
-
|
|
513
|
+
`hash_to_field(msg, count, options)` [(spec)](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3)
|
|
514
|
+
hashes arbitrary-length byte strings to a list of one or more elements of a finite field F.
|
|
515
|
+
_ `msg` a byte string containing the message to hash
|
|
516
|
+
_ `count` the number of elements of F to output
|
|
517
|
+
_ `options` `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`
|
|
518
|
+
_ Returns `[u_0, ..., u_(count - 1)]`, a list of field elements.
|
|
331
519
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
```ts
|
|
337
|
-
function expand_message_xmd(
|
|
338
|
-
msg: Uint8Array,
|
|
339
|
-
DST: Uint8Array,
|
|
340
|
-
lenInBytes: number,
|
|
341
|
-
H: CHash
|
|
342
|
-
): Uint8Array;
|
|
343
|
-
function expand_message_xof(
|
|
344
|
-
msg: Uint8Array,
|
|
345
|
-
DST: Uint8Array,
|
|
346
|
-
lenInBytes: number,
|
|
347
|
-
k: number,
|
|
348
|
-
H: CHash
|
|
349
|
-
): Uint8Array;
|
|
350
|
-
```
|
|
351
|
-
|
|
352
|
-
- `hash_to_field(msg, count, options)` [(spec)](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3)
|
|
353
|
-
hashes arbitrary-length byte strings to a list of one or more elements of a finite field F.
|
|
354
|
-
_ `msg` a byte string containing the message to hash
|
|
355
|
-
_ `count` the number of elements of F to output
|
|
356
|
-
_ `options` `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`
|
|
357
|
-
_ Returns `[u_0, ..., u_(count - 1)]`, a list of field elements.
|
|
358
|
-
|
|
359
|
-
```ts
|
|
360
|
-
function hash_to_field(msg: Uint8Array, count: number, options: htfOpts): bigint[][];
|
|
361
|
-
type htfOpts = {
|
|
362
|
-
// DST: a domain separation tag
|
|
363
|
-
// defined in section 2.2.5
|
|
364
|
-
DST: string;
|
|
365
|
-
// p: the characteristic of F
|
|
366
|
-
// where F is a finite field of characteristic p and order q = p^m
|
|
367
|
-
p: bigint;
|
|
368
|
-
// m: the extension degree of F, m >= 1
|
|
369
|
-
// where F is a finite field of characteristic p and order q = p^m
|
|
370
|
-
m: number;
|
|
371
|
-
// k: the target security level for the suite in bits
|
|
372
|
-
// defined in section 5.1
|
|
373
|
-
k: number;
|
|
374
|
-
// option to use a message that has already been processed by
|
|
375
|
-
// expand_message_xmd
|
|
376
|
-
expand?: 'xmd' | 'xof';
|
|
377
|
-
// Hash functions for: expand_message_xmd is appropriate for use with a
|
|
378
|
-
// wide range of hash functions, including SHA-2, SHA-3, BLAKE2, and others.
|
|
379
|
-
// BBS+ uses blake2: https://github.com/hyperledger/aries-framework-go/issues/2247
|
|
380
|
-
// TODO: verify that hash is shake if expand==='xof' via types
|
|
381
|
-
hash: CHash;
|
|
382
|
-
};
|
|
383
|
-
```
|
|
520
|
+
```ts
|
|
521
|
+
function hash_to_field(msg: Uint8Array, count: number, options: htfOpts): bigint[][];
|
|
522
|
+
```
|
|
384
523
|
|
|
385
524
|
### abstract/poseidon: Poseidon hash
|
|
386
525
|
|
|
387
526
|
Implements [Poseidon](https://www.poseidon-hash.info) ZK-friendly hash.
|
|
388
527
|
|
|
389
|
-
There are many poseidon
|
|
390
|
-
|
|
391
|
-
stark
|
|
528
|
+
There are many poseidon variants with different constants.
|
|
529
|
+
We don't provide them: you should construct them manually.
|
|
530
|
+
The only variant provided resides in `stark` module: inspect it for proper usage.
|
|
392
531
|
|
|
393
532
|
```ts
|
|
394
533
|
import { poseidon } from '@noble/curves/abstract/poseidon';
|
|
@@ -406,27 +545,54 @@ type PoseidonOpts = {
|
|
|
406
545
|
const instance = poseidon(opts: PoseidonOpts);
|
|
407
546
|
```
|
|
408
547
|
|
|
409
|
-
### abstract/
|
|
548
|
+
### abstract/bls
|
|
410
549
|
|
|
411
|
-
|
|
550
|
+
The module abstracts BLS (Barreto-Lynn-Scott) primitives. In theory you should be able to write BLS12-377, BLS24,
|
|
551
|
+
and others with it.
|
|
412
552
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
fp.
|
|
553
|
+
### abstract/modular: Modular arithmetics utilities
|
|
554
|
+
|
|
555
|
+
```ts
|
|
556
|
+
import * as mod from '@noble/curves/abstract/modular';
|
|
557
|
+
const fp = mod.Field(2n ** 255n - 19n); // Finite field over 2^255-19
|
|
558
|
+
fp.mul(591n, 932n); // multiplication
|
|
559
|
+
fp.pow(481n, 11024858120n); // exponentiation
|
|
560
|
+
fp.div(5n, 17n); // division: 5/17 mod 2^255-19 == 5 * invert(17)
|
|
561
|
+
fp.sqrt(21n); // square root
|
|
418
562
|
|
|
419
563
|
// Generic non-FP utils are also available
|
|
420
|
-
mod(21n, 10n); // 21 mod 10 == 1n; fixed version of 21 % 10
|
|
421
|
-
invert(17n, 10n); // invert(17) mod 10; modular multiplicative inverse
|
|
422
|
-
|
|
423
|
-
invertBatch([1n, 2n, 4n], 21n); // => [1n, 11n, 16n] in one inversion
|
|
424
|
-
sqrt(21n, 73n); // √21 mod 73; square root
|
|
564
|
+
mod.mod(21n, 10n); // 21 mod 10 == 1n; fixed version of 21 % 10
|
|
565
|
+
mod.invert(17n, 10n); // invert(17) mod 10; modular multiplicative inverse
|
|
566
|
+
mod.invertBatch([1n, 2n, 4n], 21n); // => [1n, 11n, 16n] in one inversion
|
|
425
567
|
```
|
|
426
568
|
|
|
427
|
-
|
|
569
|
+
#### Creating private keys from hashes
|
|
428
570
|
|
|
429
|
-
|
|
571
|
+
Suppose you have `sha256(something)` (e.g. from HMAC) and you want to make a private key from it.
|
|
572
|
+
Even though p256 or secp256k1 may have 32-byte private keys,
|
|
573
|
+
and sha256 output is also 32-byte, you can't just use it and reduce it modulo `CURVE.n`.
|
|
574
|
+
|
|
575
|
+
Doing so will make the result key [biased](https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/).
|
|
576
|
+
|
|
577
|
+
To avoid the bias, we implement FIPS 186 B.4.1, which allows to take arbitrary
|
|
578
|
+
byte array and produce valid scalars / private keys with bias being neglible.
|
|
579
|
+
|
|
580
|
+
Use [hash-to-curve](#abstracthash-to-curve-hashing-strings-to-curve-points) if you need
|
|
581
|
+
hashing to **public keys**; the function in the module instead operates on **private keys**.
|
|
582
|
+
|
|
583
|
+
```ts
|
|
584
|
+
import { p256 } from '@noble/curves/p256';
|
|
585
|
+
import { sha256 } from '@noble/hashes/sha256';
|
|
586
|
+
import { hkdf } from '@noble/hashes/hkdf';
|
|
587
|
+
const someKey = new Uint8Array(32).fill(2); // Needs to actually be random, not .fill(2)
|
|
588
|
+
const derived = hkdf(sha256, someKey, undefined, 'application', 40); // 40 bytes
|
|
589
|
+
const validPrivateKey = mod.hashToPrivateScalar(derived, p256.CURVE.n);
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
### abstract/utils: General utilities
|
|
594
|
+
|
|
595
|
+
```ts
|
|
430
596
|
import * as utils from '@noble/curves/abstract/utils';
|
|
431
597
|
|
|
432
598
|
utils.bytesToHex(Uint8Array.from([0xde, 0xad, 0xbe, 0xef]));
|
|
@@ -434,12 +600,11 @@ utils.hexToBytes('deadbeef');
|
|
|
434
600
|
utils.hexToNumber();
|
|
435
601
|
utils.bytesToNumberBE(Uint8Array.from([0xde, 0xad, 0xbe, 0xef]));
|
|
436
602
|
utils.bytesToNumberLE(Uint8Array.from([0xde, 0xad, 0xbe, 0xef]));
|
|
437
|
-
utils.numberToBytesBE(123n);
|
|
438
|
-
utils.numberToBytesLE(123n);
|
|
603
|
+
utils.numberToBytesBE(123n, 32);
|
|
604
|
+
utils.numberToBytesLE(123n, 64);
|
|
439
605
|
utils.numberToHexUnpadded(123n);
|
|
440
606
|
utils.concatBytes(Uint8Array.from([0xde, 0xad]), Uint8Array.from([0xbe, 0xef]));
|
|
441
607
|
utils.nLength(255n);
|
|
442
|
-
utils.hashToPrivateScalar(sha512_of_something, secp256r1.n);
|
|
443
608
|
utils.equalBytes(Uint8Array.from([0xde]), Uint8Array.from([0xde]));
|
|
444
609
|
```
|
|
445
610
|
|
|
@@ -447,80 +612,103 @@ utils.equalBytes(Uint8Array.from([0xde]), Uint8Array.from([0xde]));
|
|
|
447
612
|
|
|
448
613
|
The library had no prior security audit.
|
|
449
614
|
|
|
450
|
-
[Timing attack](https://en.wikipedia.org/wiki/Timing_attack) considerations: _JIT-compiler_ and _Garbage Collector_ make "constant time" extremely hard to achieve in a scripting language. Which means _any other JS library can't have constant-timeness_. Even statically typed Rust, a language without GC, [makes it harder to achieve constant-time](https://www.chosenplaintext.ca/open-source/rust-timing-shield/security) for some cases. If your goal is absolute security, don't use any JS lib — including bindings to native ones. Use low-level libraries & languages. Nonetheless we're targetting algorithmic constant time.
|
|
615
|
+
[Timing attack](https://en.wikipedia.org/wiki/Timing_attack) considerations: we are using non-CT bigints. However, _JIT-compiler_ and _Garbage Collector_ make "constant time" extremely hard to achieve in a scripting language. Which means _any other JS library can't have constant-timeness_. Even statically typed Rust, a language without GC, [makes it harder to achieve constant-time](https://www.chosenplaintext.ca/open-source/rust-timing-shield/security) for some cases. If your goal is absolute security, don't use any JS lib — including bindings to native ones. Use low-level libraries & languages. Nonetheless we're targetting algorithmic constant time.
|
|
616
|
+
|
|
617
|
+
We consider infrastructure attacks like rogue NPM modules very important; that's why it's crucial to minimize the amount of 3rd-party dependencies & native bindings. If your app uses 500 dependencies, any dep could get hacked and you'll be downloading malware with every `npm install`. Our goal is to minimize this attack vector. As for devDependencies used by the library:
|
|
451
618
|
|
|
452
|
-
|
|
619
|
+
- `@scure` base, bip32, bip39 (used in tests), micro-bmark (benchmark), micro-should (testing) are developed by us
|
|
620
|
+
and follow the same practices such as: minimal library size, auditability, signed releases
|
|
621
|
+
- prettier (linter), fast-check (property-based testing),
|
|
622
|
+
typescript versions are locked and rarely updated. Every update is checked with `npm-diff`.
|
|
623
|
+
The packages are big, which makes it hard to audit their source code thoroughly and fully.
|
|
624
|
+
- They are only used if you clone the git repo and want to add some feature to it. End-users won't use them.
|
|
453
625
|
|
|
454
626
|
## Speed
|
|
455
627
|
|
|
456
|
-
Benchmark results on Apple M2 with node
|
|
628
|
+
Benchmark results on Apple M2 with node v19:
|
|
457
629
|
|
|
458
630
|
```
|
|
459
631
|
secp256k1
|
|
460
|
-
init x
|
|
461
|
-
getPublicKey x
|
|
462
|
-
sign x 3,
|
|
463
|
-
verify x
|
|
464
|
-
getSharedSecret x
|
|
465
|
-
recoverPublicKey x
|
|
466
|
-
schnorr.sign x
|
|
467
|
-
schnorr.verify x
|
|
632
|
+
init x 58 ops/sec @ 17ms/op
|
|
633
|
+
getPublicKey x 5,640 ops/sec @ 177μs/op
|
|
634
|
+
sign x 3,909 ops/sec @ 255μs/op
|
|
635
|
+
verify x 780 ops/sec @ 1ms/op
|
|
636
|
+
getSharedSecret x 465 ops/sec @ 2ms/op
|
|
637
|
+
recoverPublicKey x 740 ops/sec @ 1ms/op
|
|
638
|
+
schnorr.sign x 597 ops/sec @ 1ms/op
|
|
639
|
+
schnorr.verify x 775 ops/sec @ 1ms/op
|
|
468
640
|
|
|
469
641
|
P256
|
|
470
|
-
init x
|
|
471
|
-
getPublicKey x 5,
|
|
472
|
-
sign x 3,
|
|
473
|
-
verify x
|
|
642
|
+
init x 31 ops/sec @ 31ms/op
|
|
643
|
+
getPublicKey x 5,607 ops/sec @ 178μs/op
|
|
644
|
+
sign x 3,930 ops/sec @ 254μs/op
|
|
645
|
+
verify x 540 ops/sec @ 1ms/op
|
|
474
646
|
|
|
475
647
|
P384
|
|
476
|
-
init x
|
|
477
|
-
getPublicKey x 2,
|
|
478
|
-
sign x 1,
|
|
479
|
-
verify x
|
|
648
|
+
init x 15 ops/sec @ 63ms/op
|
|
649
|
+
getPublicKey x 2,622 ops/sec @ 381μs/op
|
|
650
|
+
sign x 1,913 ops/sec @ 522μs/op
|
|
651
|
+
verify x 222 ops/sec @ 4ms/op
|
|
480
652
|
|
|
481
653
|
P521
|
|
482
|
-
init x
|
|
483
|
-
getPublicKey x 1,
|
|
484
|
-
sign x 1,
|
|
485
|
-
verify x
|
|
654
|
+
init x 8 ops/sec @ 119ms/op
|
|
655
|
+
getPublicKey x 1,371 ops/sec @ 729μs/op
|
|
656
|
+
sign x 1,090 ops/sec @ 917μs/op
|
|
657
|
+
verify x 118 ops/sec @ 8ms/op
|
|
486
658
|
|
|
487
659
|
ed25519
|
|
488
|
-
init x
|
|
489
|
-
getPublicKey x
|
|
490
|
-
sign x
|
|
491
|
-
verify x
|
|
660
|
+
init x 47 ops/sec @ 20ms/op
|
|
661
|
+
getPublicKey x 9,414 ops/sec @ 106μs/op
|
|
662
|
+
sign x 4,516 ops/sec @ 221μs/op
|
|
663
|
+
verify x 912 ops/sec @ 1ms/op
|
|
492
664
|
|
|
493
665
|
ed448
|
|
494
|
-
init x 17 ops/sec @
|
|
495
|
-
getPublicKey x 3,
|
|
496
|
-
sign x 1,
|
|
497
|
-
verify x
|
|
498
|
-
|
|
499
|
-
bls12-381
|
|
500
|
-
init x 30 ops/sec @ 33ms/op
|
|
501
|
-
getPublicKey x 788 ops/sec @ 1ms/op
|
|
502
|
-
sign x 45 ops/sec @ 21ms/op
|
|
503
|
-
verify x 32 ops/sec @ 30ms/op
|
|
504
|
-
pairing x 88 ops/sec @ 11ms/op
|
|
666
|
+
init x 17 ops/sec @ 56ms/op
|
|
667
|
+
getPublicKey x 3,363 ops/sec @ 297μs/op
|
|
668
|
+
sign x 1,615 ops/sec @ 619μs/op
|
|
669
|
+
verify x 319 ops/sec @ 3ms/op
|
|
505
670
|
|
|
506
671
|
stark
|
|
507
|
-
init x
|
|
508
|
-
pedersen
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
672
|
+
init x 35 ops/sec @ 28ms/op
|
|
673
|
+
pedersen x 884 ops/sec @ 1ms/op
|
|
674
|
+
poseidon x 8,598 ops/sec @ 116μs/op
|
|
675
|
+
verify x 528 ops/sec @ 1ms/op
|
|
676
|
+
|
|
677
|
+
bls12-381
|
|
678
|
+
init x 32 ops/sec @ 30ms/op
|
|
679
|
+
getPublicKey 1-bit x 858 ops/sec @ 1ms/op
|
|
680
|
+
getPublicKey x 858 ops/sec @ 1ms/op
|
|
681
|
+
sign x 49 ops/sec @ 20ms/op
|
|
682
|
+
verify x 34 ops/sec @ 28ms/op
|
|
683
|
+
pairing x 94 ops/sec @ 10ms/op
|
|
684
|
+
aggregatePublicKeys/8 x 116 ops/sec @ 8ms/op
|
|
685
|
+
aggregatePublicKeys/32 x 31 ops/sec @ 31ms/op
|
|
686
|
+
aggregatePublicKeys/128 x 7 ops/sec @ 125ms/op
|
|
687
|
+
aggregateSignatures/8 x 45 ops/sec @ 22ms/op
|
|
688
|
+
aggregateSignatures/32 x 11 ops/sec @ 84ms/op
|
|
689
|
+
aggregateSignatures/128 x 3 ops/sec @ 332ms/opp
|
|
515
690
|
```
|
|
516
691
|
|
|
692
|
+
## Resources
|
|
693
|
+
|
|
694
|
+
Article about some of library's features: [Learning fast elliptic-curve cryptography](https://paulmillr.com/posts/noble-secp256k1-fast-ecc/). Elliptic curve calculator: [paulmillr.com/ecc](https://paulmillr.com/ecc)
|
|
695
|
+
|
|
696
|
+
- secp256k1
|
|
697
|
+
- [btc-signer](https://github.com/paulmillr/micro-btc-signer), [eth-signer](https://github.com/paulmillr/micro-eth-signer)
|
|
698
|
+
- ed25519
|
|
699
|
+
- [sol-signer](https://github.com/paulmillr/micro-sol-signer)
|
|
700
|
+
- BLS12-381
|
|
701
|
+
- Check out `bls12-381.ts` for articles about the curve
|
|
702
|
+
- Threshold sigs demo [genthresh.com](https://genthresh.com)
|
|
703
|
+
- BBS signatures [github.com/Wind4Greg/BBS-Draft-Checks](https://github.com/Wind4Greg/BBS-Draft-Checks) following [draft-irtf-cfrg-bbs-signatures-latest](https://identity.foundation/bbs-signature/draft-irtf-cfrg-bbs-signatures.html)
|
|
704
|
+
|
|
517
705
|
## Upgrading
|
|
518
706
|
|
|
519
|
-
If you're coming from single-
|
|
707
|
+
If you're coming from single-feature noble packages, the following changes need to be kept in mind:
|
|
520
708
|
|
|
521
709
|
- 2d affine (x, y) points have been removed to reduce complexity and improve speed
|
|
522
|
-
- Removed `number` support as a type for private keys
|
|
523
|
-
- `mod`, `invert` are no longer present in `utils
|
|
710
|
+
- Removed `number` support as a type for private keys, `bigint` is still supported
|
|
711
|
+
- `mod`, `invert` are no longer present in `utils`: use `@noble/curves/abstract/modular`
|
|
524
712
|
|
|
525
713
|
Upgrading from @noble/secp256k1 1.7:
|
|
526
714
|
|