use aes_gcm::{
aead::{generic_array::GenericArray, Aead, KeyInit},
Aes256Gcm,
};
use sha2::{Digest, Sha256};
use subtle::ConstantTimeEq;
use crate::errors::{CryptoError, ErrorCode};
use crate::i18n::I18n;
pub fn aes_encrypt_with_aad(
key: &[u8],
iv: &[u8],
plaintext: &[u8],
aad: Option<&[u8]>,
i18n: &I18n,
) -> Result<Vec<u8>, CryptoError> {
use aes_gcm::aead::Payload;
let cipher = Aes256Gcm::new_from_slice(key)
.map_err(|_| CryptoError::new(ErrorCode::AesError, i18n.error_msg("aes_error")))?;
if iv.len() != 12 {
return Err(CryptoError::new(
ErrorCode::InvalidIV,
i18n.error_msg("invalid_iv"),
));
}
let nonce = GenericArray::clone_from_slice(iv);
let payload = if let Some(aad_data) = aad {
Payload {
msg: plaintext,
aad: aad_data,
}
} else {
Payload {
msg: plaintext,
aad: b"",
}
};
cipher
.encrypt(&nonce, payload)
.map_err(|_| CryptoError::new(ErrorCode::AesError, i18n.error_msg("aes_error")))
}
pub fn aes_encrypt(
key: &[u8],
iv: &[u8],
plaintext: &[u8],
i18n: &I18n,
) -> Result<Vec<u8>, CryptoError> {
aes_encrypt_with_aad(key, iv, plaintext, None, i18n)
}
pub fn aes_decrypt_with_aad(
key: &[u8],
iv: &[u8],
ciphertext: &[u8],
aad: Option<&[u8]>,
i18n: &I18n,
) -> Result<Vec<u8>, CryptoError> {
use aes_gcm::aead::Payload;
let cipher = Aes256Gcm::new_from_slice(key).map_err(|_| {
CryptoError::new(
ErrorCode::DecryptionError,
i18n.error_msg("decryption_error"),
)
})?;
if iv.len() != 12 {
return Err(CryptoError::new(
ErrorCode::InvalidIV,
i18n.error_msg("invalid_iv"),
));
}
let nonce = GenericArray::clone_from_slice(iv);
let payload = if let Some(aad_data) = aad {
Payload {
msg: ciphertext,
aad: aad_data,
}
} else {
Payload {
msg: ciphertext,
aad: b"",
}
};
cipher.decrypt(&nonce, payload).map_err(|_| {
CryptoError::new(
ErrorCode::DecryptionError,
i18n.error_msg("decryption_error"),
)
})
}
pub fn aes_decrypt(
key: &[u8],
iv: &[u8],
ciphertext: &[u8],
i18n: &I18n,
) -> Result<Vec<u8>, CryptoError> {
aes_decrypt_with_aad(key, iv, ciphertext, None, i18n)
}
pub fn compute_hash(data: &[u8]) -> Vec<u8> {
let mut hasher = Sha256::new();
hasher.update(data);
hasher.finalize().to_vec()
}
pub fn constant_time_compare(a: &[u8], b: &[u8]) -> bool {
a.ct_eq(b).into()
}
pub fn compute_hmac(key: &[u8], parts: &[&[u8]]) -> Vec<u8> {
use hmac::{Hmac, Mac};
type HmacSha256 = Hmac<Sha256>;
let mut mac = <HmacSha256 as Mac>::new_from_slice(key).expect("HMAC can take key of any size");
for part in parts {
mac.update(part);
}
mac.finalize().into_bytes().to_vec()
}
pub fn verify_mac(computed: &[u8], expected: &[u8], i18n: &I18n) -> Result<(), CryptoError> {
if !constant_time_compare(computed, expected) {
return Err(CryptoError::new(
ErrorCode::IntegrityCheckFailed,
i18n.error_msg("integrity_check_failed"),
));
}
Ok(())
}
pub fn derive_key_pbkdf2(password: &[u8], salt: &[u8], iterations: u32, dklen: usize) -> Vec<u8> {
let mut key = vec![0u8; dklen];
pbkdf2::pbkdf2_hmac::<Sha256>(password, salt, iterations, &mut key);
key
}
pub fn derive_key_hkdf(
ikm: &[u8],
salt: Option<&[u8]>,
info: &[u8],
length: usize,
) -> Result<Vec<u8>, CryptoError> {
use hkdf::Hkdf;
let hk = Hkdf::<Sha256>::new(salt, ikm);
let mut okm = vec![0u8; length];
hk.expand(info, &mut okm)
.map_err(|_| CryptoError::new(ErrorCode::KeyDerivationError, "Key derivation failed"))?;
Ok(okm)
}
pub fn generate_uuid() -> String {
use rand::Rng;
let mut rng = rand::thread_rng();
let uuid: [u8; 16] = rng.gen();
format!(
"{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
uuid[0], uuid[1], uuid[2], uuid[3],
uuid[4], uuid[5],
uuid[6], uuid[7],
uuid[8], uuid[9],
uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]
)
}
pub fn generate_iv() -> [u8; 12] {
use rand::Rng;
rand::thread_rng().gen()
}
pub fn generate_salt() -> [u8; 16] {
use rand::Rng;
rand::thread_rng().gen()
}