Skip to main content

saorsa_fec/
quantum_crypto.rs

1//! Quantum-safe encryption module using saorsa-pqc
2//!
3//! This module provides post-quantum cryptographic capabilities using ML-KEM
4//! for key encapsulation and AES-256-GCM for data encryption. It replaces
5//! the previous crypto module with quantum-safe alternatives.
6
7use anyhow::{Context, Result};
8use blake3::Hasher;
9use generic_array::GenericArray;
10use hkdf::Hkdf;
11use saorsa_pqc::api::{
12    kem::ml_kem_768,
13    symmetric::{generate_nonce, ChaCha20Poly1305},
14};
15use serde::{Deserialize, Serialize};
16use sha2::Sha256;
17use zeroize::{Zeroize, ZeroizeOnDrop};
18
19use crate::config::EncryptionMode;
20
21/// Security levels for post-quantum cryptography
22#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
23pub enum SecurityLevel {
24    /// NIST Level 1 (128-bit security)
25    Level1,
26    /// NIST Level 3 (192-bit security) - Default
27    #[default]
28    Level3,
29    /// NIST Level 5 (256-bit security)
30    Level5,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct QuantumEncryptionMetadata {
35    /// Security level used
36    pub security_level: SecurityLevel,
37    /// Encapsulated shared secret (from ML-KEM)
38    pub encapsulated_secret: Vec<u8>,
39    /// Nonce used for ChaCha20Poly1305
40    pub nonce: [u8; 12],
41    /// Key derivation method for convergent encryption
42    pub key_derivation: QuantumKeyDerivation,
43    /// Optional convergence secret identifier
44    pub convergence_secret_id: Option<[u8; 32]>,
45}
46
47/// Quantum-safe key derivation methods
48#[derive(Debug, Clone, Serialize, Deserialize)]
49pub enum QuantumKeyDerivation {
50    /// Blake3-based convergent key derivation (quantum-safe)
51    Blake3Convergent,
52    /// Random key generation using ML-KEM
53    QuantumRandom,
54}
55
56/// Convergence secret for controlled deduplication
57#[derive(Zeroize, ZeroizeOnDrop)]
58pub struct ConvergenceSecret([u8; 32]);
59
60impl ConvergenceSecret {
61    /// Create a new convergence secret
62    pub fn new(secret: [u8; 32]) -> Self {
63        Self(secret)
64    }
65
66    /// Get the secret as bytes
67    pub fn as_bytes(&self) -> &[u8; 32] {
68        &self.0
69    }
70}
71
72/// Main quantum cryptographic engine
73pub struct QuantumCryptoEngine {
74    /// Security level for operations
75    security_level: SecurityLevel,
76    /// Last nonce used (for metadata)
77    last_nonce: Option<[u8; 12]>,
78}
79
80impl Default for QuantumCryptoEngine {
81    fn default() -> Self {
82        Self::new()
83    }
84}
85
86impl QuantumCryptoEngine {
87    /// Create a new quantum crypto engine with default security level
88    pub fn new() -> Self {
89        Self {
90            security_level: SecurityLevel::default(),
91            last_nonce: None,
92        }
93    }
94
95    /// Create with specific security level
96    pub fn with_security_level(level: SecurityLevel) -> Self {
97        Self {
98            security_level: level,
99            last_nonce: None,
100        }
101    }
102
103    /// Encrypt data using the specified encryption mode
104    pub fn encrypt(
105        &mut self,
106        data: &[u8],
107        mode: EncryptionMode,
108        convergence_secret: Option<&ConvergenceSecret>,
109    ) -> Result<(Vec<u8>, QuantumEncryptionMetadata)> {
110        match mode {
111            EncryptionMode::Convergent => self.encrypt_convergent(data, None),
112            EncryptionMode::ConvergentWithSecret => {
113                let secret = convergence_secret
114                    .context("Convergence secret required for ConvergentWithSecret mode")?;
115                self.encrypt_convergent(data, Some(secret))
116            }
117            EncryptionMode::RandomKey => self.encrypt_random_key(data),
118        }
119    }
120
121    /// Decrypt data using quantum-safe algorithms
122    pub fn decrypt(
123        &self,
124        encrypted_data: &[u8],
125        metadata: &QuantumEncryptionMetadata,
126        convergence_secret: Option<&ConvergenceSecret>,
127        original_data: Option<&[u8]>,
128    ) -> Result<Vec<u8>> {
129        match metadata.key_derivation {
130            QuantumKeyDerivation::Blake3Convergent => {
131                self.decrypt_convergent(encrypted_data, metadata, convergence_secret, original_data)
132            }
133            QuantumKeyDerivation::QuantumRandom => {
134                self.decrypt_random_key(encrypted_data, metadata)
135            }
136        }
137    }
138
139    /// Get the last nonce used
140    pub fn last_nonce(&self) -> [u8; 12] {
141        self.last_nonce.unwrap_or([0u8; 12])
142    }
143
144    fn encrypt_convergent(
145        &mut self,
146        data: &[u8],
147        secret: Option<&ConvergenceSecret>,
148    ) -> Result<(Vec<u8>, QuantumEncryptionMetadata)> {
149        // Derive deterministic key from content
150        let key_bytes = self.derive_convergent_key(data, secret)?;
151
152        // Generate deterministic nonce for convergent encryption
153        let nonce = self.generate_deterministic_nonce(data, secret.map(|s| s.as_bytes()))?;
154        self.last_nonce = Some(nonce);
155
156        // Encrypt data with ChaCha20Poly1305
157        let ciphertext = self.chacha20_encrypt(data, &key_bytes, &nonce)?;
158
159        // Create metadata
160        let metadata = QuantumEncryptionMetadata {
161            security_level: self.security_level,
162            encapsulated_secret: Vec::new(), // No encapsulation for convergent
163            nonce,
164            key_derivation: QuantumKeyDerivation::Blake3Convergent,
165            convergence_secret_id: secret.map(|s| self.compute_secret_id(s.as_bytes())),
166        };
167
168        Ok((ciphertext, metadata))
169    }
170
171    fn encrypt_random_key(&mut self, data: &[u8]) -> Result<(Vec<u8>, QuantumEncryptionMetadata)> {
172        // Create ML-KEM instance
173        let kem = ml_kem_768();
174
175        // Generate keypair
176        let (public_key, _secret_key) = kem
177            .generate_keypair()
178            .map_err(|e| anyhow::anyhow!("KEM keypair generation failed: {:?}", e))?;
179
180        // Encapsulate to get shared secret
181        let (shared_secret, ciphertext) = kem
182            .encapsulate(&public_key)
183            .map_err(|e| anyhow::anyhow!("KEM encapsulation failed: {:?}", e))?;
184
185        // Derive ChaCha20 key from shared secret - need to convert to [u8; 32]
186        let shared_bytes = shared_secret.to_bytes();
187        let mut key_bytes = [0u8; 32];
188        key_bytes.copy_from_slice(&shared_bytes[..32]);
189
190        // Generate random nonce using saorsa-pqc - convert to [u8; 12]
191        let nonce_generic = generate_nonce();
192        let mut nonce = [0u8; 12];
193        nonce.copy_from_slice(&nonce_generic[..12]);
194        self.last_nonce = Some(nonce);
195
196        // Encrypt data with ChaCha20Poly1305
197        let encrypted = self.chacha20_encrypt(data, &key_bytes, &nonce)?;
198
199        // Create metadata
200        let metadata = QuantumEncryptionMetadata {
201            security_level: self.security_level,
202            encapsulated_secret: ciphertext.to_bytes(),
203            nonce,
204            key_derivation: QuantumKeyDerivation::QuantumRandom,
205            convergence_secret_id: None,
206        };
207
208        Ok((encrypted, metadata))
209    }
210
211    fn decrypt_convergent(
212        &self,
213        encrypted_data: &[u8],
214        metadata: &QuantumEncryptionMetadata,
215        convergence_secret: Option<&ConvergenceSecret>,
216        original_data: Option<&[u8]>,
217    ) -> Result<Vec<u8>> {
218        // For convergent encryption, we need the original data to derive the key
219        let data = original_data.context("Original data required for convergent decryption")?;
220
221        let secret = if metadata.convergence_secret_id.is_some() {
222            convergence_secret
223        } else {
224            None
225        };
226
227        // Derive the same key used for encryption
228        let key_bytes = self.derive_convergent_key(data, secret)?;
229
230        // Decrypt with ChaCha20Poly1305
231        self.chacha20_decrypt(encrypted_data, &key_bytes, &metadata.nonce)
232    }
233
234    /// Decrypt random key encryption using ML-KEM
235    fn decrypt_random_key(
236        &self,
237        _encrypted_data: &[u8],
238        _metadata: &QuantumEncryptionMetadata,
239    ) -> Result<Vec<u8>> {
240        anyhow::bail!("Random key decryption requires stored decapsulation key")
241    }
242
243    fn derive_convergent_key(
244        &self,
245        content: &[u8],
246        secret: Option<&ConvergenceSecret>,
247    ) -> Result<[u8; 32]> {
248        // Use Blake3 for quantum-safe content hashing
249        let mut hasher = Hasher::new();
250        hasher.update(content);
251
252        if let Some(s) = secret {
253            hasher.update(s.as_bytes());
254        }
255
256        let content_hash = hasher.finalize();
257
258        // Use HKDF for proper key derivation
259        let salt = {
260            let mut salt_hasher = Hasher::new();
261            salt_hasher.update(b"saorsa-fec-quantum-convergent");
262            if let Some(s) = secret {
263                salt_hasher.update(s.as_bytes());
264            }
265            salt_hasher.finalize()
266        };
267
268        let hkdf = Hkdf::<Sha256>::new(Some(salt.as_bytes()), content_hash.as_bytes());
269        let mut key_bytes = [0u8; 32];
270        hkdf.expand(b"saorsa-fec:quantum-chacha20:v1", &mut key_bytes)
271            .map_err(|e| anyhow::anyhow!("HKDF expansion failed: {}", e))?;
272
273        Ok(key_bytes)
274    }
275
276    fn chacha20_encrypt(&self, data: &[u8], key: &[u8; 32], nonce: &[u8; 12]) -> Result<Vec<u8>> {
277        // Convert [u8; 32] to GenericArray for ChaCha20Poly1305
278        let key_array = GenericArray::from_slice(key);
279        let cipher = ChaCha20Poly1305::new(key_array);
280
281        // Convert [u8; 12] to GenericArray for nonce
282        let nonce_array = GenericArray::from_slice(nonce);
283
284        let ciphertext = cipher
285            .encrypt(nonce_array, data)
286            .map_err(|e| anyhow::anyhow!("ChaCha20Poly1305 encryption failed: {:?}", e))?;
287
288        // Prepend nonce to ciphertext for storage
289        let mut result = Vec::with_capacity(12 + ciphertext.len());
290        result.extend_from_slice(nonce);
291        result.extend_from_slice(&ciphertext);
292
293        Ok(result)
294    }
295
296    fn chacha20_decrypt(
297        &self,
298        encrypted_data: &[u8],
299        key: &[u8; 32],
300        nonce: &[u8; 12],
301    ) -> Result<Vec<u8>> {
302        if encrypted_data.len() < 12 {
303            anyhow::bail!("Encrypted data too short to contain nonce");
304        }
305
306        let (data_nonce, ciphertext) = encrypted_data.split_at(12);
307
308        // Verify nonce matches
309        if data_nonce != nonce {
310            anyhow::bail!("Nonce mismatch in encrypted data");
311        }
312
313        // Convert [u8; 32] to GenericArray for ChaCha20Poly1305
314        let key_array = GenericArray::from_slice(key);
315        let cipher = ChaCha20Poly1305::new(key_array);
316
317        // Convert [u8; 12] to GenericArray for nonce
318        let nonce_array = GenericArray::from_slice(nonce);
319
320        let plaintext = cipher
321            .decrypt(nonce_array, ciphertext)
322            .map_err(|e| anyhow::anyhow!("ChaCha20Poly1305 decryption failed: {:?}", e))?;
323
324        Ok(plaintext)
325    }
326
327    /// Generate deterministic nonce for convergent encryption
328    fn generate_deterministic_nonce(
329        &self,
330        content: &[u8],
331        secret: Option<&[u8; 32]>,
332    ) -> Result<[u8; 12]> {
333        let mut hasher = Hasher::new();
334        hasher.update(b"nonce-derivation");
335        hasher.update(content);
336
337        if let Some(s) = secret {
338            hasher.update(s);
339        }
340
341        let hash = hasher.finalize();
342        let mut nonce = [0u8; 12];
343        nonce.copy_from_slice(&hash.as_bytes()[..12]);
344        Ok(nonce)
345    }
346
347    /// Compute secret identifier
348    fn compute_secret_id(&self, secret: &[u8; 32]) -> [u8; 32] {
349        let mut hasher = Hasher::new();
350        hasher.update(b"secret-id");
351        hasher.update(secret);
352        let hash = hasher.finalize();
353        *hash.as_bytes()
354    }
355}
356
357#[cfg(test)]
358mod tests {
359    use super::*;
360
361    #[test]
362    fn test_quantum_crypto_convergent() -> Result<()> {
363        let mut engine = QuantumCryptoEngine::new();
364        let data = b"test data for convergent encryption";
365
366        // Encrypt with convergent mode
367        let (encrypted, metadata) = engine.encrypt(data, EncryptionMode::Convergent, None)?;
368
369        // Verify metadata
370        assert!(matches!(
371            metadata.key_derivation,
372            QuantumKeyDerivation::Blake3Convergent
373        ));
374        assert!(metadata.convergence_secret_id.is_none());
375
376        // Decrypt
377        let decrypted = engine.decrypt(&encrypted, &metadata, None, Some(data))?;
378        assert_eq!(decrypted, data);
379
380        // Verify deterministic behavior
381        let mut engine2 = QuantumCryptoEngine::new();
382        let (encrypted2, metadata2) = engine2.encrypt(data, EncryptionMode::Convergent, None)?;
383
384        // Same data should produce same result
385        assert_eq!(encrypted, encrypted2);
386        assert_eq!(metadata.nonce, metadata2.nonce);
387
388        Ok(())
389    }
390
391    #[test]
392    fn test_quantum_crypto_convergent_with_secret() -> Result<()> {
393        let mut engine = QuantumCryptoEngine::new();
394        let data = b"test data for secret convergent encryption";
395        let secret = ConvergenceSecret::new([42u8; 32]);
396
397        // Encrypt with secret
398        let (encrypted, metadata) =
399            engine.encrypt(data, EncryptionMode::ConvergentWithSecret, Some(&secret))?;
400
401        // Verify metadata
402        assert!(matches!(
403            metadata.key_derivation,
404            QuantumKeyDerivation::Blake3Convergent
405        ));
406        assert!(metadata.convergence_secret_id.is_some());
407
408        // Decrypt
409        let decrypted = engine.decrypt(&encrypted, &metadata, Some(&secret), Some(data))?;
410        assert_eq!(decrypted, data);
411
412        // Different secret should produce different result
413        let secret2 = ConvergenceSecret::new([24u8; 32]);
414        let mut engine2 = QuantumCryptoEngine::new();
415        let (encrypted2, _) =
416            engine2.encrypt(data, EncryptionMode::ConvergentWithSecret, Some(&secret2))?;
417
418        assert_ne!(encrypted, encrypted2);
419
420        Ok(())
421    }
422
423    #[test]
424    fn test_quantum_crypto_random_key() -> Result<()> {
425        let mut engine = QuantumCryptoEngine::new();
426        let data = b"test data for random key encryption";
427
428        // Encrypt with random key mode
429        let (encrypted, metadata) = engine.encrypt(data, EncryptionMode::RandomKey, None)?;
430
431        // Verify metadata
432        assert!(matches!(
433            metadata.key_derivation,
434            QuantumKeyDerivation::QuantumRandom
435        ));
436        assert!(!metadata.encapsulated_secret.is_empty());
437
438        // Random key mode should produce different results
439        let mut engine2 = QuantumCryptoEngine::new();
440        let (encrypted2, metadata2) = engine2.encrypt(data, EncryptionMode::RandomKey, None)?;
441
442        assert_ne!(encrypted, encrypted2);
443        assert_ne!(metadata.nonce, metadata2.nonce);
444        assert_ne!(metadata.encapsulated_secret, metadata2.encapsulated_secret);
445
446        Ok(())
447    }
448
449    #[test]
450    fn test_security_levels() {
451        let engine1 = QuantumCryptoEngine::with_security_level(SecurityLevel::Level1);
452        let engine3 = QuantumCryptoEngine::with_security_level(SecurityLevel::Level3);
453        let engine5 = QuantumCryptoEngine::with_security_level(SecurityLevel::Level5);
454
455        assert!(matches!(engine1.security_level, SecurityLevel::Level1));
456        assert!(matches!(engine3.security_level, SecurityLevel::Level3));
457        assert!(matches!(engine5.security_level, SecurityLevel::Level5));
458    }
459}