Skip to main content

Crate bouncycastle_mlkem

Crate bouncycastle_mlkem 

Source
Expand description

This crate implements the Module Lattice Key-Encapsulation Mechanism (ML-KEM) as per FIPS 203.

§Usage

This crate has been designed to serve a wide range of use cases, from people dabbling in cryptography for the first time, to cryptographic protocol designers who need access to the internal functionality of the ML-KEM algorithm, to embedded systems developers who want access to memory and performance optimized functions.

This page gives examples of simple usage for generating keys and performing encapsulation and decapsulation operations.

More examples on advanced usage can be found on the mlkem page.

§Primer on KEM algorithms

Since Key-Encapsulation Mechanisms behave differently from the Diffie-Hellman key exchanges that many people are familiar with, we start with a brief primer on KEMs.

The core operation of a KEM is “encapsulation” which performs a mathematical operation against a KEM public key and yields a shared secret key, and a ciphertext. Internally, the encapsulation routine will use a cryptographic random number generator to ensure that the shared secret key and ciphertext are strongly unique to this encapsulation operation. The receiving party can then perform a “decapsulation” using the corresponding private key to obtain the same shared secret key from the ciphertext.

Some sources, including FIPS 203 refer to the public key as the “encapsulation key ek” and the private key as the “decapsulation key dk”. These are used interchangeable with the public key pk and private key (or secret key) sk.

The three operations of a KEM algorithm are:

  • keygen() -> (pk, sk)
  • encaps(pk) -> (ss, ct)
  • decaps(sk, ct) -> (ss)

Since both keygen and encaps require a source of randomness, it is also common for a cryptographic library to expose deterministic versions, which are often labelled as “internal” since they should be used carefully as their misuse can catastrophically reduce the algorithm’s security. For ML-KEM in particular, these are:

  • ML-KEM.keygen_internal(seed) requires a seed either written as seed: [u8;64] sometimes decomposed into (d: [u8;32], z: [u8;32]).
  • ML-KEM.encaps_internal(pk, m) requires a random message m: [u8;32].

Using these functions without sufficiently random values for seed and m is ill-advised.

§Generating Keys

use bouncycastle_mlkem::MLKEM768;
use bouncycastle_core::traits::KEM;

let (pk, sk) = MLKEM768::keygen().unwrap();

That’s it. That will use the library’s default OS-backend RNG.

Commonly with the ML-KEM algorithm, a 64-byte seed is used as the private key, and expanded into a full private key as needed. This is offered through the library’s KeyMaterialTrait object:

use bouncycastle_core::key_material::{KeyMaterial512, KeyType, KeyMaterialTrait};
use bouncycastle_mlkem::{MLKEM768, MLKEMTrait};
use bouncycastle_hex as hex;

let seed = KeyMaterial512::from_bytes_as_type(
    &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
                  202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f").unwrap(),
    KeyType::Seed,
).unwrap();

let (pk, sk) = MLKEM768::keygen_from_seed(&seed).unwrap();

See MLKEM and MLKEM::decaps_from_seed for an API that uses a merged keygen-and-decaps function to that allows you to store the private key only as a 64-byte seed.

§Encapsulating and Decapsulating

use bouncycastle_mlkem::{MLKEM768, MLKEMTrait};
use bouncycastle_core::traits::KEM;
use bouncycastle_core::errors::KEMError;

let (pk, sk) = MLKEM768::keygen().unwrap();

// Create the shared secret and ciphertext using the public key
let (ss, ct) = MLKEM768::encaps(&pk).unwrap();

// Recover the shared secret using the private key
let ss1 = match MLKEM768::decaps(&sk, &ct) {
    Err(KEMError) => panic!("Error decapsulating"),
    Ok(ss) => ss,
};

assert_eq!(ss, ss1);

And that’s the basic usage!

§Memory Footprint

The following table lists the size of the on-disk bytes encoding and the in-memory struct size of the standard key objects:

Key ObjectPK size on diskPK size in memorySK Size on diskSK size in memory
ML-KEM-512800105616322178
ML-KEM-7681184156824003202
ML-KEM-10241568208031684226

The following table lists the size of the on-disk bytes encoding and the in-memory struct size of the expanded key objects that pre-expand the public matrix A for faster repeated encaps() and decaps() operations:

Key ObjectPK size on diskPK size in memorySK Size on diskSK size in memory
ML-KEM-512_expanded800310416324226
ML-KEM-768_expanded1184617624007810
ML-KEM-1024_expanded156810272316812418

All values are in bytes. The “in memory” sizes are measured by rust’s std::mem::size_of. Values in parentheses are the usual sizes in our un-optimized implementation in the [bouncycastle_mldsa] crate.

