noble-post-quantum

Snapshot 2026-08-03 23:56:39 UTC · version 1

published
INDEXABLE MARKDOWN SNAPSHOT

Research document

Open canonical .md

noble-post-quantum

Auditable & minimal JS implementation of post-quantum public-key cryptography.

  • 🔒 Auditable
  • 🔻 Tree-shakeable: unused code is excluded from your builds
  • 🔍 Reliable: tests ensure correctness
  • 🦾 ML-KEM & CRYSTALS-Kyber: lattice-based 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, combining classic & post-quantum: XWing, KitchenSink, QSF & others
  • 🪶 16KB (gzipped) for everything, including bundled hashes, curves & ciphers

[!IMPORTANT] NIST published draft IR 8547, which proposes prohibiting classical cryptography (RSA, DSA, ECDSA, ECDH) after 2035. Australia's ASD does the same after 2030. Take this into account when designing new cryptographic systems.

This library belongs to noble cryptography

noble cryptography — high-security, easily auditable set of contained cryptographic libraries and tools.

Usage

npm install @noble/post-quantum

deno add jsr:@noble/post-quantum

We support all major platforms and runtimes. For React Native, you may need a polyfill for getRandomValues. A standalone file noble-post-quantum.js is also available.

// import * from '@noble/post-quantum'; // Error: use sub-imports instead
import { ml_kem512, ml_kem768, ml_kem1024 } from '@noble/post-quantum/ml-kem.js';
import { ml_dsa44, ml_dsa65, ml_dsa87 } from '@noble/post-quantum/ml-dsa.js';
import {
  slh_dsa_sha2_128f,
  slh_dsa_sha2_128s,
  slh_dsa_sha2_192f,
  slh_dsa_sha2_192s,
  slh_dsa_sha2_256f,
  slh_dsa_sha2_256s,
  slh_dsa_shake_128f,
  slh_dsa_shake_128s,
  slh_dsa_shake_192f,
  slh_dsa_shake_192s,
  slh_dsa_shake_256f,
  slh_dsa_shake_256s,
} from '@noble/post-quantum/slh-dsa.js';
import {
  falcon512, falcon512padded, falcon1024, falcon1024padded,
} from '@noble/post-quantum/falcon.js';
import {
  ml_kem768_x25519, ml_kem768_p256, ml_kem1024_p384,
  KitchenSink_ml_kem768_x25519, XWing,
  QSF_ml_kem768_p256, QSF_ml_kem1024_p384,
} from '@noble/post-quantum/hybrid.js';

ML-KEM / Kyber shared secrets

import { ml_kem512, ml_kem768, ml_kem1024 } from '@noble/post-quantum/ml-kem.js';
import { randomBytes } from '@noble/post-quantum/utils.js';
import { notDeepStrictEqual } from 'node:assert';
const seed = randomBytes(64); // seed is optional
const aliceKeys = ml_kem768.keygen(seed);
const { cipherText, sharedSecret: bobShared } = ml_kem768.encapsulate(aliceKeys.publicKey);
const aliceShared = ml_kem768.decapsulate(cipherText, aliceKeys.secretKey);

// Warning: Can be MITM-ed
const malloryKeys = ml_kem768.keygen();
const malloryShared = ml_kem768.decapsulate(cipherText, malloryKeys.secretKey); // No error!
notDeepStrictEqual(aliceShared, malloryShared); // Different key!

Lattice-based key encapsulation mechanism, defined in FIPS-203 (website, repo). Can be used as follows:

  1. Alice generates secret & public keys, then sends publicKey to Bob
  2. Bob generates shared secret for Alice publicKey. bobShared never leaves Bob system and is unknown to other parties
  3. Alice gets and decrypts cipherText from Bob Now, both Alice and Bob have same sharedSecret key without exchanging in plainText: aliceShared == bobShared.

There are some concerns with regards to security: see djb blog and mailing list. Old, incompatible version (Kyber) is not provided. Open an issue if you need it.

[!WARNING] Unlike ECDH, KEM doesn't verify whether it was "Bob" who've sent the ciphertext. Instead of throwing an error when the ciphertext is encrypted by a different pubkey, decapsulate will simply return a different shared secret. ML-KEM is also probabilistic and relies on quality of CSPRNG.

ML-DSA / Dilithium signatures

import { ml_dsa44, ml_dsa65, ml_dsa87 } from '@noble/post-quantum/ml-dsa.js';
import { randomBytes } from '@noble/post-quantum/utils.js';
const seed = randomBytes(32); // seed is optional
const keys = ml_dsa65.keygen(seed);
const msg = new TextEncoder().encode('hello noble');
const sig = ml_dsa65.sign(msg, keys.secretKey);
const isValid = ml_dsa65.verify(sig, msg, keys.publicKey);

Lattice-based digital signature algorithm, defined in FIPS-204 (website, repo). The internals are similar to ML-KEM, but keys and params are different.

sign / verify accept optional parameters:

