Skip to main content

lib_q_aead/
lib.rs

1//! lib-Q AEAD - Post-quantum Authenticated Encryption
2//!
3//! This crate provides a flexible, algorithm-agnostic implementation of post-quantum
4//! authenticated encryption with associated data (AEAD). It supports dynamic algorithm
5//! registration and follows libQ's architectural principles.
6
7#![cfg_attr(not(feature = "std"), no_std)]
8// Note: We need unsafe code for global registry initialization
9#![deny(unused_qualifications)]
10
11#[cfg(feature = "alloc")]
12extern crate alloc;
13
14#[cfg(feature = "alloc")]
15use alloc::{
16    boxed::Box,
17    vec::Vec,
18};
19
20// Re-export types: algorithm IDs from `lib-q-types`; crypto API from `lib-q-core`
21pub use lib_q_core::{
22    Aead,
23    AeadKey,
24    Nonce,
25    Result,
26};
27pub use lib_q_types::{
28    Algorithm,
29    AlgorithmCategory,
30};
31
32// Internal modules
33mod metadata;
34mod plugin;
35mod registry;
36pub mod security;
37
38// Re-export public API
39pub use metadata::{
40    AeadMetadata,
41    AeadWithMetadata,
42    PerformanceTier,
43};
44pub use plugin::{
45    AeadPlugin,
46    PluginRegistry,
47};
48pub use registry::AeadRegistry;
49// Re-export security sub-modules for testing
50pub use security::constant_time;
51// Re-export security API
52pub use security::{
53    SecurityConfig,
54    SecurityContext,
55    get_security_config,
56    set_security_config,
57};
58pub use security::{
59    memory,
60    nonce,
61    side_channel,
62    stack_buffer,
63    timing,
64    validation,
65};
66
67#[cfg(feature = "alloc")]
68mod provider;
69#[cfg(feature = "alloc")]
70pub use provider::LibQAeadProvider;
71
72#[cfg(feature = "wasm")]
73mod wasm;
74
75// Macro is exported at crate root via #[macro_export] in plugin.rs
76
77// Algorithm implementations
78#[cfg(feature = "duplex-sponge-aead")]
79mod duplex_aead;
80#[cfg(feature = "romulus-m")]
81mod romulus_m;
82#[cfg(feature = "romulus-n")]
83mod romulus_n;
84#[cfg(feature = "saturnin")]
85mod saturnin;
86#[cfg(feature = "shake256")]
87mod shake256;
88#[cfg(feature = "tweak-aead")]
89mod tweak_aead;
90
91// Re-export implementations
92#[cfg(feature = "duplex-sponge-aead")]
93pub use duplex_aead::DuplexSpongeAead;
94#[cfg(feature = "romulus-m")]
95pub use romulus_m::RomulusMAead;
96#[cfg(feature = "romulus-n")]
97pub use romulus_n::RomulusNAead;
98#[cfg(feature = "saturnin")]
99pub use saturnin::SaturninAead;
100#[cfg(feature = "shake256")]
101pub use shake256::Shake256Aead;
102#[cfg(feature = "tweak-aead")]
103pub use tweak_aead::TweakAead;
104
105/// Global AEAD registry instance
106///
107/// This uses thread-safe initialization when compiled with std features,
108/// and single-threaded initialization for no_std environments.
109/// The key insight is that when the HPKE crate is compiled with std features,
110/// it expects thread-safe statics, so we need to provide them.
111#[cfg(feature = "std")]
112static REGISTRY: once_cell::sync::Lazy<AeadRegistry> = once_cell::sync::Lazy::new(|| {
113    let registry = AeadRegistry::new();
114
115    // Register built-in algorithms
116    #[cfg(feature = "saturnin")]
117    let _ = registry.register_algorithm(Algorithm::Saturnin, || {
118        Ok(Box::new(SaturninAead::new()) as Box<dyn AeadWithMetadata>)
119    });
120
121    #[cfg(feature = "shake256")]
122    let _ = registry.register_algorithm(Algorithm::Shake256Aead, || {
123        Ok(Box::new(Shake256Aead::new()) as Box<dyn AeadWithMetadata>)
124    });
125
126    #[cfg(feature = "duplex-sponge-aead")]
127    let _ = registry.register_algorithm(Algorithm::DuplexSpongeAead, || {
128        Ok(Box::new(DuplexSpongeAead::new()) as Box<dyn AeadWithMetadata>)
129    });
130
131    #[cfg(feature = "tweak-aead")]
132    let _ = registry.register_algorithm(Algorithm::TweakAead, || {
133        Ok(Box::new(TweakAead::new()) as Box<dyn AeadWithMetadata>)
134    });
135
136    #[cfg(feature = "romulus-n")]
137    let _ = registry.register_algorithm(Algorithm::RomulusN, || {
138        Ok(Box::new(RomulusNAead::new()) as Box<dyn AeadWithMetadata>)
139    });
140
141    #[cfg(feature = "romulus-m")]
142    let _ = registry.register_algorithm(Algorithm::RomulusM, || {
143        Ok(Box::new(RomulusMAead::new()) as Box<dyn AeadWithMetadata>)
144    });
145
146    registry
147});
148
149/// Global AEAD registry instance for no_std environments
150///
151/// This uses thread-safe initialization for WASM and other targets that require Sync.
152/// For true no_std environments without threading, this is still safe.
153#[cfg(not(feature = "std"))]
154static REGISTRY: once_cell::sync::Lazy<AeadRegistry> = once_cell::sync::Lazy::new(|| {
155    let registry = AeadRegistry::new();
156
157    // Register built-in algorithms
158    #[cfg(feature = "saturnin")]
159    let _ = registry.register_algorithm(Algorithm::Saturnin, || {
160        Ok(Box::new(SaturninAead::new()) as Box<dyn AeadWithMetadata>)
161    });
162
163    #[cfg(feature = "shake256")]
164    let _ = registry.register_algorithm(Algorithm::Shake256Aead, || {
165        Ok(Box::new(Shake256Aead::new()) as Box<dyn AeadWithMetadata>)
166    });
167
168    #[cfg(feature = "duplex-sponge-aead")]
169    let _ = registry.register_algorithm(Algorithm::DuplexSpongeAead, || {
170        Ok(Box::new(DuplexSpongeAead::new()) as Box<dyn AeadWithMetadata>)
171    });
172
173    #[cfg(feature = "tweak-aead")]
174    let _ = registry.register_algorithm(Algorithm::TweakAead, || {
175        Ok(Box::new(TweakAead::new()) as Box<dyn AeadWithMetadata>)
176    });
177
178    #[cfg(feature = "romulus-n")]
179    let _ = registry.register_algorithm(Algorithm::RomulusN, || {
180        Ok(Box::new(RomulusNAead::new()) as Box<dyn AeadWithMetadata>)
181    });
182
183    #[cfg(feature = "romulus-m")]
184    let _ = registry.register_algorithm(Algorithm::RomulusM, || {
185        Ok(Box::new(RomulusMAead::new()) as Box<dyn AeadWithMetadata>)
186    });
187
188    registry
189});
190
191/// Get the global AEAD registry
192pub fn registry() -> &'static AeadRegistry {
193    &REGISTRY
194}
195
196/// Get available AEAD algorithms
197pub fn available_algorithms() -> Vec<Algorithm> {
198    registry().available_algorithms()
199}
200
201/// Create an AEAD instance by algorithm
202pub fn create_aead(algorithm: Algorithm) -> Result<Box<dyn AeadWithMetadata>> {
203    // Validate algorithm category
204    if algorithm.category() != AlgorithmCategory::Aead {
205        return Err(lib_q_core::Error::InvalidAlgorithm {
206            algorithm: "Algorithm is not an AEAD algorithm",
207        });
208    }
209
210    registry().create_aead(algorithm)
211}
212
213/// Check if an algorithm is available
214pub fn is_algorithm_available(algorithm: Algorithm) -> bool {
215    algorithm.category() == AlgorithmCategory::Aead && registry().is_available(algorithm)
216}
217
218/// Get algorithm metadata
219pub fn get_algorithm_metadata(algorithm: Algorithm) -> Option<&'static AeadMetadata> {
220    if algorithm.category() != AlgorithmCategory::Aead {
221        return None;
222    }
223    registry().get_metadata(algorithm)
224}
225
226/// Register a custom AEAD algorithm
227#[cfg(feature = "std")]
228pub fn register_algorithm<F>(_algorithm: Algorithm, _constructor: F) -> Result<()>
229where
230    F: Fn() -> Result<Box<dyn AeadWithMetadata>> + Send + Sync + 'static,
231{
232    // For std, we need to modify the registry, but it's immutable
233    // This is a limitation of the current design
234    Err(lib_q_core::Error::NotImplemented {
235        feature: "Dynamic algorithm registration requires mutable registry".to_string(),
236    })
237}
238
239/// Register a custom AEAD algorithm for no_std - using safe approach
240#[cfg(not(feature = "std"))]
241pub fn register_algorithm<F>(algorithm: Algorithm, constructor: F) -> Result<()>
242where
243    F: Fn() -> Result<Box<dyn AeadWithMetadata>> + Send + Sync + 'static,
244{
245    if algorithm.category() != AlgorithmCategory::Aead {
246        return Err(lib_q_core::Error::InvalidAlgorithm {
247            algorithm: "Algorithm is not an AEAD algorithm",
248        });
249    }
250
251    // Use the registry's safe registration method
252    // Note: This will only work if the registry hasn't been initialized yet
253    // In a real implementation, you might want to use a different approach
254    // such as a global mutex or atomic operations
255    registry().register_algorithm(algorithm, constructor)
256}
257
258/// Register a plugin
259#[cfg(feature = "std")]
260pub fn register_plugin(_plugin: Box<dyn AeadPlugin>) -> Result<()> {
261    // For std, we need to modify the registry, but it's immutable
262    // This is a limitation of the current design
263    Err(lib_q_core::Error::NotImplemented {
264        feature: "Dynamic plugin registration requires mutable registry".to_string(),
265    })
266}
267
268/// Register a plugin for no_std - using safe approach
269#[cfg(not(feature = "std"))]
270pub fn register_plugin(plugin: Box<dyn AeadPlugin>) -> Result<()> {
271    // Use the registry's safe registration method
272    registry().register_plugin(plugin)
273}
274
275#[cfg(test)]
276mod tests {
277    use super::*;
278
279    #[test]
280    fn test_available_algorithms() {
281        let algorithms = available_algorithms();
282        assert!(!algorithms.is_empty());
283
284        // All returned algorithms should be AEAD algorithms
285        for algorithm in algorithms {
286            assert_eq!(algorithm.category(), AlgorithmCategory::Aead);
287        }
288    }
289
290    #[test]
291    fn test_create_aead() {
292        let algorithms = available_algorithms();
293
294        for algorithm in algorithms {
295            let aead = create_aead(algorithm);
296            assert!(aead.is_ok(), "Failed to create AEAD for {:?}", algorithm);
297        }
298    }
299
300    #[test]
301    fn test_invalid_algorithm() {
302        // Try to create AEAD with non-AEAD algorithm
303        let result = create_aead(Algorithm::MlKem512);
304        assert!(result.is_err());
305
306        if let Err(lib_q_core::Error::InvalidAlgorithm { algorithm }) = result {
307            assert!(algorithm.contains("not an AEAD algorithm"));
308        } else {
309            panic!("Expected InvalidAlgorithm error");
310        }
311    }
312
313    #[test]
314    fn test_algorithm_availability() {
315        let algorithms = available_algorithms();
316
317        for algorithm in algorithms {
318            assert!(is_algorithm_available(algorithm));
319        }
320
321        // Non-AEAD algorithms should not be available
322        assert!(!is_algorithm_available(Algorithm::MlKem512));
323    }
324
325    #[test]
326    fn test_algorithm_metadata() {
327        let algorithms = available_algorithms();
328
329        for algorithm in algorithms {
330            let metadata = get_algorithm_metadata(algorithm);
331            assert!(metadata.is_some(), "No metadata for {:?}", algorithm);
332
333            if let Some(meta) = metadata {
334                assert_eq!(meta.algorithm, algorithm);
335                assert!(meta.key_size > 0);
336                assert!(meta.nonce_size > 0);
337                assert!(meta.tag_size > 0);
338            }
339        }
340    }
341
342    #[test]
343    fn test_security_config_roundtrip() {
344        let orig = get_security_config();
345        set_security_config(orig);
346        assert_eq!(get_security_config(), orig);
347    }
348
349    #[test]
350    fn test_security_context_new() {
351        let _ctx = SecurityContext::new();
352    }
353}