Skip to content

Commit

Permalink
Merge pull request #344 from cryspen/jonas/doc-pass
Browse files Browse the repository at this point in the history
Documentation pass
  • Loading branch information
franziskuskiefer authored Jul 2, 2024
2 parents 86efd54 + 7b19aff commit f1dfed0
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 28 deletions.
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

0 comments on commit f1dfed0

Please sign in to comment.