Skip to content

Commit c562472

Browse files
committed
Bump docs
1 parent 3f4ebef commit c562472

File tree

5 files changed

+307
-257
lines changed

5 files changed

+307
-257
lines changed

README.md

Lines changed: 134 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
# noble-secp256k1
22

3-
Fastest 4KB JS implementation of secp256k1 signatures & ECDH.
3+
Fastest 5KB JS implementation of secp256k1 signatures & ECDH.
44

55
- ✍️ [ECDSA](https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm)
66
signatures compliant with [RFC6979](https://www.rfc-editor.org/rfc/rfc6979)
7+
- ➰ Schnorr
8+
signatures compliant with [BIP340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki)
79
- 🤝 Elliptic Curve Diffie-Hellman [ECDH](https://en.wikipedia.org/wiki/Elliptic-curve_Diffie–Hellman)
810
- 🔒 Supports [hedged signatures](https://paulmillr.com/posts/deterministic-signatures/) guarding against fault attacks
9-
- 🪶 3.98KB gzipped (elliptic.js is 12x larger, tiny-secp256k1 is 20-40x larger)
11+
- 🪶 4.86KB (gzipped, elliptic.js is 10x larger, tiny-secp256k1 is 25x larger)
1012

1113
The module is a sister project of [noble-curves](https://github.com/paulmillr/noble-curves),
1214
focusing on smaller attack surface & better auditability.
1315
Curves are drop-in replacement and have more features:
14-
MSM, DER encoding, endomorphism, prehashing, custom point precomputes.
16+
MSM, DER encoding, endomorphism, prehashing, custom point precomputes, hash-to-curve, oprf.
1517
To upgrade from earlier version, see [Upgrading](#upgrading).
1618

1719
898-byte version of the library is available for learning purposes in `test/misc/1kb.min.js`,
@@ -45,18 +47,16 @@ We support all major platforms and runtimes. For React Native, additional polyfi
4547
```js
4648
import * as secp from '@noble/secp256k1';
4749
(async () => {
48-
// Uint8Arrays or hex strings are accepted:
49-
// Uint8Array.from([0xde, 0xad, 0xbe, 0xef]) is equal to 'deadbeef'
50-
const privKey = secp.utils.randomSecretKey(); // Secure random private key
51-
// sha256 of 'hello world'
52-
const msgHash = 'b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9';
53-
const pubKey = secp.getPublicKey(privKey);
54-
const signature = await secp.signAsync(msgHash, privKey); // Sync methods below
55-
const isValid = secp.verify(signature, msgHash, pubKey);
56-
57-
const alicesPub = secp.getPublicKey(secp.utils.randomSecretKey());
58-
const shared = secp.getSharedSecret(privKey, alicesPub); // Diffie-Hellman
59-
const pub2 = signature.recoverPublicKey(msgHash); // Public key recovery
50+
const { secretKey, publicKey } = secp.keygen();
51+
// const publicKey = secp.getPublicKey(secretKey);
52+
const msg = new TextEncoder().encode('hello noble');
53+
const sig = await secp.signAsync(msg, secretKey);
54+
const isValid = await secp.verifyAsync(sig, msg, publicKey);
55+
56+
const bobsKeys = secp.keygen();
57+
const shared = secp.getSharedSecret(secretKey, bobsKeys.publicKey); // Diffie-Hellman
58+
const sigr = await secp.signAsync(msg, secretKey, { format: 'recovered' });
59+
const publicKey2 = secp.recoverPublicKey(sigr, msg);
6060
})();
6161
```
6262

@@ -68,41 +68,52 @@ To enable sync methods:
6868
```ts
6969
import { hmac } from '@noble/hashes/hmac.js';
7070
import { sha256 } from '@noble/hashes/sha2.js';
71-
secp.etc.hmacSha256Sync = (k, ...m) => hmac(sha256, k, secp.etc.concatBytes(...m));
71+
secp.hashes.hmacSha256 = (key, msg) => hmac(sha256, key, msg);
72+
secp.hashes.sha256 = sha256;
7273
```
7374

74-
### React Native: polyfill getRandomValues and sha512
75+
### React Native: polyfill getRandomValues and sha256
7576

7677
```ts
7778
import 'react-native-get-random-values';
7879
import { hmac } from '@noble/hashes/hmac.js';
7980
import { sha256 } from '@noble/hashes/sha2.js';
80-
secp.etc.hmacSha256Sync = (k, ...m) => hmac(sha256, k, secp.etc.concatBytes(...m));
81-
secp.etc.hmacSha256Async = (k, ...m) => Promise.resolve(secp.etc.hmacSha256Sync(k, ...m));
81+
secp.hashes.hmacSha256 = (key, msg) => hmac(sha256, key, msg);
82+
secp.hashes.sha256 = sha256;
83+
secp.hashes.hmacSha256Async = async (key, msg) => hmac(sha256, key, msg);
84+
secp.hashes.sha256Async = async (msg) => sha256(msg);
8285
```
8386

8487
## API
8588

86-
There are 3 main methods:
89+
There are 4 main methods, which accept Uint8Array-s:
8790

88-
* `getPublicKey(privateKey)`
89-
* `sign(messageHash, privateKey)` and `signAsync(messageHash, privateKey)`
90-
* `verify(signature, messageHash, publicKey)`
91+
* `keygen()`
92+
* `getPublicKey(secretKey)`
93+
* `sign(messageHash, secretKey)` and `signAsync(messageHash, secretKey)`
94+
* `verify(signature, messageHash, publicKey)` and `verifyAsync(signature, messageHash, publicKey)`
9195

92-
Functions generally accept Uint8Array.
93-
There are optional utilities which convert hex strings, utf8 strings or bigints to u8a.
96+
### keygen
97+
98+
```ts
99+
import * as secp from '@noble/secp256k1';
100+
(async () => {
101+
const keys = secp.keygen();
102+
const { secretKey, publicKey } = keys;
103+
})();
104+
```
94105

95106
### getPublicKey
96107

97108
```ts
98-
import { getPublicKey, utils, ProjectivePoint } from '@noble/secp256k1';
99-
const privKey = utils.randomSecretKey();
100-
const pubKey33b = getPublicKey(privKey);
109+
import * as secp from '@noble/secp256k1';
110+
const secretKey = secp.utils.randomSecretKey();
111+
const pubKey33b = secp.getPublicKey(secretKey);
101112

102113
// Variants
103-
const pubKey65b = getPublicKey(privKey, false);
104-
const pubKeyPoint = ProjectivePoint.fromPrivateKey(privKey);
105-
const samePoint = ProjectivePoint.fromHex(pubKeyPoint.toHex());
114+
const pubKey65b = secp.getPublicKey(secretKey, false);
115+
const pubKeyPoint = secp.Point.fromBytes(pubKey65b);
116+
const samePoint = pubKeyPoint.toBytes();
106117
```
107118

108119
Generates 33-byte compressed (default) or 65-byte public key from 32-byte private key.
@@ -111,67 +122,69 @@ Generates 33-byte compressed (default) or 65-byte public key from 32-byte privat
111122

112123
```ts
113124
import * as secp from '@noble/secp256k1';
114-
import { sha256 } from '@noble/hashes/sha256';
115-
import { utf8ToBytes } from '@noble/hashes/utils';
116-
const msg = 'noble cryptography';
117-
const msgHash = sha256(utf8ToBytes(msg));
118-
const priv = secp.utils.randomSecretKey();
119-
120-
const sigA = secp.sign(msgHash, priv);
121-
122-
// Variants
123-
const sigB = await secp.signAsync(msgHash, priv);
124-
const sigC = secp.sign(msgHash, priv, { extraEntropy: true }); // hedged sig
125-
const sigC2 = secp.sign(msgHash, priv, { extraEntropy: Uint8Array.from([0xca, 0xfe]) });
126-
const sigD = secp.sign(msgHash, priv, { lowS: false }); // malleable sig
125+
const { secretKey } = secp.keygen();
126+
const msg = 'hello noble';
127+
const sig = secp.sign(msg, secretKey);
128+
129+
// async
130+
const sigB = await secp.signAsync(msg, secretKey);
131+
132+
// recovered, allows `recoverPublicKey(sigR, msg)`
133+
const sigR = secp.sign(msg, secretKey, { format: 'recovered' });
134+
// custom hash
135+
import { keccak256 } from '@noble/hashes/sha3.js';
136+
const sigH = secp.sign(keccak256(msg), secretKey, { prehash: false });
137+
// hedged sig
138+
const sigC = secp.sign(msg, secretKey, { extraEntropy: true });
139+
const sigC2 = secp.sign(msg, secretKey, { extraEntropy: Uint8Array.from([0xca, 0xfe]) });
140+
// malleable sig
141+
const sigD = secp.sign(msg, secretKey, { lowS: false });
127142
```
128143

129-
Generates low-s deterministic-k RFC6979 ECDSA signature. Requries hash of message,
130-
which means you'll need to do something like `sha256(message)` before signing.
144+
Generates low-s deterministic-k RFC6979 ECDSA signature.
131145

132-
`extraEntropy: true` enables hedged signatures. They incorporate
146+
- Message will be hashed with sha256. If you want to use a different hash function,
147+
make sure to use `{ prehash: false }`.
148+
- `extraEntropy: true` enables hedged signatures. They incorporate
133149
extra randomness into RFC6979 (described in section 3.6),
134150
to provide additional protection against fault attacks.
135151
Check out blog post [Deterministic signatures are not your friends](https://paulmillr.com/posts/deterministic-signatures/).
136152
Even if their RNG is broken, they will fall back to determinism.
137-
138-
Default behavior `lowS: true` prohibits signatures which have (sig.s >= CURVE.n/2n) and is compatible with BTC/ETH.
139-
Setting `lowS: false` allows to create malleable signatures, which is default openssl behavior.
140-
Non-malleable signatures can still be successfully verified in openssl.
153+
- Default behavior `lowS: true` prohibits signatures which have (sig.s >= CURVE.n/2n) and is compatible with BTC/ETH. Setting `lowS: false` allows to create malleable signatures, which is default openssl behavior. Non-malleable signatures can still be successfully verified in openssl.
141154

142155
### verify
143156

144157
```ts
145158
import * as secp from '@noble/secp256k1';
146-
const hex = secp.etc.hexToBytes;
147-
const sig = hex(
148-
'ddc633c5b48a1a6725c31201892715dda3058350f7b444e89d32c33c90d9c9e218d7eaf02c2254e88c3b33d755394b08bcc7efd13df02338510b750b64572983'
149-
);
150-
const msgHash = hex('736403f76264eccc1b77ba58dc8fc690e76b2b1532ba82c736a60f3862082db3');
151-
// const priv = 'd60937c2a1ece169888d4c48717dfcc0e1a7af915505823148cca11859210e9c';
152-
const pubKey = hex('020b6d70b68873ff8fd729adf5cf4bf45021b34236f991768249cba06b11136ec6');
153-
154-
// verify
155-
const isValid = secp.verify(sig, msgHash, pubKey);
156-
const isValidLoose = secp.verify(sig, msgHash, pubKey, { lowS: false });
159+
const { secretKey, publicKey } = secp.keygen();
160+
const msg = 'hello noble';
161+
const sig = secp.sign(msg, secretKey);
162+
const isValid = secp.verify(sig, msg, publicKey);
163+
164+
// custom hash
165+
import { keccak256 } from '@noble/hashes/sha3.js';
166+
const sigH = secp.sign(keccak256(msg), secretKey, { prehash: false });
157167
```
158168

159169
Verifies ECDSA signature.
160-
Default behavior `lowS: true` prohibits malleable signatures which have (`sig.s >= CURVE.n/2n`) and
161-
is compatible with BTC / ETH.
162-
Setting `lowS: false` allows to create signatures, which is default openssl behavior.
170+
171+
- Message will be hashed with sha256. If you want to use a different hash function,
172+
make sure to use `{ prehash: false }`.
173+
- Default behavior `lowS: true` prohibits malleable signatures which have (`sig.s >= CURVE.n/2n`) and
174+
is compatible with BTC / ETH.
175+
Setting `lowS: false` allows to create signatures, which is default openssl behavior.
163176

164177
### getSharedSecret
165178

166179
```ts
167180
import * as secp from '@noble/secp256k1';
168-
const bobsPriv = secp.utils.randomSecretKey();
169-
const alicesPub = secp.getPublicKey(secp.utils.randomSecretKey());
170-
171-
// ECDH between Alice and Bob
172-
const shared33b = secp.getSharedSecret(bobsPriv, alicesPub);
173-
const shared65b = secp.getSharedSecret(bobsPriv, alicesPub, false);
174-
const sharedPoint = secp.ProjectivePoint.fromHex(alicesPub).multiply(bobsPriv);
181+
const alice = secp.keygen();
182+
const bob = secp.keygen();
183+
const shared33b = secp.getSharedSecret(alice.secretKey, bob.publicKey);
184+
const shared65b = secp.getSharedSecret(bob.secretKey, alice.publicKey, false);
185+
const sharedPoint = secp.Point.fromBytes(bob.publicKey).multiply(
186+
secp.etc.secretKeyToScalar(alice.secretKey)
187+
);
175188
```
176189

177190
Computes ECDH (Elliptic Curve Diffie-Hellman) shared secret between
@@ -182,15 +195,15 @@ key A and different key B.
182195
```ts
183196
import * as secp from '@noble/secp256k1';
184197

185-
import { sha256 } from '@noble/hashes/sha256';
186-
import { utf8ToBytes } from '@noble/hashes/utils';
187-
const msg = 'noble cryptography';
188-
const msgHash = sha256(utf8ToBytes(msg));
189-
const priv = secp.utils.randomSecretKey();
190-
const pub1 = secp.getPubkicKey(priv);
191-
const sig = secp.sign(msgHash, priv);
198+
const { secretKey, publicKey } = secp.keygen();
199+
const msg = 'hello noble';
200+
const sigR = secp.sign(msg, secretKey, { format: 'recovered' });
201+
const publicKey2 = secp.recoverPublicKey(sigR, msg);
192202

193-
const pub2 = sig.recoverPublicKey(msgHash);
203+
// custom hash
204+
import { keccak256 } from '@noble/hashes/sha3.js';
205+
const sigR = secp.sign(keccak256(msg), secretKey, { format: 'recovered', prehash: false });
206+
const publicKey2 = secp.recoverPublicKey(sigR, keccak256(msg), { prehash: false });
194207
```
195208

196209
Recover public key from Signature instance with `recovery` bit set.
@@ -199,60 +212,41 @@ Recover public key from Signature instance with `recovery` bit set.
199212

200213
A bunch of useful **utilities** are also exposed:
201214

202-
```typescript
203-
type Bytes = Uint8Array;
204-
const etc: {
205-
hexToBytes: (hex: string) => Bytes;
206-
bytesToHex: (b: Bytes) => string;
207-
concatBytes: (...arrs: Bytes[]) => Bytes;
208-
bytesToNumberBE: (b: Bytes) => bigint;
209-
numberToBytesBE: (num: bigint) => Bytes;
210-
mod: (a: bigint, b?: bigint) => bigint;
211-
invert: (num: bigint, md?: bigint) => bigint;
212-
hmacSha256Async: (key: Bytes, ...msgs: Bytes[]) => Promise<Bytes>;
213-
hmacSha256Sync: HmacFnSync;
214-
hashToPrivateKey: (hash: Hex) => Bytes;
215-
randomBytes: (len: number) => Bytes;
216-
};
217-
const utils: {
218-
normPrivateKeyToScalar: (p: PrivKey) => bigint;
219-
randomSecretKey: () => Bytes; // Uses CSPRNG https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
220-
isValidPrivateKey: (key: Hex) => boolean;
221-
precompute(p: ProjectivePoint, windowSize?: number): ProjectivePoint;
222-
};
223-
class ProjectivePoint {
224-
constructor(px: bigint, py: bigint, pz: bigint);
225-
static readonly BASE: ProjectivePoint;
226-
static readonly ZERO: ProjectivePoint;
227-
static fromAffine(point: AffinePoint): ProjectivePoint;
228-
static fromHex(hex: Hex): ProjectivePoint;
229-
static fromPrivateKey(n: PrivKey): ProjectivePoint;
215+
```ts
216+
import * as secp from '@noble/secp256k1';
217+
218+
const { bytesToHex, hexToBytes, concatBytes, mod, invert, randomBytes } = secp.etc;
219+
const { isValidSecretKey, isValidPublicKey, randomSecretKey } = secp.utils;
220+
const { Point } = secp;
221+
console.log(Point.CURVE(), Point.BASE);
222+
/*
223+
class Point {
224+
static BASE: Point;
225+
static ZERO: Point;
226+
readonly X: bigint;
227+
readonly Y: bigint;
228+
readonly Z: bigint;
229+
constructor(X: bigint, Y: bigint, Z: bigint);
230+
static CURVE(): WeierstrassOpts<bigint>;
231+
static fromAffine(ap: AffinePoint): Point;
232+
static fromBytes(bytes: Bytes): Point;
233+
static fromHex(hex: string): Point;
230234
get x(): bigint;
231235
get y(): bigint;
232-
add(other: ProjectivePoint): ProjectivePoint;
233-
assertValidity(): void;
234-
equals(other: ProjectivePoint): boolean;
235-
multiply(n: bigint): ProjectivePoint;
236-
negate(): ProjectivePoint;
237-
subtract(other: ProjectivePoint): ProjectivePoint;
236+
equals(other: Point): boolean;
237+
is0(): boolean;
238+
negate(): Point;
239+
double(): Point;
240+
add(other: Point): Point;
241+
subtract(other: Point): Point;
242+
multiply(n: bigint): Point;
243+
multiplyUnsafe(scalar: bigint): Point;
238244
toAffine(): AffinePoint;
245+
assertValidity(): Point;
246+
toBytes(isCompressed?: boolean): Bytes;
239247
toHex(isCompressed?: boolean): string;
240-
toRawBytes(isCompressed?: boolean): Bytes;
241-
}
242-
class Signature {
243-
constructor(r: bigint, s: bigint, recovery?: number | undefined);
244-
static fromCompact(hex: Hex): Signature;
245-
readonly r: bigint;
246-
readonly s: bigint;
247-
readonly recovery?: number | undefined;
248-
ok(): Signature;
249-
hasHighS(): boolean;
250-
normalizeS(): Signature;
251-
recoverPublicKey(msgh: Hex): Point;
252-
toBytes(): Bytes;
253-
toCompactHex(): string;
254248
}
255-
CURVE; // curve prime; order; equation params, generator coordinates
249+
*/
256250
```
257251

258252
## Security
@@ -320,13 +314,16 @@ NIST prohibits classical cryptography (RSA, DSA, ECDSA, ECDH) [after 2035](https
320314
Benchmarks measured with Apple M4. [noble-curves](https://github.com/paulmillr/noble-curves) enable faster performance.
321315

322316
```
323-
getPublicKey(utils.randomSecretKey()) x 8,770 ops/sec @ 114μs/op
324-
signAsync x 4,848 ops/sec @ 206μs/op
325-
sign x 7,261 ops/sec @ 137μs/op
326-
verify x 817 ops/sec @ 1ms/op
327-
getSharedSecret x 688 ops/sec @ 1ms/op
328-
recoverPublicKey x 839 ops/sec @ 1ms/op
329-
Point.fromHex (decompression) x 12,937 ops/sec @ 77μs/op
317+
keygen x 7,643 ops/sec @ 130μs/op
318+
sign x 7,620 ops/sec @ 131μs/op
319+
verify x 823 ops/sec @ 1ms/op
320+
getSharedSecret x 707 ops/sec @ 1ms/op
321+
recoverPublicKey x 790 ops/sec @ 1ms/op
322+
323+
signAsync x 4,874 ops/sec @ 205μs/op
324+
verifyAsync x 811 ops/sec @ 1ms/op
325+
326+
Point.fromBytes x 13,656 ops/sec @ 73μs/op
330327
```
331328

332329
## Upgrading
@@ -374,7 +371,7 @@ The goal of v2 is to provide minimum possible JS library which is safe and fast.
374371

375372
- `npm install && npm run build && npm test` will build the code and run tests.
376373
- `npm run bench` will run benchmarks, which may need their deps first (`npm run bench:install`)
377-
- `npm run loc` will count total output size, important to be less than 4KB
374+
- `npm run build:release` will build single non-module file
378375

379376
Check out [github.com/paulmillr/guidelines](https://github.com/paulmillr/guidelines)
380377
for general coding practices and rules.

0 commit comments

Comments
 (0)