noble-post-quantum

repository·main·Indexed 43 days ago

https://github.com/paulmillr/noble-post-quantum

An auditable and minimal JavaScript implementation of post-quantum cryptography, including FIPS 203 (ML-KEM/Kyber), FIPS 204 (ML-DSA/Dilithium), FIPS 205 (SLH-DSA/SPHINCS+), and Falcon. Version 0.7.1 provides high-performance implementations of KEMs, digital signatures, and hybrid algorithms (e.g., X-Wing, KitchenSink) with minimal dependencies.

Tokens
8.5K
Snippets
22
Records
29
Agent score
56%

What's inside noble-post-quantum

  1. Overview of noble-post-quantum

    main

    noble-post-quantum

    noble-post-quantum is an auditable and minimal JavaScript implementation of post-quantum public-key cryptography. It is designed to be fast, reliable (verified via ACVP / wycheproof tests), and minimal in size (e.g., ~7KB gzipped for ML-KEM).

    Supported Algorithms:

    • ML-KEM & CRYSTALS-Kyber: Lattice-based Key Encapsulation Mechanism (KEM) from FIPS-203.
    • ML-DSA & CRYSTALS-Dilithium: Lattice-based signatures from FIPS-204.
    • SLH-DSA & SPHINCS+: Hash-based Winternitz signatures from FIPS-205.
    • Falcon: Lattice-based signatures from Falcon Round 3.
    • Hybrid algorithms: Combinations of classical and post-quantum cryptography (e.g., X-Wing, KitchenSink).
  2. Understand the security posture and constant-time limitations

    main

    The noble-post-quantum library is a pure JavaScript implementation and does not claim constant-time execution. Because of the nature of JavaScript engines (JIT, garbage collection, bigint arithmetic), it is susceptible to timing side-channel attacks.

    Key Security Considerations:

    • ML-DSA: Signing uses rejection loops and conditional arithmetic that depends on secret-key state.
    • Falcon: Signing uses data-dependent Gaussian and rejection sampling. Its timing posture is materially weaker than hardened native implementations. Avoid using deterministic or repeated signing randomness.
    • Threat Model: These limitations are critical if an attacker can measure signing timing closely (e.g., via hostile co-tenancy or high-resolution local timing oracles).
    • Recommendation: Use an isolated execution environment or a reviewed native/constant-time backend if side-channel resistance is a requirement of your threat model.

    Supply Chain Security

    • Commits: Signed with PGP keys.
    • Releases: Published via token-less GitHub CI and Trusted Publishing; verify provenance logs.
    • Dependencies: Minimized and strictly pinned. Core dependencies include noble-hashes, noble-curves, and noble-ciphers.
  3. Understand the noble cryptography ecosystem

    main

    This library is part of the noble cryptography suite, which focuses on high-security, easily auditable, and contained cryptographic tools.

    Key characteristics of noble libraries:

    • Zero or minimal dependencies.
    • Highly readable TypeScript/JS code.
    • PGP-signed releases and transparent NPM builds.

    Related libraries include:

    • noble-ciphers
    • noble-curves
    • noble-hashes
    • noble-secp256k1
    • noble-ed25519

    For high-performance needs, a WASM version is available via awasm-noble.

  4. Importing @noble/post-quantum modules

    main

    The library uses sub-imports to prevent loading unnecessary code. Do not use import * from '@noble/post-quantum'. Instead, import specific algorithms from their respective sub-modules:

    • ML-KEM: @noble/post-quantum/ml-kem.js
    • ML-DSA: @noble/post-quantum/ml-dsa.js
    • SLH-DSA: @noble/post-quantum/slh-dsa.js
    • Falcon: @noble/post-quantum/falcon.js
    • Hybrid: @noble/post-quantum/hybrid.js
    • WebCrypto wrappers: @noble/post-quantum/webcrypto.js
    • Utils: @noble/post-quantum/utils.js
    import { ml_kem768 } from '@noble/post-quantum/ml-kem.js';
    import { ml_dsa65 } from '@noble/post-quantum/ml-dsa.js';
    import { slh_dsa_sha2_128f } from '@noble/post-quantum/slh-dsa.js';
    import { falcon512 } from '@noble/post-quantum/falcon.js';
    import { ml_kem768_x25519 } from '@noble/post-quantum/hybrid.js';
  5. Evaluate performance and algorithm variants

    main

    Noble is designed for high performance in JavaScript. You can run local benchmarks using:

    npm run benchmark

    Performance Trade-offs

    • SLH-DSA: The s variants (e.g., sha2_128s) produce signatures that are 2x shorter than the f variants (e.g., sha2_128f), but signing is significantly slower.
    • WASM Acceleration: An experimental awasm branch is available which uses awasm-noble for hashing, providing significant speedups (e.g., 80% faster ML-KEM, 15x faster SLH-DSA-SHAKE).
  6. Optimize ML-KEM performance with KEMPrepared

    main

    If you are performing multiple encapsulations or decapsulations against the same public key, use the prepare() method. This creates a KEMPrepared object that caches public data (like the expanded matrix  and decoded $\hat{t}$) to skip expensive re-computation and re-validation steps.

    Lifecycle of KEMPrepared:

    1. Call ml_kem.prepare(publicKey) to get a KEMPrepared instance.
    2. Use prepared.encapsulate(msg?) and prepared.decapsulate(cipherText, secretKey).
    3. Crucial: Call prepared.clean() when finished to wipe the cached public data from memory. The object should not be used after calling clean().
    // Prepare a key for high-performance repeated use
    const prepared = ml_kem768.prepare(publicKey);
    
    // Perform multiple operations
    const res1 = prepared.encapsulate();
    const res2 = prepared.encapsulate();
    
    // Clean up cached data
    prepared.clean();
  7. Understand SLH-DSA (SPHINCS+) security and limitations

    main

    SLH-DSA is a stateless hash-based digital signature standard (FIPS-205).

    Key Concepts

    • WOTS: One-time signatures used within the structure.
    • FORS: Forest of Random Subsets used to mitigate the one-time nature of WOTS.
    • Hypertree: A structure of Merkle trees that allows for a large number of signatures.

    Limitations

    • Fixed Signature Capacity: While stateless, a Merkle tree with a specific depth allows only a finite number of distinct messages before the key effectively degrades or the tree is exhausted. For example, a tree with depth 8 allows 256 distinct messages.
    • Security Levels: Profiles are typically categorized by security bits (128, 192, 256) and trade-offs between signature size ('s' for small) and signing speed ('f' for fast).
  8. Use ML-KEM (Kyber) for Key Encapsulation

    main

    ML-KEM (Module Lattice-based Key Encapsulation Mechanism) is used to establish shared secrets. It is probabilistic and relies on a high-quality CSPRNG.

    Important Security Notes:

    • Unlike ECDH, decapsulation never throws an error; if the ciphertext was encrypted with a different key, it simply returns a different (unrelated) shared secret.
    • Decapsulation follows an implicit-rejection structure.
    • For deterministic testing or known-answer vectors, you can provide a 32-byte msg (the preimage of the shared secret) to encapsulate and a 64-byte seed to keygen. Otherwise, the library generates fresh randomness.

    Available parameter sets:

    • ml_kem512
    • ml_kem768
    • ml_kem1024
    import { ml_kem512 } from '@noble/post-quantum/ml-kem.js';
    
    // 1. Generate keys
    const seed = new Uint8Array(ml_kem512.lengths.seed!); // 64 bytes
    const { secretKey, publicKey } = ml_kem512.keygen(seed);
    
    // 2. Encapsulate (generate shared secret and ciphertext)
    const msg = new Uint8Array(ml_kem512.lengths.msgRand!); // 32 bytes
    const { cipherText, sharedSecret } = ml_kem512.encapsulate(publicKey, msg);
    
    // 3. Decapsulate (recover shared secret)
    const recovered = ml_kem512.decapsulate(cipherText, secretKey);
    
    // 4. Extract public key from secret key if needed
    const publicKey2 = ml_kem512.getPublicKey(secretKey);
  9. Use WebCrypto ML-KEM wrappers for KEM operations

    main

    The webcrypto.ts module provides asynchronous wrappers for Key Encapsulation Mechanism (KEM) operations using the browser's built-in crypto.subtle API. This is useful for offloading lattice-based cryptography to the runtime's native implementation.

    Supported Algorithms

    • ml_kem512: ML-KEM-512
    • ml_kem768: ML-KEM-768
    • ml_kem1024: ML-KEM-1024
    • ml_kem768_x25519: ML-KEM-768 + X25519 (X-Wing)

    Key Concepts

    • Deterministic Key Generation: If you provide a seed to keygen(), the resulting private key is the raw seed itself, not the expanded decapsulation key. This seed is compatible with the synchronous implementations' keygen(seed) method.
    • Support Probing: Because WebCrypto's post-quantum algorithms are experimental, always call isSupported() before attempting operations to ensure the runtime implements the full required surface (key generation, encapsulation, and decapsulation).
    import { ml_kem768 } from '@noble/post-quantum/webcrypto';
    
    // 1. Check support
    if (await ml_kem768.isSupported()) {
      // 2. Generate key pair
      const { secretKey, publicKey } = await ml_kem768.keygen();
    
      // 3. Encapsulate (Sender side)
      const { cipherText, sharedSecret: ssSender } = await ml_kem768.encapsulate(publicKey);
    
      // 4. Decapsulate (Recipient side)
      const ssRecipient = await ml_kem768.decapsulate(cipherText, secretKey);
    
      // ssSender === ssRecipient
    }
  10. Use ML-KEM (Kyber) for shared secrets

    main

    ML-KEM is a lattice-based Key Encapsulation Mechanism (KEM) used to establish shared secrets.

    Workflow:

    1. Alice generates keys using keygen(seed) and sends the publicKey to Bob.
    2. Bob uses encapsulate(publicKey) to generate a cipherText and a sharedSecret. Bob sends the cipherText to Alice.
    3. Alice uses decapsulate(cipherText, secretKey) to derive the same sharedSecret.

    Security Warning: Unlike ECDH, ML-KEM does not verify the identity of the sender. If a Man-in-the-Middle (MITM) provides a different ciphertext, decapsulate will not throw an error; it will simply return a different, incorrect shared secret.

    WebCrypto Wrapper: For environments supporting the experimental WebCrypto API, use the async wrappers from @noble/post-quantum/webcrypto.js.

    import { ml_kem768 } from '@noble/post-quantum/ml-kem.js';
    import { randomBytes } from '@noble/post-quantum/utils.js';
    
    const seed = randomBytes(64); // optional
    const aliceKeys = ml_kem768.keygen(seed);
    const { cipherText, sharedSecret: bobShared } = ml_kem768.encapsulate(aliceKeys.publicKey);
    const aliceShared = ml_kem768.decapsulate(cipherText, aliceKeys.secretKey);
  11. Use SLH-DSA (SPHINCS+) for digital signatures

    main

    SLH-DSA is a hash-based digital signature algorithm (FIPS-205). It is more conservative but slower than ML-DSA.

    Naming Convention:

    • sha2 vs shake: Indicates the internal hash function used.
    • 128 / 192 / 256: Security level in bits.
    • s vs f: Trade-off between signature size (small) and speed (fast).

    Usage: Supports the same context, extraEntropy, and prehash(hash) options as ML-DSA.

    import { slh_dsa_sha2_128f } from '@noble/post-quantum/slh-dsa.js';
    
    const keys = slh_dsa_sha2_128f.keygen();
    const msg = new TextEncoder().encode('hello noble');
    const sig = slh_dsa_sha2_128f.sign(msg, keys.secretKey);
    const isValid = slh_dsa_sha2_128f.verify(sig, msg, keys.publicKey);