1use aes_gcm::{Aes256Gcm, Nonce, KeyInit};
4use aes_gcm::aead::Aead;
5use sha2::{Sha256, Digest};
6use hmac::{Hmac, Mac};
7use rand::Rng;
8
9type HmacSha256 = Hmac<Sha256>;
10
11pub struct Crypto;
16
17impl Crypto {
18 pub fn sha256(data: &str) -> String {
20 let mut hasher = Sha256::new();
21 hasher.update(data.as_bytes());
22 hex::encode(hasher.finalize())
23 }
24
25 pub fn hmac_sha256(key: &[u8], data: &str) -> String {
27 let mut mac = <HmacSha256 as hmac::digest::KeyInit>::new_from_slice(key)
28 .expect("HMAC key size error");
29 mac.update(data.as_bytes());
30 hex::encode(mac.finalize().into_bytes())
31 }
32
33 pub fn aes_encrypt(key: &[u8], plaintext: &str) -> Option<(String, String)> {
38 if key.len() != 32 { return None; }
39 let cipher = Aes256Gcm::new_from_slice(key).ok()?;
40 let mut nonce_bytes = [0u8; 12];
41 rand::thread_rng().fill(&mut nonce_bytes);
42 let nonce = Nonce::from_slice(&nonce_bytes);
43 let ciphertext = cipher.encrypt(nonce, plaintext.as_bytes()).ok()?;
44 Some((
45 base64::Engine::encode(&base64::engine::general_purpose::STANDARD, &ciphertext),
46 base64::Engine::encode(&base64::engine::general_purpose::STANDARD, &nonce_bytes),
47 ))
48 }
49
50 pub fn aes_decrypt(key: &[u8], ciphertext_b64: &str, nonce_b64: &str) -> Option<String> {
52 if key.len() != 32 { return None; }
53 let cipher = Aes256Gcm::new_from_slice(key).ok()?;
54 let ciphertext = base64::Engine::decode(
55 &base64::engine::general_purpose::STANDARD, ciphertext_b64
56 ).ok()?;
57 let nonce_vec = base64::Engine::decode(
58 &base64::engine::general_purpose::STANDARD, nonce_b64
59 ).ok()?;
60 let nonce = Nonce::from_slice(&nonce_vec);
61 let plaintext = cipher.decrypt(nonce, ciphertext.as_ref()).ok()?;
62 String::from_utf8(plaintext).ok()
63 }
64
65 pub fn hash_password(password: &str) -> Result<String, argon2::password_hash::Error> {
67 use argon2::{Argon2, PasswordHasher};
68 let salt = argon2::password_hash::SaltString::generate(&mut rand::thread_rng());
69 let argon2 = Argon2::default();
70 let hash = argon2.hash_password(password.as_bytes(), &salt)?;
71 Ok(hash.to_string())
72 }
73
74 pub fn verify_password(password: &str, hash: &str) -> Result<bool, argon2::password_hash::Error> {
76 use argon2::{Argon2, PasswordVerifier, PasswordHash};
77 let parsed = PasswordHash::new(hash)?;
78 let argon2 = Argon2::default();
79 Ok(argon2.verify_password(password.as_bytes(), &parsed).is_ok())
80 }
81
82 pub fn base64_url_encode(data: &[u8]) -> String {
84 base64::Engine::encode(&base64::engine::general_purpose::URL_SAFE_NO_PAD, data)
85 }
86
87 pub fn base64_url_decode(s: &str) -> Option<Vec<u8>> {
89 base64::Engine::decode(&base64::engine::general_purpose::URL_SAFE_NO_PAD, s).ok()
90 }
91
92 pub fn random_key() -> Vec<u8> {
94 let mut key = vec![0u8; 32];
95 rand::thread_rng().fill(&mut key[..]);
96 key
97 }
98
99 pub fn random_token(len: usize) -> String {
101 let mut bytes = vec![0u8; len];
102 rand::thread_rng().fill(&mut bytes[..]);
103 hex::encode(bytes)
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110 #[test]
111 fn test_sha256() { assert_eq!(Crypto::sha256("hello").len(), 64); }
112 #[test]
113 fn test_encrypt_decrypt() {
114 let key = vec![0u8; 32];
115 let (ct, nonce64) = Crypto::aes_encrypt(&key, "test-data").unwrap();
116 let pt = Crypto::aes_decrypt(&key, &ct, &nonce64).unwrap();
117 assert_eq!(pt, "test-data");
118 }
119 #[test]
120 fn test_password() {
121 let hash = Crypto::hash_password("Alun@2024").unwrap();
122 assert!(Crypto::verify_password("Alun@2024", &hash).unwrap());
123 }
124 #[test]
125 fn test_random_key() { assert_eq!(Crypto::random_key().len(), 32); }
126}