noble-curves is elliptic curve maths with a small, audited surface
Audited & minimal JS implementation of elliptic curve cryptography.
At a glance
- What is it?
- noble-curves is the curve half of the noble cryptography family, an audited and minimal TypeScript implementation of the elliptic curve primitives that JavaScript applications usually need. The size budget is the design constraint, with a gzipped secp256k1 at about fifteen kilobytes and unused code excluded from your build, and the correctness argument rests on cross-library and wycheproof test vectors rather than on the implementation alone.
- Who is it for?
- noble-curves fits a team that wants audited curve arithmetic in a browser or a mobile bundle without pulling in a native dependency, since the whole point of the library is that it fits in fifteen kilobytes and is verified against published test vectors.
- 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 61 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.
Star history
- paulmillr/noble-curves971 ★ · +6
| Repository | 2026-10-21 | 2026-11-07 |
|---|---|---|
| paulmillr/noble-curves | 965 | 971 |
6 Stars in the last 17 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
Four claims, each with a mechanism
The feature list is short and each line names how the claim is delivered. Audited means the library has been reviewed by independent security firms, with the reports linked in the security section. Minimal means fifteen kilobytes gzipped for secp256k1, and that unused code is excluded from your builds, which is a build time property rather than a promise. Fast means the implementation is hand-optimized for the caveats of JavaScript engines. Reliable means cross-library and wycheproof tests ensure correctness, and that is the strongest of the four claims because it means the implementation is checked against vectors produced elsewhere rather than against itself. The functional coverage is broad. There are Weierstrass and Edwards curves, ECDSA, EdDSA, Schnorr and BLS signatures, ECDH, hash-to-curve, OPRF, FROST, the Poseidon hash and an FFT, and for ed25519 and ed448 the non-repudiation properties of SUF-CMA and SBS and the consensus friendliness of ZIP215.
The family and the smaller sisters
The project is one member of a family, and the README is explicit that this library belongs to noble cryptography, a high-security, easily auditable set of contained cryptographic libraries and tools. The family shares a set of house rules. Zero or minimal dependencies, highly readable TypeScript and JavaScript code, and PGP-signed releases with transparent npm builds. The siblings are the ciphers, the hashes, a post-quantum library, and the two small sister projects for secp256k1 and ed25519, which are about five kilobytes each and have a smaller attack surface but fewer features. There is also a WebAssembly version of the family. The split between the big library and the small ones is a security decision rather than a size one, because a smaller surface is easier to audit, and the trade is that the minimal editions drop features such as the hash-to-curve and OPRF support that this library carries.
Install, and the import discipline
The package is installed from npm as @noble/curves or added to a Deno project from the JSR registry, and the library supports all major platforms and runtimes. React Native needs a polyfill for getRandomValues, and a standalone file is published for people who want to drop the library in without a package manager. The usage example carries a warning in a comment, and it is the most important line on the page:
import { secp256k1 } from '@noble/curves/secp256k1.js';
const { secretKey, publicKey } = secp256k1.keygen();
const msg = new TextEncoder().encode('hello noble');
const sig = secp256k1.sign(msg, secretKey);
const isValid = secp256k1.verify(sig, msg, publicKey);The commented out line above it imports everything from the package root, and the comment says that is an error, with the reason being to use sub-imports so that the app stays small. The mechanism is in the package file, where the exports map lists a separate entry point for each curve and for each abstract module, so a bundler that understands the exports field can drop the curves you never touch. The rest of the examples follow the same pattern, with nist.js for the NIST curves, ed25519.js and ed448.js for the Edwards curves, and misc.js for the brainpool family.
The signature schemes and their specifications
The library covers the mainstream signature schemes and is explicit about which specification each one follows. ECDSA uses deterministic k, EdDSA follows RFC 8032, and Schnorr is secp256k1 only and follows BIP 340. Messages are always hashed first, with a prehashed signing section for the case where you want to supply the hash yourself. The ristretto255 and decaf448 variants are covered as well, with their own point, hasher and OPRF entry points, and RFC 9496 is the reference for them. The newer signature schemes live elsewhere. MuSig2 and the BIP324 ElligatorSwift mapping for secp256k1 are available in a separate package, scure-btc-signer, which is the right arrangement for a scheme that is specific to Bitcoin rather than general purpose.
Prehashing, and the default that changed
One behaviour detail is worth reading twice, because it is the kind of thing that silently breaks a migration. By default, with prehash set to true, the sign and verify functions apply the curve's built-in hash to the message first, with sha256 for secp256k1 and sha512 for p521. Setting prehash to false allows using a custom hash, for example secp256k1 with keccak_256:
import { secp256k1 } from '@noble/curves/secp256k1.js';
import { keccak_256 } from '@noble/hashes/sha3.js';
const { secretKey } = secp256k1.keygen();
const msg = new TextEncoder().encode('hello noble');
// prehash: true (default) - hash using secp256k1.hash (sha256)
const sig = secp256k1.sign(msg, secretKey);
// prehash: false - hash using custom hash
const sigKeccak = secp256k1.sign(keccak_256(msg), secretKey, { prehash: false });The sentence that makes this a migration hazard is in the documentation, that in noble-curves v1 the prehash false behaviour was the default. So code written against the one series and carried to the two series will hash differently unless the option is set explicitly, and a signature that verified before will not verify now. If you are upgrading, that is the first thing to check.
Public key recovery, and what it does not prove
The library can produce signatures in a format that allows the public key to be recovered, and the example shows the two formats side by side:
const sigRec = secp256k1.sign(msg, secretKey, { format: 'recovered' });
const publicKey_ = secp256k1.recoverPublicKey(sigRec, msg); // == publicKeyThe recovered signature is a compact signature with one extra byte, which is why the compact form is the same value with that byte sliced off. The documentation then gives a caution that is easy to skim past. Public key recovery is only supported with ECDSA, and it is a simple math operation, so there are no guarantees the signing was actually done. A forged triple of r, s and h recovers into a random public key, but it is not feasible to find a message which would lead to that specific forged h. In other words, recovery tells you which key a signature appears to belong to, and it is not evidence that anybody signed anything with it.
Package layout, tooling and the release state
The package file is the map of the library. There is one dependency, @noble/hashes at the same version, and the exports field lists the root entry plus a long list of curve specific entry points, including bls12-381.js, bn254.js, ed448.js, ed25519.js and misc.js, and an abstract/ directory with modules for bls, curve, der, edwards, fft, frost, hash-to-curve, modular, montgomery, oprf, poseidon, tower and weierstrass. The abstract modules are the reusable pieces, so a custom curve or a custom hash-to-curve is built from the same building blocks the shipped curves use. The scripts show the testing matrix, with the main test suite run by Node, and separate targets for Deno, for Bun and for the slow curve tests, plus benchmarks and a check script. The repository also holds an audit directory, a benchmark directory, a jsr.json for the Deno registry, a SECURITY.md and a CHANGELOG. The releases are 2.4.0 on 2026-08-27, 2.3.0 on 2026-08-06 and 2.2.0 on 2026-04-12, the last push was on 2026-09-08, the repository is not archived, and the licence is MIT.
Editorial conclusion
noble-curves fits a team that wants audited curve arithmetic in a browser or a mobile bundle without pulling in a native dependency, since the whole point of the library is that it fits in fifteen kilobytes and is verified against published test vectors. It does not fit an application that needs every curve at once and wants a single import, because the sub-import discipline is deliberate, and it does not fit a team that wants a maintained full featured curve library, because the README points at the smaller sister projects for that. Before you adopt it, read the prehashing section, since the default changed between the one and two series and a signature that verifies under one setting will not verify under the other, and check the audit directory for the reports covering the version you plan to pin.
Frequently asked questions
What is noble-curves?
It is an audited and minimal JavaScript and TypeScript implementation of elliptic curve cryptography, covering Weierstrass and Edwards curves with ECDSA, EdDSA, Schnorr and BLS signatures, ECDH, hash-to-curve, OPRF, FROST, Poseidon and an FFT, in a gzipped secp256k1 build of about fifteen kilobytes.
How do I install and import noble-curves?
Install @noble/curves from npm or add it from the Deno JSR registry, then import from a sub-path such as @noble/curves/secp256k1.js. Importing everything from the package root is marked as an error in the documentation, because sub-imports are what keep unused code out of your build.
What changed between noble-curves v1 and v2?
The prehashing default. In v1 the prehash false behaviour was the default, while by default sign and verify now apply the curve built-in hash first, with sha256 for secp256k1 and sha512 for p521, so code carried over from the earlier series has to set the option explicitly.
Does public key recovery prove a signature was valid?
No. The documentation says recovery is only supported with ECDSA, that it is a simple math operation, and that there are no guarantees the signing was actually done. A forged r, s and h recovers into a random public key, and it is not feasible to find a message producing that specific forged h.
How is noble-curves tested?
With cross-library and wycheproof test vectors, and the package scripts run the suite under Node, with separate targets for Deno, for Bun and for the slow curve tests, plus benchmark scripts and a check script.
Official sources
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.
[](https://hysenlabs.com/projects/paulmillr-noble-curves)Star history chart for your README
[](https://hysenlabs.com/projects/paulmillr-noble-curves)Was this useful?