Library / SDK
paulmillr/noble-secp256k1 avatar
paulmillr/noble-secp256k1

noble-secp256k1: auditable secp256k1 signatures in a few kilobytes

Fastest 5KB JS implementation of secp256k1 signatures and ECDH

887 stars127 forksTypeScriptMIT

At a glance

What is it?
noble-secp256k1 is a TypeScript implementation of secp256k1 ECDSA, Schnorr and ECDH in about 4.8 kilobytes gzipped, with RFC6979 deterministic signatures and hedged signing available. It suits teams that want a dependency you can read, for signing and key agreement on secp256k1 chains.
Who is it for?
noble-secp256k1 is a good fit for a project that signs or derives keys on a secp256k1 chain and wants a dependency small enough to read in an afternoon, and for teams whose auditors prefer code with no transitive tree. It is the wrong choice if you need the features that were deliberately left out, since multi-scalar multiplication, DER encoding and custom point precomputes live in noble-curves instead, and the documentation says so rather than leaving you to discover it.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 69 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 7, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • paulmillr/noble-secp256k1887 ★ · +0
Star history
Repository2026-10-212026-11-07
paulmillr/noble-secp256k1887887

0 Stars in the last 17 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Small enough to read, which is the design goal

The stated pitch is the size and the auditability that comes with it. The project describes itself as the fastest 5KB JavaScript implementation of secp256k1 signatures and ECDH, with the size refined in the feature list to 4.8KB gzipped and 10 to 25 times smaller than similar libraries. The reason the module is sister project of noble-curves is stated directly in the readme, along with the guidance to use noble-secp256k1 if you need smaller attack surface and better auditability, and to switch to noble-curves, which it calls a drop-in, if you need features like multi-scalar multiplication, DER encoding or custom point precomputes. That is a rare and useful thing for a library to tell you in its own readme, and it tells you the trade is deliberate. The noble family as a whole is described as a high-security, easily auditable set of contained cryptographic libraries and tools, with zero or minimal dependencies, highly readable TypeScript and JavaScript code, and PGP-signed releases with transparent npm builds. For a project whose auditors want to read the signature code, that last property matters as much as the size.

Three primitives, one curve

The feature list names what is implemented. ECDSA signatures compliant with RFC6979, which is the deterministic nonce standard that makes a signature reproducible from the key and the message. Schnorr signatures compliant with BIP340, the scheme Bitcoin standardised. Elliptic Curve Diffie-Hellman, ECDH, for key agreement. And support for hedged signatures, described as guarding against fault attacks. That is the whole surface, and the API section confirms it with the four main methods that accept Uint8Array values: keygen, getPublicKey, sign and verify, each of the latter two with a sync and an async variant. The usage example shows all three primitives in one block, with keygen producing a secret and public key, a message encoded with TextEncoder, a signature produced with signAsync and checked with verifyAsync, and then the ECDH case with two key pairs and a shared secret, followed by a recovery case where a signature is produced in the recovered format and the public key is pulled back out of it. The Schnorr case is a separate block using the schnorr namespace with its own keygen, signAsync and verifyAsync. The library is published to npm as @noble/secp256k1 and to the JSR registry as jsr:@noble/secp256k1, and it supports all major platforms and runtimes.

The signing options are where interoperability is decided

The sign method has a set of options that are not decoration, they determine whether your signature is accepted by the chain you are targeting. The message is hashed with sha256 by default, and if you want to use a different hash function you must pass the prehash false option, which the documentation is explicit about because getting that wrong produces a signature over the wrong bytes. The default behaviour is low-s true, which prohibits signatures where the s value is at or above half the curve order, and it is described as compatible with BTC and ETH, while setting lowS to false allows malleable signatures, which is the default OpenSSL behaviour. The extraEntropy option enables hedged signatures, which incorporate extra randomness into the RFC6979 construction as described in section 3.6, giving additional protection against fault attacks, and the linked article on deterministic signatures not being your friends explains the reasoning, with the note that even if the random number generator is broken they will fall back to determinism. The format option can produce a recovered signature that allows the public key to be recovered from it, which is what the recovery example in the usage section uses. The generated signature is described as a low-s deterministic-k RFC6979 ECDSA signature, and keygen is the one call that produces the key pair.

Async by default, and why the hashes are yours to supply

A design choice that surprises people is that only async methods are available by default, and the stated reason is to keep the library dependency-free. Hashing is therefore not bundled, and the library exposes a hashes object you populate yourself. The enabling snippet is short:

ts
import * as secp from '@noble/secp256k1';
import { hmac } from '@noble/hashes/hmac.js';
import { sha256 } from '@noble/hashes/sha2.js';
secp.hashes.hmacSha256 = (key, msg) => hmac(sha256, key, msg);
secp.hashes.sha256 = sha256;

The reason is a dependency cycle rather than an oversight. RFC6979 needs an HMAC, and the deterministic nonce derivation needs that HMAC, and the natural place for a hash implementation is a separate library that the signature library then depends on, which would break the zero dependency property and add a large amount of code for a project that only needs a few kilobytes. So the hashes are injected, and the cost is that you have to do it. The sync methods are enabled by installing @noble/hashes, and the same injection covers the async hash slots as well. The package metadata reflects the design, since the only dependencies listed in the dev section are the hashes package for testing, a test framework, prettier and TypeScript, and there is no runtime dependency block at all. The files array ships just index.js, index.d.ts and index.ts, and the package is marked as an ES module with side effects false, so a bundler can tree-shake it.

