use crate::ffi;
use pqcrypto_traits::kem as primitive;
use pqcrypto_traits::{Error, Result};
macro_rules! simple_struct {
($type: ident, $size: expr) => {
#[derive(Clone, Copy)]
pub struct $type([u8; $size]);
impl $type {
fn new() -> Self {
$type([0u8; $size])
}
}
impl primitive::$type for $type {
#[inline]
fn as_bytes(&self) -> &[u8] {
&self.0
}
fn from_bytes(bytes: &[u8]) -> Result<Self> {
if bytes.len() != $size {
Err(Error::BadLength {
name: stringify!($type),
actual: bytes.len(),
expected: $size,
})
} else {
let mut array = [0u8; $size];
array.copy_from_slice(bytes);
Ok($type(array))
}
}
}
impl PartialEq for $type {
fn eq(&self, other: &Self) -> bool {
self.0
.iter()
.zip(other.0.iter())
.try_for_each(|(a, b)| if a == b { Ok(()) } else { Err(()) })
.is_ok()
}
}
};
}
simple_struct!(
PublicKey,
ffi::PQCLEAN_FRODOKEM976SHAKE_OPT_CRYPTO_PUBLICKEYBYTES
);
simple_struct!(
SecretKey,
ffi::PQCLEAN_FRODOKEM976SHAKE_OPT_CRYPTO_SECRETKEYBYTES
);
simple_struct!(
Ciphertext,
ffi::PQCLEAN_FRODOKEM976SHAKE_OPT_CRYPTO_CIPHERTEXTBYTES
);
simple_struct!(SharedSecret, ffi::PQCLEAN_FRODOKEM976SHAKE_OPT_CRYPTO_BYTES);
pub const fn public_key_bytes() -> usize {
ffi::PQCLEAN_FRODOKEM976SHAKE_OPT_CRYPTO_PUBLICKEYBYTES
}
pub const fn secret_key_bytes() -> usize {
ffi::PQCLEAN_FRODOKEM976SHAKE_OPT_CRYPTO_SECRETKEYBYTES
}
pub const fn ciphertext_bytes() -> usize {
ffi::PQCLEAN_FRODOKEM976SHAKE_OPT_CRYPTO_CIPHERTEXTBYTES
}
pub const fn shared_secret_bytes() -> usize {
ffi::PQCLEAN_FRODOKEM976SHAKE_OPT_CRYPTO_BYTES
}
pub fn keypair() -> (PublicKey, SecretKey) {
let mut pk = PublicKey::new();
let mut sk = SecretKey::new();
assert_eq!(
unsafe {
ffi::PQCLEAN_FRODOKEM976SHAKE_OPT_crypto_kem_keypair(
pk.0.as_mut_ptr(),
sk.0.as_mut_ptr(),
)
},
0
);
(pk, sk)
}
pub fn encapsulate(pk: &PublicKey) -> (SharedSecret, Ciphertext) {
let mut ss = SharedSecret::new();
let mut ct = Ciphertext::new();
assert_eq!(
unsafe {
ffi::PQCLEAN_FRODOKEM976SHAKE_OPT_crypto_kem_enc(
ct.0.as_mut_ptr(),
ss.0.as_mut_ptr(),
pk.0.as_ptr(),
)
},
0,
);
(ss, ct)
}
pub fn decapsulate(ct: &Ciphertext, sk: &SecretKey) -> SharedSecret {
let mut ss = SharedSecret::new();
assert_eq!(
unsafe {
ffi::PQCLEAN_FRODOKEM976SHAKE_OPT_crypto_kem_dec(
ss.0.as_mut_ptr(),
ct.0.as_ptr(),
sk.0.as_ptr(),
)
},
0
);
ss
}
#[cfg(test)]
mod test {
use super::*;
#[test]
pub fn test_kem() {
let (pk, sk) = keypair();
let (ss1, ct) = encapsulate(&pk);
let ss2 = decapsulate(&ct, &sk);
assert!(ss1.0 == ss2.0, "Difference in shared secrets!");
}
}