1use argon2::{Algorithm, Argon2, Params, Version};
69use chacha20poly1305::{
70 aead::{Aead, KeyInit},
71 ChaCha20Poly1305, Nonce,
72};
73use rand::RngCore;
74use txgate_core::error::StoreError;
75use zeroize::{Zeroize, Zeroizing};
76
77use crate::keys::SecretKey;
78
79pub const ENCRYPTION_VERSION: u8 = 1;
87
88pub const SALT_LEN: usize = 16;
90
91pub const NONCE_LEN: usize = 12;
93
94pub const TAG_LEN: usize = 16;
96
97pub const PLAINTEXT_LEN: usize = 32;
99
100pub const ENCRYPTED_KEY_LEN: usize = 1 + SALT_LEN + NONCE_LEN + PLAINTEXT_LEN + TAG_LEN;
104
105const ARGON2_MEMORY_KIB: u32 = 65536; const ARGON2_ITERATIONS: u32 = 3;
109const ARGON2_PARALLELISM: u32 = 4;
110const ARGON2_OUTPUT_LEN: usize = 32;
111
112#[derive(Debug, Clone)]
129pub struct EncryptedKey {
130 pub version: u8,
132 pub salt: [u8; SALT_LEN],
134 pub nonce: [u8; NONCE_LEN],
136 pub ciphertext: Vec<u8>,
138}
139
140impl EncryptedKey {
141 #[must_use]
159 pub fn to_bytes(&self) -> Vec<u8> {
160 let mut bytes = Vec::with_capacity(ENCRYPTED_KEY_LEN);
161 bytes.push(self.version);
162 bytes.extend_from_slice(&self.salt);
163 bytes.extend_from_slice(&self.nonce);
164 bytes.extend_from_slice(&self.ciphertext);
165 bytes
166 }
167
168 pub fn from_bytes(bytes: &[u8]) -> Result<Self, StoreError> {
186 if bytes.len() != ENCRYPTED_KEY_LEN {
187 return Err(StoreError::InvalidFormat);
188 }
189
190 let version = *bytes.first().ok_or(StoreError::InvalidFormat)?;
192 if version != ENCRYPTION_VERSION {
193 return Err(StoreError::InvalidFormat);
194 }
195
196 let salt_start = 1;
198 let salt_end = salt_start + SALT_LEN;
199 let nonce_start = salt_end;
200 let nonce_end = nonce_start + NONCE_LEN;
201 let ciphertext_start = nonce_end;
202
203 let salt_slice = bytes
205 .get(salt_start..salt_end)
206 .ok_or(StoreError::InvalidFormat)?;
207 let salt: [u8; SALT_LEN] = salt_slice
208 .try_into()
209 .map_err(|_| StoreError::InvalidFormat)?;
210
211 let nonce_slice = bytes
213 .get(nonce_start..nonce_end)
214 .ok_or(StoreError::InvalidFormat)?;
215 let nonce: [u8; NONCE_LEN] = nonce_slice
216 .try_into()
217 .map_err(|_| StoreError::InvalidFormat)?;
218
219 let ciphertext = bytes
221 .get(ciphertext_start..)
222 .ok_or(StoreError::InvalidFormat)?
223 .to_vec();
224
225 Ok(Self {
226 version,
227 salt,
228 nonce,
229 ciphertext,
230 })
231 }
232}
233
234fn derive_key(passphrase: &str, salt: &[u8; SALT_LEN]) -> Result<[u8; 32], StoreError> {
257 let params = Params::new(
258 ARGON2_MEMORY_KIB,
259 ARGON2_ITERATIONS,
260 ARGON2_PARALLELISM,
261 Some(ARGON2_OUTPUT_LEN),
262 )
263 .map_err(|_| StoreError::EncryptionFailed)?;
264
265 let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
266
267 let mut output = [0u8; 32];
268 argon2
269 .hash_password_into(passphrase.as_bytes(), salt, &mut output)
270 .map_err(|_| StoreError::EncryptionFailed)?;
271
272 Ok(output)
273}
274
275pub fn encrypt_key(secret_key: &SecretKey, passphrase: &str) -> Result<EncryptedKey, StoreError> {
316 let mut salt = [0u8; SALT_LEN];
318 let mut nonce_bytes = [0u8; NONCE_LEN];
319 rand::rngs::OsRng.fill_bytes(&mut salt);
320 rand::rngs::OsRng.fill_bytes(&mut nonce_bytes);
321
322 let mut encryption_key = derive_key(passphrase, &salt)?;
324
325 let cipher = ChaCha20Poly1305::new_from_slice(&encryption_key)
327 .map_err(|_| StoreError::EncryptionFailed)?;
328 let nonce = Nonce::from_slice(&nonce_bytes);
329
330 let ciphertext = cipher
331 .encrypt(nonce, secret_key.as_bytes().as_ref())
332 .map_err(|_| StoreError::EncryptionFailed)?;
333
334 encryption_key.zeroize();
336
337 Ok(EncryptedKey {
338 version: ENCRYPTION_VERSION,
339 salt,
340 nonce: nonce_bytes,
341 ciphertext,
342 })
343}
344
345pub fn decrypt_key(encrypted: &EncryptedKey, passphrase: &str) -> Result<SecretKey, StoreError> {
387 if encrypted.version != ENCRYPTION_VERSION {
389 return Err(StoreError::InvalidFormat);
390 }
391
392 let expected_ciphertext_len = PLAINTEXT_LEN + TAG_LEN;
394 if encrypted.ciphertext.len() != expected_ciphertext_len {
395 return Err(StoreError::InvalidFormat);
396 }
397
398 let mut encryption_key = derive_key(passphrase, &encrypted.salt)?;
400
401 let cipher = ChaCha20Poly1305::new_from_slice(&encryption_key)
403 .map_err(|_| StoreError::DecryptionFailed)?;
404 let nonce = Nonce::from_slice(&encrypted.nonce);
405
406 let plaintext = Zeroizing::new(
408 cipher
409 .decrypt(nonce, encrypted.ciphertext.as_ref())
410 .map_err(|_| StoreError::DecryptionFailed)?,
411 );
412
413 encryption_key.zeroize();
415
416 plaintext
418 .as_slice()
419 .try_into()
420 .map_err(|_| StoreError::InvalidFormat)
421 .map(SecretKey::new)
422}
423
424#[cfg(test)]
429mod tests {
430 #![allow(clippy::expect_used)]
431 #![allow(clippy::indexing_slicing)]
432
433 use super::*;
434
435 #[test]
436 fn test_encrypt_decrypt_round_trip() {
437 let original = SecretKey::generate();
438 let passphrase = "test passphrase 123!";
439
440 let encrypted = encrypt_key(&original, passphrase).expect("encryption should succeed");
441 let decrypted = decrypt_key(&encrypted, passphrase).expect("decryption should succeed");
442
443 assert_eq!(original.as_bytes(), decrypted.as_bytes());
444 }
445
446 #[test]
447 fn test_different_passphrases_produce_different_ciphertexts() {
448 let secret_key = SecretKey::new([0x42u8; 32]);
449
450 let encrypted1 =
451 encrypt_key(&secret_key, "passphrase1").expect("encryption should succeed");
452 let encrypted2 =
453 encrypt_key(&secret_key, "passphrase2").expect("encryption should succeed");
454
455 assert_ne!(encrypted1.ciphertext, encrypted2.ciphertext);
457 }
458
459 #[test]
460 fn test_same_passphrase_different_salts() {
461 let secret_key = SecretKey::new([0x42u8; 32]);
462 let passphrase = "same passphrase";
463
464 let encrypted1 = encrypt_key(&secret_key, passphrase).expect("encryption should succeed");
465 let encrypted2 = encrypt_key(&secret_key, passphrase).expect("encryption should succeed");
466
467 assert_ne!(encrypted1.salt, encrypted2.salt);
469 assert_ne!(encrypted1.nonce, encrypted2.nonce);
470 assert_ne!(encrypted1.ciphertext, encrypted2.ciphertext);
471 }
472
473 #[test]
474 fn test_wrong_passphrase_fails_decryption() {
475 let secret_key = SecretKey::generate();
476 let correct_passphrase = "correct passphrase";
477 let wrong_passphrase = "wrong passphrase";
478
479 let encrypted =
480 encrypt_key(&secret_key, correct_passphrase).expect("encryption should succeed");
481 let result = decrypt_key(&encrypted, wrong_passphrase);
482
483 assert!(result.is_err());
484 assert!(matches!(result, Err(StoreError::DecryptionFailed)));
485 }
486
487 #[test]
488 fn test_serialization_round_trip() {
489 let secret_key = SecretKey::generate();
490 let passphrase = "test passphrase";
491
492 let encrypted = encrypt_key(&secret_key, passphrase).expect("encryption should succeed");
493 let bytes = encrypted.to_bytes();
494
495 assert_eq!(bytes.len(), ENCRYPTED_KEY_LEN);
496
497 let deserialized =
498 EncryptedKey::from_bytes(&bytes).expect("deserialization should succeed");
499 let decrypted = decrypt_key(&deserialized, passphrase).expect("decryption should succeed");
500
501 assert_eq!(secret_key.as_bytes(), decrypted.as_bytes());
502 }
503
504 #[test]
505 fn test_invalid_format_wrong_length() {
506 let too_short = vec![0u8; 10];
507 let result = EncryptedKey::from_bytes(&too_short);
508 assert!(result.is_err());
509 assert!(matches!(result, Err(StoreError::InvalidFormat)));
510
511 let too_long = vec![0u8; 100];
512 let result = EncryptedKey::from_bytes(&too_long);
513 assert!(result.is_err());
514 assert!(matches!(result, Err(StoreError::InvalidFormat)));
515 }
516
517 #[test]
518 fn test_invalid_format_wrong_version() {
519 let mut bytes = vec![0u8; ENCRYPTED_KEY_LEN];
520 bytes[0] = 99; let result = EncryptedKey::from_bytes(&bytes);
523 assert!(result.is_err());
524 assert!(matches!(result, Err(StoreError::InvalidFormat)));
525 }
526
527 #[test]
528 fn test_tampered_ciphertext_fails_decryption() {
529 let secret_key = SecretKey::generate();
530 let passphrase = "test passphrase";
531
532 let mut encrypted =
533 encrypt_key(&secret_key, passphrase).expect("encryption should succeed");
534
535 if let Some(byte) = encrypted.ciphertext.first_mut() {
537 *byte ^= 0xFF;
538 }
539
540 let result = decrypt_key(&encrypted, passphrase);
541 assert!(result.is_err());
542 assert!(matches!(result, Err(StoreError::DecryptionFailed)));
543 }
544
545 #[test]
546 fn test_tampered_tag_fails_decryption() {
547 let secret_key = SecretKey::generate();
548 let passphrase = "test passphrase";
549
550 let mut encrypted =
551 encrypt_key(&secret_key, passphrase).expect("encryption should succeed");
552
553 if let Some(byte) = encrypted.ciphertext.last_mut() {
555 *byte ^= 0xFF;
556 }
557
558 let result = decrypt_key(&encrypted, passphrase);
559 assert!(result.is_err());
560 assert!(matches!(result, Err(StoreError::DecryptionFailed)));
561 }
562
563 #[test]
564 fn test_tampered_nonce_fails_decryption() {
565 let secret_key = SecretKey::generate();
566 let passphrase = "test passphrase";
567
568 let mut encrypted =
569 encrypt_key(&secret_key, passphrase).expect("encryption should succeed");
570
571 encrypted.nonce[0] ^= 0xFF;
573
574 let result = decrypt_key(&encrypted, passphrase);
575 assert!(result.is_err());
576 assert!(matches!(result, Err(StoreError::DecryptionFailed)));
577 }
578
579 #[test]
580 fn test_tampered_salt_fails_decryption() {
581 let secret_key = SecretKey::generate();
582 let passphrase = "test passphrase";
583
584 let mut encrypted =
585 encrypt_key(&secret_key, passphrase).expect("encryption should succeed");
586
587 encrypted.salt[0] ^= 0xFF;
589
590 let result = decrypt_key(&encrypted, passphrase);
591 assert!(result.is_err());
592 assert!(matches!(result, Err(StoreError::DecryptionFailed)));
593 }
594
595 #[test]
596 fn test_encrypted_key_to_bytes_format() {
597 let secret_key = SecretKey::generate();
598 let passphrase = "test passphrase";
599
600 let encrypted = encrypt_key(&secret_key, passphrase).expect("encryption should succeed");
601 let bytes = encrypted.to_bytes();
602
603 assert_eq!(bytes.len(), 77);
605 assert_eq!(bytes[0], ENCRYPTION_VERSION);
606 assert_eq!(&bytes[1..17], &encrypted.salt);
607 assert_eq!(&bytes[17..29], &encrypted.nonce);
608 assert_eq!(&bytes[29..], &encrypted.ciphertext[..]);
609 }
610
611 #[test]
612 fn test_empty_passphrase() {
613 let secret_key = SecretKey::generate();
614 let passphrase = "";
615
616 let encrypted = encrypt_key(&secret_key, passphrase).expect("encryption should succeed");
618 let decrypted = decrypt_key(&encrypted, passphrase).expect("decryption should succeed");
619
620 assert_eq!(secret_key.as_bytes(), decrypted.as_bytes());
621 }
622
623 #[test]
624 fn test_unicode_passphrase() {
625 let secret_key = SecretKey::generate();
626 let passphrase = "test passphrase with unicode: \u{1F512}";
627
628 let encrypted = encrypt_key(&secret_key, passphrase).expect("encryption should succeed");
629 let decrypted = decrypt_key(&encrypted, passphrase).expect("decryption should succeed");
630
631 assert_eq!(secret_key.as_bytes(), decrypted.as_bytes());
632 }
633
634 #[test]
635 fn test_long_passphrase() {
636 let secret_key = SecretKey::generate();
637 let passphrase = "a".repeat(10000);
638
639 let encrypted = encrypt_key(&secret_key, &passphrase).expect("encryption should succeed");
640 let decrypted = decrypt_key(&encrypted, &passphrase).expect("decryption should succeed");
641
642 assert_eq!(secret_key.as_bytes(), decrypted.as_bytes());
643 }
644
645 #[test]
646 fn test_version_check_on_decrypt() {
647 let encrypted = EncryptedKey {
648 version: 2, salt: [0u8; SALT_LEN],
650 nonce: [0u8; NONCE_LEN],
651 ciphertext: vec![0u8; PLAINTEXT_LEN + TAG_LEN],
652 };
653
654 let result = decrypt_key(&encrypted, "passphrase");
655 assert!(result.is_err());
656 assert!(matches!(result, Err(StoreError::InvalidFormat)));
657 }
658
659 #[test]
660 fn test_ciphertext_length_validation() {
661 let encrypted = EncryptedKey {
662 version: ENCRYPTION_VERSION,
663 salt: [0u8; SALT_LEN],
664 nonce: [0u8; NONCE_LEN],
665 ciphertext: vec![0u8; 10], };
667
668 let result = decrypt_key(&encrypted, "passphrase");
669 assert!(result.is_err());
670 assert!(matches!(result, Err(StoreError::InvalidFormat)));
671 }
672
673 #[test]
674 fn test_encrypted_key_debug() {
675 let secret_key = SecretKey::generate();
676 let encrypted = encrypt_key(&secret_key, "passphrase").expect("encryption should succeed");
677
678 let debug_output = format!("{encrypted:?}");
680 assert!(debug_output.contains("EncryptedKey"));
681 }
682
683 #[test]
684 fn test_encrypted_key_clone() {
685 let secret_key = SecretKey::generate();
686 let encrypted = encrypt_key(&secret_key, "passphrase").expect("encryption should succeed");
687
688 let cloned = encrypted.clone();
689 assert_eq!(encrypted.version, cloned.version);
690 assert_eq!(encrypted.salt, cloned.salt);
691 assert_eq!(encrypted.nonce, cloned.nonce);
692 assert_eq!(encrypted.ciphertext, cloned.ciphertext);
693 }
694
695 #[test]
697 fn test_encrypted_key_is_send_sync() {
698 fn assert_send_sync<T: Send + Sync>() {}
699 assert_send_sync::<EncryptedKey>();
700 }
701
702 #[test]
704 fn test_encryption_constants() {
705 assert_eq!(ENCRYPTION_VERSION, 1);
706 assert_eq!(SALT_LEN, 16);
707 assert_eq!(NONCE_LEN, 12);
708 assert_eq!(TAG_LEN, 16);
709 assert_eq!(PLAINTEXT_LEN, 32);
710 assert_eq!(ENCRYPTED_KEY_LEN, 77);
711 }
712
713 #[test]
718 fn test_derive_key_deterministic() {
719 let passphrase = "test passphrase";
721 let salt = [0x42u8; SALT_LEN];
722
723 let key1 = derive_key(passphrase, &salt).expect("derivation should succeed");
724 let key2 = derive_key(passphrase, &salt).expect("derivation should succeed");
725
726 assert_eq!(key1, key2);
727 }
728
729 #[test]
730 fn test_derive_key_different_salts() {
731 let passphrase = "test passphrase";
732 let salt1 = [0x42u8; SALT_LEN];
733 let salt2 = [0x43u8; SALT_LEN];
734
735 let key1 = derive_key(passphrase, &salt1).expect("derivation should succeed");
736 let key2 = derive_key(passphrase, &salt2).expect("derivation should succeed");
737
738 assert_ne!(key1, key2);
740 }
741
742 #[test]
743 fn test_derive_key_different_passphrases() {
744 let salt = [0x42u8; SALT_LEN];
745
746 let key1 = derive_key("passphrase1", &salt).expect("derivation should succeed");
747 let key2 = derive_key("passphrase2", &salt).expect("derivation should succeed");
748
749 assert_ne!(key1, key2);
751 }
752
753 #[test]
754 fn test_derive_key_empty_passphrase() {
755 let salt = [0x42u8; SALT_LEN];
756
757 let result = derive_key("", &salt);
759 assert!(result.is_ok());
760 }
761
762 #[test]
763 fn test_derive_key_output_length() {
764 let passphrase = "test";
765 let salt = [0x00u8; SALT_LEN];
766
767 let key = derive_key(passphrase, &salt).expect("derivation should succeed");
768
769 assert_eq!(key.len(), 32);
771 }
772
773 #[test]
778 fn test_encrypted_key_from_bytes_empty() {
779 let result = EncryptedKey::from_bytes(&[]);
780 assert!(matches!(result, Err(StoreError::InvalidFormat)));
781 }
782
783 #[test]
784 fn test_encrypted_key_version_field() {
785 let secret_key = SecretKey::generate();
786 let encrypted = encrypt_key(&secret_key, "test").expect("encryption should succeed");
787
788 assert_eq!(encrypted.version, ENCRYPTION_VERSION);
789 }
790
791 #[test]
792 fn test_decrypt_with_invalid_ciphertext_length() {
793 let encrypted = EncryptedKey {
794 version: ENCRYPTION_VERSION,
795 salt: [0u8; SALT_LEN],
796 nonce: [0u8; NONCE_LEN],
797 ciphertext: vec![0u8; 10], };
799
800 let result = decrypt_key(&encrypted, "passphrase");
801 assert!(matches!(result, Err(StoreError::InvalidFormat)));
802 }
803
804 #[test]
805 fn test_decrypt_with_valid_length_but_wrong_data() {
806 let encrypted = EncryptedKey {
807 version: ENCRYPTION_VERSION,
808 salt: [0u8; SALT_LEN],
809 nonce: [0u8; NONCE_LEN],
810 ciphertext: vec![0u8; PLAINTEXT_LEN + TAG_LEN], };
812
813 let result = decrypt_key(&encrypted, "passphrase");
814 assert!(matches!(result, Err(StoreError::DecryptionFailed)));
815 }
816}