Skip to main content

lib_q_aead/
saturnin.rs

1//! Saturnin AEAD Implementation
2//!
3//! This module provides the Saturnin AEAD implementation using the lib-q-saturnin crate.
4
5#[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
19// Plugin trait implementation
20use crate::metadata::{
21    AeadMetadata,
22    AeadWithMetadata,
23};
24
25/// Saturnin AEAD implementation wrapper
26pub struct SaturninAead {
27    metadata: &'static AeadMetadata,
28    inner: lib_q_saturnin::SaturninAead,
29}
30
31impl SaturninAead {
32    /// Create a new Saturnin AEAD instance
33    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        // Validate inputs using security modules
51        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        // Use lib-q-saturnin for encryption.
59        #[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        // Validate inputs using security modules
81        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        // Use lib-q-saturnin for decryption.
90        #[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
148// Implement the plugin trait using the macro
149impl 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        // Test valid key
208        let key = AeadKey::new(vec![0u8; 32]);
209        assert!(aead.validate_key(&key).is_ok());
210
211        // Test invalid key size
212        let invalid_key = AeadKey::new(vec![0u8; 16]);
213        assert!(aead.validate_key(&invalid_key).is_err());
214
215        // Test valid nonce
216        let nonce = Nonce::new(vec![0u8; 16]);
217        assert!(aead.validate_nonce(&nonce).is_ok());
218
219        // Test invalid nonce size
220        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        // Encrypt
235        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        // Decrypt
242        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        // Encrypt
257        let ciphertext = aead.encrypt(&key, &nonce, plaintext, None).unwrap();
258
259        // Tamper with ciphertext
260        let mut tampered = ciphertext.clone();
261        tampered[0] ^= 0xFF;
262
263        // Decrypt should fail
264        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        // Encrypt with key1
285        let ciphertext = aead.encrypt(&key1, &nonce, plaintext, None).unwrap();
286
287        // Decrypt with key2 should fail
288        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        // Encrypt with nonce1
303        let ciphertext = aead.encrypt(&key, &nonce1, plaintext, None).unwrap();
304
305        // Decrypt with nonce2 should fail
306        let result = aead.decrypt(&key, &nonce2, &ciphertext, None);
307        assert!(result.is_err());
308    }
309}