Socket
Socket
Sign inDemoInstall

@noble/secp256k1

Package Overview
Dependencies
Maintainers
1
Versions
20
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@noble/secp256k1 - npm Package Compare versions

Comparing version 1.7.1 to 2.0.0

index.d.ts

77

package.json
{
"name": "@noble/secp256k1",
"version": "1.7.1",
"description": "Fastest JS implementation of secp256k1. Independently audited, high-security, 0-dependency ECDSA & Schnorr signatures",
"version": "2.0.0",
"description": "Fastest 4KB JS implementation of secp256k1 elliptic curve. Auditable, high-security, 0-dependency ECDH & ECDSA signatures compliant with RFC6979",
"files": [
"lib"
"index.js",
"index.d.ts",
"index.ts"
],
"main": "lib/index.js",
"module": "lib/esm/index.js",
"types": "lib/index.d.ts",
"type": "module",
"main": "index.js",
"module": "index.js",
"types": "index.d.ts",
"scripts": {
"build": "tsc && tsc -p tsconfig.esm.json",
"build": "tsc",
"build:release": "rollup -c rollup.config.js",
"lint": "prettier --print-width 100 --single-quote --check index.ts",
"format": "prettier --print-width 100 --single-quote --write index.ts",
"test": "jest",
"coverage": "jest --coverage",
"bench": "node test/benchmark.js"
"test": "node test/secp256k1.test.mjs",
"bench": "node test/benchmark.js",
"min": "cd test/build; npm install; npm run terser",
"loc": "echo \"`npm run --silent min | wc -c` symbols `wc -l < index.ts` LOC, `npm run --silent min | gzip -c8 | wc -c`B gzipped\""
},

@@ -27,52 +29,29 @@ "author": "Paul Miller (https://paulmillr.com)",

"license": "MIT",
"browser": {
"crypto": false
},
"devDependencies": {
"@noble/hashes": "1.1.2",
"@rollup/plugin-commonjs": "22.0.0",
"@rollup/plugin-node-resolve": "13.3.0",
"@types/jest": "28.1.1",
"@types/node": "17.0.18",
"@noble/hashes": "1.3.0",
"fast-check": "3.0.0",
"jest": "28.1.0",
"micro-bmark": "0.2.0",
"prettier": "2.6.2",
"rollup": "2.75.5",
"ts-jest": "28.0.4",
"typescript": "4.7.3"
"micro-bmark": "0.3.0",
"micro-should": "0.4.0",
"typescript": "5.0.2"
},
"keywords": [
"secp256k1",
"secp",
"secp256",
"elliptic",
"rfc6979",
"signature",
"ecdsa",
"noble",
"cryptography",
"elliptic curve",
"ecc",
"curve",
"signature",
"ecc",
"rfc6979",
"schnorr",
"sig",
"bip0340",
"bip340",
"ecdsa",
"endomorphism",
"cryptography",
"security",
"noble"
"bitcoin",
"ethereum"
],
"exports": {
".": {
"types": "./lib/index.d.ts",
"import": "./lib/esm/index.js",
"default": "./lib/index.js"
"types": "./index.d.ts",
"default": "./index.js"
}
},
"jest": {
"testRegex": "/test/.*?\\.ts",
"transform": {
"^.+\\.ts$": "ts-jest"
}
},
"funding": [

@@ -79,0 +58,0 @@ {

@@ -1,9 +0,15 @@

# noble-secp256k1 ![Node CI](https://github.com/paulmillr/noble-secp256k1/workflows/Node%20CI/badge.svg) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)
# noble-secp256k1
[Fastest](#speed) JS implementation of [secp256k1](https://www.secg.org/sec2-v2.pdf),
an elliptic curve that could be used for asymmetric encryption,
ECDH key agreement protocol and signature schemes. Supports deterministic **ECDSA** from RFC6979 and **Schnorr** signatures from [BIP0340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki).
[Fastest](#speed) 4KB JS implementation of [secp256k1](https://www.secg.org/sec2-v2.pdf)
elliptic curve. Auditable, high-security, 0-dependency ECDH & ECDSA signatures compliant with RFC6979.
[**Audited**](#security) by an independent security firm. Check out [the online demo](https://paulmillr.com/ecc) and blog post: [Learning fast elliptic-curve cryptography in JS](https://paulmillr.com/posts/noble-secp256k1-fast-ecc/)
The library is a tiny single-feature version of
[noble-curves](https://github.com/paulmillr/noble-curves), with some features
removed. Check out curves as a drop-in replacement with
Schnorr signatures, DER encoding and support for different hash functions.
Take a look at: [Upgrading](#upgrading) section for v1 to v2 transition instructions,
[the online demo](https://paulmillr.com/noble/) and blog post
[Learning fast elliptic-curve cryptography in JS](https://paulmillr.com/posts/noble-secp256k1-fast-ecc/).
### This library belongs to _noble_ crypto

@@ -13,10 +19,10 @@

- No dependencies, one small file
- Easily auditable TypeScript/JS code
- No dependencies, protection against supply chain attacks
- Auditable TypeScript / JS code
- Supported in all major browsers and stable node.js versions
- All releases are signed with PGP keys
- Check out [homepage](https://paulmillr.com/noble/) & all libraries:
[secp256k1](https://github.com/paulmillr/noble-secp256k1),
[ed25519](https://github.com/paulmillr/noble-ed25519),
[bls12-381](https://github.com/paulmillr/noble-bls12-381),
[curves](https://github.com/paulmillr/noble-curves)
(4kb versions [secp256k1](https://github.com/paulmillr/noble-secp256k1),
[ed25519](https://github.com/paulmillr/noble-ed25519)),
[hashes](https://github.com/paulmillr/noble-hashes)

@@ -26,4 +32,3 @@

Use NPM in node.js / browser, or include single file from
[GitHub's releases page](https://github.com/paulmillr/noble-secp256k1/releases):
Browser, deno, node.js and unpkg are supported:

@@ -33,315 +38,169 @@ > npm install @noble/secp256k1

```js
// Common.js and ECMAScript Modules (ESM)
import * as secp from '@noble/secp256k1';
// If you're using single file, use global variable instead: `window.nobleSecp256k1`
// Supports both async and sync methods, see docs
import * as secp from '@noble/secp256k1'; // ESM-only. Use bundler for common.js
// import * as secp from "https://deno.land/x/secp256k1/mod.ts"; // Deno
// import * as secp from "https://unpkg.com/@noble/secp256k1"; // Unpkg
(async () => {
// keys, messages & other inputs can be Uint8Arrays or hex strings
// Uint8Array.from([0xde, 0xad, 0xbe, 0xef]) === 'deadbeef'
const privKey = secp.utils.randomPrivateKey();
const pubKey = secp.getPublicKey(privKey);
const msgHash = await secp.utils.sha256('hello world');
const signature = await secp.sign(msgHash, privKey);
const isValid = secp.verify(signature, msgHash, pubKey);
const privKey = secp.utils.randomPrivateKey(); // Secure random private key
// sha256 of 'hello world'
const msgHash = 'b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9';
const pubKey = secp.getPublicKey(privKey); // Make pubkey from the private key
const signature = await secp.signAsync(msgHash, privKey); // sign
const isValid = secp.verify(signature, msgHash, pubKey); // verify
// Schnorr signatures
const rpub = secp.schnorr.getPublicKey(privKey);
const rsignature = await secp.schnorr.sign(message, privKey);
const risValid = await secp.schnorr.verify(rsignature, message, rpub);
const pubKey2 = getPublicKey(secp.utils.randomPrivateKey()); // Key of user 2
secp.getSharedSecret(privKey, alicesPubkey); // Elliptic curve diffie-hellman
signature.recoverPublicKey(msgHash); // Public key recovery
})();
```
To use the module with [Deno](https://deno.land),
you will need [import map](https://deno.land/manual/linking_to_external_code/import_maps):
Advanced examples:
- `deno run --import-map=imports.json app.ts`
- app.ts: `import * as secp from "https://deno.land/x/secp256k1/mod.ts";`
- imports.json: `{"imports": {"crypto": "https://deno.land/std@0.153.0/node/crypto.ts"}}`
```ts
// 1. Use the shim to enable synchronous methods.
// Only async methods are available by default to keep library dependency-free.
import { hmac } from '@noble/hashes/hmac';
import { sha256 } from '@noble/hashes/sha256';
secp.etc.hmacSha256Sync = (k, ...m) => hmac(sha256, k, secp.etc.concatBytes(...m))
const signature2 = secp.sign(msgHash, privKey); // Can be used now
## API
// 2. Use the shim only for node.js <= 18 BEFORE importing noble-secp256k1.
// The library depends on global variable crypto to work. It is available in
// all browsers and many environments, but node.js <= 18 don't have it.
import { webcrypto } from 'node:crypto';
// @ts-ignore
if (!globalThis.crypto) globalThis.crypto = webcrypto;
- [`getPublicKey(privateKey)`](#getpublickeyprivatekey)
- [`sign(msgHash, privateKey)`](#signmsghash-privatekey)
- [`verify(signature, msgHash, publicKey)`](#verifysignature-msghash-publickey)
- [`getSharedSecret(privateKeyA, publicKeyB)`](#getsharedsecretprivatekeya-publickeyb)
- [`recoverPublicKey(hash, signature, recovery)`](#recoverpublickeyhash-signature-recovery)
- [`schnorr.getPublicKey(privateKey)`](#schnorrgetpublickeyprivatekey)
- [`schnorr.sign(message, privateKey)`](#schnorrsignmessage-privatekey)
- [`schnorr.verify(signature, message, publicKey)`](#schnorrverifysignature-message-publickey)
- [Utilities](#utilities)
// Other stuff
// Malleable signatures, incompatible with BTC/ETH, but compatible with openssl
// `lowS: true` prohibits signatures which have (sig.s >= CURVE.n/2n) because of
// malleability
const signatureMalleable = secp.sign(msgHash, privKey, { lowS: false });
##### `getPublicKey(privateKey)`
```typescript
function getPublicKey(privateKey: Uint8Array | string | bigint, isCompressed = false): Uint8Array;
// Signatures with improved security: adds additional entropy `k` for
// deterministic signature, follows section 3.6 of RFC6979. When `true`, it
// would be filled with 32b from CSPRNG. **Strongly recommended** to pass `true`
// to improve security:
// - No disadvantage: if an entropy generator is broken, sigs would be the same
// as they are without the option
// - It would help a lot in case there is an error somewhere in `k` gen.
// Exposing `k` could leak private keys
// - Sigs with extra entropy would have different `r` / `s`, which means they
// would still be valid, but may break some test vectors if you're
// cross-testing against other libs
const signatureImproved = secp.sign(msgHash, privKey, { extraEntropy: true });
```
Creates public key for the corresponding private key. The default is full 65-byte key.
## API
- `isCompressed = false` determines whether to return compact (33-byte), or full (65-byte) key.
There are 3 main methods: `getPublicKey(privateKey)`,
`sign(messageHash, privateKey)` and
`verify(signature, messageHash, publicKey)`.
Internally, it does `Point.BASE.multiply(privateKey)`. If you need actual `Point` instead of
`Uint8Array`, use `Point.fromPrivateKey(privateKey)`.
```typescript
type Hex = Uint8Array | string;
##### `sign(msgHash, privateKey)`
// Generates public key from 32-byte private key.
// isCompressed=true by default, meaning 33-byte output. Set to false for 65b.
function getPublicKey(privateKey: Hex, isCompressed?: boolean): Uint8Array;
// Use:
// - `ProjectivePoint.fromPrivateKey(privateKey)` for Point instance
// - `ProjectivePoint.fromHex(publicKey)` to convert hex / bytes into Point.
```typescript
// Generates low-s deterministic-k RFC6979 ECDSA signature.
// Use with `extraEntropy: true` to improve security.
function sign(
msgHash: Uint8Array | string,
privateKey: Uint8Array | string,
opts?: Options
): Promise<Uint8Array>;
function sign(
msgHash: Uint8Array | string,
privateKey: Uint8Array | string,
opts?: Options
): Promise<[Uint8Array, number]>;
```
messageHash: Hex, // message hash (not message) which would be signed
privateKey: Hex, // private key which will sign the hash
opts?: { lowS: boolean, extraEntropy: boolean | Hex } // optional params
): Signature;
function signAsync(
messageHash: Hex,
privateKey: Hex,
opts?: { lowS: boolean; extraEntropy: boolean | Hex }
): Promise<Signature>;
Generates low-s deterministic ECDSA signature as per RFC6979.
- `msgHash: Uint8Array | string` - 32-byte message hash which would be signed
- `privateKey: Uint8Array | string | bigint` - private key which will sign the hash
- `options?: Options` - _optional_ object related to signature value and format with following keys:
- `recovered: boolean = false` - whether the recovered bit should be included in the result. In this case, the result would be an array of two items.
- `canonical: boolean = true` - whether a signature `s` should be no more than 1/2 prime order.
`true` (default) makes signatures compatible with libsecp256k1,
`false` makes signatures compatible with openssl
- `der: boolean = true` - whether the returned signature should be in DER format. If `false`, it would be in Compact format (32-byte r + 32-byte s)
- `extraEntropy: Uint8Array | string | true` - additional entropy `k'` for deterministic signature, follows section 3.6 of RFC6979. When `true`, it would automatically be filled with 32 bytes of cryptographically secure entropy. **Strongly recommended** to pass `true` to improve security:
- Schnorr signatures are doing it every time
- It would help a lot in case there is an error somewhere in `k` generation. Exposing `k` could leak private keys
- If the entropy generator is broken, signatures would be the same as they are without the option
- Signatures with extra entropy would have different `r` / `s`, which means they
would still be valid, but may break some test vectors if you're cross-testing against other libs
The function is asynchronous because we're utilizing built-in HMAC API to not rely on dependencies.
```ts
(async () => {
// Signatures with improved security
const signatureE = await secp.sign(msgHash, privKey, { extraEntropy: true });
// Malleable signatures, but compatible with openssl
const signatureM = await secp.sign(msgHash, privKey, { canonical: false });
})();
```
```typescript
function signSync(
msgHash: Uint8Array | string,
privateKey: Uint8Array | string,
opts?: Options
): Uint8Array | [Uint8Array, number];
```
`signSync` counterpart could also be used, you need to set `utils.hmacSha256Sync` to a function with signature `key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array`. Example with `noble-hashes` package:
```ts
import { hmac } from '@noble/hashes/hmac';
import { sha256 } from '@noble/hashes/sha256';
secp256k1.utils.hmacSha256Sync = (key, ...msgs) => hmac(sha256, key, secp256k1.utils.concatBytes(...msgs))
secp256k1.utils.sha256Sync = (...msgs) => sha256(secp256k1.utils.concatBytes(...msgs))
// Can be used now
secp256k1.signSync(msgHash, privateKey);
schnorr.signSync(message, privateKey)
```
##### `verify(signature, msgHash, publicKey)`
```typescript
// Verifies ECDSA signature.
// lowS option Ensures a signature.s is in the lower-half of CURVE.n.
// Used in BTC, ETH.
// `{ lowS: false }` should only be used if you need OpenSSL-compatible signatures
function verify(
signature: Uint8Array | string,
msgHash: Uint8Array | string,
publicKey: Uint8Array | string
signature: Hex | Signature, // returned by the `sign` function
messageHash: Hex, // message hash (not message) that must be verified
publicKey: Hex, // public (not private) key
opts?: { lowS: boolean } // optional params; { lowS: true } by default
): boolean;
function verify(signature: Signature, msgHash: Uint8Array | string, publicKey: Point): boolean;
```
- `signature: Uint8Array | string | { r: bigint, s: bigint }` - object returned by the `sign` function
- `msgHash: Uint8Array | string` - message hash that needs to be verified
- `publicKey: Uint8Array | string | Point` - e.g. that was generated from `privateKey` by `getPublicKey`
- `options?: Options` - _optional_ object related to signature value and format
- `strict: boolean = true` - whether a signature `s` should be no more than 1/2 prime order.
`true` (default) makes signatures compatible with libsecp256k1,
`false` makes signatures compatible with openssl
- Returns `boolean`: `true` if `signature == hash`; otherwise `false`
##### `getSharedSecret(privateKeyA, publicKeyB)`
```typescript
// Computes ECDH (Elliptic Curve Diffie-Hellman) shared secret between
// key A and different key B.
function getSharedSecret(
privateKeyA: Uint8Array | string | bigint,
publicKeyB: Uint8Array | string | Point,
isCompressed = false
privateKeyA: Uint8Array | string, // Alices's private key
publicKeyB: Uint8Array | string, // Bob's public key
isCompressed = true // optional arg. (default) true=33b key, false=65b.
): Uint8Array;
```
// Use `ProjectivePoint.fromHex(publicKeyB).multiply(privateKeyA)` for Point instance
Computes ECDH (Elliptic Curve Diffie-Hellman) shared secret between a private key and a different public key.
- To get Point instance, use `Point.fromHex(publicKeyB).multiply(privateKeyA)`
- `isCompressed = false` determines whether to return compact (33-byte), or full (65-byte) key
- If you have one public key you'll be creating lots of secrets against,
consider massive speed-up by using precomputations:
```js
const pub = secp.utils.precompute(8, publicKeyB);
// Use pub everywhere instead of publicKeyB
getSharedSecret(privKey, pub); // Now 12x faster
```
##### `recoverPublicKey(hash, signature, recovery)`
```typescript
function recoverPublicKey(
msgHash: Uint8Array | string,
signature: Uint8Array | string,
recovery: number,
isCompressed = false
// Recover public key from Signature instance with `recovery` bit set
signature.recoverPublicKey(
msgHash: Uint8Array | string
): Uint8Array | undefined;
```
Recovers public key from message hash, signature & recovery bit. The default is full 65-byte key.
A bunch of useful **utilities** are also exposed:
- `msgHash: Uint8Array | string` - message hash which would be signed
- `signature: Uint8Array | string | { r: bigint, s: bigint }` - object returned by the `sign` function
- `recovery: number` - recovery bit returned by `sign` with `recovered` option
- `isCompressed = false` determines whether to return compact (33-byte), or full (65-byte) key
Public key is generated by doing scalar multiplication of a base Point(x, y) by a fixed
integer. The result is another `Point(x, y)` which we will by default encode to hex Uint8Array.
If signature is invalid - function will return `undefined` as result.
To get Point instance, use `Point.fromSignature(hash, signature, recovery)`.
##### `schnorr.getPublicKey(privateKey)`
```typescript
function schnorrGetPublicKey(privateKey: Uint8Array | string): Uint8Array;
```
Calculates 32-byte public key from a private key.
_Warning:_ it is incompatible with non-schnorr pubkey. Specifically, its _y_ coordinate may be flipped. See [BIP340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki) for clarification.
##### `schnorr.sign(message, privateKey)`
```typescript
function schnorrSign(
message: Uint8Array | string,
privateKey: Uint8Array | string,
auxilaryRandom?: Uint8Array
): Promise<Uint8Array>;
```
Generates Schnorr signature as per BIP0340. Asynchronous, so use `await`.
- `message: Uint8Array | string` - message (not hash) which would be signed
- `privateKey: Uint8Array | string | bigint` - private key which will sign the hash
- `auxilaryRandom?: Uint8Array` — optional 32 random bytes. By default, the method gathers cryptogarphically secure entropy
- Returns Schnorr signature in Hex format.
##### `schnorr.verify(signature, message, publicKey)`
```typescript
function schnorrVerify(
signature: Uint8Array | string,
message: Uint8Array | string,
publicKey: Uint8Array | string
): boolean;
```
- `signature: Uint8Array | string | { r: bigint, s: bigint }` - object returned by the `sign` function
- `message: Uint8Array | string` - message (not hash) that needs to be verified
- `publicKey: Uint8Array | string | Point` - e.g. that was generated from `privateKey` by `getPublicKey`
- Returns `boolean`: `true` if `signature == hash`; otherwise `false`
#### Utilities
secp256k1 exposes a few internal utilities for improved developer experience.
```js
// Default output is Uint8Array. If you need hex string as an output:
console.log(secp.utils.bytesToHex(pubKey));
```
```typescript
const utils: {
// Can take 40 or more bytes of uniform input e.g. from CSPRNG or KDF
// and convert them into private key, with the modulo bias being neglible.
// As per FIPS 186 B.1.1.
hashToPrivateKey: (hash: Hex) => Uint8Array;
// Returns `Uint8Array` of 32 cryptographically secure random bytes that can be used as private key
randomPrivateKey: () => Uint8Array;
// Checks private key for validity
isValidPrivateKey(privateKey: PrivKey): boolean;
// Returns `Uint8Array` of x cryptographically secure random bytes.
randomBytes: (bytesLength?: number) => Uint8Array;
// Converts Uint8Array to hex string
bytesToHex(uint8a: Uint8Array): string;
hexToBytes(hex: string): Uint8Array;
concatBytes(...arrays: Uint8Array[]): Uint8Array;
// Modular division over curve prime
mod: (number: number | bigint, modulo = CURVE.P): bigint;
// Modular inversion
invert(number: bigint, modulo?: bigint): bigint;
sha256: (message: Uint8Array) => Promise<Uint8Array>;
hmacSha256: (key: Uint8Array, ...messages: Uint8Array[]) => Promise<Uint8Array>;
// You can set up your synchronous methods for `signSync`/`signSchnorrSync` to work.
// The argument order is identical to async methods from above
sha256Sync: undefined;
hmacSha256Sync: undefined;
// BIP0340-style tagged hashes
taggedHash: (tag: string, ...messages: Uint8Array[]) => Promise<Uint8Array>;
taggedHashSync: (tag: string, ...messages: Uint8Array[]) => Uint8Array;
// 1. Returns cached point which you can use to pass to `getSharedSecret` or to `#multiply` by it.
// 2. Precomputes point multiplication table. Is done by default on first `getPublicKey()` call.
// If you want your first getPublicKey to take 0.16ms instead of 20ms, make sure to call
// utils.precompute() somewhere without arguments first.
precompute(windowSize?: number, point?: Point): Point;
type Bytes = Uint8Array;
export declare const etc: {
hexToBytes: (hex: string) => Bytes;
bytesToHex: (b: Bytes) => string;
concatBytes: (...arrs: Bytes[]) => Uint8Array;
bytesToNumberBE: (b: Bytes) => bigint;
numberToBytesBE: (num: bigint) => Bytes;
mod: (a: bigint, b?: bigint) => bigint;
invert: (num: bigint, md?: bigint) => bigint;
hmacSha256Async: (key: Bytes, ...msgs: Bytes[]) => Promise<Bytes>;
hmacSha256Sync: HmacFnSync;
hashToPrivateKey: (hash: Hex) => Bytes;
randomBytes: (len: number) => Bytes;
};
secp256k1.CURVE.P // Field, 2 ** 256 - 2 ** 32 - 977
secp256k1.CURVE.n // Order, 2 ** 256 - 432420386565659656852420866394968145599
secp256k1.Point.BASE // new secp256k1.Point(Gx, Gy) where
// Gx = 55066263022277343669578718895168534326250603453777594175500187360389116729240n
// Gy = 32670510020758816978083085130507043184471273380659243275938904335757337482424n;
// Elliptic curve point in Affine (x, y) coordinates.
secp256k1.Point {
constructor(x: bigint, y: bigint);
// Supports compressed and non-compressed hex
static fromHex(hex: Uint8Array | string);
static fromPrivateKey(privateKey: Uint8Array | string | number | bigint);
static fromSignature(
msgHash: Hex,
signature: Signature,
recovery: number | bigint
): Point | undefined {
toRawBytes(isCompressed = false): Uint8Array;
toHex(isCompressed = false): string;
export declare const utils: {
normPrivateKeyToScalar: (p: PrivKey) => bigint;
randomPrivateKey: () => Bytes;
isValidPrivateKey: (key: Hex) => boolean;
precompute(p: Point, windowSize?: number): Point;
};
class ProjectivePoint {
readonly px: bigint;
readonly py: bigint;
readonly pz: bigint;
constructor(px: bigint, py: bigint, pz: bigint);
static readonly BASE: Point;
static readonly ZERO: Point;
static fromHex(hex: Hex): Point;
static fromPrivateKey(n: PrivKey): Point;
get x(): bigint;
get y(): bigint;
equals(other: Point): boolean;
add(other: Point): Point;
multiply(n: bigint): Point;
negate(): Point;
add(other: Point): Point;
subtract(other: Point): Point;
// Constant-time scalar multiplication.
multiply(scalar: bigint | Uint8Array): Point;
toAffine(): AffinePoint;
assertValidity(): Point;
toHex(isCompressed?: boolean): string;
toRawBytes(isCompressed?: boolean): Uint8Array;
}
secp256k1.Signature {
constructor(r: bigint, s: bigint);
// DER encoded ECDSA signature
static fromDER(hex: Uint8Array | string);
// R, S 32-byte each
static fromCompact(hex: Uint8Array | string);
assertValidity(): void;
hasHighS(): boolean; // high-S sigs cannot be produced using { canonical: true }
toDERRawBytes(): Uint8Array;
toDERHex(): string;
class Signature {
readonly r: bigint;
readonly s: bigint;
readonly recovery?: number | undefined;
constructor(r: bigint, s: bigint, recovery?: number | undefined);
ok(): Signature;
static fromCompact(hex: Hex): Signature;
hasHighS(): boolean;
recoverPublicKey(msgh: Hex): Point;
toCompactRawBytes(): Uint8Array;
toCompactHex(): string;
}
CURVE // curve prime; order; equation params, generator coordinates
```

@@ -351,27 +210,41 @@

Noble is production-ready.
The module is production-ready.
It is cross-tested against [noble-curves](https://github.com/paulmillr/noble-curves),
and has similar security.
1. The library has been audited by an independent security firm cure53: [PDF](https://cure53.de/pentest-report_noble-lib.pdf). See [changes since audit](https://github.com/paulmillr/noble-secp256k1/compare/1.2.0..main).
- The audit has been [crowdfunded](https://gitcoin.co/grants/2451/audit-of-noble-secp256k1-cryptographic-library) by community with help of [Umbra.cash](https://umbra.cash).
2. The library has also been fuzzed by [Guido Vranken's cryptofuzz](https://github.com/guidovranken/cryptofuzz). You can run the fuzzer by yourself to check it.
1. The current version is rewrite of v1, which has been audited by cure53:
[PDF](https://cure53.de/pentest-report_noble-lib.pdf) (funded by [Umbra.cash](https://umbra.cash) & community).
2. It's being fuzzed by [Guido Vranken's cryptofuzz](https://github.com/guidovranken/cryptofuzz):
run the fuzzer by yourself to check.
We're using built-in JS `BigInt`, which is potentially vulnerable to [timing attacks](https://en.wikipedia.org/wiki/Timing_attack) as [per official spec](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#cryptography). But, _JIT-compiler_ and _Garbage Collector_ make "constant time" extremely hard to achieve in a scripting language. Which means _any other JS library doesn't use constant-time bigints_. Including bn.js or anything else. 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've hardened implementation of ec curve multiplication to be algorithmically constant time.
Our EC multiplication is hardened to be algorithmically constant time.
We're using built-in JS `BigInt`, which is potentially vulnerable to
[timing attacks](https://en.wikipedia.org/wiki/Timing_attack) as
[per MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#cryptography).
But, _JIT-compiler_ and _Garbage Collector_ make "constant time" extremely hard
to achieve in a scripting language. Which means _any other JS library doesn't
use constant-time bigints_. Including bn.js or anything else.
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.
We however 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.
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.
## Speed
Benchmarks measured with Apple M2 on MacOS 12 with node.js 18.8.
Use [noble-curves](https://github.com/paulmillr/noble-curves) if you need even higher performance.
getPublicKey(utils.randomPrivateKey()) x 7,093 ops/sec @ 140μs/op
sign x 5,615 ops/sec @ 178μs/op
signSync (@noble/hashes) x 5,209 ops/sec @ 191μs/op
verify x 1,114 ops/sec @ 896μs/op
recoverPublicKey x 1,018 ops/sec @ 982μs/op
getSharedSecret aka ecdh x 665 ops/sec @ 1ms/op
getSharedSecret (precomputed) x 7,426 ops/sec @ 134μs/op
Point.fromHex (decompression) x 14,582 ops/sec @ 68μs/op
schnorr.sign x 805 ops/sec @ 1ms/op
schnorr.verify x 1,129 ops/sec @ 885μs/op
Benchmarks measured with Apple M2 on MacOS 13 with node.js 19.
getPublicKey(utils.randomPrivateKey()) x 5,540 ops/sec @ 180μs/op
sign x 3,301 ops/sec @ 302μs/op
verify x 517 ops/sec @ 1ms/op
getSharedSecret x 433 ops/sec @ 2ms/op
recoverPublicKey x 526 ops/sec @ 1ms/op
Point.fromHex (decompression) x 8,415 ops/sec @ 118μs/op
Compare to other libraries on M1 (`openssl` uses native bindings, not JS):

@@ -398,4 +271,2 @@

Check out a blog post about this library: [Learning fast elliptic-curve cryptography in JS](https://paulmillr.com/posts/noble-secp256k1-fast-ecc/).
1. Clone the repository.

@@ -406,6 +277,52 @@ 2. `npm install` to install build dependencies like TypeScript

Special thanks to [Roman Koblov](https://github.com/romankoblov), who have helped to improve scalar multiplication speed.
Special thanks to [Roman Koblov](https://github.com/romankoblov), who have
helped to improve scalar multiplication speed.
## Upgrading
noble-secp256k1 v2 features improved security and smaller attack surface.
The goal of v2 is to provide minimum possible JS library which is safe and fast.
That means the library was reduced 4x, to just over 400 lines. In order to
achieve the goal, **some features were moved** to
[noble-curves](https://github.com/paulmillr/noble-curves), which is
even safer and faster drop-in replacement library with same API.
Switch to curves if you intend to keep using these features:
- DER encoding: toDERHex, toDERRawBytes, signing / verification of DER sigs
- Schnorr signatures
- Using `utils.precompute()` for non-base point
- Support for environments which don't support bigint literals
- Common.js support
- Support for node.js 18 and older without [shim](#usage)
Other changes for upgrading from @noble/secp256k1 1.7 to 2.0:
- `getPublicKey`
- now produce 33-byte compressed signatures by default
- to use old behavior, which produced 65-byte uncompressed keys, set
argument `isCompressed` to `false`: `getPublicKey(priv, false)`
- `sign`
- is now sync; use `signAsync` for async version
- now returns `Signature` instance with `{ r, s, recovery }` properties
- `canonical` option was renamed to `lowS`
- `recovered` option has been removed because recovery bit is always returned now
- `der` option has been removed. There are 2 options:
1. Use compact encoding: `fromCompact`, `toCompactRawBytes`, `toCompactHex`.
Compact encoding is simply a concatenation of 32-byte r and 32-byte s.
2. If you must use DER encoding, switch to noble-curves (see above).
- `verify`
- `strict` option was renamed to `lowS`
- `getSharedSecret`
- now produce 33-byte compressed signatures by default
- to use old behavior, which produced 65-byte uncompressed keys, set
argument `isCompressed` to `false`: `getSharedSecret(a, b, false)`
- `recoverPublicKey(msg, sig, rec)` was changed to `sig.recoverPublicKey(msg)`
- `number` type for private keys have been removed: use `bigint` instead
- `Point` (2d xy) has been changed to `ProjectivePoint` (3d xyz)
- `utils` were split into `utils` (same api as in noble-curves) and
`etc` (`hmacSha256Sync` and others)
## License
MIT (c) Paul Miller [(https://paulmillr.com)](https://paulmillr.com), see LICENSE file.
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc