Skip to main content

lib_q_aead/
metadata.rs

1//! AEAD Algorithm Metadata
2//!
3//! This module provides metadata structures for AEAD algorithms, including
4//! key sizes, nonce sizes, tag sizes, and other algorithm properties.
5
6use lib_q_core::{
7    Aead,
8    AeadKey,
9    Algorithm,
10    Nonce,
11    Result,
12};
13
14/// Performance tier for AEAD algorithms
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16pub enum PerformanceTier {
17    /// Ultra-secure tier with maximum security
18    UltraSecure,
19    /// Balanced tier with good security and performance
20    Balanced,
21    /// Performance tier optimized for speed
22    Performance,
23    /// Hybrid tier with algorithm diversity
24    Hybrid,
25}
26
27/// Metadata for AEAD algorithms
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub struct AeadMetadata {
30    /// The algorithm identifier
31    pub algorithm: Algorithm,
32    /// Key size in bytes
33    pub key_size: usize,
34    /// Nonce size in bytes
35    pub nonce_size: usize,
36    /// Authentication tag size in bytes
37    pub tag_size: usize,
38    /// Security level (1, 3, 4, 5)
39    pub security_level: u32,
40    /// Human-readable algorithm name
41    pub name: &'static str,
42    /// Algorithm description
43    pub description: &'static str,
44    /// Whether the canonical `lib-q-aead` / leaf types for this algorithm implement
45    /// [`lib_q_core::AeadDecryptSemantic`] (Layer B semantic decrypt).
46    ///
47    /// This reflects the **algorithm** capability table, not runtime guarantees for arbitrary
48    /// `dyn AeadWithMetadata` test doubles; those may override `AeadWithMetadata::supports_semantic_decrypt`.
49    pub supports_semantic_decrypt: bool,
50}
51
52impl AeadMetadata {
53    /// Create new metadata.
54    ///
55    /// `supports_semantic_decrypt` is explicit per static table row (rather than inferred) so
56    /// capability stays auditable next to the numeric sizes in `get_metadata`.
57    #[allow(clippy::too_many_arguments)]
58    pub const fn new(
59        algorithm: Algorithm,
60        key_size: usize,
61        nonce_size: usize,
62        tag_size: usize,
63        security_level: u32,
64        name: &'static str,
65        description: &'static str,
66        supports_semantic_decrypt: bool,
67    ) -> Self {
68        Self {
69            algorithm,
70            key_size,
71            nonce_size,
72            tag_size,
73            security_level,
74            name,
75            description,
76            supports_semantic_decrypt,
77        }
78    }
79
80    /// Get the performance tier for this algorithm
81    pub fn performance_tier(&self) -> PerformanceTier {
82        match self.security_level {
83            1 => PerformanceTier::Performance,
84            3 => PerformanceTier::Balanced,
85            4 => PerformanceTier::UltraSecure,
86            5 => PerformanceTier::Hybrid,
87            _ => PerformanceTier::Balanced,
88        }
89    }
90
91    /// Check if this algorithm is suitable for the given security level
92    pub fn is_suitable_for_security_level(&self, required_level: u32) -> bool {
93        self.security_level >= required_level
94    }
95
96    /// Get the total overhead (nonce + tag) for this algorithm
97    pub fn total_overhead(&self) -> usize {
98        self.nonce_size + self.tag_size
99    }
100
101    /// Validate key size
102    pub fn validate_key_size(&self, key_size: usize) -> bool {
103        key_size == self.key_size
104    }
105
106    /// Validate nonce size
107    pub fn validate_nonce_size(&self, nonce_size: usize) -> bool {
108        nonce_size == self.nonce_size
109    }
110
111    /// Validate tag size
112    pub fn validate_tag_size(&self, tag_size: usize) -> bool {
113        tag_size == self.tag_size
114    }
115}
116
117/// Trait for AEAD implementations that provide metadata
118pub trait AeadWithMetadata: Aead {
119    /// Get the algorithm metadata
120    fn metadata(&self) -> &'static AeadMetadata;
121
122    /// Get the algorithm identifier
123    fn algorithm(&self) -> Algorithm {
124        self.metadata().algorithm
125    }
126
127    /// Get the key size in bytes
128    fn key_size(&self) -> usize {
129        self.metadata().key_size
130    }
131
132    /// Get the nonce size in bytes
133    fn nonce_size(&self) -> usize {
134        self.metadata().nonce_size
135    }
136
137    /// Get the tag size in bytes
138    fn tag_size(&self) -> usize {
139        self.metadata().tag_size
140    }
141
142    /// Get the security level
143    fn security_level(&self) -> u32 {
144        self.metadata().security_level
145    }
146
147    /// Get the algorithm name
148    fn algorithm_name(&self) -> &'static str {
149        self.metadata().name
150    }
151
152    /// Get the algorithm description
153    fn algorithm_description(&self) -> &'static str {
154        self.metadata().description
155    }
156
157    /// Whether this algorithm’s reference stack implements [`lib_q_core::AeadDecryptSemantic`]
158    /// (Layer B), per [`AeadMetadata::supports_semantic_decrypt`].
159    ///
160    /// Custom `dyn AeadWithMetadata` implementations that are not full algorithm facades should
161    /// override this to return `false` when they do not implement semantic decrypt.
162    fn supports_semantic_decrypt(&self) -> bool {
163        self.metadata().supports_semantic_decrypt
164    }
165
166    /// Validate key size
167    fn validate_key(&self, key: &AeadKey) -> Result<()> {
168        if !self.metadata().validate_key_size(key.as_bytes().len()) {
169            return Err(lib_q_core::Error::InvalidKeySize {
170                expected: self.key_size(),
171                actual: key.as_bytes().len(),
172            });
173        }
174        Ok(())
175    }
176
177    /// Validate nonce size
178    fn validate_nonce(&self, nonce: &Nonce) -> Result<()> {
179        if !self.metadata().validate_nonce_size(nonce.as_bytes().len()) {
180            return Err(lib_q_core::Error::InvalidNonceSize {
181                expected: self.nonce_size(),
182                actual: nonce.as_bytes().len(),
183            });
184        }
185        Ok(())
186    }
187
188    /// Validate ciphertext size (must be at least tag size)
189    fn validate_ciphertext_size(&self, ciphertext_size: usize) -> Result<()> {
190        if ciphertext_size < self.tag_size() {
191            return Err(lib_q_core::Error::InvalidCiphertextSize {
192                expected: self.tag_size(),
193                actual: ciphertext_size,
194            });
195        }
196        Ok(())
197    }
198}
199
200/// Helper function to get metadata for an algorithm
201pub fn get_metadata(algorithm: Algorithm) -> Option<&'static AeadMetadata> {
202    // Use static metadata to avoid lifetime issues
203    static SATURNIN_METADATA: AeadMetadata = AeadMetadata::new(
204        Algorithm::Saturnin,
205        32, // 256-bit key
206        16, // 128-bit nonce
207        32, // 256-bit tag
208        1,  // Level 1 security
209        "Saturnin",
210        "Lightweight post-quantum symmetric algorithm suite for IoT and constrained devices",
211        true,
212    );
213
214    static SHAKE256_METADATA: AeadMetadata = AeadMetadata::new(
215        Algorithm::Shake256Aead,
216        32, // 256-bit key
217        16, // 128-bit nonce
218        32, // 256-bit tag
219        1,  // Level 1 security
220        "SHAKE256-AEAD",
221        "SHAKE256-based AEAD construction using post-quantum hash function",
222        true,
223    );
224
225    static DUPLEX_SPONGE_AEAD_METADATA: AeadMetadata = AeadMetadata::new(
226        Algorithm::DuplexSpongeAead,
227        32,
228        16,
229        32,
230        4,
231        "Duplex-Sponge-AEAD",
232        "Keccak-f[1600] duplex-sponge authenticated encryption",
233        true,
234    );
235
236    static TWEAK_AEAD_METADATA: AeadMetadata = AeadMetadata::new(
237        Algorithm::TweakAead,
238        32,
239        16,
240        32,
241        4,
242        "Tweak-AEAD",
243        "Parallel tweakable-block CTR AEAD over Keccak-f[1600]",
244        true,
245    );
246
247    static ROMULUS_N_METADATA: AeadMetadata = AeadMetadata::new(
248        Algorithm::RomulusN,
249        16,
250        16,
251        16,
252        1,
253        "Romulus-N",
254        "Romulus-N nonce-based AEAD (SKINNY-128-384+), LWC v1.3",
255        true,
256    );
257
258    static ROMULUS_M_METADATA: AeadMetadata = AeadMetadata::new(
259        Algorithm::RomulusM,
260        16,
261        16,
262        16,
263        1,
264        "Romulus-M",
265        "Romulus-M misuse-resistant AEAD (SKINNY-128-384+), LWC v1.3",
266        true,
267    );
268
269    match algorithm {
270        Algorithm::Saturnin => Some(&SATURNIN_METADATA),
271        Algorithm::Shake256Aead => Some(&SHAKE256_METADATA),
272        Algorithm::DuplexSpongeAead => Some(&DUPLEX_SPONGE_AEAD_METADATA),
273        Algorithm::TweakAead => Some(&TWEAK_AEAD_METADATA),
274        Algorithm::RomulusN => Some(&ROMULUS_N_METADATA),
275        Algorithm::RomulusM => Some(&ROMULUS_M_METADATA),
276        _ => None,
277    }
278}
279
280#[cfg(test)]
281mod tests {
282    use super::*;
283
284    #[test]
285    fn test_metadata_creation() {
286        let metadata = AeadMetadata::new(
287            Algorithm::Saturnin,
288            32,
289            16,
290            32,
291            1,
292            "Saturnin",
293            "Test algorithm",
294            true,
295        );
296
297        assert_eq!(metadata.algorithm, Algorithm::Saturnin);
298        assert_eq!(metadata.key_size, 32);
299        assert_eq!(metadata.nonce_size, 16);
300        assert_eq!(metadata.tag_size, 32);
301        assert_eq!(metadata.security_level, 1);
302        assert_eq!(metadata.name, "Saturnin");
303        assert_eq!(metadata.description, "Test algorithm");
304        assert!(metadata.supports_semantic_decrypt);
305    }
306
307    #[test]
308    fn test_performance_tier() {
309        let metadata = AeadMetadata::new(Algorithm::Saturnin, 32, 16, 32, 1, "Test", "Test", false);
310        assert_eq!(metadata.performance_tier(), PerformanceTier::Performance);
311
312        let metadata = AeadMetadata::new(Algorithm::Saturnin, 32, 16, 32, 3, "Test", "Test", false);
313        assert_eq!(metadata.performance_tier(), PerformanceTier::Balanced);
314
315        let metadata = AeadMetadata::new(Algorithm::Saturnin, 32, 16, 32, 4, "Test", "Test", false);
316        assert_eq!(metadata.performance_tier(), PerformanceTier::UltraSecure);
317
318        let metadata = AeadMetadata::new(Algorithm::Saturnin, 32, 16, 32, 5, "Test", "Test", false);
319        assert_eq!(metadata.performance_tier(), PerformanceTier::Hybrid);
320    }
321
322    #[test]
323    fn test_security_level_suitability() {
324        let metadata = AeadMetadata::new(Algorithm::Saturnin, 32, 16, 32, 3, "Test", "Test", false);
325
326        assert!(metadata.is_suitable_for_security_level(1));
327        assert!(metadata.is_suitable_for_security_level(3));
328        assert!(!metadata.is_suitable_for_security_level(4));
329        assert!(!metadata.is_suitable_for_security_level(5));
330    }
331
332    #[test]
333    fn test_size_validation() {
334        let metadata = AeadMetadata::new(Algorithm::Saturnin, 32, 16, 32, 1, "Test", "Test", false);
335
336        assert!(metadata.validate_key_size(32));
337        assert!(!metadata.validate_key_size(16));
338        assert!(!metadata.validate_key_size(64));
339
340        assert!(metadata.validate_nonce_size(16));
341        assert!(!metadata.validate_nonce_size(12));
342        assert!(!metadata.validate_nonce_size(24));
343
344        assert!(metadata.validate_tag_size(32));
345        assert!(!metadata.validate_tag_size(16));
346        assert!(!metadata.validate_tag_size(64));
347    }
348
349    #[test]
350    fn test_total_overhead() {
351        let metadata = AeadMetadata::new(Algorithm::Saturnin, 32, 16, 32, 1, "Test", "Test", false);
352
353        assert_eq!(metadata.total_overhead(), 48); // 16 + 32
354    }
355
356    #[test]
357    fn test_get_metadata() {
358        let metadata = get_metadata(Algorithm::Saturnin);
359        assert!(metadata.is_some());
360
361        if let Some(meta) = metadata {
362            assert_eq!(meta.algorithm, Algorithm::Saturnin);
363            assert_eq!(meta.name, "Saturnin");
364        }
365
366        let metadata = get_metadata(Algorithm::MlKem512);
367        assert!(metadata.is_none());
368    }
369
370    #[test]
371    fn registered_aead_metadata_marks_semantic_decrypt() {
372        for alg in [
373            Algorithm::Saturnin,
374            Algorithm::Shake256Aead,
375            Algorithm::DuplexSpongeAead,
376            Algorithm::TweakAead,
377            Algorithm::RomulusN,
378            Algorithm::RomulusM,
379        ] {
380            let m = get_metadata(alg).expect("AEAD metadata");
381            assert!(m.supports_semantic_decrypt, "{alg:?}");
382        }
383    }
384}