#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use crate::error::Error as KemError; use core::marker::PhantomData;
use dcrypt_algorithms::error::Error as AlgoError;
use dcrypt_api::{
error::Error as ApiError,
traits::serialize::{Serialize, SerializeSecret},
Kem as KemTrait, Key as ApiKey, Result as ApiResult,
};
use rand::{CryptoRng, RngCore};
use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
use super::ind_cca::{kem_decaps, kem_encaps, kem_keygen};
use super::params::KyberParams;
#[derive(Clone, Debug, Zeroize)]
pub struct KyberPublicKey(Vec<u8>);
impl KyberPublicKey {
pub fn new(data: Vec<u8>) -> Self {
Self(data)
}
pub fn into_vec(self) -> Vec<u8> {
self.0
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
pub fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
Ok(Self(bytes.to_vec()))
}
pub fn to_bytes(&self) -> Vec<u8> {
self.0.clone()
}
}
impl Serialize for KyberPublicKey {
fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
Self::from_bytes(bytes)
}
fn to_bytes(&self) -> Vec<u8> {
self.to_bytes()
}
}
#[derive(Clone, Debug, Zeroize, ZeroizeOnDrop)]
pub struct KyberSecretKey(Vec<u8>);
impl KyberSecretKey {
pub fn new(data: Vec<u8>) -> Self {
Self(data)
}
pub fn to_vec(&self) -> Vec<u8> {
self.0.clone()
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub(crate) fn as_bytes(&self) -> &[u8] {
&self.0
}
pub fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
Ok(Self(bytes.to_vec()))
}
pub fn to_bytes_zeroizing(&self) -> Zeroizing<Vec<u8>> {
Zeroizing::new(self.0.clone())
}
}
impl SerializeSecret for KyberSecretKey {
fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
Self::from_bytes(bytes)
}
fn to_bytes_zeroizing(&self) -> Zeroizing<Vec<u8>> {
self.to_bytes_zeroizing()
}
}
#[derive(Clone, Debug)]
pub struct KyberCiphertext(Vec<u8>);
impl KyberCiphertext {
pub fn new(data: Vec<u8>) -> Self {
Self(data)
}
pub fn into_vec(self) -> Vec<u8> {
self.0
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
Ok(Self(bytes.to_vec()))
}
pub fn to_bytes(&self) -> Vec<u8> {
self.0.clone()
}
}
impl Serialize for KyberCiphertext {
fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
Self::from_bytes(bytes)
}
fn to_bytes(&self) -> Vec<u8> {
self.to_bytes()
}
}
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
pub struct KyberSharedSecret(ApiKey);
impl KyberSharedSecret {
pub fn new(key: ApiKey) -> Self {
Self(key)
}
pub fn to_key(&self) -> ApiKey {
self.0.clone()
}
pub fn len(&self) -> usize {
self.0.as_ref().len()
}
pub fn is_empty(&self) -> bool {
self.0.as_ref().is_empty()
}
pub(crate) fn as_bytes(&self) -> &[u8] {
self.0.as_ref()
}
pub fn to_bytes_zeroizing(&self) -> Zeroizing<Vec<u8>> {
Zeroizing::new(self.0.as_ref().to_vec())
}
}
impl SerializeSecret for KyberSharedSecret {
fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
Ok(Self(ApiKey::new(bytes)))
}
fn to_bytes_zeroizing(&self) -> Zeroizing<Vec<u8>> {
self.to_bytes_zeroizing()
}
}
impl core::fmt::Debug for KyberSharedSecret {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("KyberSharedSecret")
.field("length", &self.len())
.finish()
}
}
pub struct KyberKem<P: KyberParams> {
_params: PhantomData<P>,
}
impl<P: KyberParams> KemTrait for KyberKem<P> {
type PublicKey = KyberPublicKey;
type SecretKey = KyberSecretKey;
type SharedSecret = KyberSharedSecret;
type Ciphertext = KyberCiphertext;
type KeyPair = (Self::PublicKey, Self::SecretKey);
fn name() -> &'static str {
P::NAME
}
fn keypair<R: RngCore + CryptoRng>(rng: &mut R) -> ApiResult<Self::KeyPair> {
let (pk_bytes, sk_bytes) =
kem_keygen::<P, R>(rng).map_err(|algo_err| ApiError::from(KemError::from(algo_err)))?;
Ok((KyberPublicKey::new(pk_bytes), KyberSecretKey::new(sk_bytes)))
}
fn public_key(keypair: &Self::KeyPair) -> Self::PublicKey {
keypair.0.clone()
}
fn secret_key(keypair: &Self::KeyPair) -> Self::SecretKey {
keypair.1.clone()
}
fn encapsulate<R: RngCore + CryptoRng>(
rng: &mut R,
public_key: &Self::PublicKey,
) -> ApiResult<(Self::Ciphertext, Self::SharedSecret)> {
if public_key.as_bytes().len() != P::PUBLIC_KEY_BYTES {
return Err(ApiError::InvalidKey {
context: "Kyber public key",
#[cfg(feature = "std")]
message: format!(
"Incorrect length: expected {}, got {}",
P::PUBLIC_KEY_BYTES,
public_key.as_bytes().len()
),
});
}
let (ct_bytes, ss_bytes_fixed) = kem_encaps::<P, R>(&public_key.0, rng)
.map_err(|algo_err| ApiError::from(KemError::from(algo_err)))?;
Ok((
KyberCiphertext::new(ct_bytes),
KyberSharedSecret::new(ApiKey::new(ss_bytes_fixed.as_ref())),
))
}
fn decapsulate(
secret_key: &Self::SecretKey,
ciphertext: &Self::Ciphertext,
) -> ApiResult<Self::SharedSecret> {
if secret_key.as_bytes().len() != P::SECRET_KEY_BYTES {
return Err(ApiError::InvalidKey {
context: "Kyber secret key",
#[cfg(feature = "std")]
message: format!(
"Incorrect length: expected {}, got {}",
P::SECRET_KEY_BYTES,
secret_key.as_bytes().len()
),
});
}
if ciphertext.as_bytes().len() != P::CIPHERTEXT_BYTES {
return Err(ApiError::InvalidCiphertext {
context: "Kyber ciphertext",
#[cfg(feature = "std")]
message: format!(
"Incorrect length: expected {}, got {}",
P::CIPHERTEXT_BYTES,
ciphertext.as_bytes().len()
),
});
}
let ss_bytes_fixed =
kem_decaps::<P>(&secret_key.0, &ciphertext.0).map_err(|algo_err| match algo_err {
AlgoError::Processing { .. } => ApiError::DecryptionFailed {
context: P::NAME,
#[cfg(feature = "std")]
message: "Decapsulation failed".into(),
},
_ => ApiError::from(KemError::from(algo_err)),
})?;
Ok(KyberSharedSecret::new(ApiKey::new(ss_bytes_fixed.as_ref())))
}
}