import { sha512 } from '@noble/hashes/sha2.js';
const ctx = new Uint8Array([1, 2, 3]);
const sigCtx = ml_dsa65.sign(msg, keys.secretKey, { context: ctx }); // verify needs same context
const sigDet = ml_dsa65.sign(msg, keys.secretKey, { extraEntropy: false }); // deterministic
const hml = ml_dsa65.prehash(sha512); // HashML-DSA
const sigPre = hml.sign(msg, keys.secretKey);
const isValidPre = hml.verify(sigPre, msg, keys.publicKey);
  • context: domain-separation byte string, up to 255 bytes; must match between sign and verify
  • extraEntropy: hedged-signing randomness. Default is 32 random bytes; false produces deterministic signatures; custom 32-byte value is also allowed
  • externalMu: treat msg as the precomputed 64-byte message representative µ
  • prehash(hash): pre-hash variant (HashML-DSA) from FIPS-204

SLH-DSA / SPHINCS+ signatures

import {
  slh_dsa_sha2_128f as sph,
  slh_dsa_sha2_128s,
  slh_dsa_sha2_192f,
  slh_dsa_sha2_192s,
  slh_dsa_sha2_256f,
  slh_dsa_sha2_256s,
  slh_dsa_shake_128f,
  slh_dsa_shake_128s,
  slh_dsa_shake_192f,
  slh_dsa_shake_192s,
  slh_dsa_shake_256f,
  slh_dsa_shake_256s,
} from '@noble/post-quantum/slh-dsa.js';

const keys2 = sph.keygen();
const msg2 = new TextEncoder().encode('hello noble');
const sig2 = sph.sign(msg2, keys2.secretKey);
const isValid2 = sph.verify(sig2, msg2, keys2.publicKey);

Hash-based digital signature algorithm, defined in FIPS-205 (website, repo). We implement spec v3.1 with FIPS adjustments.

  • sha2 vs shake (sha3): indicates internal hash function used
  • 128 / 192 / 256: indicates security level in bits
  • s / f: indicates small vs fast trade-off

sign / verify accept the same optional context, extraEntropy and prehash(hash) (HashSLH-DSA) parameters as ML-DSA. With extraEntropy: false, signing is deterministic.

SLH-DSA is slow: see benchmarks for key size & speed.

Falcon signatures

import { falcon512, falcon1024 } from '@noble/post-quantum/falcon.js';
import { randomBytes } from '@noble/post-quantum/utils.js';
const seed3 = randomBytes(48); // seed is optional
const keys3 = falcon512.keygen(seed3);
const msg3 = new TextEncoder().encode('hello noble');
const sig3 = falcon512.sign(msg3, keys3.secretKey);
const isValid3 = falcon512.verify(sig3, msg3, keys3.publicKey);

Lattice-based digital signature algorithm, submitted to NIST PQC Round 3 (website, Round 3 submissions).

[!WARNING] This is Falcon Round 3, not FN-DSA. FN-DSA is not final yet. FN-DSA (FIPS-206) would most likely be backwards-incompatible with Falcon. The implementation passes the published Round 3 KATs.

  • falcon512, falcon1024: variable-length detached signatures
  • falcon512padded, falcon1024padded: fixed-length detached signatures
  • attached.seal(...) / attached.open(...): attached-signature API for Round 3 vectors and interop

hybrid: XWing, KitchenSink and others

import {
  ml_kem768_x25519, ml_kem768_p256, ml_kem1024_p384,
  KitchenSink_ml_kem768_x25519, XWing,
  QSF_ml_kem768_p256, QSF_ml_kem1024_p384,
} from '@noble/post-quantum/hybrid.js';

The hybrid submodule combines post-quantum algorithms with elliptic curve cryptography:

  • ml_kem768_x25519: ML-KEM-768 + X25519 (CG Framework, same as XWing)
  • ml_kem768_p256: ML-KEM-768 + P-256 (CG Framework)
  • ml_kem1024_p384: ML-KEM-1024 + P-384 (CG Framework)
  • KitchenSink_ml_kem768_x25519: ML-KEM-768 + X25519 with HKDF-SHA256 combiner
  • QSF_ml_kem768_p256: ML-KEM-768 + P-256 (QSF construction)
  • QSF_ml_kem1024_p384: ML-KEM-1024 + P-384 (QSF construction)

The following spec drafts are matched:

What should I use?

Speed Key size Sig / CT size Created in Popularized in Post-quantum?
RSA Normal 256B - 2KB 256B - 2KB 1970s 1990s No
ECC Normal 32 - 256B 48 - 128B 1980s 2010s No
ML-KEM Fast 0.8 - 1.6KB 0.8 - 1.6KB 1990s 2020s Yes
ML-DSA Normal 1.3 - 2.5KB 2.5 - 4.5KB 1990s 2020s Yes
SLH-DSA Slow 32 - 128B 17 - 50KB 1970s 2020s Yes
FN-DSA Slow 0.9 - 1.8KB 0.6 - 1.2KB 1990s 2020s Yes

ML-KEM is a KEM, not a signature scheme: its last column is ciphertext (CT) size. We suggest using ECC + ML-KEM for key agreement, ECC + SLH-DSA for signatures.

