1use aes_gcm::aead::Aead;
8use aes_gcm::{Aes256Gcm, KeyInit, Nonce};
9use pbkdf2::password_hash::{Salt, SaltString};
10use pbkdf2::{password_hash::PasswordHasher, Pbkdf2};
11use rand::{thread_rng, RngCore};
12
13use crate::constants::{HASH_PARAMS, NONCE_SIZE, SALT_SIZE};
14use crate::error::CipherError;
15
16pub struct CipherData {
17 pub salt: [u8; SALT_SIZE],
18 pub nonce: [u8; NONCE_SIZE],
19 pub encrypted_bytes: Vec<u8>,
20}
21
22pub fn encrypt(password: &str, data: &[u8]) -> Result<CipherData, CipherError> {
26 let mut rng = thread_rng();
29 let mut raw_salt = [0u8; SALT_SIZE];
30 rng.fill_bytes(&mut raw_salt);
31 let salt = SaltString::encode_b64(&raw_salt)
32 .map_err(|e| CipherError::EncryptionError(format!("Failed to encode salt: {e:?}")))?;
33
34 let password_hash = Pbkdf2
36 .hash_password_customized(
37 password.as_bytes(),
38 None,
39 None,
40 HASH_PARAMS,
41 Salt::from(&salt),
42 )
43 .map_err(|e| CipherError::EncryptionError(e.to_string()))?
44 .hash
45 .expect("content is missing after a successful hash");
46
47 let mut nonce_bytes = [0u8; NONCE_SIZE];
49 thread_rng().fill_bytes(&mut nonce_bytes);
50 let nonce = Nonce::from_slice(&nonce_bytes);
51
52 let cipher = Aes256Gcm::new_from_slice(password_hash.as_bytes()).expect("invalid key length");
54 let encrypted_bytes = cipher
55 .encrypt(nonce, data.as_ref())
56 .map_err(|e| CipherError::EncryptionError(e.to_string()))?;
57
58 let result = CipherData {
60 salt: raw_salt,
61 nonce: nonce_bytes,
62 encrypted_bytes,
63 };
64 Ok(result)
65}