1use crate::error::CipherError;
2use crate::pbkdf2::key_and_iv;
3use aes::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit};
4use base64::engine::general_purpose::STANDARD as base64;
5use base64::Engine;
6use cbc::cipher::block_padding::Pkcs7;
7use rand::distributions::Alphanumeric;
8use rand::Rng;
9
10const SALTED_MAGIC: &[u8] = b"Salted__";
11
12pub fn decrypt(ciphertext: &str, key: &str) -> Result<String, CipherError> {
13 base64
14 .decode(ciphertext.as_bytes())
15 .map_err(CipherError::InvalidBase64Encoding)
16 .and_then(|v| decrypt_bytes(&v, key))
17}
18
19pub fn decrypt_bytes(ciphertext: &[u8], key: &str) -> Result<String, CipherError> {
20 if ciphertext.len() < 16 {
21 return Err(CipherError::CipherTextIsTooShort());
22 }
23 let (salted, rest) = ciphertext.split_at(8);
24 if salted != SALTED_MAGIC {
25 return Err(CipherError::InvalidCipherHeader());
26 }
27 let (salt, rest) = rest.split_at(8);
28 let (key, iv) = key_and_iv(key.as_bytes(), salt)?;
29 let cipher = cbc::Decryptor::<aes::Aes256>::new_from_slices(&key, &iv)?;
30 let res = cipher
31 .decrypt_padded_vec_mut::<Pkcs7>(rest)
32 .map_err(CipherError::InvalidKey)?;
33 String::from_utf8(res).map_err(CipherError::InvalidUtf8Encoding)
34}
35
36pub fn encrypt<R: Rng>(plaintext: &str, key: &str, rng: &mut R) -> Result<String, CipherError> {
37 let message = encrypt_bytes(plaintext, key, rng)?;
38 Ok(base64.encode(message))
39}
40
41pub fn encrypt_bytes<R: Rng>(
42 plaintext: &str,
43 key: &str,
44 rng: &mut R,
45) -> Result<Vec<u8>, CipherError> {
46 let salt: String = rng
47 .sample_iter(&Alphanumeric)
48 .take(8)
49 .map(char::from)
50 .collect();
51
52 let (key, iv) = key_and_iv(key.as_bytes(), salt.as_bytes())?;
53 let cipher = cbc::Encryptor::<aes::Aes256>::new_from_slices(&key, &iv)?;
54 let plaintext = plaintext.as_bytes().to_vec();
55 let ciphertext = cipher.encrypt_padded_vec_mut::<Pkcs7>(&plaintext);
56 Ok([SALTED_MAGIC, salt.as_bytes(), &ciphertext].concat())
57}
58
59#[cfg(test)]
60mod test {
61 use super::*;
62 use rand::thread_rng;
63 use rand_chacha::rand_core::SeedableRng;
64
65 #[test]
66 fn test_encrypt_decrypt() {
67 let key = "password";
68 let plaintext = "Hello, world!";
69 let ciphertext = encrypt(plaintext, key, &mut thread_rng()).unwrap();
70 let decrypted = decrypt(&ciphertext, key).unwrap();
71 assert_eq!(plaintext, decrypted);
72 }
73
74 #[test]
75 fn test_encrypt() {
76 let password = "test";
77 let plaintext = "test";
78 let encrypted = encrypt(
79 plaintext,
80 password,
81 &mut rand_chacha::ChaCha8Rng::seed_from_u64(10),
82 )
83 .unwrap();
84 assert_eq!(encrypted, "U2FsdGVkX19Wak5BUmlqMxLr7IxaMTjyOObe/snFRY4=");
85 }
86
87 #[test]
88 fn test_decrypt() {
89 let password = "test";
90 let encrypted = "U2FsdGVkX18OtYlc5sMYSNdZ8zUWhACPqYSSwVuPSPA=";
95 let decrypted = decrypt(encrypted, password).unwrap();
96 assert_eq!(decrypted, "test");
97 }
98}