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.

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 hash

  • sign(msg, privKey, {prehash: true}) option can be used if you want to pass the message itself

Weierstrass points:

  1. Exported as ProjectivePoint

  2. Represented in projective (homogeneous) coordinates: (x, y, z) ∋ (x=x/z, y=y/z)

  3. Use complete exception-free formulas for addition and doubling

  4. Can be decoded/encoded from/to Uint8Array / hex strings using ProjectivePoint.fromHex and ProjectivePoint#toRawBytes()

  5. Have assertValidity() which checks for being on-curve

  6. Have toAffine() and x / y getters 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:

  1. Exported as ExtendedPoint

  2. Represented in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z)

  3. Use complete exception-free formulas for addition and doubling

  4. Can be decoded/encoded from/to Uint8Array / hex strings using ExtendedPoint.fromHex and ExtendedPoint#toRawBytes()

  5. Have assertValidity() which checks for being on-curve

  6. Have toAffine() and x / y getters which convert to 2d xy affine coordinates

  7. Have isTorsionFree(), clearCofactor() and isSmallOrder() 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:

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-diff

    • One 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:

  • 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

    • 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

    • is now sync

    • 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)

Upgrading from @noble/ed25519 1.7:

  • Methods are now sync by default

  • bigint is no longer allowed in getPublicKey, sign, verify. Reason: ed25519 is LE, can lead to bugs

  • Point (2d xy) has been changed to ExtendedPoint (xyzt)

  • Signature was removed: just use raw bytes or hex now

  • utils were split into utils (same api as in noble-curves) and etc (sha512Sync and others)

  • getSharedSecret was moved to x25519 module

  • toX25519 has been moved to edwardsToMontgomeryPub and edwardsToMontgomeryPriv methods

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 test will build the code and run tests.

  • npm run lint / npm run format will run linter / fix linter issues.

  • npm run bench will run benchmarks, which may need their deps first (npm run bench:install)

  • cd build && npm install && npm run build:release will 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