React Native is the awkward case, and it says so

The platform caveat is handled honestly rather than hidden. The documentation states that React Native does not provide secure getRandomValues by default, and that this cannot be securely polyfilled from the library's end, so a React Native specific compile-time dependency is needed. The example shows the shape of that, importing a random values polyfill and then wiring up the hash functions including the async variants:

ts
import 'react-native-get-random-values';
import * as secp from '@noble/secp256k1';
import { hmac } from '@noble/hashes/hmac.js';
import { sha256 } from '@noble/hashes/sha2.js';
secp.hashes.hmacSha256 = (key, msg) => hmac(sha256, key, msg);
secp.hashes.sha256 = sha256;

The reasoning is worth repeating because it is a good general principle for anyone writing crypto in JavaScript. A random number source that is not secure cannot be made secure by a wrapper, so the library declines to pretend, and a user who skips the polyfill gets a key generator that is predictable rather than one that throws. The same instinct runs through the rest of the project. There is an 898-byte version of the library kept in the test directory for learning purposes, created for an article on learning fast elliptic curve cryptography, and there is a note that a WebAssembly version of the noble libraries exists as a separate project, which is the route for a runtime where JavaScript performance on curve arithmetic is not acceptable.

Releases, benchmarks and the audit trail

The release history shows the project is actively maintained, with 3.2.0 on 2026-08-30, 3.1.0 on 2026-04-11 and 3.0.0 on 2025-08-25, and the last push on 2026-08-30. The major version at 3.0.0 came with the ECMAScript module conversion, since the package is now declared as type module with an exports-free single entry, and the main and module fields both point at index.js with the types field at index.d.ts. The repository carries a benchmark directory with its own script, and the package metadata shows a benchmark script that runs the TypeScript benchmark directly under Node, plus a size benchmark that measures the bundle size of individual entry points such as getPublicKey, sign, verifyAsync, keygen, schnorr and utils separately, using a tool called bismar. That is the interesting detail for anyone tempted by the size claim, because the size is measured per function rather than for the whole module, so an application that only signs pays for only the signing path. The tests run under Node directly with a test script, the check is a separate jsbt-check script, and the repository carries an audit directory and a security policy, which is the kind of paperwork a cryptographic library is expected to have.

Editorial conclusion

noble-secp256k1 is a good fit for a project that signs or derives keys on a secp256k1 chain and wants a dependency small enough to read in an afternoon, and for teams whose auditors prefer code with no transitive tree. It is the wrong choice if you need the features that were deliberately left out, since multi-scalar multiplication, DER encoding and custom point precomputes live in noble-curves instead, and the documentation says so rather than leaving you to discover it. It is also the wrong choice if you need a WebAssembly build, since the WASM versions are a separate project, or if you are on React Native and cannot take on the polyfill situation, where the library states plainly that secure random values cannot be securely polyfilled from its end. Before adopting it, read the signing options carefully, because the defaults around hashing, low-s normalisation and hedged randomness are the ones that determine whether your signatures interoperate with the chain you target. The project is MIT licensed, the last push was on 2026-08-30, and the current release is 3.2.0 from the same day.

Frequently asked questions

What does noble-secp256k1 implement and how small is it?

It implements ECDSA signatures compliant with RFC6979, Schnorr signatures compliant with BIP340, and ECDH key agreement, for the secp256k1 curve. The project describes it as the fastest 5KB JavaScript implementation, with the feature list refining the size to 4.8KB gzipped, 10 to 25 times smaller than similar libraries.

Why do I have to supply my own hash functions to noble-secp256k1?

Only async methods are available by default, in order to keep the library dependency-free, so the hashes object is populated by you. RFC6979 needs an HMAC for the deterministic nonce and the natural place for a hash implementation is a separate package, which would add a large dependency tree. You install @noble/hashes and assign hmacSha256 and sha256 onto the library to enable the sync methods.

What are the lowS and extraEntropy options in noble-secp256k1?

By default lowS is true, which prohibits signatures whose s value is at or above half the curve order and is compatible with BTC and ETH, while setting it to false allows malleable signatures. The extraEntropy option enables hedged signatures, which mix extra randomness into the RFC6979 construction for protection against fault attacks, and fall back to determinism if the random source is broken.

What is the difference between noble-secp256k1 and noble-curves?

The readme says to use noble-secp256k1 if you need a smaller attack surface and better auditability, and to switch to noble-curves, which it calls a drop-in, if you need features like multi-scalar multiplication, DER encoding or custom point precomputes. noble-secp256k1 is the smaller sister project for the secp256k1 curve specifically.

Does noble-secp256k1 work in React Native?

With additional polyfills, and the library is explicit that React Native does not provide secure getRandomValues by default and that this cannot be securely polyfilled from its end, so a React Native specific compile-time dependency is required. The documented example imports a random values polyfill and then wires up the hash functions.

Official sources

  1. License: MIT
  2. paulmillr/noble-secp256k1 on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/paulmillr-noble-secp256k1.svg)](https://hysenlabs.com/projects/paulmillr-noble-secp256k1)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/paulmillr-noble-secp256k1.svg)](https://hysenlabs.com/projects/paulmillr-noble-secp256k1)

Was this useful?