1use ring::aead::{Aad, LessSafeKey, Nonce, UnboundKey, AES_256_GCM, CHACHA20_POLY1305};
42use ring::hkdf::{Salt, HKDF_SHA256};
43use ring::rand::{SecureRandom, SystemRandom};
44use secrecy::{ExposeSecret, SecretBox};
45use serde::{Deserialize, Serialize};
46use std::collections::HashMap;
47use std::fmt;
48use std::fs;
49use std::io;
50use std::path::PathBuf;
51use std::sync::atomic::{AtomicU32, Ordering};
52use std::sync::Arc;
53use thiserror::Error;
54
55const NONCE_SIZE: usize = 12;
57
58const TAG_SIZE: usize = 16;
60
61const KEY_SIZE: usize = 32;
63
64const ENCRYPTION_MAGIC: [u8; 4] = [0x52, 0x56, 0x45, 0x4E]; const FORMAT_VERSION: u8 = 1;
69
70#[derive(Debug, Error)]
72pub enum EncryptionError {
73 #[error("key provider error: {0}")]
74 KeyProvider(String),
75
76 #[error("encryption failed: {0}")]
77 Encryption(String),
78
79 #[error("decryption failed: {0}")]
80 Decryption(String),
81
82 #[error("invalid key: {0}")]
83 InvalidKey(String),
84
85 #[error("key rotation error: {0}")]
86 KeyRotation(String),
87
88 #[error("io error: {0}")]
89 Io(#[from] io::Error),
90
91 #[error("invalid format: {0}")]
92 InvalidFormat(String),
93
94 #[error("unsupported version: {0}")]
95 UnsupportedVersion(u8),
96
97 #[error("key not found: version {0}")]
98 KeyNotFound(u32),
99}
100
101pub type Result<T> = std::result::Result<T, EncryptionError>;
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
106#[serde(rename_all = "kebab-case")]
107pub enum Algorithm {
108 #[default]
110 Aes256Gcm,
111 ChaCha20Poly1305,
113}
114
115impl Algorithm {
116 pub fn key_size(&self) -> usize {
118 match self {
119 Algorithm::Aes256Gcm => 32,
120 Algorithm::ChaCha20Poly1305 => 32,
121 }
122 }
123
124 pub fn nonce_size(&self) -> usize {
126 match self {
127 Algorithm::Aes256Gcm => 12,
128 Algorithm::ChaCha20Poly1305 => 12,
129 }
130 }
131
132 pub fn tag_size(&self) -> usize {
134 match self {
135 Algorithm::Aes256Gcm => 16,
136 Algorithm::ChaCha20Poly1305 => 16,
137 }
138 }
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize)]
143#[serde(tag = "type", rename_all = "kebab-case")]
144pub enum KeyProvider {
145 File { path: PathBuf },
147
148 Environment { variable: String },
150
151 #[serde(skip)]
155 InMemory(#[serde(skip)] Vec<u8>),
156}
157
158impl Default for KeyProvider {
159 fn default() -> Self {
160 KeyProvider::Environment {
161 variable: "RIVVEN_ENCRYPTION_KEY".to_string(),
162 }
163 }
164}
165
166#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct EncryptionConfig {
169 #[serde(default)]
171 pub enabled: bool,
172
173 #[serde(default)]
175 pub algorithm: Algorithm,
176
177 #[serde(default)]
179 pub key_provider: KeyProvider,
180
181 #[serde(default)]
183 pub key_rotation_days: u32,
184
185 #[serde(default = "default_aad_scope")]
187 pub aad_scope: String,
188}
189
190fn default_aad_scope() -> String {
191 "rivven".to_string()
192}
193
194impl Default for EncryptionConfig {
195 fn default() -> Self {
196 Self {
197 enabled: false,
198 algorithm: Algorithm::default(),
199 key_provider: KeyProvider::default(),
200 key_rotation_days: 0,
201 aad_scope: default_aad_scope(),
202 }
203 }
204}
205
206impl EncryptionConfig {
207 pub fn new() -> Self {
209 Self::default()
210 }
211
212 pub fn enabled(mut self) -> Self {
214 self.enabled = true;
215 self
216 }
217
218 pub fn with_algorithm(mut self, algorithm: Algorithm) -> Self {
220 self.algorithm = algorithm;
221 self
222 }
223
224 pub fn with_key_provider(mut self, provider: KeyProvider) -> Self {
226 self.key_provider = provider;
227 self
228 }
229
230 pub fn with_key_rotation_days(mut self, days: u32) -> Self {
232 self.key_rotation_days = days;
233 self
234 }
235}
236
237#[derive(Debug, Clone)]
248pub struct EncryptedHeader {
249 pub version: u8,
250 pub algorithm: Algorithm,
251 pub key_version: u32,
252 pub nonce: [u8; NONCE_SIZE],
253}
254
255impl EncryptedHeader {
256 pub const SIZE: usize = 24;
258
259 pub fn new(algorithm: Algorithm, key_version: u32, nonce: [u8; NONCE_SIZE]) -> Self {
261 Self {
262 version: FORMAT_VERSION,
263 algorithm,
264 key_version,
265 nonce,
266 }
267 }
268
269 pub fn to_bytes(&self) -> [u8; Self::SIZE] {
271 let mut buf = [0u8; Self::SIZE];
272 buf[0..4].copy_from_slice(&ENCRYPTION_MAGIC);
273 buf[4] = self.version;
274 buf[5] = self.algorithm as u8;
275 buf[6..10].copy_from_slice(&self.key_version.to_be_bytes());
276 buf[10..22].copy_from_slice(&self.nonce);
277 buf
279 }
280
281 pub fn from_bytes(data: &[u8]) -> Result<Self> {
283 if data.len() < Self::SIZE {
284 return Err(EncryptionError::InvalidFormat(format!(
285 "header too short: {} < {}",
286 data.len(),
287 Self::SIZE
288 )));
289 }
290
291 if data[0..4] != ENCRYPTION_MAGIC {
293 return Err(EncryptionError::InvalidFormat("invalid magic bytes".into()));
294 }
295
296 let version = data[4];
297 if version != FORMAT_VERSION {
298 return Err(EncryptionError::UnsupportedVersion(version));
299 }
300
301 let algorithm = match data[5] {
302 0 => Algorithm::Aes256Gcm,
303 1 => Algorithm::ChaCha20Poly1305,
304 v => {
305 return Err(EncryptionError::InvalidFormat(format!(
306 "unknown algorithm: {}",
307 v
308 )))
309 }
310 };
311
312 let key_version = u32::from_be_bytes([data[6], data[7], data[8], data[9]]);
313 let mut nonce = [0u8; NONCE_SIZE];
314 nonce.copy_from_slice(&data[10..22]);
315
316 Ok(Self {
317 version,
318 algorithm,
319 key_version,
320 nonce,
321 })
322 }
323}
324
325pub struct MasterKey {
327 key: SecretBox<[u8; KEY_SIZE]>,
328 version: u32,
329}
330
331impl MasterKey {
332 pub fn new(key: Vec<u8>, version: u32) -> Result<Self> {
334 if key.len() != KEY_SIZE {
335 return Err(EncryptionError::InvalidKey(format!(
336 "key must be {} bytes, got {}",
337 KEY_SIZE,
338 key.len()
339 )));
340 }
341 let mut key_array = [0u8; KEY_SIZE];
342 key_array.copy_from_slice(&key);
343 Ok(Self {
344 key: SecretBox::new(Box::new(key_array)),
345 version,
346 })
347 }
348
349 pub fn generate(version: u32) -> Result<Self> {
351 let rng = SystemRandom::new();
352 let mut key = vec![0u8; KEY_SIZE];
353 rng.fill(&mut key)
354 .map_err(|_| EncryptionError::KeyProvider("failed to generate random key".into()))?;
355 Self::new(key, version)
356 }
357
358 pub fn from_provider(provider: &KeyProvider) -> Result<Self> {
360 match provider {
361 KeyProvider::File { path } => {
362 let data = fs::read(path)?;
363 let key = if data.len() == KEY_SIZE {
364 data
366 } else {
367 let hex_str = String::from_utf8(data)
369 .map_err(|_| EncryptionError::InvalidKey("invalid key file format".into()))?
370 .trim()
371 .to_string();
372 hex::decode(&hex_str).map_err(|e| {
373 EncryptionError::InvalidKey(format!("invalid hex key: {}", e))
374 })?
375 };
376 Self::new(key, 1)
377 }
378 KeyProvider::Environment { variable } => {
379 let hex_key = std::env::var(variable).map_err(|_| {
380 EncryptionError::KeyProvider(format!(
381 "environment variable '{}' not set",
382 variable
383 ))
384 })?;
385 let key = hex::decode(hex_key.trim()).map_err(|e| {
386 EncryptionError::InvalidKey(format!("invalid hex key in env var: {}", e))
387 })?;
388 Self::new(key, 1)
389 }
390 KeyProvider::InMemory(key) => Self::new(key.clone(), 1),
391 #[allow(unreachable_patterns)]
392 _ => Err(EncryptionError::KeyProvider(
393 "unsupported key provider".into(),
394 )),
395 }
396 }
397
398 pub fn version(&self) -> u32 {
400 self.version
401 }
402
403 fn derive_data_key(&self, info: &[u8]) -> Result<[u8; KEY_SIZE]> {
405 let salt = Salt::new(HKDF_SHA256, b"rivven-encryption-v1");
406 let prk = salt.extract(self.key.expose_secret());
407 let info_refs = [info];
408 let okm = prk
409 .expand(&info_refs, DataKeyLen)
410 .map_err(|_| EncryptionError::Encryption("key derivation failed".into()))?;
411
412 let mut data_key = [0u8; KEY_SIZE];
413 okm.fill(&mut data_key)
414 .map_err(|_| EncryptionError::Encryption("key expansion failed".into()))?;
415 Ok(data_key)
416 }
417}
418
419impl fmt::Debug for MasterKey {
420 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
421 f.debug_struct("MasterKey")
422 .field("version", &self.version)
423 .field("key", &"[REDACTED]")
424 .finish()
425 }
426}
427
428struct DataKeyLen;
430
431impl ring::hkdf::KeyType for DataKeyLen {
432 fn len(&self) -> usize {
433 KEY_SIZE
434 }
435}
436
437pub struct EncryptionManager {
439 config: EncryptionConfig,
440 master_key: MasterKey,
441 data_key: LessSafeKey,
443 key_store: parking_lot::RwLock<HashMap<u32, LessSafeKey>>,
445 rng: SystemRandom,
446 current_key_version: AtomicU32,
447}
448
449fn ring_algorithm(algo: Algorithm) -> &'static ring::aead::Algorithm {
451 match algo {
452 Algorithm::Aes256Gcm => &AES_256_GCM,
453 Algorithm::ChaCha20Poly1305 => &CHACHA20_POLY1305,
454 }
455}
456
457impl EncryptionManager {
458 pub fn new(config: EncryptionConfig) -> Result<Arc<Self>> {
460 let master_key = MasterKey::from_provider(&config.key_provider)?;
461 let data_key_bytes = master_key.derive_data_key(config.aad_scope.as_bytes())?;
462
463 let algo = ring_algorithm(config.algorithm);
464 let unbound_key = UnboundKey::new(algo, &data_key_bytes)
465 .map_err(|_| EncryptionError::InvalidKey("failed to create encryption key".into()))?;
466 let data_key = LessSafeKey::new(unbound_key);
467
468 let version = master_key.version();
470 let store_unbound = UnboundKey::new(algo, &data_key_bytes)
471 .map_err(|_| EncryptionError::InvalidKey("failed to create store key".into()))?;
472 let mut key_store = HashMap::new();
473 key_store.insert(version, LessSafeKey::new(store_unbound));
474
475 Ok(Arc::new(Self {
476 config,
477 current_key_version: AtomicU32::new(version),
478 master_key,
479 data_key,
480 key_store: parking_lot::RwLock::new(key_store),
481 rng: SystemRandom::new(),
482 }))
483 }
484
485 pub fn rotate_key(&self, new_master: MasterKey) -> Result<()> {
488 let new_version = new_master.version();
489 if new_version <= self.current_key_version.load(Ordering::Relaxed) {
490 return Err(EncryptionError::KeyRotation(
491 "new key version must be greater than current".into(),
492 ));
493 }
494
495 let data_key_bytes = new_master.derive_data_key(self.config.aad_scope.as_bytes())?;
496 let algo = ring_algorithm(self.config.algorithm);
497
498 let new_key = UnboundKey::new(algo, &data_key_bytes)
499 .map_err(|_| EncryptionError::KeyRotation("failed to create new key".into()))?;
500
501 {
503 let mut store = self.key_store.write();
504 store.insert(new_version, LessSafeKey::new(new_key));
505 }
506
507 self.current_key_version
509 .store(new_version, Ordering::Release);
510
511 Ok(())
512 }
513
514 fn get_key_for_version(&self, version: u32) -> Result<()> {
516 let store = self.key_store.read();
517 if store.contains_key(&version) {
518 Ok(())
519 } else {
520 Err(EncryptionError::KeyNotFound(version))
521 }
522 }
523
524 pub fn disabled() -> Arc<DisabledEncryption> {
526 Arc::new(DisabledEncryption)
527 }
528
529 pub fn is_enabled(&self) -> bool {
531 self.config.enabled
532 }
533
534 pub fn key_version(&self) -> u32 {
536 self.current_key_version.load(Ordering::Relaxed)
537 }
538
539 fn generate_nonce(&self, lsn: u64) -> [u8; NONCE_SIZE] {
541 let mut nonce = [0u8; NONCE_SIZE];
542 nonce[0..8].copy_from_slice(&lsn.to_be_bytes());
544 self.rng.fill(&mut nonce[8..12]).ok();
545 nonce
546 }
547
548 pub fn encrypt(&self, plaintext: &[u8], lsn: u64) -> Result<Vec<u8>> {
550 let nonce_bytes = self.generate_nonce(lsn);
551 let nonce = Nonce::assume_unique_for_key(nonce_bytes);
552
553 let header = EncryptedHeader::new(
554 self.config.algorithm,
555 self.current_key_version.load(Ordering::Relaxed),
556 nonce_bytes,
557 );
558
559 let mut output = Vec::with_capacity(EncryptedHeader::SIZE + plaintext.len() + TAG_SIZE);
561 output.extend_from_slice(&header.to_bytes());
562 output.extend_from_slice(plaintext);
563
564 let ciphertext_start = EncryptedHeader::SIZE;
566 let tag = self
567 .data_key
568 .seal_in_place_separate_tag(
569 nonce,
570 Aad::from(self.config.aad_scope.as_bytes()),
571 &mut output[ciphertext_start..],
572 )
573 .map_err(|_| EncryptionError::Encryption("seal failed".into()))?;
574
575 output.extend_from_slice(tag.as_ref());
577
578 Ok(output)
579 }
580
581 pub fn decrypt(&self, ciphertext: &[u8], _lsn: u64) -> Result<Vec<u8>> {
583 if ciphertext.len() < EncryptedHeader::SIZE + TAG_SIZE {
584 return Err(EncryptionError::InvalidFormat(
585 "ciphertext too short".into(),
586 ));
587 }
588
589 let header = EncryptedHeader::from_bytes(ciphertext)?;
590
591 self.get_key_for_version(header.key_version)?;
593
594 let nonce = Nonce::assume_unique_for_key(header.nonce);
595
596 let algo = ring_algorithm(header.algorithm);
598
599 let mut buffer = ciphertext[EncryptedHeader::SIZE..].to_vec();
601
602 let store = self.key_store.read();
604 let key = store
605 .get(&header.key_version)
606 .ok_or(EncryptionError::KeyNotFound(header.key_version))?;
607
608 if *key.algorithm() != *algo {
610 drop(store);
612 let data_key_bytes = self
614 .master_key
615 .derive_data_key(self.config.aad_scope.as_bytes())?;
616 let unbound = UnboundKey::new(algo, &data_key_bytes)
617 .map_err(|_| EncryptionError::Decryption("key re-derive failed".into()))?;
618 let temp_key = LessSafeKey::new(unbound);
619 let plaintext = temp_key
620 .open_in_place(
621 nonce,
622 Aad::from(self.config.aad_scope.as_bytes()),
623 &mut buffer,
624 )
625 .map_err(|_| EncryptionError::Decryption("authentication failed".into()))?;
626 return Ok(plaintext.to_vec());
627 }
628
629 let plaintext = key
630 .open_in_place(
631 nonce,
632 Aad::from(self.config.aad_scope.as_bytes()),
633 &mut buffer,
634 )
635 .map_err(|_| EncryptionError::Decryption("authentication failed".into()))?;
636
637 Ok(plaintext.to_vec())
638 }
639
640 pub fn encrypted_size(&self, plaintext_len: usize) -> usize {
642 EncryptedHeader::SIZE + plaintext_len + TAG_SIZE
643 }
644}
645
646impl fmt::Debug for EncryptionManager {
647 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
648 f.debug_struct("EncryptionManager")
649 .field("enabled", &self.config.enabled)
650 .field("algorithm", &self.config.algorithm)
651 .field("key_version", &self.key_version())
652 .finish()
653 }
654}
655
656#[derive(Debug)]
658pub struct DisabledEncryption;
659
660impl DisabledEncryption {
661 pub fn encrypt(&self, plaintext: &[u8], _lsn: u64) -> Result<Vec<u8>> {
663 Ok(plaintext.to_vec())
664 }
665
666 pub fn decrypt(&self, ciphertext: &[u8], _lsn: u64) -> Result<Vec<u8>> {
668 Ok(ciphertext.to_vec())
669 }
670
671 pub fn encrypted_size(&self, plaintext_len: usize) -> usize {
673 plaintext_len
674 }
675
676 pub fn is_enabled(&self) -> bool {
677 false
678 }
679}
680
681pub trait Encryptor: Send + Sync + std::fmt::Debug {
683 fn encrypt(&self, plaintext: &[u8], lsn: u64) -> Result<Vec<u8>>;
684 fn decrypt(&self, ciphertext: &[u8], lsn: u64) -> Result<Vec<u8>>;
685 fn encrypted_size(&self, plaintext_len: usize) -> usize;
686 fn is_enabled(&self) -> bool;
687}
688
689impl Encryptor for EncryptionManager {
690 fn encrypt(&self, plaintext: &[u8], lsn: u64) -> Result<Vec<u8>> {
691 self.encrypt(plaintext, lsn)
692 }
693
694 fn decrypt(&self, ciphertext: &[u8], lsn: u64) -> Result<Vec<u8>> {
695 self.decrypt(ciphertext, lsn)
696 }
697
698 fn encrypted_size(&self, plaintext_len: usize) -> usize {
699 self.encrypted_size(plaintext_len)
700 }
701
702 fn is_enabled(&self) -> bool {
703 self.is_enabled()
704 }
705}
706
707impl Encryptor for DisabledEncryption {
708 fn encrypt(&self, plaintext: &[u8], lsn: u64) -> Result<Vec<u8>> {
709 self.encrypt(plaintext, lsn)
710 }
711
712 fn decrypt(&self, ciphertext: &[u8], lsn: u64) -> Result<Vec<u8>> {
713 self.decrypt(ciphertext, lsn)
714 }
715
716 fn encrypted_size(&self, plaintext_len: usize) -> usize {
717 self.encrypted_size(plaintext_len)
718 }
719
720 fn is_enabled(&self) -> bool {
721 false
722 }
723}
724
725pub fn generate_key_file(path: &std::path::Path) -> Result<()> {
727 let key = MasterKey::generate(1)?;
728 let hex_key = hex::encode(key.key.expose_secret());
729 fs::write(path, hex_key)?;
730
731 #[cfg(unix)]
733 {
734 use std::os::unix::fs::PermissionsExt;
735 let mut perms = fs::metadata(path)?.permissions();
736 perms.set_mode(0o600);
737 fs::set_permissions(path, perms)?;
738 }
739
740 Ok(())
741}
742
743#[cfg(test)]
744mod tests {
745 use super::*;
746
747 fn test_config() -> EncryptionConfig {
748 let key = vec![0u8; 32]; EncryptionConfig {
750 enabled: true,
751 algorithm: Algorithm::Aes256Gcm,
752 key_provider: KeyProvider::InMemory(key),
753 key_rotation_days: 0,
754 aad_scope: "test".to_string(),
755 }
756 }
757
758 #[test]
759 fn test_encrypt_decrypt() {
760 let manager = EncryptionManager::new(test_config()).unwrap();
761 let plaintext = b"Hello, World! This is sensitive data.";
762 let lsn = 12345u64;
763
764 let ciphertext = manager.encrypt(plaintext, lsn).unwrap();
765 assert_ne!(ciphertext.as_slice(), plaintext);
766 assert!(ciphertext.len() > plaintext.len());
767
768 let decrypted = manager.decrypt(&ciphertext, lsn).unwrap();
769 assert_eq!(decrypted, plaintext);
770 }
771
772 #[test]
773 fn test_encrypted_size() {
774 let manager = EncryptionManager::new(test_config()).unwrap();
775 let plaintext_len = 1000;
776
777 let expected = EncryptedHeader::SIZE + plaintext_len + TAG_SIZE;
778 assert_eq!(manager.encrypted_size(plaintext_len), expected);
779 }
780
781 #[test]
782 fn test_header_roundtrip() {
783 let nonce = [1u8; NONCE_SIZE];
784 let header = EncryptedHeader::new(Algorithm::Aes256Gcm, 42, nonce);
785
786 let bytes = header.to_bytes();
787 let parsed = EncryptedHeader::from_bytes(&bytes).unwrap();
788
789 assert_eq!(parsed.version, header.version);
790 assert_eq!(parsed.algorithm, header.algorithm);
791 assert_eq!(parsed.key_version, header.key_version);
792 assert_eq!(parsed.nonce, header.nonce);
793 }
794
795 #[test]
796 fn test_invalid_ciphertext() {
797 let manager = EncryptionManager::new(test_config()).unwrap();
798
799 let result = manager.decrypt(&[0u8; 10], 1);
801 assert!(result.is_err());
802
803 let mut bad_magic = vec![0u8; 100];
805 let result = manager.decrypt(&bad_magic, 1);
806 assert!(result.is_err());
807
808 bad_magic[0..4].copy_from_slice(&ENCRYPTION_MAGIC);
810 bad_magic[4] = FORMAT_VERSION;
811 let result = manager.decrypt(&bad_magic, 1);
812 assert!(result.is_err());
813 }
814
815 #[test]
816 fn test_tamper_detection() {
817 let manager = EncryptionManager::new(test_config()).unwrap();
818 let plaintext = b"Sensitive data that must not be tampered with";
819
820 let mut ciphertext = manager.encrypt(plaintext, 1).unwrap();
821
822 let tamper_pos = EncryptedHeader::SIZE + 10;
824 ciphertext[tamper_pos] ^= 0x01;
825
826 let result = manager.decrypt(&ciphertext, 1);
828 assert!(result.is_err());
829 }
830
831 #[test]
832 fn test_different_lsns_produce_different_ciphertexts() {
833 let manager = EncryptionManager::new(test_config()).unwrap();
834 let plaintext = b"Same plaintext";
835
836 let ct1 = manager.encrypt(plaintext, 1).unwrap();
837 let ct2 = manager.encrypt(plaintext, 2).unwrap();
838
839 assert_ne!(ct1, ct2);
841
842 assert_eq!(manager.decrypt(&ct1, 1).unwrap(), plaintext);
844 assert_eq!(manager.decrypt(&ct2, 2).unwrap(), plaintext);
845 }
846
847 #[test]
848 fn test_disabled_encryption_passthrough() {
849 let disabled = DisabledEncryption;
850 let plaintext = b"Not encrypted";
851
852 let encrypted = disabled.encrypt(plaintext, 1).unwrap();
853 assert_eq!(&encrypted[..], plaintext);
854
855 let decrypted = disabled.decrypt(plaintext, 1).unwrap();
856 assert_eq!(&decrypted[..], plaintext);
857
858 assert_eq!(disabled.encrypted_size(100), 100);
859 assert!(!disabled.is_enabled());
860 }
861
862 #[test]
863 fn test_master_key_validation() {
864 let result = MasterKey::new(vec![0u8; 16], 1);
866 assert!(result.is_err());
867
868 let result = MasterKey::new(vec![0u8; 32], 1);
870 assert!(result.is_ok());
871 }
872
873 #[test]
874 fn test_key_derivation_consistency() {
875 let key = MasterKey::new(vec![42u8; 32], 1).unwrap();
876
877 let dk1 = key.derive_data_key(b"scope1").unwrap();
878 let dk2 = key.derive_data_key(b"scope1").unwrap();
879 let dk3 = key.derive_data_key(b"scope2").unwrap();
880
881 assert_eq!(dk1, dk2);
883
884 assert_ne!(dk1, dk3);
886 }
887
888 #[test]
889 fn test_large_data_encryption() {
890 let manager = EncryptionManager::new(test_config()).unwrap();
891
892 let plaintext: Vec<u8> = (0..1_000_000).map(|i| (i % 256) as u8).collect();
894
895 let ciphertext = manager.encrypt(&plaintext, 999999).unwrap();
896 let decrypted = manager.decrypt(&ciphertext, 999999).unwrap();
897
898 assert_eq!(decrypted, plaintext);
899 }
900
901 #[test]
902 fn test_generate_key() {
903 let key = MasterKey::generate(1).unwrap();
904 assert_eq!(key.version(), 1);
905 }
906
907 #[test]
908 fn test_chacha20_poly1305_encrypt_decrypt() {
909 let config = EncryptionConfig {
910 enabled: true,
911 algorithm: Algorithm::ChaCha20Poly1305,
912 key_provider: KeyProvider::InMemory(vec![0u8; 32]),
913 key_rotation_days: 0,
914 aad_scope: "test".to_string(),
915 };
916 let manager = EncryptionManager::new(config).unwrap();
917 let plaintext = b"ChaCha20-Poly1305 test payload";
918 let lsn = 42u64;
919
920 let ciphertext = manager.encrypt(plaintext, lsn).unwrap();
921 assert_ne!(ciphertext.as_slice(), plaintext.as_slice());
922
923 let header = EncryptedHeader::from_bytes(&ciphertext).unwrap();
925 assert_eq!(header.algorithm, Algorithm::ChaCha20Poly1305);
926
927 let decrypted = manager.decrypt(&ciphertext, lsn).unwrap();
928 assert_eq!(decrypted, plaintext);
929 }
930
931 #[test]
932 fn test_key_rotation() {
933 let config = EncryptionConfig {
934 enabled: true,
935 algorithm: Algorithm::Aes256Gcm,
936 key_provider: KeyProvider::InMemory(vec![1u8; 32]),
937 key_rotation_days: 30,
938 aad_scope: "test".to_string(),
939 };
940 let manager = EncryptionManager::new(config).unwrap();
941
942 let plaintext = b"data encrypted with key v1";
944 let ct_v1 = manager.encrypt(plaintext, 100).unwrap();
945 assert_eq!(manager.key_version(), 1);
946
947 let new_master = MasterKey::new(vec![2u8; 32], 2).unwrap();
949 manager.rotate_key(new_master).unwrap();
950 assert_eq!(manager.key_version(), 2);
951
952 let decrypted = manager.decrypt(&ct_v1, 100).unwrap();
954 assert_eq!(decrypted, plaintext);
955
956 let bad_master = MasterKey::new(vec![3u8; 32], 1).unwrap();
958 assert!(manager.rotate_key(bad_master).is_err());
959 }
960}