1#[cfg(feature = "alloc")]
6extern crate alloc;
7
8#[cfg(feature = "alloc")]
9use alloc::vec::Vec;
10
11#[cfg(feature = "alloc")]
12use lib_q_core::api::{
13 AeadOperations,
14 Algorithm,
15 AlgorithmCategory,
16 CryptoProvider,
17 HashOperations,
18 KemOperations,
19 SignatureOperations,
20};
21#[cfg(feature = "alloc")]
22use lib_q_core::error::Result;
23#[cfg(feature = "alloc")]
24use lib_q_core::security::SecurityValidator;
25#[cfg(feature = "alloc")]
26use lib_q_core::traits::{
27 AeadKey,
28 Nonce,
29};
30
31#[cfg(feature = "alloc")]
32use crate::{
33 Aead,
34 create_aead,
35};
36
37#[cfg(feature = "alloc")]
39#[derive(Clone)]
40pub struct LibQAeadProvider {
41 security_validator: SecurityValidator,
42}
43
44#[cfg(feature = "alloc")]
45impl core::fmt::Debug for LibQAeadProvider {
46 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
47 f.debug_struct("LibQAeadProvider")
48 .field("security_validator", &"<SecurityValidator>")
49 .finish()
50 }
51}
52
53#[cfg(feature = "alloc")]
54impl LibQAeadProvider {
55 pub fn new() -> Result<Self> {
57 Ok(Self {
58 security_validator: SecurityValidator::new()?,
59 })
60 }
61
62 pub fn security_validator(&self) -> &SecurityValidator {
64 &self.security_validator
65 }
66}
67
68#[cfg(feature = "alloc")]
69impl AeadOperations for LibQAeadProvider {
70 fn encrypt(
71 &self,
72 algorithm: Algorithm,
73 key: &AeadKey,
74 nonce: &Nonce,
75 plaintext: &[u8],
76 associated_data: Option<&[u8]>,
77 ) -> Result<Vec<u8>> {
78 self.security_validator
79 .validate_algorithm_category(algorithm, AlgorithmCategory::Aead)?;
80 self.security_validator
81 .validate_key_material(key.as_bytes())?;
82 self.security_validator.validate_nonce(nonce.as_bytes())?;
83 self.security_validator.validate_aead_message(plaintext)?;
84 if let Some(ad) = associated_data {
85 self.security_validator.validate_aead_message(ad)?;
86 }
87
88 let aead = create_aead(algorithm)?;
89 Aead::encrypt(&*aead, key, nonce, plaintext, associated_data)
90 }
91
92 fn decrypt(
93 &self,
94 algorithm: Algorithm,
95 key: &AeadKey,
96 nonce: &Nonce,
97 ciphertext: &[u8],
98 associated_data: Option<&[u8]>,
99 ) -> Result<Vec<u8>> {
100 self.security_validator
101 .validate_algorithm_category(algorithm, AlgorithmCategory::Aead)?;
102 self.security_validator
103 .validate_key_material(key.as_bytes())?;
104 self.security_validator.validate_nonce(nonce.as_bytes())?;
105 if ciphertext.is_empty() {
107 return Err(lib_q_core::Error::InvalidCiphertextSize {
108 expected: 1,
109 actual: 0,
110 });
111 }
112 self.security_validator.validate_aead_message(ciphertext)?;
113 if let Some(ad) = associated_data {
114 self.security_validator.validate_aead_message(ad)?;
115 }
116
117 let aead = create_aead(algorithm)?;
118 Aead::decrypt(&*aead, key, nonce, ciphertext, associated_data)
119 }
120}
121
122#[cfg(feature = "alloc")]
123impl CryptoProvider for LibQAeadProvider {
124 fn kem(&self) -> Option<&dyn KemOperations> {
125 None
126 }
127
128 fn signature(&self) -> Option<&dyn SignatureOperations> {
129 None
130 }
131
132 fn hash(&self) -> Option<&dyn HashOperations> {
133 None
134 }
135
136 fn aead(&self) -> Option<&dyn AeadOperations> {
137 Some(self)
138 }
139}
140
141#[cfg(all(test, feature = "alloc"))]
142mod tests {
143 #[cfg(not(feature = "std"))]
144 use alloc::vec;
145
146 use lib_q_core::{
147 Algorithm,
148 Error,
149 };
150
151 use super::*;
152
153 #[test]
154 fn test_libq_aead_provider_creation() {
155 assert!(LibQAeadProvider::new().is_ok());
156 }
157
158 #[test]
159 fn test_non_aead_algorithm_rejected() {
160 let provider = LibQAeadProvider::new().unwrap();
161 let key = AeadKey::new(vec![0u8; 32]);
162 let nonce = Nonce::new(vec![0u8; 16]);
163 let result = provider.encrypt(Algorithm::MlKem512, &key, &nonce, b"test", None);
164 assert!(result.is_err());
165 assert!(matches!(result, Err(Error::InvalidAlgorithm { .. })));
166 }
167
168 #[test]
169 fn test_provider_security_validator_accessor() {
170 let provider = LibQAeadProvider::new().unwrap();
171 let _validator = provider.security_validator();
172 }
173
174 #[test]
175 fn test_non_aead_algorithm_rejected_on_decrypt() {
176 let provider = LibQAeadProvider::new().unwrap();
177 let key = AeadKey::new(vec![0u8; 32]);
178 let nonce = Nonce::new(vec![0u8; 16]);
179 let result = provider.decrypt(Algorithm::MlKem512, &key, &nonce, b"ciphertext", None);
180 assert!(matches!(result, Err(Error::InvalidAlgorithm { .. })));
181 }
182
183 #[cfg(feature = "shake256")]
184 #[test]
185 fn test_empty_ciphertext_rejected_before_backend_decrypt() {
186 let provider = LibQAeadProvider::new().unwrap();
187 let mut key_bytes = vec![0u8; 32];
188 let mut nonce_bytes = vec![0u8; 16];
189 for (i, b) in key_bytes.iter_mut().enumerate() {
190 *b = (i as u8).wrapping_mul(0x17).wrapping_add(0x41);
191 }
192 for (i, b) in nonce_bytes.iter_mut().enumerate() {
193 *b = (i as u8).wrapping_mul(0x29).wrapping_add(0x13);
194 }
195 let key = AeadKey::new(key_bytes);
196 let nonce = Nonce::new(nonce_bytes);
197 let result = provider.decrypt(Algorithm::Shake256Aead, &key, &nonce, b"", None);
198 assert!(matches!(
199 result,
200 Err(Error::InvalidCiphertextSize {
201 expected: 1,
202 actual: 0
203 })
204 ));
205 }
206
207 #[cfg(feature = "shake256")]
208 #[test]
209 fn test_shake256_aead_roundtrip_via_provider() {
210 let provider = LibQAeadProvider::new().unwrap();
211 let mut key_bytes = vec![0u8; 32];
212 let mut nonce_bytes = vec![0u8; 16];
213 for (i, b) in key_bytes.iter_mut().enumerate() {
214 *b = (i as u8).wrapping_mul(0x1F).wrapping_add(0x2B);
215 }
216 for (i, b) in nonce_bytes.iter_mut().enumerate() {
217 *b = (i as u8).wrapping_mul(0x3D).wrapping_add(0x7E);
218 }
219 let key = AeadKey::new(key_bytes);
220 let nonce = Nonce::new(nonce_bytes);
221 let pt = b"provider roundtrip";
222 let ad = b"ad";
223
224 let ct = AeadOperations::encrypt(
225 &provider,
226 Algorithm::Shake256Aead,
227 &key,
228 &nonce,
229 pt.as_slice(),
230 Some(ad.as_slice()),
231 )
232 .expect("encrypt");
233 let out = AeadOperations::decrypt(
234 &provider,
235 Algorithm::Shake256Aead,
236 &key,
237 &nonce,
238 &ct,
239 Some(ad.as_slice()),
240 )
241 .expect("decrypt");
242 assert_eq!(out.as_slice(), pt.as_slice());
243 }
244
245 #[test]
246 fn test_crypto_provider_trait_exposes_only_aead_operations() {
247 let provider = LibQAeadProvider::new().unwrap();
248 let crypto_provider: &dyn CryptoProvider = &provider;
249 assert!(crypto_provider.kem().is_none());
250 assert!(crypto_provider.signature().is_none());
251 assert!(crypto_provider.hash().is_none());
252 assert!(crypto_provider.aead().is_some());
253 }
254}