1use blake2::{Blake2b512, Blake2s256};
2use blake3::Hasher as Blake3Hasher;
3use md5::Md5;
4use sha2::{Digest, Sha224, Sha256, Sha384, Sha512};
5use sha3::{Keccak224, Keccak256, Keccak384, Keccak512, Sha3_224, Sha3_256, Sha3_384, Sha3_512};
6use std::hash::Hasher;
7use twox_hash::xxhash3_128::Hasher as Xxh3Hash128;
8use twox_hash::xxhash3_64::Hasher as Xxh3Hash64;
9use twox_hash::{XxHash32, XxHash64};
10
11#[derive(Debug, Clone, Default)]
13pub struct XxHashConfig {
14 pub seed: u64,
16 pub secret: Option<Vec<u8>>,
18}
19
20impl XxHashConfig {
21 pub fn with_seed(seed: u64) -> Self {
23 Self { seed, secret: None }
24 }
25
26 pub fn with_secret(seed: u64, secret: Vec<u8>) -> Result<Self, String> {
29 if secret.len() < 136 {
30 return Err(format!(
31 "XXH3 secret must be >= 136 bytes, got {}",
32 secret.len()
33 ));
34 }
35 Ok(Self {
36 seed,
37 secret: Some(secret),
38 })
39 }
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub enum HashAlgorithm {
45 Md5,
46 Sha224,
47 Sha256,
48 Sha384,
49 Sha512,
50 Sha3_224,
51 Sha3_256,
52 Sha3_384,
53 Sha3_512,
54 Keccak224,
55 Keccak256,
56 Keccak384,
57 Keccak512,
58 Blake2b,
59 Blake2s,
60 Blake3,
61 Crc32,
63 Crc32c,
64 Crc16,
65 Crc64,
66 XxHash32,
68 XxHash64,
69 XxHash3_64,
70 XxHash3_128,
71}
72
73impl HashAlgorithm {
74 pub fn from_str(s: &str) -> Result<Self, String> {
76 match s.to_lowercase().as_str() {
77 "md5" => Ok(HashAlgorithm::Md5),
78 "sha224" | "sha-224" => Ok(HashAlgorithm::Sha224),
79 "sha256" | "sha-256" => Ok(HashAlgorithm::Sha256),
80 "sha384" | "sha-384" => Ok(HashAlgorithm::Sha384),
81 "sha512" | "sha-512" => Ok(HashAlgorithm::Sha512),
82 "sha3-224" | "sha3_224" => Ok(HashAlgorithm::Sha3_224),
83 "sha3-256" | "sha3_256" => Ok(HashAlgorithm::Sha3_256),
84 "sha3-384" | "sha3_384" => Ok(HashAlgorithm::Sha3_384),
85 "sha3-512" | "sha3_512" => Ok(HashAlgorithm::Sha3_512),
86 "keccak224" | "keccak-224" => Ok(HashAlgorithm::Keccak224),
87 "keccak256" | "keccak-256" => Ok(HashAlgorithm::Keccak256),
88 "keccak384" | "keccak-384" => Ok(HashAlgorithm::Keccak384),
89 "keccak512" | "keccak-512" => Ok(HashAlgorithm::Keccak512),
90 "blake2b" | "blake2b-512" => Ok(HashAlgorithm::Blake2b),
91 "blake2s" | "blake2s-256" => Ok(HashAlgorithm::Blake2s),
92 "blake3" => Ok(HashAlgorithm::Blake3),
93 "crc32" => Ok(HashAlgorithm::Crc32),
94 "crc32c" => Ok(HashAlgorithm::Crc32c),
95 "crc16" => Ok(HashAlgorithm::Crc16),
96 "crc64" => Ok(HashAlgorithm::Crc64),
97 "xxhash32" | "xxh32" => Ok(HashAlgorithm::XxHash32),
98 "xxhash64" | "xxh64" => Ok(HashAlgorithm::XxHash64),
99 "xxhash3" | "xxh3" | "xxhash3-64" | "xxh3-64" => Ok(HashAlgorithm::XxHash3_64),
100 "xxhash3-128" | "xxh3-128" => Ok(HashAlgorithm::XxHash3_128),
101 _ => Err(format!("Unknown hash algorithm: {}", s)),
102 }
103 }
104
105 pub fn as_str(&self) -> &str {
106 match self {
107 HashAlgorithm::Md5 => "md5",
108 HashAlgorithm::Sha224 => "sha224",
109 HashAlgorithm::Sha256 => "sha256",
110 HashAlgorithm::Sha384 => "sha384",
111 HashAlgorithm::Sha512 => "sha512",
112 HashAlgorithm::Sha3_224 => "sha3-224",
113 HashAlgorithm::Sha3_256 => "sha3-256",
114 HashAlgorithm::Sha3_384 => "sha3-384",
115 HashAlgorithm::Sha3_512 => "sha3-512",
116 HashAlgorithm::Keccak224 => "keccak224",
117 HashAlgorithm::Keccak256 => "keccak256",
118 HashAlgorithm::Keccak384 => "keccak384",
119 HashAlgorithm::Keccak512 => "keccak512",
120 HashAlgorithm::Blake2b => "blake2b",
121 HashAlgorithm::Blake2s => "blake2s",
122 HashAlgorithm::Blake3 => "blake3",
123 HashAlgorithm::Crc32 => "crc32",
124 HashAlgorithm::Crc32c => "crc32c",
125 HashAlgorithm::Crc16 => "crc16",
126 HashAlgorithm::Crc64 => "crc64",
127 HashAlgorithm::XxHash32 => "xxhash32",
128 HashAlgorithm::XxHash64 => "xxhash64",
129 HashAlgorithm::XxHash3_64 => "xxhash3-64",
130 HashAlgorithm::XxHash3_128 => "xxhash3-128",
131 }
132 }
133
134 pub fn output_size(&self) -> usize {
136 match self {
137 HashAlgorithm::Md5 => 16,
138 HashAlgorithm::Sha224 => 28,
139 HashAlgorithm::Sha256 => 32,
140 HashAlgorithm::Sha384 => 48,
141 HashAlgorithm::Sha512 => 64,
142 HashAlgorithm::Sha3_224 => 28,
143 HashAlgorithm::Sha3_256 => 32,
144 HashAlgorithm::Sha3_384 => 48,
145 HashAlgorithm::Sha3_512 => 64,
146 HashAlgorithm::Keccak224 => 28,
147 HashAlgorithm::Keccak256 => 32,
148 HashAlgorithm::Keccak384 => 48,
149 HashAlgorithm::Keccak512 => 64,
150 HashAlgorithm::Blake2b => 64,
151 HashAlgorithm::Blake2s => 32,
152 HashAlgorithm::Blake3 => 32,
153 HashAlgorithm::Crc16 => 2,
154 HashAlgorithm::Crc32 => 4,
155 HashAlgorithm::Crc32c => 4,
156 HashAlgorithm::Crc64 => 8,
157 HashAlgorithm::XxHash32 => 4,
158 HashAlgorithm::XxHash64 => 8,
159 HashAlgorithm::XxHash3_64 => 8,
160 HashAlgorithm::XxHash3_128 => 16,
161 }
162 }
163}
164
165pub fn hash(data: &[u8], algorithm: HashAlgorithm) -> Vec<u8> {
168 hash_with_config(data, algorithm, &XxHashConfig::default())
169}
170
171pub fn hash_with_config(data: &[u8], algorithm: HashAlgorithm, config: &XxHashConfig) -> Vec<u8> {
173 match algorithm {
174 HashAlgorithm::Md5 => {
175 let mut hasher = Md5::new();
176 hasher.update(data);
177 hasher.finalize().to_vec()
178 }
179 HashAlgorithm::Sha224 => {
180 let mut hasher = Sha224::new();
181 hasher.update(data);
182 hasher.finalize().to_vec()
183 }
184 HashAlgorithm::Sha256 => {
185 let mut hasher = Sha256::new();
186 hasher.update(data);
187 hasher.finalize().to_vec()
188 }
189 HashAlgorithm::Sha384 => {
190 let mut hasher = Sha384::new();
191 hasher.update(data);
192 hasher.finalize().to_vec()
193 }
194 HashAlgorithm::Sha512 => {
195 let mut hasher = Sha512::new();
196 hasher.update(data);
197 hasher.finalize().to_vec()
198 }
199 HashAlgorithm::Sha3_224 => {
200 let mut hasher = Sha3_224::new();
201 hasher.update(data);
202 hasher.finalize().to_vec()
203 }
204 HashAlgorithm::Sha3_256 => {
205 let mut hasher = Sha3_256::new();
206 hasher.update(data);
207 hasher.finalize().to_vec()
208 }
209 HashAlgorithm::Sha3_384 => {
210 let mut hasher = Sha3_384::new();
211 hasher.update(data);
212 hasher.finalize().to_vec()
213 }
214 HashAlgorithm::Sha3_512 => {
215 let mut hasher = Sha3_512::new();
216 hasher.update(data);
217 hasher.finalize().to_vec()
218 }
219 HashAlgorithm::Keccak224 => {
220 let mut hasher = Keccak224::new();
221 hasher.update(data);
222 hasher.finalize().to_vec()
223 }
224 HashAlgorithm::Keccak256 => {
225 let mut hasher = Keccak256::new();
226 hasher.update(data);
227 hasher.finalize().to_vec()
228 }
229 HashAlgorithm::Keccak384 => {
230 let mut hasher = Keccak384::new();
231 hasher.update(data);
232 hasher.finalize().to_vec()
233 }
234 HashAlgorithm::Keccak512 => {
235 let mut hasher = Keccak512::new();
236 hasher.update(data);
237 hasher.finalize().to_vec()
238 }
239 HashAlgorithm::Blake2b => {
240 let mut hasher = Blake2b512::new();
241 hasher.update(data);
242 hasher.finalize().to_vec()
243 }
244 HashAlgorithm::Blake2s => {
245 let mut hasher = Blake2s256::new();
246 hasher.update(data);
247 hasher.finalize().to_vec()
248 }
249 HashAlgorithm::Blake3 => {
250 let mut hasher = Blake3Hasher::new();
251 hasher.update(data);
252 hasher.finalize().as_bytes().to_vec()
253 }
254 HashAlgorithm::Crc16 => {
255 let crc = crc::Crc::<u16>::new(&crc::CRC_16_IBM_SDLC);
256 let result = crc.checksum(data);
257 result.to_be_bytes().to_vec()
258 }
259 HashAlgorithm::Crc32 => {
260 let crc = crc::Crc::<u32>::new(&crc::CRC_32_ISO_HDLC);
261 let result = crc.checksum(data);
262 result.to_be_bytes().to_vec()
263 }
264 HashAlgorithm::Crc32c => {
265 let crc = crc::Crc::<u32>::new(&crc::CRC_32_ISCSI);
266 let result = crc.checksum(data);
267 result.to_be_bytes().to_vec()
268 }
269 HashAlgorithm::Crc64 => {
270 let crc = crc::Crc::<u64>::new(&crc::CRC_64_ECMA_182);
271 let result = crc.checksum(data);
272 result.to_be_bytes().to_vec()
273 }
274 HashAlgorithm::XxHash32 => {
275 let mut hasher = XxHash32::with_seed(config.seed as u32);
276 hasher.write(data);
277 (hasher.finish() as u32).to_be_bytes().to_vec()
278 }
279 HashAlgorithm::XxHash64 => {
280 let mut hasher = XxHash64::with_seed(config.seed);
281 hasher.write(data);
282 hasher.finish().to_be_bytes().to_vec()
283 }
284 HashAlgorithm::XxHash3_64 => {
285 let mut hasher = if let Some(ref secret) = config.secret {
286 Xxh3Hash64::with_seed_and_secret(config.seed, secret.as_slice()).expect(
287 "XXH3 secret validation should have been done in XxHashConfig::with_secret",
288 )
289 } else {
290 Xxh3Hash64::with_seed(config.seed)
291 };
292 hasher.write(data);
293 hasher.finish().to_be_bytes().to_vec()
294 }
295 HashAlgorithm::XxHash3_128 => {
296 let mut hasher = if let Some(ref secret) = config.secret {
297 Xxh3Hash128::with_seed_and_secret(config.seed, secret.as_slice()).expect(
298 "XXH3 secret validation should have been done in XxHashConfig::with_secret",
299 )
300 } else {
301 Xxh3Hash128::with_seed(config.seed)
302 };
303 hasher.write(data);
304 hasher.finish_128().to_be_bytes().to_vec()
305 }
306 }
307}
308
309#[cfg(test)]
310mod tests {
311 use super::*;
312
313 #[test]
314 fn test_md5() {
315 let data = b"hello world";
316 let hash = hash(data, HashAlgorithm::Md5);
317 assert_eq!(hash.len(), 16);
318 assert_eq!(hex::encode(&hash), "5eb63bbbe01eeed093cb22bb8f5acdc3");
320 }
321
322 #[test]
323 fn test_sha256() {
324 let data = b"hello world";
325 let hash = hash(data, HashAlgorithm::Sha256);
326 assert_eq!(hash.len(), 32);
327 assert_eq!(
329 hex::encode(&hash),
330 "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
331 );
332 }
333
334 #[test]
335 fn test_sha512() {
336 let data = b"hello world";
337 let hash = hash(data, HashAlgorithm::Sha512);
338 assert_eq!(hash.len(), 64);
339 }
340
341 #[test]
342 fn test_sha3_256() {
343 let data = b"hello world";
344 let hash = hash(data, HashAlgorithm::Sha3_256);
345 assert_eq!(hash.len(), 32);
346 }
347
348 #[test]
349 fn test_blake2b() {
350 let data = b"hello world";
351 let hash = hash(data, HashAlgorithm::Blake2b);
352 assert_eq!(hash.len(), 64);
353 }
354
355 #[test]
356 fn test_blake2s() {
357 let data = b"hello world";
358 let hash = hash(data, HashAlgorithm::Blake2s);
359 assert_eq!(hash.len(), 32);
360 }
361
362 #[test]
363 fn test_blake3() {
364 let data = b"hello world";
365 let hash = hash(data, HashAlgorithm::Blake3);
366 assert_eq!(hash.len(), 32);
367 }
368
369 #[test]
370 fn test_empty_input() {
371 let data = b"";
372 let hash = hash(data, HashAlgorithm::Sha256);
373 assert_eq!(hash.len(), 32);
374 assert_eq!(
376 hex::encode(&hash),
377 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
378 );
379 }
380
381 #[test]
382 fn test_output_sizes() {
383 assert_eq!(HashAlgorithm::Md5.output_size(), 16);
384 assert_eq!(HashAlgorithm::Sha256.output_size(), 32);
385 assert_eq!(HashAlgorithm::Sha512.output_size(), 64);
386 assert_eq!(HashAlgorithm::Blake3.output_size(), 32);
387 assert_eq!(HashAlgorithm::Crc16.output_size(), 2);
388 assert_eq!(HashAlgorithm::Crc32.output_size(), 4);
389 assert_eq!(HashAlgorithm::Crc64.output_size(), 8);
390 assert_eq!(HashAlgorithm::XxHash32.output_size(), 4);
391 assert_eq!(HashAlgorithm::XxHash64.output_size(), 8);
392 assert_eq!(HashAlgorithm::XxHash3_64.output_size(), 8);
393 assert_eq!(HashAlgorithm::XxHash3_128.output_size(), 16);
394 }
395
396 #[test]
397 fn test_crc32() {
398 let data = b"hello world";
399 let result = hash(data, HashAlgorithm::Crc32);
400 assert_eq!(result.len(), 4);
401 let result2 = hash(data, HashAlgorithm::Crc32);
403 assert_eq!(result, result2);
404 }
405
406 #[test]
407 fn test_crc32c() {
408 let data = b"hello world";
409 let result = hash(data, HashAlgorithm::Crc32c);
410 assert_eq!(result.len(), 4);
411 }
412
413 #[test]
414 fn test_crc16() {
415 let data = b"hello world";
416 let result = hash(data, HashAlgorithm::Crc16);
417 assert_eq!(result.len(), 2);
418 }
419
420 #[test]
421 fn test_crc64() {
422 let data = b"hello world";
423 let result = hash(data, HashAlgorithm::Crc64);
424 assert_eq!(result.len(), 8);
425 }
426
427 #[test]
428 fn test_xxhash32() {
429 let data = b"hello world";
430 let result = hash(data, HashAlgorithm::XxHash32);
431 assert_eq!(result.len(), 4);
432 let result2 = hash(data, HashAlgorithm::XxHash32);
434 assert_eq!(result, result2);
435 }
436
437 #[test]
438 fn test_xxhash64() {
439 let data = b"hello world";
440 let result = hash(data, HashAlgorithm::XxHash64);
441 assert_eq!(result.len(), 8);
442 }
443
444 #[test]
445 fn test_xxhash3_64() {
446 let data = b"hello world";
447 let result = hash(data, HashAlgorithm::XxHash3_64);
448 assert_eq!(result.len(), 8);
449 }
450
451 #[test]
452 fn test_xxhash3_128() {
453 let data = b"hello world";
454 let result = hash(data, HashAlgorithm::XxHash3_128);
455 assert_eq!(result.len(), 16);
456 }
457
458 #[test]
459 fn test_xxhash_config_default() {
460 let config = XxHashConfig::default();
461 assert_eq!(config.seed, 0);
462 assert!(config.secret.is_none());
463 }
464
465 #[test]
466 fn test_xxhash_config_secret_too_short() {
467 let result = XxHashConfig::with_secret(0, vec![0u8; 100]);
468 assert!(result.is_err());
469 assert!(result.unwrap_err().contains("136 bytes"));
470 }
471
472 #[test]
473 fn test_xxhash_config_secret_valid() {
474 let result = XxHashConfig::with_secret(42, vec![0u8; 136]);
475 assert!(result.is_ok());
476 let config = result.unwrap();
477 assert_eq!(config.seed, 42);
478 assert_eq!(config.secret.as_ref().unwrap().len(), 136);
479 }
480
481 #[test]
482 fn test_hash_seed_changes_output() {
483 let data = b"test";
484 let h1 = hash_with_config(data, HashAlgorithm::XxHash64, &XxHashConfig::with_seed(0));
485 let h2 = hash_with_config(data, HashAlgorithm::XxHash64, &XxHashConfig::with_seed(42));
486 assert_ne!(h1, h2);
487 }
488
489 #[test]
490 fn test_backward_compatibility() {
491 let data = b"test";
492 let old = hash(data, HashAlgorithm::XxHash64);
493 let new = hash_with_config(data, HashAlgorithm::XxHash64, &XxHashConfig::default());
494 assert_eq!(old, new);
495 }
496
497 #[test]
498 fn test_xxhash3_with_seed() {
499 let data = b"test data for secret hashing";
500
501 let h1 = hash_with_config(data, HashAlgorithm::XxHash3_64, &XxHashConfig::with_seed(0));
503 let h2 = hash_with_config(
504 data,
505 HashAlgorithm::XxHash3_64,
506 &XxHashConfig::with_seed(123),
507 );
508 assert_ne!(h1, h2, "Different seeds should produce different hashes");
509
510 let h3 = hash_with_config(
512 data,
513 HashAlgorithm::XxHash3_64,
514 &XxHashConfig::with_seed(123),
515 );
516 assert_eq!(h2, h3, "Same seed should produce same hash");
517 }
518
519 #[test]
520 fn test_xxhash32_with_seed() {
521 let data = b"test";
522 let h1 = hash_with_config(data, HashAlgorithm::XxHash32, &XxHashConfig::with_seed(0));
523 let h2 = hash_with_config(data, HashAlgorithm::XxHash32, &XxHashConfig::with_seed(999));
524 assert_ne!(h1, h2);
525 }
526}