§Security

All functionality exposed by this crate is considered secure to use. In other words, this crate does not contain any “hazmat” except for the obvious points about handling your private keys properly: if you post your private key to github, or you generate production keys from a weak seed, I can’t help you, that’s on you. It is worth mentioning, however, that if using a MLKEM::keygen_from_seed, then it is your responsibility to ensure that the seed is cryptographically random and unpredictable. And also that MLKEM::encaps_internal requires you to provide the randomness, so the ciphertext will only be as strong as the randomness that you provide.

A note about cryptographic side-channel attacks: considerable effort has been expended to attempt to make this implementation constant-time, which generally means that the core mathematical algorithm code that handles secret data uses bitshift-and-xor type constructions instead of if-and-loop constructions. That should give this implementation reasonably good resistance to timing and power analysis key extraction attacks, however: A) this is a “best-effort” and not formally verified, and B) the Rust compiler does not guarantee constant-time behaviour no matter how clever your code, so like all Safe Rust code (ie Rust code that does not include inline assembly), we are at the mercy of the Rust compiler’s optimizer for whether our bitshift-and-xor code actually remains constant-time after compilation.

Re-exports§

pub use mlkem::MLKEM;
pub use mlkem::MLKEM512;
pub use mlkem::MLKEM768;
pub use mlkem::MLKEM1024;
pub use mlkem::MLKEMTrait;
pub use mlkem::ML_KEM_512_NAME;
pub use mlkem::ML_KEM_768_NAME;
pub use mlkem::ML_KEM_1024_NAME;
pub use mlkem::MLKEM_RND_LEN;
pub use mlkem::MLKEM_SEED_LEN;
pub use mlkem::MLKEM_SS_LEN;
pub use mlkem::MLKEM512_CT_LEN;
pub use mlkem::MLKEM512_PK_LEN;
pub use mlkem::MLKEM512_SK_LEN;
pub use mlkem::MLKEM768_CT_LEN;
pub use mlkem::MLKEM768_PK_LEN;
pub use mlkem::MLKEM768_SK_LEN;
pub use mlkem::MLKEM1024_CT_LEN;
pub use mlkem::MLKEM1024_PK_LEN;
pub use mlkem::MLKEM1024_SK_LEN;

Modules§

aux_functions
Implements auxiliary functions for ML-DSA as defined in Section 7 of FIPS 204.
mlkem
This page documents advanced features of the Module Lattice Key-Encapsulation Algorithm (ML-KEM) available in this crate.
polynomial
Represents a polynomial over the ML-DSA ring.

Structs§

MLKEMPrivateKey
An ML-KEM private key.
MLKEMPrivateKeyExpanded
A fully expanded ML-KEM private key that includes the intermediate values needed for performing multiple decaps operations with the same private key, which causes the private key struct to take up more memory, but results in more efficient repeated decaps() operations.
MLKEMPublicKey
An ML-KEM public key.
MLKEMPublicKeyExpanded
A fully expanded ML-KEM public key that includes the intermediate values needed for performing multiple encaps operations against the same public key, which causes the MLKEMPublicKey struct to take up more memory, but results in more efficient repeated encaps() operations.
Matrix
A matrix over the ML-KEM ring.

Traits§

MLKEMPrivateKeyTrait
General trait for all ML-KEM private keys types.
MLKEMPublicKeyTrait
General trait for all ML-KEM public keys types.

Type Aliases§

MLKEM512PrivateKey
ML-KEM-512 Private Key
MLKEM512PrivateKeyExpanded
ML-KEM-512 Private Key with a pre-expanded public matrix A for repeated decaps operations.
MLKEM512PublicKey
ML-KEM-512 Public Key
MLKEM512PublicKeyExpanded
ML-KEM-512 Public Key with a pre-expanded public matrix A for repeated encaps operations.
MLKEM768PrivateKey
ML-KEM-768 Private Key
MLKEM768PrivateKeyExpanded
ML-KEM-768 Private Key with a pre-expanded public matrix A for repeated decaps operations.
MLKEM768PublicKey
ML-KEM-768 Public Key
MLKEM768PublicKeyExpanded
ML-KEM-768 Public Key with a pre-expanded public matrix A for repeated encaps operations.
MLKEM1024PrivateKey
ML-KEM-1024 Private Key
MLKEM1024PrivateKeyExpanded
ML-KEM-1024 Private Key with a pre-expanded public matrix A for repeated decaps operations.
MLKEM1024PublicKey
ML-KEM-1024 Public Key
MLKEM1024PublicKeyExpanded
ML-KEM-1024 Public Key with a pre-expanded public matrix A for repeated encaps operations.