dcrypt_kem/kyber/
kem.rs

1// crates/kem/src/kyber/kem.rs
2
3//! Core Kyber KEM logic using the `api::Kem` trait.
4#![cfg_attr(not(feature = "std"), no_std)]
5
6#[cfg(feature = "alloc")]
7extern crate alloc;
8#[cfg(feature = "alloc")]
9use alloc::vec::Vec;
10
11use crate::error::Error as KemError; // KEM-specific errors
12use core::marker::PhantomData;
13use dcrypt_algorithms::error::Error as AlgoError;
14use dcrypt_api::{
15    error::Error as ApiError,
16    traits::serialize::{Serialize, SerializeSecret},
17    Kem as KemTrait, Key as ApiKey, Result as ApiResult,
18};
19use rand::{CryptoRng, RngCore};
20use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
21
22use super::ind_cca::{kem_decaps, kem_encaps, kem_keygen};
23use super::params::KyberParams; // IND-CCA2 scheme components
24
25/// Kyber Public Key (byte representation).
26///
27/// # Security Note
28/// No direct byte access is provided. Use explicit methods for serialization.
29#[derive(Clone, Debug, Zeroize)]
30pub struct KyberPublicKey(Vec<u8>);
31
32impl KyberPublicKey {
33    pub fn new(data: Vec<u8>) -> Self {
34        Self(data)
35    }
36    pub fn into_vec(self) -> Vec<u8> {
37        self.0
38    }
39    pub fn as_bytes(&self) -> &[u8] {
40        &self.0
41    }
42    pub fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
43        Ok(Self(bytes.to_vec()))
44    }
45    pub fn to_bytes(&self) -> Vec<u8> {
46        self.0.clone()
47    }
48}
49
50impl Serialize for KyberPublicKey {
51    fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
52        Self::from_bytes(bytes)
53    }
54    fn to_bytes(&self) -> Vec<u8> {
55        self.to_bytes()
56    }
57}
58
59/// Kyber Secret Key (byte representation).
60///
61/// # Security Note
62/// - Implements Zeroize for secure cleanup
63/// - No direct byte access through traits
64/// - All access must be explicit and auditable
65#[derive(Clone, Debug, Zeroize, ZeroizeOnDrop)]
66pub struct KyberSecretKey(Vec<u8>);
67
68impl KyberSecretKey {
69    pub fn new(data: Vec<u8>) -> Self {
70        Self(data)
71    }
72    pub fn to_vec(&self) -> Vec<u8> {
73        self.0.clone()
74    }
75    pub fn len(&self) -> usize {
76        self.0.len()
77    }
78    pub fn is_empty(&self) -> bool {
79        self.0.is_empty()
80    }
81    pub(crate) fn as_bytes(&self) -> &[u8] {
82        &self.0
83    }
84    pub fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
85        Ok(Self(bytes.to_vec()))
86    }
87    pub fn to_bytes_zeroizing(&self) -> Zeroizing<Vec<u8>> {
88        Zeroizing::new(self.0.clone())
89    }
90}
91
92impl SerializeSecret for KyberSecretKey {
93    fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
94        Self::from_bytes(bytes)
95    }
96    fn to_bytes_zeroizing(&self) -> Zeroizing<Vec<u8>> {
97        self.to_bytes_zeroizing()
98    }
99}
100
101/// Kyber Ciphertext (byte representation).
102///
103/// # Security Note
104/// No direct byte access prevents tampering.
105#[derive(Clone, Debug)]
106pub struct KyberCiphertext(Vec<u8>);
107
108impl KyberCiphertext {
109    pub fn new(data: Vec<u8>) -> Self {
110        Self(data)
111    }
112    pub fn into_vec(self) -> Vec<u8> {
113        self.0
114    }
115    pub fn as_bytes(&self) -> &[u8] {
116        &self.0
117    }
118    pub fn len(&self) -> usize {
119        self.0.len()
120    }
121    pub fn is_empty(&self) -> bool {
122        self.0.is_empty()
123    }
124    pub fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
125        Ok(Self(bytes.to_vec()))
126    }
127    pub fn to_bytes(&self) -> Vec<u8> {
128        self.0.clone()
129    }
130}
131
132impl Serialize for KyberCiphertext {
133    fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
134        Self::from_bytes(bytes)
135    }
136    fn to_bytes(&self) -> Vec<u8> {
137        self.to_bytes()
138    }
139}
140
141/// Kyber Shared Secret.
142///
143/// # Security Note
144/// - Implements Zeroize for secure cleanup
145/// - No direct byte access through traits
146/// - Should be used immediately for key derivation
147#[derive(Clone, Zeroize, ZeroizeOnDrop)]
148pub struct KyberSharedSecret(ApiKey);
149
150impl KyberSharedSecret {
151    pub fn new(key: ApiKey) -> Self {
152        Self(key)
153    }
154    pub fn to_key(&self) -> ApiKey {
155        self.0.clone()
156    }
157    pub fn len(&self) -> usize {
158        self.0.as_ref().len()
159    }
160    pub fn is_empty(&self) -> bool {
161        self.0.as_ref().is_empty()
162    }
163    pub(crate) fn as_bytes(&self) -> &[u8] {
164        self.0.as_ref()
165    }
166    pub fn to_bytes_zeroizing(&self) -> Zeroizing<Vec<u8>> {
167        Zeroizing::new(self.0.as_ref().to_vec())
168    }
169}
170
171impl SerializeSecret for KyberSharedSecret {
172    fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
173        Ok(Self(ApiKey::new(bytes)))
174    }
175    fn to_bytes_zeroizing(&self) -> Zeroizing<Vec<u8>> {
176        self.to_bytes_zeroizing()
177    }
178}
179
180impl core::fmt::Debug for KyberSharedSecret {
181    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
182        f.debug_struct("KyberSharedSecret")
183            .field("length", &self.len())
184            .finish()
185    }
186}
187
188/// Generic Kyber KEM structure parameterized by KyberParams.
189pub struct KyberKem<P: KyberParams> {
190    _params: PhantomData<P>,
191}
192
193impl<P: KyberParams> KemTrait for KyberKem<P> {
194    type PublicKey = KyberPublicKey;
195    type SecretKey = KyberSecretKey;
196    type SharedSecret = KyberSharedSecret;
197    type Ciphertext = KyberCiphertext;
198    type KeyPair = (Self::PublicKey, Self::SecretKey);
199
200    fn name() -> &'static str {
201        P::NAME
202    }
203
204    fn keypair<R: RngCore + CryptoRng>(rng: &mut R) -> ApiResult<Self::KeyPair> {
205        let (pk_bytes, sk_bytes) =
206            kem_keygen::<P, R>(rng).map_err(|algo_err| ApiError::from(KemError::from(algo_err)))?;
207        Ok((KyberPublicKey::new(pk_bytes), KyberSecretKey::new(sk_bytes)))
208    }
209
210    fn public_key(keypair: &Self::KeyPair) -> Self::PublicKey {
211        keypair.0.clone()
212    }
213
214    fn secret_key(keypair: &Self::KeyPair) -> Self::SecretKey {
215        keypair.1.clone()
216    }
217
218    fn encapsulate<R: RngCore + CryptoRng>(
219        rng: &mut R,
220        public_key: &Self::PublicKey,
221    ) -> ApiResult<(Self::Ciphertext, Self::SharedSecret)> {
222        if public_key.as_bytes().len() != P::PUBLIC_KEY_BYTES {
223            return Err(ApiError::InvalidKey {
224                context: "Kyber public key",
225                #[cfg(feature = "std")]
226                message: format!(
227                    "Incorrect length: expected {}, got {}",
228                    P::PUBLIC_KEY_BYTES,
229                    public_key.as_bytes().len()
230                ),
231            });
232        }
233
234        let (ct_bytes, ss_bytes_fixed) = kem_encaps::<P, R>(&public_key.0, rng)
235            .map_err(|algo_err| ApiError::from(KemError::from(algo_err)))?;
236
237        Ok((
238            KyberCiphertext::new(ct_bytes),
239            KyberSharedSecret::new(ApiKey::new(ss_bytes_fixed.as_ref())),
240        ))
241    }
242
243    fn decapsulate(
244        secret_key: &Self::SecretKey,
245        ciphertext: &Self::Ciphertext,
246    ) -> ApiResult<Self::SharedSecret> {
247        if secret_key.as_bytes().len() != P::SECRET_KEY_BYTES {
248            return Err(ApiError::InvalidKey {
249                context: "Kyber secret key",
250                #[cfg(feature = "std")]
251                message: format!(
252                    "Incorrect length: expected {}, got {}",
253                    P::SECRET_KEY_BYTES,
254                    secret_key.as_bytes().len()
255                ),
256            });
257        }
258        if ciphertext.as_bytes().len() != P::CIPHERTEXT_BYTES {
259            return Err(ApiError::InvalidCiphertext {
260                context: "Kyber ciphertext",
261                #[cfg(feature = "std")]
262                message: format!(
263                    "Incorrect length: expected {}, got {}",
264                    P::CIPHERTEXT_BYTES,
265                    ciphertext.as_bytes().len()
266                ),
267            });
268        }
269
270        let ss_bytes_fixed =
271            kem_decaps::<P>(&secret_key.0, &ciphertext.0).map_err(|algo_err| match algo_err {
272                AlgoError::Processing { .. } => ApiError::DecryptionFailed {
273                    context: P::NAME,
274                    #[cfg(feature = "std")]
275                    message: "Decapsulation failed".into(),
276                },
277                _ => ApiError::from(KemError::from(algo_err)),
278            })?;
279
280        Ok(KyberSharedSecret::new(ApiKey::new(ss_bytes_fixed.as_ref())))
281    }
282}