March Docs

MlKem

MlKem: ML-KEM-768 key encapsulation (FIPS 203), the NIST post-quantum KEM.

Two parties agree on a 32-byte shared secret: the receiver publishes an encapsulation (public) key, the sender encapsulates against it and sends the ciphertext, and the receiver decapsulates the ciphertext with its secret key. Use the secret as input keying material (HKDF), and combine it with a classical exchange (X25519) where you need a hybrid.

Backed by the vendored mlkem-native (runtime/pqc.c, runtime/third_party/), portable C, the same code on the compiled and interpreted backends. The builtins are deterministic: this module draws the randomness from Crypto.random_bytes, and the *_from_seed / encapsulate_with_coins variants take it explicitly (for test vectors).

Types

typeKeyPairKeyPair = { public_key : Bytes, secret_key : Bytes }#

Functions

fnciphertext_bytesciphertext_bytes() : Int do 1088 end#

Ciphertext size in bytes: 1088.

fndecapsulatedecapsulate(secret_key : Bytes, ciphertext : Bytes) : Result(Bytes, String)#

The shared secret a [ciphertext] carries, recovered with [secret_key]. Err for a wrong-length argument or a corrupted secret key. A ciphertext made for a different key, or tampered with, is NOT an error: ML-KEM's implicit rejection returns an unrelated pseudorandom secret, so the parties' keys simply disagree (authenticate what you derive from it).

fnencapsulateencapsulate(public_key : Bytes) : Result((Bytes, Bytes), String)#

Encapsulate a fresh shared secret against [public_key]: Ok((ciphertext, shared_secret)). Send the ciphertext; keep the secret.

match MlKem.encapsulate(peer_public_key) do
  Ok((ct, secret)) -> ...
  Err(e) -> ...
end
fnencapsulate_with_coinsencapsulate_with_coins(public_key : Bytes, coins : Bytes) : Result((Bytes, Bytes), String)#

Encapsulate against [public_key] with explicit 32-byte [coins]: Ok((ciphertext, shared_secret)). Err for a wrong-length argument or a public key that fails FIPS 203's modulus check. Prefer encapsulate/1; reusing coins reuses the secret.

fnkeypairkeypair() : KeyPair#

A fresh key pair from 64 random bytes.

let kp = MlKem.keypair()
-- kp.public_key: 1184 bytes, kp.secret_key: 2400 bytes
fnkeypair_from_seedkeypair_from_seed(seed : Bytes) : Result(KeyPair, String)#

The key pair a 64-byte seed (d || z, FIPS 203 ML-KEM.KeyGen_internal) determines. Err for a seed of any other length.

fnpublic_key_bytespublic_key_bytes() : Int do 1184 end#

Encapsulation (public) key size in bytes: 1184.

fnsecret_key_bytessecret_key_bytes() : Int do 2400 end#

Decapsulation (secret) key size in bytes: 2400.

fnshared_secret_bytesshared_secret_bytes() : Int do 32 end#

Shared-secret size in bytes: 32.