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
- 🍡 Hybrid algorithms, combining classic & post-quantum: Concrete, XWing, KitchenSink
- 🪶 16KB (gzipped) for everything, including bundled hashes & curves
Take a glance at GitHub Discussions for questions and support.
Important
NIST published IR 8547, prohibiting classical cryptography (RSA, DSA, ECDSA, ECDH) after 2035. Australian ASD does same thing after 2030. Take it into an account while designing a new cryptographic system.
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
- All libraries: ciphers, curves, hashes, post-quantum, 5kb secp256k1 / ed25519
- Check out the homepage for reading resources, documentation, and apps built with noble
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 {
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
- ML-DSA / Dilithium
- SLH-DSA / SPHINCS+
- hybrid: XWing, KitchenSink and others
- What should I use?
- Security
- Speed
- Contributing & testing
- License
import { ml_kem512, ml_kem768, ml_kem1024 } from '@noble/post-quantum/ml-kem.js';
import { randomBytes } from '@noble/post-quantum/utils.js';
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:
- Alice generates secret & public keys, then sends publicKey to Bob
- Bob generates shared secret for Alice publicKey. bobShared never leaves Bob system and is unknown to other parties
- 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.
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.
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
SLH-DSA is slow: see benchmarks for key size & speed.
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';Hybrid submodule combine 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 combinerQSF_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:
- irtf-cfrg-hybrid-kems-07
- irtf-cfrg-concrete-hybrid-kems-02
- connolly-cfrg-xwing-kem-09
- tls-westerbaan-xyber768d00-03
| Speed | Key size | Sig 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 | 1.6 - 31KB | 1KB | 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 |
We suggest to use 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 to use cat-3+, while australian ASD only allows cat-5 after 2030.
It's also useful to check out 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.
The library has not been independently audited yet.
If you see anything unusual: investigate and report.
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.
- 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 is 1 dependency; and a few dev dependencies:
- noble-hashes provides cryptographic hashing functionality
- 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
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.
npm run bench
Noble is the fastest JS implementation of post-quantum algorithms. WASM libraries can be faster.
Benchmarks on Apple M4 (higher is better):
| OPs/sec | Keygen | Signing | Verification | Shared secret |
|---|---|---|---|---|
| ECC x/ed25519 | 14216 | 6849 | 1400 | 1981 |
| ML-KEM-768 | 3778 | 3750 | ||
| ML-DSA65 | 580 | 272 | 546 | |
| SLH-DSA-SHA2-192f | 245 | 8 | 169 |
# ML-KEM768
keygen x 3,778 ops/sec @ 264μs/op
encapsulate x 3,220 ops/sec @ 310μs/op
decapsulate x 4,029 ops/sec @ 248μs/op
# ML-DSA65
keygen x 580 ops/sec @ 1ms/op
sign x 272 ops/sec @ 3ms/op
verify x 546 ops/sec @ 1ms/op
# SLH-DSA SHA2 192f
keygen x 245 ops/sec @ 4ms/op
sign x 8 ops/sec @ 114ms/op
verify x 169 ops/sec @ 5ms/op
SLH-DSA:
| sig size | keygen | sign | verify | |
|---|---|---|---|---|
| sha2_128f | 18088 | 4ms | 90ms | 6ms |
| sha2_192f | 35664 | 6ms | 160ms | 9ms |
| sha2_256f | 49856 | 15ms | 340ms | 9ms |
| sha2_128s | 7856 | 260ms | 2000ms | 2ms |
| sha2_192s | 16224 | 380ms | 3800ms | 3ms |
| sha2_256s | 29792 | 250ms | 3400ms | 4ms |
| shake_192f | 35664 | 21ms | 553ms | 29ms |
| shake_192s | 16224 | 260ms | 2635ms | 2ms |
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 benchmarksnpm 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.
The MIT License (MIT)
Copyright (c) 2024 Paul Miller (https://paulmillr.com)
See LICENSE file.