noble-curves
Audited & minimal JS implementation of elliptic curve cryptography.
🔒 Audited by independent security firms
🔻 Tree-shakeable: unused code is excluded from your builds
🏎 Fast: hand-optimized for caveats of JS engines
🔍 Reliable: cross-library / wycheproof tests and fuzzing ensure correctness
➰ Short Weierstrass, Edwards, Montgomery curves
✍️ ECDSA, EdDSA, Schnorr, BLS, ECDH, hashing to curves, Poseidon ZK-friendly hash
🔖 SUF-CMA, SBS (non-repudiation), ZIP215 (consensus friendliness) features for ed25519 & ed448
🪶 93KB for everything with hashes, 26KB (11KB gzipped) for single-curve build
Curves have 4KB sister projects secp256k1 & ed25519. They have smaller attack surface, but less features.
Take a glance at GitHub Discussions for questions and support.
This library belongs to noble cryptography
noble cryptography — high-security, easily auditable set of contained cryptographic libraries and tools.
Zero or minimal dependencies
Highly readable TypeScript / JS code
PGP-signed releases and transparent NPM builds
Check out homepage for reading resources, documentation and apps built with noble
Usage
npm install @noble/curves
deno add jsr:@noble/curves
deno doc jsr:@noble/curves# command-line documentation
We support all major platforms and runtimes. For React Native, you may need a polyfill for getRandomValues. A standalone file noble-curves.js is also available.
Implementations
Implementations use noble-hashes. If you want to use a different hashing library, abstract API doesn't depend on them.
ECDSA signatures over secp256k1 and others
The same code would work for NIST P256 (secp256r1), P384 (secp384r1) & P521 (secp521r1).
ECDSA public key recovery & extra entropy
ECDH: Elliptic Curve Diffie-Hellman
Schnorr signatures over secp256k1 (BIP340)
ed25519, X25519, ristretto255
Default verify behavior follows ZIP215 and can be used in consensus-critical applications. It has SUF-CMA (strong unforgeability under chosen message attacks). zip215: false option switches verification criteria to strict RFC8032 / FIPS 186-5 and additionally provides non-repudiation with SBS.
X25519 follows RFC7748.
ristretto255 follows irtf draft.
ed448, X448, decaf448
ECDH using Curve448 aka X448, follows RFC7748.
decaf448 follows irtf draft.
Same RFC7748 / RFC8032 / IRTF draft are followed.
bls12-381
See abstract/bls. For example usage, check out the implementation of BLS EVM precompiles.
bn254 aka alt_bn128
The API mirrors BLS. The curve was previously called alt_bn128. The implementation is compatible with EIP-196 and EIP-197.
Keep in mind that we don't implement Point methods toHex / toRawBytes. It's because different implementations of bn254 do it differently - there is no standard. Points of divergence:
Endianness: LE vs BE (byte-swapped)
Flags as first hex bits (similar to BLS) vs no-flags
Imaginary part last in G2 vs first (c0, c1 vs c1, c0)
For example usage, check out the implementation of bn254 EVM precompiles.
Multi-scalar-multiplication
Pippenger algorithm is used underneath. Multi-scalar-multiplication (MSM) is basically (Pa + Qb + Rc + ...). It's 10-30x faster vs naive addition for large amount of points.
Accessing a curve's variables
All available imports
Abstract API
Abstract API allows to define custom curves. All arithmetics is done with JS bigints over finite fields, which is defined from modular sub-module. For scalar multiplication, we use precomputed tables with w-ary non-adjacent form (wNAF). Precomputes are enabled for weierstrass and edwards BASE points of a curve. You could precompute any other point (e.g. for ECDH) using utils.precompute() method: check out examples.
weierstrass: Short Weierstrass curve
Short Weierstrass curve's formula is y² = x³ + ax + b. weierstrass expects arguments a, b, field Fp, curve order n, cofactor h and coordinates Gx, Gy of generator point.
k generation is done deterministically, following RFC6979. It is suggested to use extraEntropy option, which incorporates randomness into signatures to increase their security.
For k generation, specifying hmac & hash is required, which in our implementations is done by noble-hashes. If you're using different hashing library, make sure to wrap it in the following interface:
Message hash is expected instead of message itself:
sign(msgHash, privKey)is default behavior, assuming you pre-hash msg with sha2, or other hashsign(msg, privKey, {prehash: true})option can be used if you want to pass the message itself
Weierstrass points:
Exported as
ProjectivePointRepresented in projective (homogeneous) coordinates: (x, y, z) ∋ (x=x/z, y=y/z)
Use complete exception-free formulas for addition and doubling
Can be decoded/encoded from/to Uint8Array / hex strings using
ProjectivePoint.fromHexandProjectivePoint#toRawBytes()Have
assertValidity()which checks for being on-curveHave
toAffine()andx/ygetters which convert to 2d xy affine coordinates
ECDSA signatures are represented by Signature instances and can be described by the interface:
More examples:
edwards: Twisted Edwards curve
Twisted Edwards curve's formula is ax² + y² = 1 + dx²y². You must specify a, d, field Fp, order n, cofactor h and coordinates Gx, Gy of generator point.
For EdDSA signatures, hash param required. adjustScalarBytes which instructs how to change private scalars could be specified.
We support non-repudiation, which help in following scenarios:
Contract Signing: if A signed an agreement with B using key that allows repudiation, it can later claim that it signed a different contract
E-voting: malicious voters may pick keys that allow repudiation in order to deny results
Blockchains: transaction of amount X might also be valid for a different amount Y
Edwards points:
Exported as
ExtendedPointRepresented in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z)
Use complete exception-free formulas for addition and doubling
Can be decoded/encoded from/to Uint8Array / hex strings using
ExtendedPoint.fromHexandExtendedPoint#toRawBytes()Have
assertValidity()which checks for being on-curveHave
toAffine()andx/ygetters which convert to 2d xy affine coordinatesHave
isTorsionFree(),clearCofactor()andisSmallOrder()utilities to handle torsions
montgomery: Montgomery curve
The module contains methods for x-only ECDH on Curve25519 / Curve448 from RFC7748. Proper Elliptic Curve Points are not implemented yet.
You must specify curve params Fp, a, Gu coordinate of u, montgomeryBits and nByteLength.
bls: Barreto-Lynn-Scott curves
The module abstracts BLS (Barreto-Lynn-Scott) pairing-friendly elliptic curve construction. They allow to construct zk-SNARKs and use aggregated, batch-verifiable threshold signatures, using Boneh-Lynn-Shacham signature scheme.
The module doesn't expose CURVE property: use G1.CURVE, G2.CURVE instead. Only BLS12-381 is currently implemented. Defining BLS12-377 and BLS24 should be straightforward.
The default BLS uses short public keys (with public keys in G1 and signatures in G2). Short signatures (public keys in G2 and signatures in G1) are also supported.
hash-to-curve: Hashing strings to curve points
The module allows to hash arbitrary strings to elliptic curve points. Implements RFC 9380.
Every curve has exported hashToCurve and encodeToCurve methods. You should always prefer hashToCurve for security:
Low-level methods from the spec:
poseidon: Poseidon hash
Implements Poseidon ZK-friendly hash.
There are many poseidon variants with different constants. We don't provide them: you should construct them manually. Check out micro-starknet package for a proper example.
modular: Modular arithmetics utilities
Field operations are not constant-time: they are using JS bigints, see security. The fact is mostly irrelevant, but the important method to keep in mind is pow, which may leak exponent bits, when used naïvely.
mod.Field is always field over prime number. Non-prime fields aren't supported for now. We don't test for prime-ness for speed and because algorithms are probabilistic anyway. Initializing a non-prime field could make your app suspectible to DoS (infilite loop) on Tonelli-Shanks square root calculation.
Unlike mod.inv, mod.invertBatch won't throw on 0: make sure to throw an error yourself.
Creating private keys from hashes
You can't simply make a 32-byte private key from a 32-byte hash. Doing so will make the key biased.
To make the bias negligible, we follow FIPS 186-5 A.2 and RFC 9380. This means, for 32-byte key, we would need 48-byte hash to get 2^-128 bias, which matches curve security level.
hashToPrivateScalar() that hashes to private key was created for this purpose. Use abstract/hash-to-curve if you need to hash to public key.
utils: Useful utilities
Security
The library has been independently audited:
at version 1.2.0, in Sep 2023, by Kudelski Security
PDFs: in-repo
Scope: scure-starknet and its related abstract modules of noble-curves:
curve,modular,poseidon,weierstrassThe audit has been funded by Starkware
at version 0.7.3, in Feb 2023, by Trail of Bits
Scope: abstract modules
curve,hash-to-curve,modular,poseidon,utils,weierstrassand top-level modules_shortw_utilsandsecp256k1The audit has been funded by Ryan Shea
It is tested against property-based, cross-library and Wycheproof vectors, and has fuzzing by Guido Vranken's cryptofuzz.
If you see anything unusual: investigate and report.
Constant-timeness
JIT-compiler and Garbage Collector make "constant time" extremely hard to achieve timing attack resistance in a scripting language. Which means any other JS library can't have constant-timeness. 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. Nonetheless we're targetting algorithmic constant time.
Supply chain security
Commits are signed with PGP keys, to prevent forgery. Make sure to verify commit signatures.
Releases are transparent and built on GitHub CI. Make sure to verify provenance logs
Rare releasing is followed to ensure less re-audit need for end-users
Dependencies are minimized and locked-down:
If your app has 500 dependencies, any dep could get hacked and you'll be downloading malware with every install. We make sure to use as few dependencies as possible
We prevent automatic dependency updates by locking-down version ranges. Every update is checked with
npm-diffOne dependency noble-hashes is used, by the same author, to provide hashing functionality
Dev Dependencies are only used if you want to contribute to the repo. They are disabled for end-users:
scure-base, scure-bip32, scure-bip39, micro-bmark and micro-should are developed by the same author and follow identical security practices
prettier (linter), fast-check (property-based testing) and typescript are used for code quality, vector generation and ts compilation. The packages are big, which makes it hard to audit their source code thoroughly and fully
Randomness
We're deferring to built-in crypto.getRandomValues which is considered cryptographically secure (CSPRNG).
In the past, browsers had bugs that made it weak: it may happen again. Implementing a userspace CSPRNG to get resilient to the weakness is even worse: there is no reliable userspace source of quality entropy.
Quantum computers
Cryptographically relevant quantum computer, if built, will allow to break elliptic curve cryptography (both ECDSA / EdDSA & ECDH) using Shor's algorithm.
Consider switching to newer / hybrid algorithms, such as SPHINCS+. They are available in noble-post-quantum.
NIST prohibits classical cryptography (RSA, DSA, ECDSA, ECDH) after 2035. Australian ASD prohibits it after 2030.
Speed
Benchmark results on Apple M2 with node v22:
Upgrading
Previously, the library was split into single-feature packages noble-secp256k1, noble-ed25519 and noble-bls12-381.
Curves continue their original work. The single-feature packages changed their direction towards providing minimal 4kb implementations of cryptography, which means they have less features.
Upgrading from noble-secp256k1 2.0 or noble-ed25519 2.0: no changes, libraries are compatible.
Upgrading from noble-secp256k1 1.7:
getPublicKeynow produce 33-byte compressed signatures by default
to use old behavior, which produced 65-byte uncompressed keys, set argument
isCompressedtofalse:getPublicKey(priv, false)
signis now sync
now returns
Signatureinstance with{ r, s, recovery }propertiescanonicaloption was renamed tolowSrecoveredoption has been removed because recovery bit is always returned nowderoption has been removed. There are 2 options:Use compact encoding:
fromCompact,toCompactRawBytes,toCompactHex. Compact encoding is simply a concatenation of 32-byte r and 32-byte s.If you must use DER encoding, switch to noble-curves (see above).
verifyis now sync
strictoption was renamed tolowS
getSharedSecretnow produce 33-byte compressed signatures by default
to use old behavior, which produced 65-byte uncompressed keys, set argument
isCompressedtofalse:getSharedSecret(a, b, false)
recoverPublicKey(msg, sig, rec)was changed tosig.recoverPublicKey(msg)numbertype for private keys have been removed: usebigintinsteadPoint(2d xy) has been changed toProjectivePoint(3d xyz)utilswere split intoutils(same api as in noble-curves) andetc(hmacSha256Syncand others)
Upgrading from @noble/ed25519 1.7:
Methods are now sync by default
bigintis no longer allowed ingetPublicKey,sign,verify. Reason: ed25519 is LE, can lead to bugsPoint(2d xy) has been changed toExtendedPoint(xyzt)Signaturewas removed: just use raw bytes or hex nowutilswere split intoutils(same api as in noble-curves) andetc(sha512Syncand others)getSharedSecretwas moved tox25519moduletoX25519has been moved toedwardsToMontgomeryPubandedwardsToMontgomeryPrivmethods
Upgrading from @noble/bls12-381:
Methods and classes were renamed:
PointG1 -> G1.Point, PointG2 -> G2.Point
PointG2.fromSignature -> Signature.decode, PointG2.toSignature -> Signature.encode
Fp2 ORDER was corrected
Contributing & testing
npm install && npm run build && npm testwill build the code and run tests.npm run lint/npm run formatwill run linter / fix linter issues.npm run benchwill run benchmarks, which may need their deps first (npm run bench:install)cd build && npm install && npm run build:releasewill build single file
Check out github.com/paulmillr/guidelines for general coding practices and rules.
See paulmillr.com/noble for useful resources, articles, documentation and demos related to the library.
License
The MIT License (MIT)
Copyright (c) 2022 Paul Miller (https://paulmillr.com)
See LICENSE file.
Last updated