Expand description
This page documents advanced features of the Module Lattice Key-Encapsulation Algorithm (ML-KEM) available in this crate.
§Pre-expanding the public key for repeated use
Within the usual ML-KEM public key representation, the public matrix A is stored as a seed rho, which means that both the ML-KEM.encops() and ML-KEM.decaps() operations need to expand it into a full matrix before performing the matrix multiplication. We offer a version of the public and private key structs that pre-expand the public matrix for repeated use.
When done as part of the keygen, expansion of the public matrix accounts for roughly 25% of the keygen time, however it accounts for roughly 35% / 60% / 80% of an encaps and 30% / 45% / 65% of a decaps for MLKEM512 / MLKEM768 / MLKEM1024.
Most often, ML-KEM is used in an ephemeral mode where a key pair is generated, used for a single encaps and decaps and then discarded. In this mode, there is no performance difference to whether the public matrix A is expanded as part of keygen or as part of encaps / decaps, but it does make both the public and private key take up more space in memory, so the default ML-KEM public and private key objects defer expansion until it is needed.
However, in non-ephemeral uses where many encaps or decaps operations are performed against the same key pair in quick succession, there can be substantial performance improvements to pre-computing this and holding on to a larger key object. This is accomplished via constructing a MLKEMPublicKeyExpanded or MLKEMPrivateKeyExpanded object of the appropriate parameter set from the original key, and then using this with MLKEM::encaps_for_expanded_key or MLKEM::decaps_with_expanded_key. Both MLKEMPublicKeyExpanded and MLKEMPrivateKeyExpanded implement the same traits and therefore behave the same as their non-expanded counterparts in most regards.
use bouncycastle_mlkem::{MLKEM768, MLKEMTrait};
use bouncycastle_mlkem::{MLKEM768PublicKeyExpanded, MLKEM768PrivateKeyExpanded};
use bouncycastle_core::traits::KEM;
use bouncycastle_core::errors::KEMError;
let (pk, sk) = MLKEM768::keygen().unwrap();
// Pre-expand the public key uses more memory, but has performance
// improvements if doing multiple encapsulations for the same key
let pk_expanded = MLKEM768PublicKeyExpanded::from(&pk);
let (ss, ct) = MLKEM768::encaps_for_expanded_key(&pk_expanded).unwrap();
// Pre-expand the private key, which uses more memory, but has performance
// improvements if doing multiple decapsulations with the same key
let sk_expanded = MLKEM768PrivateKeyExpanded::from(&sk);
let ss1 = match MLKEM768::decaps_with_expanded_key(&sk_expanded, &ct) {
Err(KEMError) => panic!("Error decapsulating"),
Ok(ss) => ss,
};
assert_eq!(ss, ss1);§decaps_from_seed
This mode is intended for users who want the simplicity of storing only the seed form of the private key. This is merely a convnience function that calls [MLKEM::keygen_from_seed) before performing a decapsulation.
Example usage:
use bouncycastle_mlkem::{MLKEM768, MLKEMTrait};
use bouncycastle_core::traits::KEM;
use bouncycastle_core::errors::KEMError;
use bouncycastle_core::key_material::{KeyMaterial512, KeyType};
use bouncycastle_hex as hex;
let seed = KeyMaterial512::from_bytes_as_type(
&hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f").unwrap(),
KeyType::Seed,
).unwrap();
// for this demo, we do need to run keygen only to get the public key
let (pk, _sk) = MLKEM768::keygen_from_seed(&seed).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 seed
let ss1 = match MLKEM768::decaps_from_seed(&seed, &ct) {
Err(KEMError) => panic!("Error decapsulating"),
Ok(ss) => ss,
};
assert_eq!(ss, ss1);While this is currently only supported when operating from a seed-based private key, something analogous could be done that merges the sk_decode() and sign() routines when working with the standardized private key encoding (which is often called the “semi-expanded format” since the in-memory representation is still larger). Contact us if you need such a thing implemented.
§Deterministic encapsulation
This section pertains to MLKEM::encaps_internal which allows you to pass in the encapsulation randomness and thus obtain a deterministic encapsulation.
The only good reasons for doing this are: A) testing if you need reproducible results, or B) if you want to use your own source of randomness, such as a hardware RNG, instead of the library’s default RNG. If you think you will invent same clever cryptographic scheme by making clever use of this parameter: don’t; you will almost certainly end up with something completely insecure.
use bouncycastle_mlkem::{MLKEM768, MLKEMTrait};
use bouncycastle_core::traits::{KEM};
use bouncycastle_core::errors::KEMError;
use bouncycastle_core::key_material::KeyMaterialTrait;
let (pk, sk) = MLKEM768::keygen().unwrap();
// note: totally insecure and for demonstration purposes only.
// The message `m` needs to be sourced from a cryptographically-secure RNG.
let m: [u8; 32] = [0; 32];
// Create the shared secret and ciphertext using the public key and the random message `m`
let (ss, ct) = MLKEM768::encaps_internal(&pk, None, m);
// 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.ref_to_bytes());Structs§
- MLKEM
- The core internal implementation of the ML-KEM algorithm. This needs to be public for the compiler to be able to find it, but you shouldn’t ever need to use this directly. Please use the named public types.
Constants§
- MLKE
M512_ CT_ LEN - Length of the [u8] holding a ML-KEM-512 ciphertext.
- MLKE
M512_ PK_ LEN - Length of the [u8] holding a ML-KEM-512 public key.
- MLKE
M512_ SK_ LEN - Length of the [u8] holding a ML-KEM-512 private key.
- MLKE
M768_ CT_ LEN - Length of the [u8] holding a ML-KEM-768 ciphertext.
- MLKE
M768_ PK_ LEN - Length of the [u8] holding a ML-KEM-768 public key.
- MLKE
M768_ SK_ LEN - Length of the [u8] holding a ML-KEM-768 private key.
- MLKE
M1024_ CT_ LEN - Length of the [u8] holding a ML-KEM-1024 ciphertext.
- MLKE
M1024_ PK_ LEN - Length of the [u8] holding a ML-KEM-1024 public key.
- MLKE
M1024_ SK_ LEN - Length of the [u8] holding a ML-KEM-1024 private key.
- MLKEM_
RND_ LEN - Length of the [u8] holding an ML-KEM encaps random value, also sometimes called the message
m - MLKEM_
SEED_ LEN - Length of the [u8] holding an ML-KEM seed value.
- MLKEM_
SS_ LEN - Size of in bytes of an ML-KEM shared secret key.
- ML_
KEM_ 512_ NAME - ML_
KEM_ 768_ NAME - ML_
KEM_ 1024_ NAME
Traits§
- MLKEM
Trait - Trait for all three of the ML-DSA algorithm variants.