Skip to main content
Version: 3.9.0

ML-KEM (Kyber) – Rust bindings

note

For an in-depth explanation of the primitive, performance advice and backend-specific tuning knobs see the C++ ML-KEM guide.

Overview

icicle-ml-kem is a Rust wrapper around Icicle's batched ML-KEM implementation (Kyber). It exposes three functions – keygen, encapsulate and decapsulate – that work on host or device memory.


Public API

Key pair generation

pub fn keygen<P: KyberParams>(
entropy: &(impl HostOrDeviceSlice<u8> + ?Sized), // batch_size × 64 bytes
config: &MlKemConfig,
public_keys: &mut (impl HostOrDeviceSlice<u8> + ?Sized), // batch_size × PUBLIC_KEY_BYTES
secret_keys: &mut (impl HostOrDeviceSlice<u8> + ?Sized), // batch_size × SECRET_KEY_BYTES
) -> Result<(), eIcicleError>;

Encapsulation

pub fn encapsulate<P: KyberParams>(
message: &(impl HostOrDeviceSlice<u8> + ?Sized), // batch_size × 32 bytes
public_keys: &(impl HostOrDeviceSlice<u8> + ?Sized), // batch_size × PUBLIC_KEY_BYTES
config: &MlKemConfig,
ciphertexts: &mut (impl HostOrDeviceSlice<u8> + ?Sized), // batch_size × CIPHERTEXT_BYTES
shared_secrets: &mut (impl HostOrDeviceSlice<u8> + ?Sized), // batch_size × SHARED_SECRET_BYTES
) -> Result<(), eIcicleError>;

Decapsulation

pub fn decapsulate<P: KyberParams>(
secret_keys: &(impl HostOrDeviceSlice<u8> + ?Sized), // batch_size × SECRET_KEY_BYTES
ciphertexts: &(impl HostOrDeviceSlice<u8> + ?Sized), // batch_size × CIPHERTEXT_BYTES
config: &MlKemConfig,
shared_secrets: &mut (impl HostOrDeviceSlice<u8> + ?Sized), // batch_size × SHARED_SECRET_BYTES
) -> Result<(), eIcicleError>;

All buffers may live either on the host or on the currently-active device.

MlKemConfig

pub struct MlKemConfig {
pub stream: IcicleStreamHandle,
pub is_async: bool,

// Location hints
pub messages_on_device: bool,
pub entropy_on_device: bool,
pub public_keys_on_device: bool,
pub secret_keys_on_device: bool,
pub ciphertexts_on_device: bool,
pub shared_secrets_on_device: bool,

pub batch_size: i32,
pub ext: ConfigExtension,
}

use Default::default() to get the default configuration. Setting is_async = true lets the call return immediately; remember to synchronize the stream before reading results.


Quick-start example (Kyber768, host buffers)

use icicle_ml_kem as mlkem;
use icicle_ml_kem::{keygen, encapsulate, decapsulate};
use icicle_ml_kem::kyber_params::{Kyber768Params, ENTROPY_BYTES, MESSAGE_BYTES};
use icicle_runtime::memory::HostSlice;
use rand::{RngCore, rngs::OsRng};

const BATCH: usize = 1 << 12;

fn main() {
// Allocate buffers on the host
let mut entropy = vec![0u8; BATCH * ENTROPY_BYTES];
let mut msg = vec![0u8; BATCH * MESSAGE_BYTES];
OsRng.fill_bytes(&mut entropy);
OsRng.fill_bytes(&mut msg);

let mut pk = vec![0u8; BATCH * Kyber768Params::PUBLIC_KEY_BYTES];
let mut sk = vec![0u8; BATCH * Kyber768Params::SECRET_KEY_BYTES];
let mut ct = vec![0u8; BATCH * Kyber768Params::CIPHERTEXT_BYTES];
let mut ss_enc = vec![0u8; BATCH * Kyber768Params::SHARED_SECRET_BYTES];
let mut ss_dec = vec![0u8; BATCH * Kyber768Params::SHARED_SECRET_BYTES];

// Configuration – everything stays on host
let mut cfg = mlkem::config::MlKemConfig::default();
cfg.batch_size = BATCH as i32;

// Key generation
keygen::<Kyber768Params>(
HostSlice::from_slice(&entropy),
&cfg,
HostSlice::from_mut_slice(&mut pk),
HostSlice::from_mut_slice(&mut sk),
).unwrap();

// Encapsulation
encapsulate::<Kyber768Params>(
HostSlice::from_slice(&msg),
HostSlice::from_slice(&pk),
&cfg,
HostSlice::from_mut_slice(&mut ct),
HostSlice::from_mut_slice(&mut ss_enc),
).unwrap();

// Decapsulation
decapsulate::<Kyber768Params>(
HostSlice::from_slice(&sk),
HostSlice::from_slice(&ct),
&cfg,
HostSlice::from_mut_slice(&mut ss_dec),
).unwrap();

assert_eq!(ss_enc, ss_dec);
println!("{} successful KEM operations!", BATCH);
}

Device & async execution

The API works identically for Device buffers – allocate input/output DeviceVecs, and provide an IcicleStream for non-blocking execution. See tests.rs in the crate for an end-to-end example.


Error handling

All functions return Result<(), eIcicleError>. An error is raised for invalid buffer sizes, mismatched device selection, or when the selected backend is not available.


See also