secp256k1, an elliptic curve that could be used for assymetric encryption, ECDH key agreement protocol and deterministic ECDSA signature scheme from RFC 6939.
Algorithmically resistant to timing attacks. Tested against thousands of vectors from tiny-secp256k1.
noble-crypto — high-security, easily auditable set of contained cryptographic libraries and tools.
- No dependencies, one small file
- Easily auditable TypeScript/JS code
- Uses es2019 bigint. Supported in Chrome, Firefox, node 10+
- All releases are signed and trusted
- Check out all libraries: secp256k1, ed25519, bls12-381, ripemd160, secretbox-aes-gcm
npm install noble-secp256k1
import * as secp256k1 from "noble-secp256k1";
// You can also pass Uint8Array and BigInt.
const privateKey = "00000000000000000000000000000000000000a665a45920422f9d417e4867ef";
const messageHash = "9c1185a5c5e9fc54612808977ee8f548b2258d31";
const publicKey = secp256k1.getPublicKey(privateKey);
const signature = secp256k1.sign(messageHash, privateKey);
const isMessageSigned = secp256k1.verify(signature, messageHash, publicKey);
// Or:
// const privateKey = Uint8Array.from([
// 0xa6, 0x65, 0xa4, 0x59, 0x20, 0x42, 0x2f,
// 0x9d, 0x41, 0x7e, 0x48, 0x67, 0xef
// ]);
// const privateKey = 0xa665a45920422f9d417e4867efn;
getPublicKey(privateKey)
getSharedSecret(privateKeyA, publicKeyB)
sign(hash, privateKey)
verify(signature, hash)
recoverPublicKey(hash, signature, recovery)
- Helpers
function getPublicKey(privateKey: Uint8Array, isCompressed?: false): Uint8Array;
function getPublicKey(privateKey: string, isCompressed?: false): string;
function getPublicKey(privateKey: bigint): Uint8Array;
privateKey
will be used to generate public 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.
isCompressed
(default is false
) determines whether the output should contain y
coordinate of the point.
To get Point instance, use Point.fromPrivateKey(privateKey)
.
function getSharedSecret(privateKeyA: Uint8Array, publicKeyB: Uint8Array): Uint8Array;
function getSharedSecret(privateKeyA: string, publicKeyB: string): string;
function getSharedSecret(privateKeyA: bigint, publicKeyB: Point): Uint8Array;
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)
.
function sign(msgHash: Uint8Array, privateKey: Uint8Array, opts?: Options): Promise<Uint8Array>;
function sign(msgHash: string, privateKey: string, opts?: Options): Promise<string>;
function sign(msgHash: Uint8Array, privateKey: Uint8Array, opts?: Options): Promise<[Uint8Array | string, number]>;
Generates deterministic ECDSA signature as per RFC6979. Asynchronous, so use await
.
msgHash: Uint8Array | string
- message hash which would be signedprivateKey: Uint8Array | string | bigint
- private key which will sign the hashoptions?: Options
- optional object related to signature value and formatoptions?.recovered: boolean = false
- determines whether the recovered bit should be included in the result. In this case, the result would be an array of two items.options?.canonical: boolean = false
- determines whether a signatures
should be no more than 1/2 prime order- Returns DER encoded ECDSA signature, as hex uint8a / string and recovered bit if
options.recovered == true
.
function verify(signature: Uint8Array, msgHash: Uint8Array, publicKey: Uint8Array): boolean
function verify(signature: string, msgHash: string, publicKey: string): boolean
signature: Uint8Array | string | { r: bigint, s: bigint }
- object returned by thesign
functionmsgHash: Uint8Array | string
- message hash that needs to be verifiedpublicKey: Uint8Array | string | Point
- e.g. that was generated fromprivateKey
bygetPublicKey
- Returns
boolean
:true
ifsignature == hash
; otherwisefalse
export declare function recoverPublicKey(msgHash: string, signature: string, recovery: number): string | undefined;
export declare function recoverPublicKey(msgHash: Uint8Array, signature: Uint8Array, recovery: number): Uint8Array | undefined;
msgHash: Uint8Array | string
- message hash which would be signedsignature: Uint8Array | string | { r: bigint, s: bigint }
- object returned by thesign
functionrecovery: number
- recovery bit returned bysign
withrecovered
option Public key is generated by doing scalar multiplication of a base Point(x, y) by a fixed integer. The result is anotherPoint(x, y)
which we will by default encode to hex Uint8Array. If signature is invalid - function will returnundefined
as result.
To get Point instance, use Point.fromSignature(hash, signature, recovery)
.
// 𝔽p
secp256k1.P // 2 ** 256 - 2 ** 32 - 977
// Prime order
secp256k1.PRIME_ORDER // 2 ** 256 - 432420386565659656852420866394968145599
// Base point
secp256k1.BASE_POINT // new secp256k1.Point(x, y) where
// x = 55066263022277343669578718895168534326250603453777594175500187360389116729240n
// y = 32670510020758816978083085130507043184471273380659243275938904335757337482424n;
// Elliptic curve point
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 {
toHex(): string;
add(other: Point): Point;
// Constant-time scalar multiplication.
multiply(scalar: bigint | Uint8Array): Point;
}
secp256k1.SignResult {
constructor(r: bigint, s: bigint);
// DER encoded ECDSA signature
static fromHex(hex: Uint8Array | string);
toHex(): string;
}
- Clone the repository.
npm install
to install build dependencies like TypeScriptnpm run compile
to compile TypeScript codenpm run test
to run jest ontest/index.ts
Noble is production-ready & secure. Our goal is to have it audited by a good security expert.
We're using built-in JS BigInt
, which is "unsuitable for use in cryptography" as per official spec. This means that the lib is potentially vulnerable to timing attacks. 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 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 rootkits with every
npm install
. Our goal is to minimize this attack vector. - We've hardened implementation of koblitz curve multiplication to be algorithmically timing-resistant.
MIT (c) Paul Miller (https://paulmillr.com), see LICENSE file.