Skip to content

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

License

Notifications You must be signed in to change notification settings

paulmillr/noble-post-quantum

Repository files navigation

noble-post-quantum

Auditable & minimal JS implementation of public-key post-quantum 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
  • 🪶 37KB (15KB gzipped) for everything with bundled hashes

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.

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

We support all major platforms and runtimes. For Deno, ensure to use npm specifier. 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';
import { ml_dsa44, ml_dsa65, ml_dsa87 } from '@noble/post-quantum/ml-dsa';
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';
// import { ml_kem768 } from 'npm:@noble/[email protected]/ml-kem'; // Deno

ML-KEM / Kyber shared secrets

import { ml_kem512, ml_kem768, ml_kem1024 } from '@noble/post-quantum/ml-kem';
import { randomBytes } from '@noble/post-quantum/utils';

// 1. [Alice] generates secret & public keys, then sends publicKey to Bob
const seed = randomBytes(64); // seed is optional
const aliceKeys = ml_kem768.keygen(seed);

// 2. [Bob] generates shared secret for Alice publicKey
// bobShared never leaves [Bob] system and is unknown to other parties
const { cipherText, sharedSecret: bobShared } = ml_kem768.encapsulate(aliceKeys.publicKey);

// 3. [Alice] gets and decrypts cipherText from Bob
const aliceShared = ml_kem768.decapsulate(cipherText, aliceKeys.secretKey);

// Now, both Alice and Both have same sharedSecret key
// without exchanging in plainText: aliceShared == bobShared

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

Lattice-based key encapsulation mechanism, defined in FIPS-203.

See website and repo. 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';
import { utf8ToBytes, randomBytes } from '@noble/post-quantum/utils';
const seed = randomBytes(32); // seed is optional
const keys = ml_dsa65.keygen(seed);
const msg = utf8ToBytes('hello noble');
const sig = ml_dsa65.sign(keys.secretKey, msg);
const isValid = ml_dsa65.verify(keys.publicKey, msg, sig);

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

SLH-DSA / SPHINCS+ signatures

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';
import { utf8ToBytes } from '@noble/post-quantum/utils';

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

Hash-based digital signature algorithm, defined in FIPS-205. See website and repo. We implement spec v3.1 with FIPS adjustments. Some wasm libraries use older specs.

Note

SLH-DSA is slow: see benchmarks below

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-[SHA2/shake]-192[s/f]
  • Category 5 (~AES-256): ML-KEM-1024, ML-DSA-87, SLH-DSA-[SHA2/shake]-256[s/f]

NIST recommends to use cat-3+, while australian ASD only allows cat-5 after 2030.

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.

There is no protection against side-channel attacks. Keep in mind that even hardware versions ML-KEM are vulnerable.

If you see anything unusual: investigate and report.

Speed

Noble is the fastest JS implementation of post-quantum algorithms. WASM libraries can be faster.

OPs/sec Keygen Signing Verification Shared secret
ECC x/ed25519 10270 5110 1050 1470
ML-KEM-768 2300 2000
ML-DSA65 386 120 367
SLH-DSA-SHA2-192f 166 6 111

For SLH-DSA, SHAKE slows everything down 8x, and -s versions do another 20-50x slowdown.

Detailed benchmarks on Apple M2:

ML-KEM
keygen
├─ML-KEM-512 x 3,784 ops/sec @ 264μs/op
├─ML-KEM-768 x 2,305 ops/sec @ 433μs/op
└─ML-KEM-1024 x 1,510 ops/sec @ 662μs/op
encrypt
├─ML-KEM-512 x 3,283 ops/sec @ 304μs/op
├─ML-KEM-768 x 1,993 ops/sec @ 501μs/op
└─ML-KEM-1024 x 1,366 ops/sec @ 731μs/op
decrypt
├─ML-KEM-512 x 3,450 ops/sec @ 289μs/op
├─ML-KEM-768 x 2,035 ops/sec @ 491μs/op
└─ML-KEM-1024 x 1,343 ops/sec @ 744μs/op

ML-DSA
keygen
├─ML-DSA44 x 669 ops/sec @ 1ms/op
├─ML-DSA65 x 386 ops/sec @ 2ms/op
└─ML-DSA87 x 236 ops/sec @ 4ms/op
sign
├─ML-DSA44 x 123 ops/sec @ 8ms/op
├─ML-DSA65 x 120 ops/sec @ 8ms/op
└─ML-DSA87 x 78 ops/sec @ 12ms/op
verify
├─ML-DSA44 x 618 ops/sec @ 1ms/op
├─ML-DSA65 x 367 ops/sec @ 2ms/op
└─ML-DSA87 x 220 ops/sec @ 4ms/op

SLH-DSA
keygen
├─slh_dsa_sha2_128f x 245 ops/sec @ 4ms/op
├─slh_dsa_sha2_192f x 166 ops/sec @ 6ms/op
├─slh_dsa_sha2_256f x 64 ops/sec @ 15ms/op
├─slh_dsa_shake_128f x 35 ops/sec @ 28ms/op
├─slh_dsa_shake_192f x 23 ops/sec @ 41ms/op
├─slh_dsa_shake_256f x 9 ops/sec @ 110ms/op
├─slh_dsa_sha2_128s x 3 ops/sec @ 257ms/op
├─slh_dsa_sha2_192s x 2 ops/sec @ 381ms/op
└─slh_dsa_sha2_256s x 3 ops/sec @ 250ms/op
sign
├─slh_dsa_sha2_128f x 10 ops/sec @ 94ms/op
├─slh_dsa_sha2_192f x 6 ops/sec @ 163ms/op
├─slh_dsa_sha2_256f x 2 ops/sec @ 338ms/op
├─slh_dsa_shake_128f x 1 ops/sec @ 671ms/op
├─slh_dsa_shake_192f x 0 ops/sec @ 1088ms/op
├─slh_dsa_shake_256f x 0 ops/sec @ 2219ms/op
├─slh_dsa_sha2_128s x 0 ops/sec @ 1954ms/op
├─slh_dsa_sha2_192s x 0 ops/sec @ 3789ms/op
└─slh_dsa_sha2_256s x 0 ops/sec @ 3404ms/op
verify
├─slh_dsa_sha2_128f x 162 ops/sec @ 6ms/op
├─slh_dsa_sha2_192f x 111 ops/sec @ 9ms/op
├─slh_dsa_sha2_256f x 105 ops/sec @ 9ms/op
├─slh_dsa_shake_128f x 24 ops/sec @ 40ms/op
├─slh_dsa_shake_192f x 17 ops/sec @ 58ms/op
├─slh_dsa_shake_256f x 16 ops/sec @ 59ms/op
├─slh_dsa_sha2_128s x 495 ops/sec @ 2ms/op
├─slh_dsa_sha2_192s x 293 ops/sec @ 3ms/op
└─slh_dsa_sha2_256s x 220 ops/sec @ 4ms/op

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, which may need their deps first (npm run bench:install)
  • cd build && npm install && npm run build:release 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.

License

The MIT License (MIT)

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

See LICENSE file.