noble-curves

repository·main·Indexed 21 days ago

https://github.com/paulmillr/noble-curves

An audited, minimal, and fast JavaScript implementation of elliptic curve cryptography. It supports ECDSA, EdDSA, and Schnorr signatures, ECDH key exchange, and advanced primitives including BLS signatures (bls12-381, bn254), FROST threshold signatures, Poseidon hash, and RFC 9380 hash-to-curve. The library provides support for Weierstrass and Edwards curves, a WebCrypto-compatible wrapper, and targets algorithmic constant-time execution to mitigate timing attacks.

Tokens
23.6K
Snippets
65
Records
91
Agent score
75%

What's inside @noble/curves

  1. Overview of noble-curves capabilities

    main

    noble-curves is an audited, minimal, and fast JavaScript implementation of elliptic curve cryptography. It supports a wide range of cryptographic primitives including:

    • Signatures: ECDSA, EdDSA, and Schnorr signatures.
    • Key Exchange: ECDH (Diffie-Hellman shared secrets).
    • Advanced Primitives: BLS signatures (bls12-381, bn254/alt_bn128), hash-to-curve, OPRF, FROST, Poseidon hash, and FFT.
    • Curve Types: Weierstrass and Edwards curves.
    • Security Features: Non-repudiation (SUF-CMA, SBS) and consensus-friendliness (ZIP215) in ed25519 and ed448.
    • WebCrypto: Provides a wrapper with an identical API over native WebCrypto.

    Note: For use cases requiring an even smaller attack surface with fewer features, there are 5kb sister projects: secp256k1 and ed25519.

  2. Ed25519: Consensus-friendliness vs E-voting mode

    main

    In ed25519, you can choose between two verification modes via the zip215 option in verify():

    1. Consensus-friendly (zip215: true, default): Uses permissive verification rules defined in ZIP215.
    2. E-voting / Strict mode (zip215: false): Enforces strict RFC 8032 / FIPS 186-5 verification. This adds SBS-based non-repudiation, which is recommended for contract signing and e-voting to prevent signers from later claiming they signed a different document.
    import { ed25519 } from '@noble/curves/ed25519.js';
    const { secretKey, publicKey } = ed25519.keygen();
    const msg = new TextEncoder().encode('hello noble');
    const sig = ed25519.sign(msg, secretKey);
    
    // Consensus-friendly (default)
    const isValidZip = ed25519.verify(sig, msg, publicKey, { zip215: true });
    
    // Strict RFC 8032 / E-voting mode
    const isValidRfc = ed25519.verify(sig, msg, publicKey, { zip215: false });
  3. Security and Constant-timeness considerations

    main

    The library targets algorithmic constant time to mitigate timing attacks. It employs several techniques:

    • Fixed operation sequence: multiply() uses signed fixed-window tables with data-oblivious table scans, ensuring the number and order of point operations are independent of the scalar value.
    • Scalar blinding: Secret scalars are masked as s + r·n (where r is a random 128-bit value) before multiplication on cofactor-1 curves (p256, p384, p521, secp256k1) and for all base-point multiplications.

    Important Limitations:

    • JavaScript Environment: Due to JIT compilers and Garbage Collection, absolute constant-time execution cannot be guaranteed in JS. For absolute security, use low-level languages.
    • Cofactored Edwards Curves: On ed25519 and ed448, multiplying a non-base point by a secret scalar is not blinded. EdDSA signing and X25519/X448 implementations are unaffected.
    • Memory: Secrets (bigints, hex strings, Uint8Arrays) may persist in memory longer than anticipated because JS does not provide reliable zeroization for these types.
  4. Upgrade to noble-curves v2

    main

    Upgrading from v1 to v2 involves several breaking changes. To minimize friction, it is recommended to upgrade to version 1.9.x first to identify and fix deprecation warnings in your editor.

    Key Changes in v2

    • ESM-only: The package is now ESM-only. On Node.js v20.19+, ESM can be loaded from CommonJS.
    • Explicit Extensions: You must use the .js extension in all module imports (e.g., @noble/curves/ed25519.js instead of @noble/curves/ed25519).
    • Input Types: Most methods now require Uint8Array. String hex inputs are generally prohibited for security and to reduce malleability. For point creation, use Point.fromBytes for Uint8Array or Point.fromHex for strings.
    • ECDSA Changes:
      • Methods now expect unhashed messages (prehashed messages). To use old behavior, pass { prehash: false }.
      • lowS signatures are now the default. To revert, use { lowS: false }.
      • Signatures default to compact format. To use DER, you must explicitly specify { format: 'der' } in verify.
    • BLS Changes: Methods are now organized into bls.longSignatures (G1 pubkeys, G2 sigs) and bls.shortSignatures (G1 sigs, G2 pubkeys).
  5. Manage base point precomputation for speed

    main

    To improve performance, noble-curves generates base point precomputations (which can take ~10-80ms depending on the curve). This generation is deferred until the first cryptographic operation (like pubkey, sign, or verify) is called.

    If you want to avoid a latency spike during your first critical operation, you can manually trigger the precomputation process.

    // Example of manually forcing precomputation
    // Note: windowSize and the boolean flag are implementation-specific
    Point.BASE.precompute(windowSize, false);
  6. Install @noble/curves

    main

    You can install the library using npm or add it via JSR for Deno. For React Native environments, you may need to install a polyfill for getRandomValues, such as react-native-get-random-values.

    npm install @noble/curves
    
    deno add jsr:@noble/curves
  7. Importing curves and utilities

    main

    To keep your application bundle size small, do not use a wildcard import (e.g., import * from '@noble/curves'). Instead, use specific sub-imports for the curves and utilities you need.

    Common import patterns include:

    • Curves: @noble/curves/secp256k1.js, @noble/curves/ed25519.js, @noble/curves/nist.js, etc.
    • Hash-to-curve: @noble/curves/secp256k1.js (for secp256k1_hasher), @noble/curves/nist.js (for p256_hasher, etc.)
    • OPRFs: @noble/curves/nist.js, @noble/curves/ed25519.js, etc.
    • Utils: @noble/curves/utils.js
    • Abstract math: @noble/curves/abstract/modular.js, @noble/curves/abstract/weierstrass.js, etc.
    import { secp256k1, schnorr } from '@noble/curves/secp256k1.js';
    import { ed25519, x25519 } from '@noble/curves/ed25519.js';
    import { p256, p384 } from '@noble/curves/nist.js';
    import { bytesToHex, hexToBytes } from '@noble/curves/utils.js';
  8. How FROST ciphersuites are constructed

    main

    A FROST ciphersuite is defined by the FrostOpts object passed to createFROST.

    Required Fields:

    • name: A string identifier for the suite.
    • Point: A constructor implementing FROSTPointConstructor (must include fromBytes and the scalar field Fn).
    • hash: A function (msg: Uint8Array) => Uint8Array used for general hashing.

    Key Optional Hooks:

    • validatePoint: Tightens canonical decoding with subgroup/identity checks.
    • parsePublicKey: Custom parser for encoded public keys.
    • hashToScalar: Custom implementation for hashing to the scalar field.
    • adjustTx: Object containing encode and decode methods to transform transaction bytes before/after signing.
    • adjustDKG: Hook to modify the DKG output Key package.
    • H1 through H5 and HDKG/HID: Custom string prefixes for the various RFC 9591 hashes.
  9. How OPRF, VOPRF, and POPRF modes work

    main

    The library implements RFC 9497: Oblivious Pseudorandom Functions (OPRFs) using three distinct modes of operation:

    1. OPRF (Simple Mode): A two-party protocol where the client learns the output F(k, x) but nothing about the server's secret key k. The server learns nothing about the client's input x. This mode is not verifiable; the client cannot prove the server used a specific key.
    2. VOPRF (Verifiable Mode): Extends OPRF by providing a DLEQ proof. This allows the client to verify that the server used the secret key corresponding to a known public key.
    3. POPRF (Partially Oblivious Mode): Extends VOPRF by adding a public info parameter (domain separation) that is cryptographically bound to the final output. This is useful for application-level domain separation.

    There is also a non-interactive mode (evaluate) which allows an entity with knowledge of all inputs to compute the output directly.

  10. How hash-to-curve and map-to-curve differ

    main

    In the context of RFC 9380, these terms describe different ways to move from arbitrary bytes or field elements to a curve point:

    1. hashToCurve: Encodes random bytes to a curve point. It uses a random-oracle construction (hashing the input and then mapping the resulting field elements to the curve).
    2. encodeToCurve: Encodes non-uniform bytes to a curve point. It uses the map_to_curve logic on the output of a hash, but is designed for cases where the input bytes are not already uniform.
    3. mapToCurve: Encodes non-uniform scalars to a curve point. This is a deterministic mapping from field elements directly to curve coordinates without an internal hashing step. It assumes the input scalars are already the result of a hash_to_field operation.
  11. How `PrimeEdwardsPoint` works

    main

    The PrimeEdwardsPoint is an abstract base class used for prime-order groups like Ristretto255 or Decaf448. It wraps an underlying EdwardsPoint to eliminate cofactor issues by representing equivalence classes of points.

    While the underlying Edwards representative might have torsion, the PrimeEdwardsPoint abstraction ensures that all operations (addition, multiplication, etc.) behave as if they are in a prime-order group.

    Key behaviors:

    • isTorsionFree(): Always returns true for these wrappers.
    • clearCofactor(): Is a no-op because the wrapper itself handles the group logic.
    • assertValidity(): Validates the underlying Edwards representative.