ML-KEM and ML-DSA are lattice-based. SLH-DSA is hash-based, which means it is built on top of older, more conservative primitives. NIST guidance for security levels:

  • Category 3 (~AES-192): ML-KEM-768, ML-DSA-65, SLH-DSA-192
  • Category 5 (~AES-256): ML-KEM-1024, ML-DSA-87, SLH-DSA-256

NIST recommends cat-3+, while Australian ASD only allows cat-5 after 2030.

It's also useful to check out draft NIST SP 800-131Ar3 for "Transitioning the Use of Cryptographic Algorithms and Key Lengths".

For hashes, use SHA512 or SHA3-512 (not SHA256); and for ciphers ensure AES-256 or ChaCha.

Security

The library has not been independently audited yet.

  • at version 0.6.1, in Apr 2026, it was audited by ourselves (self-audited)

If you see anything unusual: investigate and report.

Constant-timeness

There is no protection against side-channel attacks. We actively research how to provide this property for post-quantum algorithms in JS. Keep in mind that even hardware versions ML-KEM are vulnerable.

Supply chain security

  • Commits are signed with PGP keys to prevent forgery. Be sure to verify the commit signatures
  • Releases are made transparently through token-less GitHub CI and Trusted Publishing. Be sure to verify the provenance logs for authenticity.
  • Rare releasing is practiced to minimize the need for re-audits by end-users.
  • Dependencies are minimized and strictly pinned to reduce supply-chain risk.
    • We use as few dependencies as possible.
    • Version ranges are locked, and changes are checked with npm-diff.
  • Dev dependencies are excluded from end-user installs; they're only used for development and build steps.

For this package, there are 3 dependencies; and a few dev dependencies:

  • noble-hashes provides cryptographic hashing functionality, used internally in every algorithm
  • noble-curves provides elliptic curve cryptography for hybrid algorithms
  • noble-ciphers provides AES-CTR DRBG and ChaCha20, used internally in Falcon
  • jsbt is used for benchmarking / testing / build tooling and developed by the same author
  • prettier, fast-check and typescript are used for code quality / test generation / ts compilation

Randomness

We rely on the built-in crypto.getRandomValues, which is considered a cryptographically secure PRNG.

Browsers have had weaknesses in the past - and could again - but implementing a userspace CSPRNG is even worse, as there’s no reliable userspace source of high-quality entropy.

Contributing & testing

  • npm install && npm run build && npm test will build the code and run tests.
  • npm run format will run prettier and fix formatting issues.
  • npm run benchmark will run benchmarks
  • npm run bundle 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.

Speed

npm run benchmark

Noble is the fastest JS implementation of post-quantum algorithms.

There is experimental git branch, which uses WASM-based awasm-noble for hashing. It has 80% faster ML-KEM, 30% faster ML-DSA, 2.3x faster SLH-DSA-SHA256, 15x faster SLH-DSA-SHAKE. Try it out.

Benchmarks on Apple M4 (operations/sec, higher is better):

Primitive Keygen Signing Verification Shared secret
ML-KEM-768 4661 4089
ML-DSA-65 719 294 610
Falcon512 14 749 2160
SLH-DSA-SHA2-192f 321 11 198
Pre-quantum x/ed25519 12648 6157 1255 1981

SLH-DSA (s variants have 2x shorter signatures; SHAKE is very slow):

keygen sign verify
sha2_128f 2ms 47ms 3ms
shake_128f 10ms 237ms 14ms
sha2_192f 3.2ms 93ms 5.1ms
shake_192f 15ms 396ms 21ms
sha2_256f 8.5ms 187ms 5.2ms
shake_256f 40ms 813ms 22ms
sha2_128s 140ms 1068ms 1.1ms
shake_128s 673ms 5114ms 5.2ms
sha2_192s 209ms 2114ms 1.9ms
shake_192s 974ms 8779ms 7.1ms
sha2_256s 137ms 1941ms 2.7ms
shake_256s 645ms 7689ms 11ms

Key and signature sizes:

Variant Public key Secret key Signature / Ciphertext
ML-KEM-512 800 1632 768
ML-KEM-768 1184 2400 1088
ML-KEM-1024 1568 3168 1568
ML-DSA-44 1312 2560 2420
ML-DSA-65 1952 4032 3309
ML-DSA-87 2592 4896 4627
Falcon512 897 1281 666
Falcon1024 1793 2305 1280
SLH-DSA-128f 32 64 17088
SLH-DSA-128s 32 64 7856
SLH-DSA-192f 48 96 35664
SLH-DSA-192s 48 96 16224
SLH-DSA-256f 64 128 49856
SLH-DSA-256s 64 128 29792

License

The MIT License (MIT)

Copyright (c) 2024 Paul Miller (https://paulmillr.com)

See LICENSE file.

MARKDOWN METRICS
1578words
16headings
56links
7code blocks
MDRSS ASSESSMENT
Evidence46/100high confidence
Why MDRSS assigned this score
  • Production catalog audit 2026-08-04
  • Taxonomy classified from title, annotation, source and Markdown signals
  • Agent usefulness evaluated from structure, procedures, examples, evidence and retrieval value
Evidence (1)

Discussion 0

Sign in to join the discussion.