Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documentation pass #344

Merged
merged 5 commits into from
Jul 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions libcrux-ml-kem/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# ML-KEM

This crate implements all three ML-KEM ([FIPS 203](https://csrc.nist.gov/pubs/fips/203/ipd) (Initial Public Draft)) variants 512, 768, and 1024. It is
formally verified using [hax](https://cryspen.com/hax) and
[F*](https://fstar-lang.org).

Functions in this crate use CPU feature detection to pick the most efficient version
on each platform. To use a specific version with your own feature detection
use e.g. one of the following
- `mlkem768::avx2::generate_key_pair`,
- `mlkem768::neon::generate_key_pair`,
- `mlkem768::portable::generate_key_pair`,

analogously for encapsulation and decapsulation.

```Rust
use rand::{rngs::OsRng, RngCore};

// Ensure you use good randomness.
// It is not recommended to use OsRng directly!
// Instead it is highly encouraged to use RNGs like NISTs DRBG to account for
// bad system entropy.
fn random_array<const L: usize>() -> [u8; L] {
let mut rng = OsRng;
let mut seed = [0; L];
rng.try_fill_bytes(&mut seed).unwrap();
seed
}

use libcrux_ml_kem::*;

// This example uses ML-KEM 768. The other variants can be used the same way.

// Generate a key pair.
let randomness = random_array();
let key_pair = mlkem768::generate_key_pair(randomness);

// Encapsulating a shared secret to a public key.
let randomness = random_array();
let (ciphertext, shared_secret) = mlkem768::encapsulate(key_pair.public_key(), randomness);

// Decapsulating a shared secret with a private key.
let shared_secret_decapsulated = mlkem768::decapsulate(key_pair.private_key(), &ciphertext);
```


## Features

By default, all ML-KEM parameter sets are enabled. If required, they are
available individually under feature flags `mlkem512`, `mlkem768`,
`mlkem1024`.

In addition to the verified implementations of the ML-KEM variants, the
feature flag `pre-verification` gives access to, as yet, unverified
implementations of ML-KEM that are optimized for SIMD instruction sets.

### Kyber Round 3
The `kyber` flag (in combination with `pre-verification`) also gives access
to an, as yet, unverified implementation of Kyber as submitted in Round 3 of
the NIST PQ competition.

22 changes: 19 additions & 3 deletions libcrux-ml-kem/src/kem/kyber/kyber1024.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@ const ETA2_RANDOMNESS_SIZE: usize = ETA2 * 64;
const IMPLICIT_REJECTION_HASH_INPUT_SIZE: usize = SHARED_SECRET_SIZE + CPA_PKE_CIPHERTEXT_SIZE_1024;

// Kyber 1024 types
/// An ML-KEM 1024 Ciphertext
pub type MlKem1024Ciphertext = MlKemCiphertext<CPA_PKE_CIPHERTEXT_SIZE_1024>;
/// An ML-KEM 1024 Private key
pub type MlKem1024PrivateKey = MlKemPrivateKey<SECRET_KEY_SIZE_1024>;
/// An ML-KEM 1024 Public key
pub type MlKem1024PublicKey = MlKemPublicKey<CPA_PKE_PUBLIC_KEY_SIZE_1024>;

/// Validate a public key.
Expand All @@ -55,6 +58,9 @@ pub fn validate_public_key(public_key: MlKem1024PublicKey) -> Option<MlKem1024Pu
}

/// Generate ML-KEM 1024 Key Pair
///
/// Generate an ML-KEM key pair. The input is a byte array of size
/// [`crate::KEY_GENERATION_SEED_SIZE`].
pub fn generate_key_pair(
randomness: [u8; KEY_GENERATION_SEED_SIZE],
) -> MlKemKeyPair<SECRET_KEY_SIZE_1024, CPA_PKE_PUBLIC_KEY_SIZE_1024> {
Expand All @@ -69,9 +75,11 @@ pub fn generate_key_pair(
>(randomness)
}

pub type MlKem1024State = MlKemState<RANK_1024>;
#[allow(unused)]
pub(crate) type MlKem1024State = MlKemState<RANK_1024>;

pub fn generate_key_pair_unpacked(
#[allow(unused)]
pub(crate) fn generate_key_pair_unpacked(
randomness: [u8; KEY_GENERATION_SEED_SIZE],
) -> (MlKem1024State, MlKem1024PublicKey) {
generate_keypair_unpacked::<
Expand All @@ -86,6 +94,10 @@ pub fn generate_key_pair_unpacked(
}

/// Encapsulate ML-KEM 1024
///
/// Generates an ([`MlKem1024Ciphertext`], [`MlKemSharedSecret`]) tuple.
/// The input is a reference to an [`MlKem1024PublicKey`] and [`crate::SHARED_SECRET_SIZE`]
/// bytes of `randomness`.
pub fn encapsulate(
public_key: &MlKemPublicKey<CPA_PKE_PUBLIC_KEY_SIZE_1024>,
randomness: [u8; SHARED_SECRET_SIZE],
Expand All @@ -111,6 +123,9 @@ pub fn encapsulate(
}

/// Decapsulate ML-KEM 1024
///
/// Generates an [`MlKemSharedSecret`].
/// The input is a reference to an [`MlKem1024PrivateKey`] and an [`MlKem1024Ciphertext`].
pub fn decapsulate(
secret_key: &MlKemPrivateKey<SECRET_KEY_SIZE_1024>,
ciphertext: &MlKemCiphertext<CPA_PKE_CIPHERTEXT_SIZE_1024>,
Expand All @@ -135,7 +150,8 @@ pub fn decapsulate(
>(secret_key, ciphertext)
}

pub fn decapsulate_unpacked(
#[allow(unused)]
pub(crate) fn decapsulate_unpacked(
state: &MlKem1024State,
ciphertext: &MlKemCiphertext<CPA_PKE_CIPHERTEXT_SIZE_1024>,
) -> [u8; SHARED_SECRET_SIZE] {
Expand Down
21 changes: 18 additions & 3 deletions libcrux-ml-kem/src/kem/kyber/kyber512.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,11 @@ const ETA2_RANDOMNESS_SIZE: usize = ETA2 * 64;
const IMPLICIT_REJECTION_HASH_INPUT_SIZE: usize = SHARED_SECRET_SIZE + CPA_PKE_CIPHERTEXT_SIZE_512;

// Kyber 512 types
/// An ML-KEM 512 Ciphertext
pub type MlKem512Ciphertext = MlKemCiphertext<CPA_PKE_CIPHERTEXT_SIZE_512>;
/// An ML-KEM 512 Private key
pub type MlKem512PrivateKey = MlKemPrivateKey<SECRET_KEY_SIZE_512>;
/// An ML-KEM 512 Public key
pub type MlKem512PublicKey = MlKemPublicKey<CPA_PKE_PUBLIC_KEY_SIZE_512>;

/// Validate a public key.
Expand All @@ -53,6 +56,9 @@ pub fn validate_public_key(public_key: MlKem512PublicKey) -> Option<MlKem512Publ
}

/// Generate ML-KEM 512 Key Pair
///
/// The input is a byte array of size
/// [`crate::KEY_GENERATION_SEED_SIZE`].
pub fn generate_key_pair(
randomness: [u8; KEY_GENERATION_SEED_SIZE],
) -> MlKemKeyPair<SECRET_KEY_SIZE_512, CPA_PKE_PUBLIC_KEY_SIZE_512> {
Expand All @@ -67,9 +73,11 @@ pub fn generate_key_pair(
>(randomness)
}

pub type MlKem512State = MlKemState<RANK_512>;
#[allow(unused)]
pub(crate) type MlKem512State = MlKemState<RANK_512>;

pub fn generate_key_pair_unpacked(
#[allow(unused)]
pub(crate) fn generate_key_pair_unpacked(
randomness: [u8; KEY_GENERATION_SEED_SIZE],
) -> (MlKem512State, MlKem512PublicKey) {
generate_keypair_unpacked::<
Expand All @@ -84,6 +92,9 @@ pub fn generate_key_pair_unpacked(
}

/// Encapsulate ML-KEM 512
///
/// Generates an ([`MlKem512Ciphertext`], [`MlKemSharedSecret`]) tuple.
/// The input is a reference to an [`MlKem512PublicKey`] and [`crate::SHARED_SECRET_SIZE`]
pub fn encapsulate(
public_key: &MlKemPublicKey<CPA_PKE_PUBLIC_KEY_SIZE_512>,
randomness: [u8; SHARED_SECRET_SIZE],
Expand All @@ -109,6 +120,9 @@ pub fn encapsulate(
}

/// Decapsulate ML-KEM 512
///
/// Generates an [`MlKemSharedSecret`].
/// The input is a reference to an [`MlKem512PrivateKey`] and an [`MlKem512Ciphertext`].
pub fn decapsulate(
secret_key: &MlKemPrivateKey<SECRET_KEY_SIZE_512>,
ciphertext: &MlKemCiphertext<CPA_PKE_CIPHERTEXT_SIZE_512>,
Expand All @@ -133,7 +147,8 @@ pub fn decapsulate(
>(secret_key, ciphertext)
}

pub fn decapsulate_unpacked(
#[allow(unused)]
pub(crate) fn decapsulate_unpacked(
state: &MlKem512State,
ciphertext: &MlKemCiphertext<CPA_PKE_CIPHERTEXT_SIZE_512>,
) -> [u8; SHARED_SECRET_SIZE] {
Expand Down
22 changes: 19 additions & 3 deletions libcrux-ml-kem/src/kem/kyber/kyber768.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,11 @@ const ETA2_RANDOMNESS_SIZE: usize = ETA2 * 64;
const IMPLICIT_REJECTION_HASH_INPUT_SIZE: usize = SHARED_SECRET_SIZE + CPA_PKE_CIPHERTEXT_SIZE_768;

// Kyber 768 types
/// An ML-KEM 768 Ciphertext
pub type MlKem768Ciphertext = MlKemCiphertext<CPA_PKE_CIPHERTEXT_SIZE_768>;
/// An ML-KEM 768 Private key
pub type MlKem768PrivateKey = MlKemPrivateKey<SECRET_KEY_SIZE_768>;
/// An ML-KEM 768 Public key
pub type MlKem768PublicKey = MlKemPublicKey<CPA_PKE_PUBLIC_KEY_SIZE_768>;

/// Validate a public key.
Expand All @@ -54,6 +57,9 @@ pub fn validate_public_key(public_key: MlKem768PublicKey) -> Option<MlKem768Publ
}

/// Generate ML-KEM 768 Key Pair
///
/// Generate an ML-KEM key pair. The input is a byte array of size
/// [`crate::KEY_GENERATION_SEED_SIZE`].
pub fn generate_key_pair(
randomness: [u8; KEY_GENERATION_SEED_SIZE],
) -> MlKemKeyPair<SECRET_KEY_SIZE_768, CPA_PKE_PUBLIC_KEY_SIZE_768> {
Expand All @@ -68,9 +74,11 @@ pub fn generate_key_pair(
>(randomness)
}

pub type MlKem768State = MlKemState<RANK_768>;
#[allow(unused)]
pub(crate) type MlKem768State = MlKemState<RANK_768>;

pub fn generate_key_pair_unpacked(
#[allow(unused)]
pub(crate) fn generate_key_pair_unpacked(
randomness: [u8; KEY_GENERATION_SEED_SIZE],
) -> (MlKem768State, MlKem768PublicKey) {
generate_keypair_unpacked::<
Expand All @@ -85,6 +93,10 @@ pub fn generate_key_pair_unpacked(
}

/// Encapsulate ML-KEM 768
///
/// Generates an ([`MlKem768Ciphertext`], [`MlKemSharedSecret`]) tuple.
/// The input is a reference to an [`MlKem768PublicKey`] and [`crate::SHARED_SECRET_SIZE`]
/// bytes of `randomness`.
pub fn encapsulate(
public_key: &MlKemPublicKey<CPA_PKE_PUBLIC_KEY_SIZE_768>,
randomness: [u8; SHARED_SECRET_SIZE],
Expand All @@ -110,6 +122,9 @@ pub fn encapsulate(
}

/// Decapsulate ML-KEM 768
///
/// Generates an [`MlKemSharedSecret`].
/// The input is a reference to an [`MlKem768PrivateKey`] and an [`MlKem768Ciphertext`].
pub fn decapsulate(
secret_key: &MlKemPrivateKey<SECRET_KEY_SIZE_768>,
ciphertext: &MlKemCiphertext<CPA_PKE_CIPHERTEXT_SIZE_768>,
Expand All @@ -134,7 +149,8 @@ pub fn decapsulate(
>(secret_key, ciphertext)
}

pub fn decapsulate_unpacked(
#[allow(unused)]
pub(crate) fn decapsulate_unpacked(
state: &MlKem768State,
ciphertext: &MlKemCiphertext<CPA_PKE_CIPHERTEXT_SIZE_768>,
) -> [u8; SHARED_SECRET_SIZE] {
Expand Down
25 changes: 18 additions & 7 deletions libcrux-ml-kem/src/kem/kyber/types.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
macro_rules! impl_generic_struct {
($name:ident) => {
($name:ident, $doc:expr) => {
#[doc = $doc]
pub struct $name<const SIZE: usize> {
pub(super) value: [u8; SIZE],
}
Expand Down Expand Up @@ -42,14 +43,18 @@ macro_rules! impl_generic_struct {
}

impl<const SIZE: usize> $name<SIZE> {
/// A reference to the raw byte slice.
pub fn as_slice(&self) -> &[u8; SIZE] {
&self.value
}

pub fn split_at(&self, mid: usize) -> (&[u8], &[u8]) {
// This is only used for some of the macro callers.
#[allow(dead_code)]
// /// Split this value and return the raw byte slices.
pub(crate) fn split_at(&self, mid: usize) -> (&[u8], &[u8]) {
self.value.split_at(mid)
}

/// The number of bytes
pub const fn len() -> usize {
SIZE
}
Expand Down Expand Up @@ -92,9 +97,9 @@ macro_rules! impl_index_impls_for_generic_struct {
};
}

impl_generic_struct!(MlKemCiphertext);
impl_generic_struct!(MlKemPrivateKey);
impl_generic_struct!(MlKemPublicKey);
impl_generic_struct!(MlKemCiphertext, "An ML-KEM Ciphertext");
impl_generic_struct!(MlKemPrivateKey, "An ML-KEM Private key");
impl_generic_struct!(MlKemPublicKey, "An ML-KEM Public key");

// These traits are used only in `ind_cpa` for kyber cipher text.
mod index_impls {
Expand All @@ -121,29 +126,35 @@ impl<const PRIVATE_KEY_SIZE: usize, const PUBLIC_KEY_SIZE: usize>
}
}

/// Create a new [`MlKemKeyPair`] from the secret and public key.
pub fn from(
sk: MlKemPrivateKey<PRIVATE_KEY_SIZE>,
pk: MlKemPublicKey<PUBLIC_KEY_SIZE>,
) -> Self {
Self { sk, pk }
}

/// Get a reference to the [`MlKemPublicKey<PUBLIC_KEY_SIZE>`].
pub fn public_key(&self) -> &MlKemPublicKey<PUBLIC_KEY_SIZE> {
&self.pk
}

/// Get a reference to the [`MlKemPrivateKey<PRIVATE_KEY_SIZE>`].
pub fn private_key(&self) -> &MlKemPrivateKey<PRIVATE_KEY_SIZE> {
&self.sk
}

/// Get a reference to the raw public key bytes.
pub fn pk(&self) -> &[u8; PUBLIC_KEY_SIZE] {
self.pk.as_slice()
}

/// Get a reference to the raw private key bytes.
pub fn sk(&self) -> &[u8; PRIVATE_KEY_SIZE] {
self.sk.as_slice()
}


/// Separate this key into the public and private key.
pub fn into_parts(
self,
) -> (
Expand Down
Loading
Loading