Skip to main content

shared/utils/crypto/
aes.rs

1use aes_gcm::{
2    AeadCore, Aes256Gcm, KeyInit, Nonce,
3    aead::{Aead, OsRng},
4};
5use base64::{Engine, prelude::BASE64_URL_SAFE_NO_PAD};
6use secrecy::{ExposeSecret, SecretString};
7
8use crate::error::{CoreError, Result};
9
10#[derive(Clone, Default)]
11pub struct Aes {
12    pub secret_key: String,
13}
14
15impl Aes {
16    pub fn new(secret_key: &SecretString) -> Self {
17        Self {
18            secret_key: secret_key.expose_secret().to_string(),
19        }
20    }
21}
22
23impl Aes {
24    pub fn encrypt(self, msg: &str) -> Result<String> {
25        let nonce = Aes256Gcm::generate_nonce(OsRng); // 96-bit / 12 bytes
26
27        let key_bytes = hex::decode(&self.secret_key).expect("Invalid hex key");
28        let key: &[u8; 32] = key_bytes.as_slice().try_into().expect("Invalid key length");
29
30        let cipher = Aes256Gcm::new(key.into());
31
32        // Encrypt
33        let ciphertext = cipher
34            .encrypt(&nonce, msg.as_bytes())
35            .map_err(|_| CoreError::Internal(crate::error::InternalError::Hashing))?;
36
37        // Prepend nonce to ciphertext, then base64-encode for valid UTF-8
38        let mut signature = Vec::with_capacity(12 + ciphertext.len());
39        signature.extend_from_slice(&nonce[..]); // fix 1: index instead of .as_slice()
40        signature.extend_from_slice(&ciphertext);
41
42        Ok(BASE64_URL_SAFE_NO_PAD.encode(&signature))
43    }
44
45    pub fn decrypt(self, ciphertext: &str) -> Result<String> {
46        let bytes = BASE64_URL_SAFE_NO_PAD
47            .decode(ciphertext.as_bytes())
48            .map_err(|_| CoreError::Internal(crate::error::InternalError::Hashing))?;
49
50        let (nonce_bytes, ciphertext_bytes) = bytes.split_at(12);
51        let nonce = Nonce::from(
52            <[u8; 12]>::try_from(nonce_bytes)
53                .map_err(|_| CoreError::Internal(crate::error::InternalError::Hashing))?,
54        );
55
56        let key_bytes = hex::decode(&self.secret_key).expect("Invalid hex key");
57        let key: &[u8; 32] = key_bytes.as_slice().try_into().expect("Invalid key length");
58        let cipher = Aes256Gcm::new(key.into());
59
60        let data = cipher
61            .decrypt(&nonce, ciphertext_bytes)
62            .map_err(|_| CoreError::Internal(crate::error::InternalError::Hashing))?;
63        String::from_utf8(data)
64            .map_err(|_| CoreError::Internal(crate::error::InternalError::Hashing))
65    }
66}