1#[cfg(feature = "alloc")]
6use alloc::boxed::Box;
7use alloc::vec::Vec;
8
9use lib_q_core::{
10 Aead,
11 AeadDecryptSemantic,
12 AeadKey,
13 Algorithm,
14 DecryptSemanticOutcome,
15 Nonce,
16 Result,
17};
18
19use crate::metadata::{
21 AeadMetadata,
22 AeadWithMetadata,
23};
24
25pub struct SaturninAead {
27 metadata: &'static AeadMetadata,
28 inner: lib_q_saturnin::SaturninAead,
29}
30
31impl SaturninAead {
32 pub fn new() -> Self {
34 Self {
35 metadata: crate::metadata::get_metadata(Algorithm::Saturnin)
36 .expect("Saturnin metadata not found"),
37 inner: lib_q_saturnin::SaturninAead::new(),
38 }
39 }
40}
41
42impl Aead for SaturninAead {
43 fn encrypt(
44 &self,
45 key: &AeadKey,
46 nonce: &Nonce,
47 plaintext: &[u8],
48 associated_data: Option<&[u8]>,
49 ) -> Result<Vec<u8>> {
50 self.validate_key(key)?;
52 self.validate_nonce(nonce)?;
53 crate::security::validation::validate_plaintext(plaintext)?;
54
55 let associated_data = associated_data.unwrap_or(&[]);
56 crate::security::validation::validate_associated_data(associated_data)?;
57
58 #[cfg(feature = "saturnin")]
60 {
61 self.inner
62 .encrypt(key, nonce, plaintext, Some(associated_data))
63 }
64
65 #[cfg(not(feature = "saturnin"))]
66 {
67 Err(lib_q_core::Error::NotImplemented {
68 feature: "Saturnin AEAD implementation requires 'saturnin' feature",
69 })
70 }
71 }
72
73 fn decrypt(
74 &self,
75 key: &AeadKey,
76 nonce: &Nonce,
77 ciphertext: &[u8],
78 associated_data: Option<&[u8]>,
79 ) -> Result<Vec<u8>> {
80 self.validate_key(key)?;
82 self.validate_nonce(nonce)?;
83 self.validate_ciphertext_size(ciphertext.len())?;
84 crate::security::validation::validate_ciphertext(ciphertext)?;
85
86 let associated_data = associated_data.unwrap_or(&[]);
87 crate::security::validation::validate_associated_data(associated_data)?;
88
89 #[cfg(feature = "saturnin")]
91 {
92 self.inner
93 .decrypt(key, nonce, ciphertext, Some(associated_data))
94 }
95
96 #[cfg(not(feature = "saturnin"))]
97 {
98 Err(lib_q_core::Error::NotImplemented {
99 feature: "Saturnin AEAD implementation requires 'saturnin' feature",
100 })
101 }
102 }
103}
104
105impl AeadDecryptSemantic for SaturninAead {
106 fn decrypt_semantic(
107 &self,
108 key: &AeadKey,
109 nonce: &Nonce,
110 ciphertext: &[u8],
111 associated_data: Option<&[u8]>,
112 ) -> Result<DecryptSemanticOutcome> {
113 self.validate_key(key)?;
114 self.validate_nonce(nonce)?;
115 self.validate_ciphertext_size(ciphertext.len())?;
116 crate::security::validation::validate_ciphertext(ciphertext)?;
117
118 let associated_data = associated_data.unwrap_or(&[]);
119 crate::security::validation::validate_associated_data(associated_data)?;
120
121 #[cfg(feature = "saturnin")]
122 {
123 self.inner
124 .decrypt_semantic(key, nonce, ciphertext, Some(associated_data))
125 }
126
127 #[cfg(not(feature = "saturnin"))]
128 {
129 Err(lib_q_core::Error::NotImplemented {
130 feature: "Saturnin AEAD implementation requires 'saturnin' feature",
131 })
132 }
133 }
134}
135
136impl AeadWithMetadata for SaturninAead {
137 fn metadata(&self) -> &'static AeadMetadata {
138 self.metadata
139 }
140}
141
142impl Default for SaturninAead {
143 fn default() -> Self {
144 Self::new()
145 }
146}
147
148impl crate::plugin::AeadPlugin for SaturninAead {
150 fn algorithm(&self) -> Algorithm {
151 Algorithm::Saturnin
152 }
153
154 fn create(&self) -> Result<Box<dyn AeadWithMetadata>> {
155 Ok(Box::new(Self::new()))
156 }
157
158 fn metadata(&self) -> &'static AeadMetadata {
159 crate::metadata::get_metadata(Algorithm::Saturnin)
160 .expect("Metadata not found for algorithm")
161 }
162
163 fn name(&self) -> &'static str {
164 "Saturnin AEAD"
165 }
166
167 fn version(&self) -> &'static str {
168 "1.0.0"
169 }
170
171 fn description(&self) -> &'static str {
172 "Lightweight post-quantum symmetric algorithm suite for IoT and constrained devices"
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179
180 #[test]
181 fn test_saturnin_creation() {
182 let aead = SaturninAead::new();
183 assert_eq!(aead.algorithm(), Algorithm::Saturnin);
184 assert_eq!(aead.key_size(), 32);
185 assert_eq!(aead.nonce_size(), 16);
186 assert_eq!(aead.tag_size(), 32);
187 assert_eq!(aead.security_level(), 1);
188 }
189
190 #[test]
191 fn test_saturnin_metadata() {
192 let aead = SaturninAead::new();
193 let metadata = aead.metadata();
194
195 assert_eq!(metadata.algorithm, Algorithm::Saturnin);
196 assert_eq!(metadata.name, "Saturnin");
197 assert_eq!(metadata.key_size, 32);
198 assert_eq!(metadata.nonce_size, 16);
199 assert_eq!(metadata.tag_size, 32);
200 assert_eq!(metadata.security_level, 1);
201 }
202
203 #[test]
204 fn test_saturnin_validation() {
205 let aead = SaturninAead::new();
206
207 let key = AeadKey::new(vec![0u8; 32]);
209 assert!(aead.validate_key(&key).is_ok());
210
211 let invalid_key = AeadKey::new(vec![0u8; 16]);
213 assert!(aead.validate_key(&invalid_key).is_err());
214
215 let nonce = Nonce::new(vec![0u8; 16]);
217 assert!(aead.validate_nonce(&nonce).is_ok());
218
219 let invalid_nonce = Nonce::new(vec![0u8; 12]);
221 assert!(aead.validate_nonce(&invalid_nonce).is_err());
222 }
223
224 #[cfg(feature = "saturnin")]
225 #[test]
226 fn test_saturnin_encrypt_decrypt() {
227 let aead = SaturninAead::new();
228
229 let key = AeadKey::new(vec![0u8; 32]);
230 let nonce = Nonce::new(vec![0u8; 16]);
231 let plaintext = b"Hello, World!";
232 let associated_data = b"metadata";
233
234 let ciphertext = aead.encrypt(&key, &nonce, plaintext, Some(associated_data.as_slice()));
236 assert!(ciphertext.is_ok());
237
238 let ciphertext = ciphertext.unwrap();
239 assert_eq!(ciphertext.len(), plaintext.len() + aead.tag_size());
240
241 let decrypted = aead.decrypt(&key, &nonce, &ciphertext, Some(associated_data.as_slice()));
243 assert!(decrypted.is_ok());
244 assert_eq!(decrypted.unwrap(), plaintext);
245 }
246
247 #[cfg(feature = "saturnin")]
248 #[test]
249 fn test_saturnin_authentication_failure() {
250 let aead = SaturninAead::new();
251
252 let key = AeadKey::new(vec![0u8; 32]);
253 let nonce = Nonce::new(vec![0u8; 16]);
254 let plaintext = b"Hello, World!";
255
256 let ciphertext = aead.encrypt(&key, &nonce, plaintext, None).unwrap();
258
259 let mut tampered = ciphertext.clone();
261 tampered[0] ^= 0xFF;
262
263 let result = aead.decrypt(&key, &nonce, &tampered, None);
265 assert!(result.is_err());
266
267 if let Err(lib_q_core::Error::VerificationFailed { operation }) = result {
268 assert!(operation.contains("AEAD tag verification"));
269 } else {
270 panic!("Expected VerificationFailed error");
271 }
272 }
273
274 #[cfg(feature = "saturnin")]
275 #[test]
276 fn test_saturnin_wrong_key() {
277 let aead = SaturninAead::new();
278
279 let key1 = AeadKey::new(vec![0u8; 32]);
280 let key2 = AeadKey::new(vec![1u8; 32]);
281 let nonce = Nonce::new(vec![0u8; 16]);
282 let plaintext = b"Hello, World!";
283
284 let ciphertext = aead.encrypt(&key1, &nonce, plaintext, None).unwrap();
286
287 let result = aead.decrypt(&key2, &nonce, &ciphertext, None);
289 assert!(result.is_err());
290 }
291
292 #[cfg(feature = "saturnin")]
293 #[test]
294 fn test_saturnin_wrong_nonce() {
295 let aead = SaturninAead::new();
296
297 let key = AeadKey::new(vec![0u8; 32]);
298 let nonce1 = Nonce::new(vec![0u8; 16]);
299 let nonce2 = Nonce::new(vec![1u8; 16]);
300 let plaintext = b"Hello, World!";
301
302 let ciphertext = aead.encrypt(&key, &nonce1, plaintext, None).unwrap();
304
305 let result = aead.decrypt(&key, &nonce2, &ciphertext, None);
307 assert!(result.is_err());
308 }
309}