1use 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#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
23pub enum SecurityLevel {
24 Level1,
26 #[default]
28 Level3,
29 Level5,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct QuantumEncryptionMetadata {
35 pub security_level: SecurityLevel,
37 pub encapsulated_secret: Vec<u8>,
39 pub nonce: [u8; 12],
41 pub key_derivation: QuantumKeyDerivation,
43 pub convergence_secret_id: Option<[u8; 32]>,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
49pub enum QuantumKeyDerivation {
50 Blake3Convergent,
52 QuantumRandom,
54}
55
56#[derive(Zeroize, ZeroizeOnDrop)]
58pub struct ConvergenceSecret([u8; 32]);
59
60impl ConvergenceSecret {
61 pub fn new(secret: [u8; 32]) -> Self {
63 Self(secret)
64 }
65
66 pub fn as_bytes(&self) -> &[u8; 32] {
68 &self.0
69 }
70}
71
72pub struct QuantumCryptoEngine {
74 security_level: SecurityLevel,
76 last_nonce: Option<[u8; 12]>,
78}
79
80impl Default for QuantumCryptoEngine {
81 fn default() -> Self {
82 Self::new()
83 }
84}
85
86impl QuantumCryptoEngine {
87 pub fn new() -> Self {
89 Self {
90 security_level: SecurityLevel::default(),
91 last_nonce: None,
92 }
93 }
94
95 pub fn with_security_level(level: SecurityLevel) -> Self {
97 Self {
98 security_level: level,
99 last_nonce: None,
100 }
101 }
102
103 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 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 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 let key_bytes = self.derive_convergent_key(data, secret)?;
151
152 let nonce = self.generate_deterministic_nonce(data, secret.map(|s| s.as_bytes()))?;
154 self.last_nonce = Some(nonce);
155
156 let ciphertext = self.chacha20_encrypt(data, &key_bytes, &nonce)?;
158
159 let metadata = QuantumEncryptionMetadata {
161 security_level: self.security_level,
162 encapsulated_secret: Vec::new(), 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 let kem = ml_kem_768();
174
175 let (public_key, _secret_key) = kem
177 .generate_keypair()
178 .map_err(|e| anyhow::anyhow!("KEM keypair generation failed: {:?}", e))?;
179
180 let (shared_secret, ciphertext) = kem
182 .encapsulate(&public_key)
183 .map_err(|e| anyhow::anyhow!("KEM encapsulation failed: {:?}", e))?;
184
185 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 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 let encrypted = self.chacha20_encrypt(data, &key_bytes, &nonce)?;
198
199 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 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 let key_bytes = self.derive_convergent_key(data, secret)?;
229
230 self.chacha20_decrypt(encrypted_data, &key_bytes, &metadata.nonce)
232 }
233
234 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 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 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 let key_array = GenericArray::from_slice(key);
279 let cipher = ChaCha20Poly1305::new(key_array);
280
281 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 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 if data_nonce != nonce {
310 anyhow::bail!("Nonce mismatch in encrypted data");
311 }
312
313 let key_array = GenericArray::from_slice(key);
315 let cipher = ChaCha20Poly1305::new(key_array);
316
317 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 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 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 let (encrypted, metadata) = engine.encrypt(data, EncryptionMode::Convergent, None)?;
368
369 assert!(matches!(
371 metadata.key_derivation,
372 QuantumKeyDerivation::Blake3Convergent
373 ));
374 assert!(metadata.convergence_secret_id.is_none());
375
376 let decrypted = engine.decrypt(&encrypted, &metadata, None, Some(data))?;
378 assert_eq!(decrypted, data);
379
380 let mut engine2 = QuantumCryptoEngine::new();
382 let (encrypted2, metadata2) = engine2.encrypt(data, EncryptionMode::Convergent, None)?;
383
384 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 let (encrypted, metadata) =
399 engine.encrypt(data, EncryptionMode::ConvergentWithSecret, Some(&secret))?;
400
401 assert!(matches!(
403 metadata.key_derivation,
404 QuantumKeyDerivation::Blake3Convergent
405 ));
406 assert!(metadata.convergence_secret_id.is_some());
407
408 let decrypted = engine.decrypt(&encrypted, &metadata, Some(&secret), Some(data))?;
410 assert_eq!(decrypted, data);
411
412 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 let (encrypted, metadata) = engine.encrypt(data, EncryptionMode::RandomKey, None)?;
430
431 assert!(matches!(
433 metadata.key_derivation,
434 QuantumKeyDerivation::QuantumRandom
435 ));
436 assert!(!metadata.encapsulated_secret.is_empty());
437
438 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}