#![no_std]
#![deny(clippy::pedantic, warnings, missing_docs, unsafe_code)]
#![deny(absolute_paths_not_starting_with_crate, dead_code)]
#![deny(elided_lifetimes_in_paths, explicit_outlives_requirements, keyword_idents)]
#![deny(let_underscore_drop, macro_use_extern_crate, meta_variable_misuse, missing_abi)]
#![deny(non_ascii_idents, rust_2021_incompatible_closure_captures)]
#![deny(rust_2021_incompatible_or_patterns, rust_2021_prefixes_incompatible_syntax)]
#![deny(rust_2021_prelude_collisions, single_use_lifetimes, trivial_casts)]
#![deny(trivial_numeric_casts, unreachable_pub, unsafe_op_in_unsafe_fn, unstable_features)]
#![deny(unused_extern_crates, unused_import_braces, unused_lifetimes, unused_macro_rules)]
#![deny(unused_qualifications, unused_results, variant_size_differences)]
#![doc = include_str!("../README.md")]
pub use rand_core::{CryptoRng, Error as RngError, RngCore};
use crate::traits::SerDes;
use subtle::ConstantTimeEq;
use zeroize::{Zeroize, ZeroizeOnDrop};
mod byte_fns;
mod helpers;
mod k_pke;
mod ml_kem;
mod ntt;
mod sampling;
mod types;
pub mod traits;
const Q: u16 = 3329;
const ZETA: u16 = 17;
pub const SSK_LEN: usize = 32;
#[derive(Clone, Debug, Zeroize, ZeroizeOnDrop)]
pub struct SharedSecretKey([u8; SSK_LEN]);
impl SerDes for SharedSecretKey {
type ByteArray = [u8; SSK_LEN];
fn into_bytes(self) -> Self::ByteArray { self.0 }
fn try_from_bytes(ssk: Self::ByteArray) -> Result<Self, &'static str> {
Ok(SharedSecretKey(ssk))
}
}
impl PartialEq for SharedSecretKey {
fn eq(&self, other: &Self) -> bool { bool::from(self.0.ct_eq(&other.0)) }
}
macro_rules! functionality {
() => {
use crate::byte_fns::byte_decode;
use crate::helpers::{ensure, h};
use crate::ml_kem::{
ml_kem_decaps, ml_kem_encaps, ml_kem_key_gen, ml_kem_key_gen_internal,
};
use crate::traits::{Decaps, Encaps, KeyGen, SerDes};
use crate::SharedSecretKey;
use rand_core::CryptoRngCore;
pub type EncapsKey = crate::types::EncapsKey<EK_LEN>;
pub type DecapsKey = crate::types::DecapsKey<DK_LEN>;
pub type CipherText = crate::types::CipherText<CT_LEN>;
pub struct KG();
impl KeyGen for KG {
type DecapsByteArray = [u8; DK_LEN];
type DecapsKey = DecapsKey;
type EncapsByteArray = [u8; EK_LEN];
type EncapsKey = EncapsKey;
fn try_keygen_with_rng(
rng: &mut impl CryptoRngCore,
) -> Result<(EncapsKey, DecapsKey), &'static str> {
let (mut ek, mut dk) = ([0u8; EK_LEN], [0u8; DK_LEN]);
ml_kem_key_gen::<K, { ETA1 as usize * 64 }>(rng, &mut ek, &mut dk)?;
Ok((EncapsKey { 0: ek }, DecapsKey { 0: dk }))
}
fn keygen_from_seed(d: [u8; 32], z: [u8; 32]) -> (EncapsKey, DecapsKey) {
let (mut ek, mut dk) = ([0u8; EK_LEN], [0u8; DK_LEN]);
ml_kem_key_gen_internal::<K, { ETA1 as usize * 64 }>(d, z, &mut ek, &mut dk);
(EncapsKey { 0: ek }, DecapsKey { 0: dk })
}
fn validate_keypair_with_rng_vartime(
rng: &mut impl CryptoRngCore, ek: &Self::EncapsByteArray,
dk: &Self::DecapsByteArray,
) -> bool {
let len_ek_pke = 384 * K + 32;
let len_dk_pke = 384 * K;
if !(*ek == dk[len_dk_pke..(len_dk_pke + len_ek_pke)]) {
return false;
};
if !(h(ek) == dk[(len_dk_pke + len_ek_pke)..(len_dk_pke + len_ek_pke + 32)]) {
return false;
};
let ek = EncapsKey::try_from_bytes(*ek);
let dk = DecapsKey::try_from_bytes(*dk);
if ek.is_err() || dk.is_err() {
return false;
};
let ek_res = ek.unwrap().try_encaps_with_rng(rng);
if ek_res.is_err() {
return false;
};
let dk_res = dk.unwrap().try_decaps(&ek_res.as_ref().unwrap().1);
if dk_res.is_err() {
return false;
};
return ek_res.unwrap().0 == dk_res.unwrap();
}
}
impl Encaps for EncapsKey {
type CipherText = CipherText;
type SharedSecretKey = SharedSecretKey;
fn try_encaps_with_rng(
&self, rng: &mut impl CryptoRngCore,
) -> Result<(Self::SharedSecretKey, Self::CipherText), &'static str> {
let mut ct = [0u8; CT_LEN];
let ssk = ml_kem_encaps::<K, { ETA1 as usize * 64 }, { ETA2 as usize * 64 }>(
rng, DU, DV, &self.0, &mut ct,
)?;
Ok((ssk, CipherText { 0: ct }))
}
}
impl Decaps for DecapsKey {
type CipherText = CipherText;
type SharedSecretKey = SharedSecretKey;
fn try_decaps(&self, ct: &CipherText) -> Result<SharedSecretKey, &'static str> {
let ssk = ml_kem_decaps::<
K,
{ ETA1 as usize * 64 },
{ ETA2 as usize * 64 },
{ 32 + 32 * (DU as usize * K + DV as usize) },
CT_LEN,
>(DU, DV, &self.0, &ct.0);
ssk
}
}
impl SerDes for EncapsKey {
type ByteArray = [u8; EK_LEN];
fn into_bytes(self) -> Self::ByteArray { self.0 }
fn try_from_bytes(ek: Self::ByteArray) -> Result<Self, &'static str> {
for i in 0..K {
let _ek_hat = byte_decode(12, &ek[384 * i..384 * (i + 1)])?;
}
Ok(EncapsKey { 0: ek })
}
}
impl SerDes for DecapsKey {
type ByteArray = [u8; DK_LEN];
fn into_bytes(self) -> Self::ByteArray { self.0 }
fn try_from_bytes(dk: Self::ByteArray) -> Result<Self, &'static str> {
let len_ek_pke = 384 * K + 32;
let len_dk_pke = 384 * K;
let ek = &dk[len_dk_pke..len_dk_pke + EK_LEN];
let _res =
EncapsKey::try_from_bytes(ek.try_into().map_err(|_| "Malformed encaps key")?)?;
ensure!(
h(ek) == dk[(len_dk_pke + len_ek_pke)..(len_dk_pke + len_ek_pke + 32)],
"Encaps hash wrong"
);
Ok(DecapsKey { 0: dk })
}
}
impl SerDes for CipherText {
type ByteArray = [u8; CT_LEN];
fn into_bytes(self) -> Self::ByteArray { self.0 }
fn try_from_bytes(ct: Self::ByteArray) -> Result<Self, &'static str> {
Ok(CipherText { 0: ct })
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::EncapsKey;
use rand_chacha::rand_core::SeedableRng;
#[test]
fn smoke_test() {
let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(123);
let (ek, dk) = KG::keygen_from_seed([1u8; 32], [2u8; 32]);
let (ssk1, ct) = ek.encaps_from_seed(&[3u8; 32]);
let ssk2 = dk.try_decaps(&ct).unwrap();
assert_eq!(ssk1, ssk2);
for _i in 0..100 {
let (ek, dk) = KG::try_keygen_with_rng(&mut rng).unwrap();
let (ssk1, ct) = ek.try_encaps_with_rng(&mut rng).unwrap();
let ssk2 = dk.try_decaps(&ct).unwrap();
assert!(KG::validate_keypair_with_rng_vartime(
&mut rng,
&ek.clone().into_bytes(),
&dk.clone().into_bytes()
));
assert_eq!(ssk1, ssk2);
assert_eq!(ek.clone().0, EncapsKey::try_from_bytes(ek.into_bytes()).unwrap().0);
assert_eq!(dk.clone().0, DecapsKey::try_from_bytes(dk.into_bytes()).unwrap().0);
}
}
}
};
}
#[cfg(feature = "ml-kem-512")]
pub mod ml_kem_512 {
const K: usize = 2;
const ETA1: u32 = 3;
const ETA2: u32 = 2;
const DU: u32 = 10;
const DV: u32 = 4;
pub const EK_LEN: usize = 800;
pub const DK_LEN: usize = 1632;
pub const CT_LEN: usize = 768;
functionality!();
}
#[cfg(feature = "ml-kem-768")]
pub mod ml_kem_768 {
const K: usize = 3;
const ETA1: u32 = 2;
const ETA2: u32 = 2;
const DU: u32 = 10;
const DV: u32 = 4;
pub const EK_LEN: usize = 1184;
pub const DK_LEN: usize = 2400;
pub const CT_LEN: usize = 1088;
functionality!();
}
#[cfg(feature = "ml-kem-1024")]
pub mod ml_kem_1024 {
const K: usize = 4;
const ETA1: u32 = 2;
const ETA2: u32 = 2;
const DU: u32 = 11;
const DV: u32 = 5;
pub const EK_LEN: usize = 1568;
pub const DK_LEN: usize = 3168;
pub const CT_LEN: usize = 1568;
functionality!();
}