Skip to content

Conversation

@jschneider-bensch
Copy link
Contributor

@jschneider-bensch jschneider-bensch commented Oct 20, 2025

This PR ports over some of the APIs that are provided for ML-KEM in mainline libcrux, namely:

  • The KEM-trait API.
  • The PQCP "packed" APIs. We don't have the unpacked APIs here, so we also can't provide those for PQCP.
  • The (de-)serialization of public keys and ciphertexts with tls_codec

The PR does not port the incremental API.

Fixes https://github.com/cryspen/home/issues/431

Base automatically changed from jonas/mlkem-secrets to main November 4, 2025 15:42
@jschneider-bensch
Copy link
Contributor Author

Blocked on #117 getting merged, which updates the C extraction scripts.

@jschneider-bensch jschneider-bensch marked this pull request as ready for review December 16, 2025 13:18
Copy link
Member

@keks keks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this looks like it should work, just the copying is unfortunate. Do you think there is something we can do about with little effort or is that a bigger thing?

Comment on lines +178 to +228
macro_rules! impl_kem_trait {
($variant:ty, $pk:ty, $sk:ty, $ct:ty) => {
impl
libcrux_traits::kem::arrayref::Kem<
CPA_PKE_PUBLIC_KEY_SIZE,
SECRET_KEY_SIZE,
CPA_PKE_CIPHERTEXT_SIZE,
SHARED_SECRET_SIZE,
KEY_GENERATION_SEED_SIZE,
SHARED_SECRET_SIZE,
> for $variant
{
fn keygen(
ek: &mut [u8; CPA_PKE_PUBLIC_KEY_SIZE],
dk: &mut [libcrux_secrets::U8; SECRET_KEY_SIZE],
rand: &[libcrux_secrets::U8; KEY_GENERATION_SEED_SIZE],
) -> Result<(), libcrux_traits::kem::owned::KeyGenError> {
let key_pair = generate_key_pair(*rand);
ek.copy_from_slice(key_pair.pk());
dk.copy_from_slice(key_pair.sk());

Ok(())
}

fn encaps(
ct: &mut [u8; CPA_PKE_CIPHERTEXT_SIZE],
ss: &mut [libcrux_secrets::U8; SHARED_SECRET_SIZE],
ek: &[u8; CPA_PKE_PUBLIC_KEY_SIZE],
rand: &[libcrux_secrets::U8; SHARED_SECRET_SIZE],
) -> Result<(), libcrux_traits::kem::owned::EncapsError> {
let public_key: $pk = ek.into();

let (ct_, ss_) = encapsulate(&public_key, *rand);
ct.copy_from_slice(ct_.as_slice());
ss.copy_from_slice(ss_.as_slice());

Ok(())
}

fn decaps(
ss: &mut [libcrux_secrets::U8; SHARED_SECRET_SIZE],
ct: &[u8; CPA_PKE_CIPHERTEXT_SIZE],
dk: &[libcrux_secrets::U8; SECRET_KEY_SIZE],
) -> Result<(), libcrux_traits::kem::owned::DecapsError> {
let secret_key: $sk = dk.into();
let ciphertext: $ct = ct.into();

let ss_ = decapsulate(&secret_key, &ciphertext);

ss.copy_from_slice(ss_.as_slice());

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit unfortunate that we have to copy everywhere here. I suppose we don't have &mut in this implementation yet?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, it is unfortunate. At the moment, we do not have any top-level APIs here that operate on pre-allocated inputs. This would be good to have and I'll file an issue for it, but since this PR is about matching mainline APIs I would rather not add new top-level APIs here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense! Let's get this in as-is

@keks keks self-requested a review January 14, 2026 13:56
Copy link
Member

@keks keks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's get this in!

@jschneider-bensch jschneider-bensch merged commit 843cbc6 into main Jan 14, 2026
35 checks passed
@jschneider-bensch jschneider-bensch deleted the jonas/mlkem-apis branch January 14, 2026 16:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants