Files

367 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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: Concrete, XWing, KitchenSink
- 🪶 16KB (gzipped) for everything, including bundled hashes & curves
Take a glance at [GitHub Discussions](https://github.com/paulmillr/noble-post-quantum/discussions) for questions and support.
> [!IMPORTANT]
> NIST published [IR 8547](https://nvlpubs.nist.gov/nistpubs/ir/2024/NIST.IR.8547.ipd.pdf),
> prohibiting classical cryptography (RSA, DSA, ECDSA, ECDH) after 2035.
> Australian ASD does same thing [after 2030](https://www.cyber.gov.au/resources-business-and-government/essential-cyber-security/ism/cyber-security-guidelines/guidelines-cryptography).
> Take it into an account while designing a new cryptographic system.
### This library belongs to _noble_ cryptography
> **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](https://github.com/paulmillr/noble-ciphers),
[curves](https://github.com/paulmillr/noble-curves),
[hashes](https://github.com/paulmillr/noble-hashes),
[post-quantum](https://github.com/paulmillr/noble-post-quantum),
5kb [secp256k1](https://github.com/paulmillr/noble-secp256k1) /
[ed25519](https://github.com/paulmillr/noble-ed25519)
- [Check out the homepage](https://paulmillr.com/noble/)
for reading resources, documentation, and apps built with noble
## 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](https://github.com/LinusU/react-native-get-random-values).
A standalone file
[noble-post-quantum.js](https://github.com/paulmillr/noble-post-quantum/releases) is also available.
```js
// 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](#ml-kem--kyber-shared-secrets)
- [ML-DSA / Dilithium](#ml-dsa--dilithium-signatures)
- [SLH-DSA / SPHINCS+](#slh-dsa--sphincs-signatures)
- [Falcon](#falcon-signatures)
- [hybrid: XWing, KitchenSink and others](#hybrid-xwing-kitchensink-and-others)
- [What should I use?](#what-should-i-use)
- [Security](#security)
- [Speed](#speed)
- [Contributing & testing](#contributing--testing)
- [License](#license)
### ML-KEM / Kyber shared secrets
```ts
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](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.pdf) ([website](https://www.pq-crystals.org/kyber/resources.shtml), [repo](https://github.com/pq-crystals/kyber)).
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](https://blog.cr.yp.to/20231003-countcorrectly.html) and
[mailing list](https://groups.google.com/a/list.nist.gov/g/pqc-forum/c/W2VOzy0wz_E).
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
```ts
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](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.204.pdf) ([website](https://www.pq-crystals.org/dilithium/index.shtml),
[repo](https://github.com/pq-crystals/dilithium)).
The internals are similar to ML-KEM, but keys and params are different.
### SLH-DSA / SPHINCS+ signatures
```ts
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](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.205.pdf) ([website](https://sphincs.org), [repo](https://github.com/sphincs/sphincsplus)). 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](#speed) for key size & speed.
### Falcon signatures
```ts
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](https://falcon-sign.info/), [Round 3 submissions](https://csrc.nist.gov/projects/post-quantum-cryptography/post-quantum-cryptography-standardization/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
```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';
```
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 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:
- [irtf-cfrg-hybrid-kems-07](https://datatracker.ietf.org/doc/draft-irtf-cfrg-hybrid-kems/)
- [irtf-cfrg-concrete-hybrid-kems-02](https://datatracker.ietf.org/doc/draft-irtf-cfrg-concrete-hybrid-kems/)
- [connolly-cfrg-xwing-kem-09](https://datatracker.ietf.org/doc/draft-connolly-cfrg-xwing-kem/)
- [tls-westerbaan-xyber768d00-03](https://datatracker.ietf.org/doc/draft-tls-westerbaan-xyber768d00/)
### What should I use?
| | 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](https://www.cyber.gov.au/resources-business-and-government/essential-cyber-security/ism/cyber-security-guidelines/guidelines-cryptography).
It's also useful to check out [NIST SP 800-131Ar3](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar3.ipd.pdf)
for "Transitioning the Use of Cryptographic Algorithms and Key Lengths".
For [hashes](https://github.com/paulmillr/noble-hashes), use SHA512 or SHA3-512 (not SHA256); and for [ciphers](https://github.com/paulmillr/noble-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)
- Scope: everything
- [Changes since audit](https://github.com/paulmillr/noble-post-quantum/compare/0.6.1..main)
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](https://eprint.iacr.org/2023/1084).
### 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](https://docs.npmjs.com/generating-provenance-statements) 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 2 dependencies; and a few dev dependencies:
- [noble-hashes](https://github.com/paulmillr/noble-hashes) provides cryptographic hashing functionality, used internally in every algorithm
- [noble-curves](https://github.com/paulmillr/noble-curves) provides elliptic curve cryptography for hybrid algorithms
- 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`](https://developer.mozilla.org/en-US/docs/Web/API/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 theres 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 lint` / `npm run format` will run linter / fix linter issues.
- `npm run bench` will run benchmarks
- `npm run build:release` will build single file
Check out [github.com/paulmillr/guidelines](https://github.com/paulmillr/guidelines)
for general coding practices and rules.
See [paulmillr.com/noble](https://paulmillr.com/noble/)
for useful resources, articles, documentation and demos
related to the library.
## Speed
> `npm run bench`
Noble is the fastest JS implementation of post-quantum algorithms.
Benchmarks on Apple M4 (**higher is better**):
```
# ML-KEM768
keygen x 4,277 ops/sec @ 233μs/op
encapsulate x 3,470 ops/sec @ 288μs/op
decapsulate x 3,757 ops/sec @ 266μs/op
# ML-DSA65
keygen x 669 ops/sec @ 1ms/op
sign x 271 ops/sec @ 3ms/op
verify x 565 ops/sec @ 1ms/op
# SLH-DSA SHA2 192f
keygen x 235 ops/sec @ 4ms/op
sign x 8 ops/sec @ 117ms/op
verify x 159 ops/sec @ 6ms/op
# Falcon512
keygen x 14 ops/sec @ 66ms/op ± 11.01% (56ms..96ms)
sign x 749 ops/sec @ 1ms/op
verify x 2,160 ops/sec @ 462μs/op
# Falcon1024
keygen x 4 ops/sec @ 247ms/op ± 5.22% (234ms..266ms)
sign x 343 ops/sec @ 2ms/op
verify x 950 ops/sec @ 1ms/op
```
Compared with pre-quantum:
| OPs/sec | Keygen | Signing | Verification | Shared secret |
| ----------------- | ------ | ------- | ------------ | ------------- |
| ECC x/ed25519 | 12648 | 6157 | 1255 | 1981 |
| ML-KEM-768 | 4277 | | | 3757 |
| ML-DSA65 | 669 | 271 | 565 | |
| SLH-DSA-SHA2-192f | 235 | 8 | 159 | |
| Falcon512 | 14 | 749 | 950 | |
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 |
## License
The MIT License (MIT)
Copyright (c) 2024 Paul Miller [(https://paulmillr.com)](https://paulmillr.com)
See LICENSE file.