1use crate::{Error, Result};
4use alloc::{format, string::String, vec, vec::Vec};
5use hmac::{Hmac, Mac};
6use sha2::{Digest, Sha256, Sha512};
7
8const SALTED_HASH_DOMAIN: &[u8] = b"voided:hash-with-salt:v2";
9
10pub const MAX_FINGERPRINT_BYTES: usize = 32;
12
13pub const MAX_FINGERPRINT_GROUP_SIZE: usize = 32;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum HashAlgorithm {
19 Sha256,
21 Sha512,
23}
24
25impl HashAlgorithm {
26 pub fn output_len(&self) -> usize {
28 match self {
29 HashAlgorithm::Sha256 => 32,
30 HashAlgorithm::Sha512 => 64,
31 }
32 }
33}
34
35enum StreamingHasherState {
36 Sha256(Sha256),
37 Sha512(Sha512),
38}
39
40pub struct StreamingHasher {
45 state: StreamingHasherState,
46 bytes_hashed: u128,
47}
48
49impl StreamingHasher {
50 pub fn new(algorithm: HashAlgorithm) -> Self {
52 let state = match algorithm {
53 HashAlgorithm::Sha256 => StreamingHasherState::Sha256(Sha256::new()),
54 HashAlgorithm::Sha512 => StreamingHasherState::Sha512(Sha512::new()),
55 };
56
57 Self {
58 state,
59 bytes_hashed: 0,
60 }
61 }
62
63 pub fn update(&mut self, data: &[u8]) {
65 match &mut self.state {
66 StreamingHasherState::Sha256(hasher) => hasher.update(data),
67 StreamingHasherState::Sha512(hasher) => hasher.update(data),
68 }
69 self.bytes_hashed += data.len() as u128;
70 }
71
72 pub fn bytes_hashed(&self) -> u128 {
74 self.bytes_hashed
75 }
76
77 pub fn finalize_bytes(self) -> Vec<u8> {
79 match self.state {
80 StreamingHasherState::Sha256(hasher) => hasher.finalize().to_vec(),
81 StreamingHasherState::Sha512(hasher) => hasher.finalize().to_vec(),
82 }
83 }
84
85 pub fn finalize_hex(self) -> String {
87 hex::encode(self.finalize_bytes())
88 }
89}
90
91pub fn hash(data: &[u8], algorithm: HashAlgorithm) -> Vec<u8> {
93 let mut hasher = StreamingHasher::new(algorithm);
94 hasher.update(data);
95 hasher.finalize_bytes()
96}
97
98pub fn hash_hex(data: &[u8], algorithm: HashAlgorithm) -> String {
100 hex::encode(hash(data, algorithm))
101}
102
103pub fn hash_with_salt(data: &[u8], salt: &[u8], algorithm: HashAlgorithm) -> Vec<u8> {
105 let mut combined =
108 Vec::with_capacity(SALTED_HASH_DOMAIN.len() + 16 + data.len().saturating_add(salt.len()));
109 combined.extend_from_slice(SALTED_HASH_DOMAIN);
110 combined.extend_from_slice(&(data.len() as u64).to_be_bytes());
111 combined.extend_from_slice(data);
112 combined.extend_from_slice(&(salt.len() as u64).to_be_bytes());
113 combined.extend_from_slice(salt);
114 hash(&combined, algorithm)
115}
116
117pub fn hash_with_salt_hex(data: &[u8], salt: &[u8], algorithm: HashAlgorithm) -> String {
119 hex::encode(hash_with_salt(data, salt, algorithm))
120}
121
122pub fn compare_hashes(a: &[u8], b: &[u8]) -> bool {
124 constant_time_eq::constant_time_eq(a, b)
125}
126
127pub fn generate_hmac(data: &[u8], key: &[u8], algorithm: HashAlgorithm) -> Result<Vec<u8>> {
129 match algorithm {
130 HashAlgorithm::Sha256 => {
131 let mut mac = Hmac::<Sha256>::new_from_slice(key)
132 .map_err(|e| Error::HashFailed(e.to_string()))?;
133 mac.update(data);
134 Ok(mac.finalize().into_bytes().to_vec())
135 }
136 HashAlgorithm::Sha512 => {
137 let mut mac = Hmac::<Sha512>::new_from_slice(key)
138 .map_err(|e| Error::HashFailed(e.to_string()))?;
139 mac.update(data);
140 Ok(mac.finalize().into_bytes().to_vec())
141 }
142 }
143}
144
145pub fn generate_hmac_sha256(data: &[u8], key: &[u8]) -> Result<[u8; 32]> {
147 let mut mac =
148 Hmac::<Sha256>::new_from_slice(key).map_err(|e| Error::HashFailed(e.to_string()))?;
149 mac.update(data);
150 let mut output = [0u8; 32];
151 output.copy_from_slice(&mac.finalize().into_bytes());
152 Ok(output)
153}
154
155pub fn generate_hmac_parts(
157 parts: &[&[u8]],
158 key: &[u8],
159 algorithm: HashAlgorithm,
160) -> Result<Vec<u8>> {
161 match algorithm {
162 HashAlgorithm::Sha256 => {
163 let mut mac = Hmac::<Sha256>::new_from_slice(key)
164 .map_err(|e| Error::HashFailed(e.to_string()))?;
165 for part in parts {
166 mac.update(*part);
167 }
168 Ok(mac.finalize().into_bytes().to_vec())
169 }
170 HashAlgorithm::Sha512 => {
171 let mut mac = Hmac::<Sha512>::new_from_slice(key)
172 .map_err(|e| Error::HashFailed(e.to_string()))?;
173 for part in parts {
174 mac.update(*part);
175 }
176 Ok(mac.finalize().into_bytes().to_vec())
177 }
178 }
179}
180
181pub fn generate_hmac_sha256_parts(parts: &[&[u8]], key: &[u8]) -> Result<[u8; 32]> {
183 let mut mac =
184 Hmac::<Sha256>::new_from_slice(key).map_err(|e| Error::HashFailed(e.to_string()))?;
185 for part in parts {
186 mac.update(*part);
187 }
188 let mut output = [0u8; 32];
189 output.copy_from_slice(&mac.finalize().into_bytes());
190 Ok(output)
191}
192
193pub fn generate_hmac_hex(data: &[u8], key: &[u8], algorithm: HashAlgorithm) -> Result<String> {
195 Ok(hex::encode(generate_hmac(data, key, algorithm)?))
196}
197
198pub fn verify_hmac(
200 data: &[u8],
201 expected_mac: &[u8],
202 key: &[u8],
203 algorithm: HashAlgorithm,
204) -> Result<bool> {
205 let actual_mac = generate_hmac(data, key, algorithm)?;
206 Ok(compare_hashes(&actual_mac, expected_mac))
207}
208
209pub fn hash_with_pbkdf2(data: &[u8], salt: &[u8], iterations: u32) -> Result<Vec<u8>> {
211 use pbkdf2::pbkdf2_hmac;
212
213 crate::encryption::validate_pbkdf2_parameters(salt, iterations)?;
214 let mut output = [0u8; 32];
215 pbkdf2_hmac::<Sha256>(data, salt, iterations, &mut output);
216 Ok(output.to_vec())
217}
218
219pub fn verify_pbkdf2(
221 data: &[u8],
222 expected_hash: &[u8],
223 salt: &[u8],
224 iterations: u32,
225) -> Result<bool> {
226 let actual_hash = hash_with_pbkdf2(data, salt, iterations)?;
227 Ok(compare_hashes(&actual_hash, expected_hash))
228}
229
230pub fn generate_fingerprint(data: &[u8], length: usize) -> String {
233 let hash = hash_hex(data, HashAlgorithm::Sha256);
234 let hex_len = length.min(MAX_FINGERPRINT_BYTES) * 2;
236 hash[..hex_len].to_string()
237}
238
239pub fn generate_safety_numbers(data: &[u8], group_size: usize) -> Result<String> {
244 if !(1..=MAX_FINGERPRINT_GROUP_SIZE).contains(&group_size) {
245 return Err(Error::InvalidConfiguration(format!(
246 "fingerprint group size must be between 1 and {MAX_FINGERPRINT_GROUP_SIZE}"
247 )));
248 }
249 let hash_bytes = hash(data, HashAlgorithm::Sha256);
250 Ok(format_safety_numbers(&hash_bytes, group_size))
251}
252
253fn format_safety_numbers(hash_bytes: &[u8], group_size: usize) -> String {
254 let mut groups = Vec::new();
255
256 for chunk in hash_bytes.chunks(group_size) {
257 let group: Vec<String> = chunk.iter().map(|&byte| format!("{:03}", byte)).collect();
258 groups.push(group.join(" "));
259 }
260
261 groups.join(" ")
262}
263
264pub fn generate_random_bytes(length: usize) -> Vec<u8> {
266 use rand::RngCore;
267 let mut bytes = vec![0u8; length];
268 rand::thread_rng().fill_bytes(&mut bytes);
269 bytes
270}
271
272pub fn generate_salt(length: usize) -> Vec<u8> {
274 generate_random_bytes(length)
275}
276
277pub fn secure_wipe(buffer: &mut [u8]) {
279 use zeroize::Zeroize;
280 buffer.zeroize();
281}
282
283#[cfg(test)]
284mod tests {
285 use super::*;
286
287 fn assert_streaming_matches_one_shot(
288 data: &[u8],
289 algorithm: HashAlgorithm,
290 chunk_sizes: &[usize],
291 ) {
292 let expected_bytes = hash(data, algorithm);
293 let expected_hex = hash_hex(data, algorithm);
294
295 let mut bytes_hasher = StreamingHasher::new(algorithm);
296 let mut offset = 0;
297 let mut chunk_index = 0;
298 while offset < data.len() {
299 let chunk_size = chunk_sizes[chunk_index % chunk_sizes.len()];
300 let end = (offset + chunk_size).min(data.len());
301 bytes_hasher.update(&data[offset..end]);
302 offset = end;
303 chunk_index += 1;
304 }
305 assert_eq!(bytes_hasher.bytes_hashed(), data.len() as u128);
306 assert_eq!(bytes_hasher.finalize_bytes(), expected_bytes);
307
308 let mut hex_hasher = StreamingHasher::new(algorithm);
309 for chunk in data.chunks(113) {
310 hex_hasher.update(chunk);
311 }
312 assert_eq!(hex_hasher.bytes_hashed(), data.len() as u128);
313 assert_eq!(hex_hasher.finalize_hex(), expected_hex);
314 }
315
316 #[test]
317 fn streaming_hash_matches_one_shot_for_sha256_and_sha512() {
318 let short = b"incremental hashing across uneven chunks";
319 let multi_megabyte: Vec<u8> = (0..(3 * 1024 * 1024 + 257))
320 .map(|index| ((index * 31 + index / 251) % 256) as u8)
321 .collect();
322
323 for (algorithm, block_size) in [
324 (HashAlgorithm::Sha256, 64usize),
325 (HashAlgorithm::Sha512, 128usize),
326 ] {
327 assert_streaming_matches_one_shot(&[], algorithm, &[1]);
328 assert_streaming_matches_one_shot(short, algorithm, &[1, 2, 7, 19]);
329
330 for length in [block_size - 1, block_size, block_size + 1, block_size * 2] {
331 let block_boundary: Vec<u8> = (0..length)
332 .map(|index| ((index * 17 + 11) % 256) as u8)
333 .collect();
334 assert_streaming_matches_one_shot(
335 &block_boundary,
336 algorithm,
337 &[1, block_size - 1, block_size + 3],
338 );
339 }
340
341 assert_streaming_matches_one_shot(&multi_megabyte, algorithm, &[1, 31, 4_096, 65_537]);
342 }
343 }
344
345 #[test]
346 fn test_sha256() {
347 let data = b"hello world";
348 let hash = hash_hex(data, HashAlgorithm::Sha256);
349 assert_eq!(
351 hash,
352 "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
353 );
354 }
355
356 #[test]
357 fn test_sha512() {
358 let data = b"hello world";
359 let hash = hash_hex(data, HashAlgorithm::Sha512);
360 assert_eq!(hash.len(), 128); }
362
363 #[test]
364 fn test_hash_with_salt() {
365 let data = b"password";
366 let salt = b"random_salt";
367
368 let hash1 = hash_with_salt_hex(data, salt, HashAlgorithm::Sha256);
369 let hash2 = hash_with_salt_hex(data, salt, HashAlgorithm::Sha256);
370
371 assert_eq!(hash1, hash2);
373
374 let hash3 = hash_with_salt_hex(data, b"different_salt", HashAlgorithm::Sha256);
376 assert_ne!(hash1, hash3);
377
378 assert_ne!(
379 hash_with_salt(b"a", b"bc", HashAlgorithm::Sha256),
380 hash_with_salt(b"ab", b"c", HashAlgorithm::Sha256)
381 );
382 }
383
384 #[test]
385 fn test_hmac() {
386 let data = b"message";
387 let key = b"secret_key";
388
389 let mac = generate_hmac_hex(data, key, HashAlgorithm::Sha256).unwrap();
390 assert_eq!(mac.len(), 64); let mac_bytes = hex::decode(&mac).unwrap();
394 assert!(verify_hmac(data, &mac_bytes, key, HashAlgorithm::Sha256).unwrap());
395
396 assert!(!verify_hmac(b"wrong", &mac_bytes, key, HashAlgorithm::Sha256).unwrap());
398 }
399
400 #[test]
401 fn test_pbkdf2() {
402 let password = b"my_password";
403 let salt = b"16-byte-test-salt";
404 let iterations = crate::encryption::PBKDF2_MIN_ITERATIONS;
405
406 let hash1 = hash_with_pbkdf2(password, salt, iterations).unwrap();
407 let hash2 = hash_with_pbkdf2(password, salt, iterations).unwrap();
408
409 assert_eq!(hash1, hash2);
410 assert!(verify_pbkdf2(password, &hash1, salt, iterations).unwrap());
411 assert!(!verify_pbkdf2(b"wrong_password", &hash1, salt, iterations).unwrap());
412 assert!(hash_with_pbkdf2(password, salt, 0).is_err());
413 }
414
415 #[test]
416 fn test_compare_hashes_constant_time() {
417 let hash1 = hash(b"test", HashAlgorithm::Sha256);
418 let hash2 = hash(b"test", HashAlgorithm::Sha256);
419 let hash3 = hash(b"different", HashAlgorithm::Sha256);
420
421 assert!(compare_hashes(&hash1, &hash2));
422 assert!(!compare_hashes(&hash1, &hash3));
423 }
424
425 #[test]
426 fn test_fingerprint() {
427 let data = b"some key material";
428 let fp = generate_fingerprint(data, 8);
430 assert_eq!(fp.len(), 16);
431
432 let fp2 = generate_fingerprint(data, 4);
434 assert_eq!(fp2.len(), 8);
435 }
436
437 #[test]
438 fn test_safety_numbers() {
439 let data = b"public key data";
440 let numbers = generate_safety_numbers(data, 5).unwrap();
441 assert!(!numbers.is_empty());
442 assert!(numbers.contains(' '));
444 assert!(generate_safety_numbers(data, 0).is_err());
445 }
446}