1use crate::{Error, Result};
4use alloc::{string::String, vec::Vec};
5use hmac::{Hmac, Mac};
6use sha2::{Digest, Sha256, Sha512};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum HashAlgorithm {
11 Sha256,
13 Sha512,
15}
16
17impl HashAlgorithm {
18 pub fn output_len(&self) -> usize {
20 match self {
21 HashAlgorithm::Sha256 => 32,
22 HashAlgorithm::Sha512 => 64,
23 }
24 }
25}
26
27pub fn hash(data: &[u8], algorithm: HashAlgorithm) -> Vec<u8> {
29 match algorithm {
30 HashAlgorithm::Sha256 => {
31 let mut hasher = Sha256::new();
32 hasher.update(data);
33 hasher.finalize().to_vec()
34 }
35 HashAlgorithm::Sha512 => {
36 let mut hasher = Sha512::new();
37 hasher.update(data);
38 hasher.finalize().to_vec()
39 }
40 }
41}
42
43pub fn hash_hex(data: &[u8], algorithm: HashAlgorithm) -> String {
45 hex::encode(hash(data, algorithm))
46}
47
48pub fn hash_with_salt(data: &[u8], salt: &[u8], algorithm: HashAlgorithm) -> Vec<u8> {
50 let mut combined = Vec::with_capacity(data.len() + salt.len());
51 combined.extend_from_slice(data);
52 combined.extend_from_slice(salt);
53 hash(&combined, algorithm)
54}
55
56pub fn hash_with_salt_hex(data: &[u8], salt: &[u8], algorithm: HashAlgorithm) -> String {
58 hex::encode(hash_with_salt(data, salt, algorithm))
59}
60
61pub fn compare_hashes(a: &[u8], b: &[u8]) -> bool {
63 constant_time_eq::constant_time_eq(a, b)
64}
65
66pub fn generate_hmac(data: &[u8], key: &[u8], algorithm: HashAlgorithm) -> Result<Vec<u8>> {
68 match algorithm {
69 HashAlgorithm::Sha256 => {
70 let mut mac = Hmac::<Sha256>::new_from_slice(key)
71 .map_err(|e| Error::HashFailed(e.to_string()))?;
72 mac.update(data);
73 Ok(mac.finalize().into_bytes().to_vec())
74 }
75 HashAlgorithm::Sha512 => {
76 let mut mac = Hmac::<Sha512>::new_from_slice(key)
77 .map_err(|e| Error::HashFailed(e.to_string()))?;
78 mac.update(data);
79 Ok(mac.finalize().into_bytes().to_vec())
80 }
81 }
82}
83
84pub fn generate_hmac_hex(data: &[u8], key: &[u8], algorithm: HashAlgorithm) -> Result<String> {
86 Ok(hex::encode(generate_hmac(data, key, algorithm)?))
87}
88
89pub fn verify_hmac(data: &[u8], expected_mac: &[u8], key: &[u8], algorithm: HashAlgorithm) -> Result<bool> {
91 let actual_mac = generate_hmac(data, key, algorithm)?;
92 Ok(compare_hashes(&actual_mac, expected_mac))
93}
94
95pub fn hash_with_pbkdf2(
97 data: &[u8],
98 salt: &[u8],
99 iterations: u32,
100) -> Vec<u8> {
101 use pbkdf2::pbkdf2_hmac;
102
103 let mut output = [0u8; 32];
104 pbkdf2_hmac::<Sha256>(data, salt, iterations, &mut output);
105 output.to_vec()
106}
107
108pub fn verify_pbkdf2(
110 data: &[u8],
111 expected_hash: &[u8],
112 salt: &[u8],
113 iterations: u32,
114) -> bool {
115 let actual_hash = hash_with_pbkdf2(data, salt, iterations);
116 compare_hashes(&actual_hash, expected_hash)
117}
118
119pub fn generate_fingerprint(data: &[u8], length: usize) -> String {
122 let hash = hash_hex(data, HashAlgorithm::Sha256);
123 let hex_len = (length * 2).min(hash.len());
125 hash[..hex_len].to_string()
126}
127
128pub fn generate_safety_numbers(data: &[u8], group_size: usize) -> String {
130 let hash_bytes = hash(data, HashAlgorithm::Sha256);
131 format_safety_numbers(&hash_bytes, group_size)
132}
133
134fn format_safety_numbers(hash_bytes: &[u8], group_size: usize) -> String {
135 let mut groups = Vec::new();
136
137 for chunk in hash_bytes.chunks(group_size) {
138 let group: Vec<String> = chunk
139 .iter()
140 .map(|&byte| format!("{:03}", byte))
141 .collect();
142 groups.push(group.join(" "));
143 }
144
145 groups.join(" ")
146}
147
148pub fn generate_random_bytes(length: usize) -> Vec<u8> {
150 use rand::RngCore;
151 let mut bytes = vec![0u8; length];
152 rand::thread_rng().fill_bytes(&mut bytes);
153 bytes
154}
155
156pub fn generate_salt(length: usize) -> Vec<u8> {
158 generate_random_bytes(length)
159}
160
161pub fn secure_wipe(buffer: &mut [u8]) {
163 use zeroize::Zeroize;
164 buffer.zeroize();
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170
171 #[test]
172 fn test_sha256() {
173 let data = b"hello world";
174 let hash = hash_hex(data, HashAlgorithm::Sha256);
175 assert_eq!(
177 hash,
178 "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
179 );
180 }
181
182 #[test]
183 fn test_sha512() {
184 let data = b"hello world";
185 let hash = hash_hex(data, HashAlgorithm::Sha512);
186 assert_eq!(hash.len(), 128); }
188
189 #[test]
190 fn test_hash_with_salt() {
191 let data = b"password";
192 let salt = b"random_salt";
193
194 let hash1 = hash_with_salt_hex(data, salt, HashAlgorithm::Sha256);
195 let hash2 = hash_with_salt_hex(data, salt, HashAlgorithm::Sha256);
196
197 assert_eq!(hash1, hash2);
199
200 let hash3 = hash_with_salt_hex(data, b"different_salt", HashAlgorithm::Sha256);
202 assert_ne!(hash1, hash3);
203 }
204
205 #[test]
206 fn test_hmac() {
207 let data = b"message";
208 let key = b"secret_key";
209
210 let mac = generate_hmac_hex(data, key, HashAlgorithm::Sha256).unwrap();
211 assert_eq!(mac.len(), 64); let mac_bytes = hex::decode(&mac).unwrap();
215 assert!(verify_hmac(data, &mac_bytes, key, HashAlgorithm::Sha256).unwrap());
216
217 assert!(!verify_hmac(b"wrong", &mac_bytes, key, HashAlgorithm::Sha256).unwrap());
219 }
220
221 #[test]
222 fn test_pbkdf2() {
223 let password = b"my_password";
224 let salt = b"my_salt";
225 let iterations = 1000;
226
227 let hash1 = hash_with_pbkdf2(password, salt, iterations);
228 let hash2 = hash_with_pbkdf2(password, salt, iterations);
229
230 assert_eq!(hash1, hash2);
231 assert!(verify_pbkdf2(password, &hash1, salt, iterations));
232 assert!(!verify_pbkdf2(b"wrong_password", &hash1, salt, iterations));
233 }
234
235 #[test]
236 fn test_compare_hashes_constant_time() {
237 let hash1 = hash(b"test", HashAlgorithm::Sha256);
238 let hash2 = hash(b"test", HashAlgorithm::Sha256);
239 let hash3 = hash(b"different", HashAlgorithm::Sha256);
240
241 assert!(compare_hashes(&hash1, &hash2));
242 assert!(!compare_hashes(&hash1, &hash3));
243 }
244
245 #[test]
246 fn test_fingerprint() {
247 let data = b"some key material";
248 let fp = generate_fingerprint(data, 8);
250 assert_eq!(fp.len(), 16);
251
252 let fp2 = generate_fingerprint(data, 4);
254 assert_eq!(fp2.len(), 8);
255 }
256
257 #[test]
258 fn test_safety_numbers() {
259 let data = b"public key data";
260 let numbers = generate_safety_numbers(data, 5);
261 assert!(!numbers.is_empty());
262 assert!(numbers.contains(' '));
264 }
265}
266