Skip to main content

lib_q_core/contexts/
aead.rs

1//! AEAD context implementation for lib-Q Core
2//!
3//! This module provides the AEAD context that handles authenticated encryption
4//! operations with proper security validation.
5
6#[cfg(feature = "alloc")]
7use alloc::{
8    boxed::Box,
9    string::String,
10    vec::Vec,
11};
12
13use super::BaseContext;
14use crate::api::{
15    AeadOperations,
16    Algorithm,
17    AlgorithmCategory,
18    CryptoProvider,
19    HashOperations,
20    KemOperations,
21    SignatureOperations,
22};
23use crate::error::Result;
24use crate::traits::{
25    AeadKey,
26    Nonce,
27};
28
29/// Wraps a concrete [`AeadOperations`] value as a [`CryptoProvider`] that only exposes AEAD.
30#[cfg(feature = "alloc")]
31pub struct AeadOperationsBridge {
32    aead: Box<dyn AeadOperations + Send + Sync>,
33}
34
35#[cfg(feature = "alloc")]
36impl CryptoProvider for AeadOperationsBridge {
37    fn kem(&self) -> Option<&dyn KemOperations> {
38        None
39    }
40
41    fn signature(&self) -> Option<&dyn SignatureOperations> {
42        None
43    }
44
45    fn hash(&self) -> Option<&dyn HashOperations> {
46        None
47    }
48
49    fn aead(&self) -> Option<&dyn AeadOperations> {
50        Some(self.aead.as_ref())
51    }
52}
53
54/// AEAD context for authenticated encryption operations
55#[cfg(feature = "alloc")]
56pub struct AeadContext {
57    inner: BaseContext<Self>,
58}
59
60#[cfg(feature = "alloc")]
61impl AeadContext {
62    /// Create an AEAD context with no provider configured.
63    ///
64    /// Operations return [`Error::ProviderNotConfigured`](crate::error::Error::ProviderNotConfigured)
65    /// until you call [`set_provider`](Self::set_provider) or build via [`with_aead_operations`](Self::with_aead_operations).
66    /// The `lib-q` crate provides `libq::aead::context()` wired to `lib-q-aead`.
67    pub fn new() -> Self {
68        Self {
69            inner: BaseContext::new(),
70        }
71    }
72
73    /// Create a new AEAD context with a provider
74    pub fn with_provider(provider: Box<dyn CryptoProvider>) -> Self {
75        Self {
76            inner: BaseContext::with_provider(provider),
77        }
78    }
79
80    /// Create a new AEAD context backed by the given AEAD implementation only.
81    ///
82    /// The `lib-q` facade typically injects `lib_q_aead::LibQAeadProvider` this way so `lib-q-core`
83    /// does not depend on `lib-q-aead`.
84    pub fn with_aead_operations(aead: Box<dyn AeadOperations + Send + Sync>) -> Self {
85        Self {
86            inner: BaseContext::with_provider(Box::new(AeadOperationsBridge { aead })),
87        }
88    }
89
90    /// Create a new AEAD context with the default provider
91    #[cfg(feature = "alloc")]
92    pub fn with_default_provider() -> Self {
93        Self {
94            inner: match crate::providers::LibQCryptoProvider::new() {
95                Ok(provider) => BaseContext::with_provider(Box::new(provider)),
96                Err(_) => BaseContext::new(),
97            },
98        }
99    }
100
101    /// Set the cryptographic provider
102    pub fn set_provider(&mut self, provider: Box<dyn CryptoProvider>) {
103        self.inner.set_provider(provider);
104    }
105
106    /// Get the current provider
107    pub fn provider(&self) -> Option<&dyn CryptoProvider> {
108        self.inner.provider()
109    }
110
111    /// Encrypt data using the specified algorithm
112    pub fn encrypt(
113        &mut self,
114        algorithm: Algorithm,
115        key: &AeadKey,
116        nonce: &Nonce,
117        plaintext: &[u8],
118        associated_data: Option<&[u8]>,
119    ) -> Result<Vec<u8>> {
120        self.inner.ensure_initialized()?;
121
122        // Validate algorithm category
123        if algorithm.category() != AlgorithmCategory::Aead {
124            return Err(crate::error::Error::InvalidAlgorithm {
125                algorithm: "Algorithm is not an AEAD algorithm",
126            });
127        }
128
129        // Use provider if available
130        match self.inner.provider().and_then(|p| p.aead()) {
131            Some(aead_ops) => aead_ops.encrypt(algorithm, key, nonce, plaintext, associated_data),
132            None => Err(crate::error::Error::ProviderNotConfigured {
133                operation: String::from("AEAD"),
134            }),
135        }
136    }
137
138    /// Decrypt data using the specified algorithm.
139    ///
140    /// Dispatches through [`AeadOperations::decrypt`] (**Layer A**
141    /// `Result` only). Semantic decrypt ([`crate::AeadDecryptSemantic`]) is not part of this
142    /// context; see ADR `docs/adr/003-aead-decrypt-layers.md`.
143    pub fn decrypt(
144        &self,
145        algorithm: Algorithm,
146        key: &AeadKey,
147        nonce: &Nonce,
148        ciphertext: &[u8],
149        associated_data: Option<&[u8]>,
150    ) -> Result<Vec<u8>> {
151        if !self.inner.is_initialized() {
152            return Err(crate::error::Error::InvalidState {
153                operation: String::from("decrypt"),
154                reason: String::from("Context not initialized"),
155            });
156        }
157
158        // Validate algorithm category
159        if algorithm.category() != AlgorithmCategory::Aead {
160            return Err(crate::error::Error::InvalidAlgorithm {
161                algorithm: "Algorithm is not an AEAD algorithm",
162            });
163        }
164
165        // Use provider if available
166        match self.inner.provider().and_then(|p| p.aead()) {
167            Some(aead_ops) => aead_ops.decrypt(algorithm, key, nonce, ciphertext, associated_data),
168            None => Err(crate::error::Error::ProviderNotConfigured {
169                operation: String::from("AEAD"),
170            }),
171        }
172    }
173
174    /// Check if the context is initialized
175    pub fn is_initialized(&self) -> bool {
176        self.inner.is_initialized()
177    }
178}
179
180#[cfg(feature = "alloc")]
181impl Default for AeadContext {
182    fn default() -> Self {
183        Self::new()
184    }
185}
186
187#[cfg(test)]
188mod tests {
189    use super::*;
190    use crate::api::{
191        AeadOperations,
192        CryptoProvider,
193        HashOperations,
194        KemOperations,
195        SignatureOperations,
196    };
197
198    // Mock provider for testing
199    struct MockAeadProvider;
200
201    impl CryptoProvider for MockAeadProvider {
202        fn kem(&self) -> Option<&dyn KemOperations> {
203            None
204        }
205        fn signature(&self) -> Option<&dyn SignatureOperations> {
206            None
207        }
208        fn hash(&self) -> Option<&dyn HashOperations> {
209            None
210        }
211        fn aead(&self) -> Option<&dyn AeadOperations> {
212            Some(self)
213        }
214    }
215
216    impl AeadOperations for MockAeadProvider {
217        fn encrypt(
218            &self,
219            _algorithm: Algorithm,
220            _key: &AeadKey,
221            _nonce: &Nonce,
222            _plaintext: &[u8],
223            _associated_data: Option<&[u8]>,
224        ) -> Result<Vec<u8>> {
225            Err(crate::error::Error::NotImplemented {
226                feature: "Mock AEAD operations not implemented".to_string(),
227            })
228        }
229
230        fn decrypt(
231            &self,
232            _algorithm: Algorithm,
233            _key: &AeadKey,
234            _nonce: &Nonce,
235            _ciphertext: &[u8],
236            _associated_data: Option<&[u8]>,
237        ) -> Result<Vec<u8>> {
238            Err(crate::error::Error::NotImplemented {
239                feature: "Mock AEAD operations not implemented".to_string(),
240            })
241        }
242    }
243
244    #[test]
245    fn test_aead_context_with_aead_operations_bridge() {
246        let context = AeadContext::with_aead_operations(Box::new(MockAeadProvider));
247        assert!(!context.is_initialized());
248        assert!(context.provider().is_some());
249        assert!(context.provider().unwrap().aead().is_some());
250    }
251
252    #[test]
253    fn test_aead_context_creation() {
254        let context = AeadContext::new();
255        assert!(!context.is_initialized());
256        assert!(context.provider().is_none());
257    }
258
259    #[test]
260    fn test_aead_context_with_provider() {
261        let provider = Box::new(MockAeadProvider);
262        let context = AeadContext::with_provider(provider);
263        assert!(!context.is_initialized());
264        assert!(context.provider().is_some());
265    }
266
267    #[test]
268    fn test_aead_context_provider_management() {
269        let mut context = AeadContext::new();
270        assert!(context.provider().is_none());
271
272        let provider = Box::new(MockAeadProvider);
273        context.set_provider(provider);
274        assert!(context.provider().is_some());
275    }
276
277    #[test]
278    fn test_aead_context_initialization() {
279        let mut context = AeadContext::new();
280        assert!(!context.is_initialized());
281
282        let key = AeadKey::new(vec![0u8; 32]);
283        let nonce = Nonce::new(vec![0u8; 16]);
284
285        // Should initialize automatically on first operation
286        let result = context.encrypt(Algorithm::Saturnin, &key, &nonce, b"test data", None);
287        assert!(result.is_err()); // Will fail due to no provider, but context should be initialized
288        assert!(context.is_initialized());
289    }
290
291    #[test]
292    fn test_aead_context_algorithm_validation() {
293        let mut context = AeadContext::new();
294        let key = AeadKey::new(vec![0u8; 32]);
295        let nonce = Nonce::new(vec![0u8; 16]);
296
297        // Try to use a non-AEAD algorithm
298        let result = context.encrypt(Algorithm::MlKem512, &key, &nonce, b"test data", None);
299        assert!(result.is_err());
300        if let Err(crate::error::Error::InvalidAlgorithm { .. }) = result {
301            // Expected error
302        } else {
303            panic!("Expected InvalidAlgorithm error");
304        }
305    }
306}