1use sha2::{Sha224, Sha256, Sha384, Sha512, Digest};
2use sha3::{Sha3_224, Sha3_256, Sha3_384, Sha3_512, Keccak224, Keccak256, Keccak384, Keccak512};
3use blake2::{Blake2b512, Blake2s256};
4use blake3::Hasher as Blake3Hasher;
5use md5::Md5;
6use twox_hash::{XxHash32, XxHash64};
7use twox_hash::xxhash3_64::Hasher as Xxh3Hash64;
8use twox_hash::xxhash3_128::Hasher as Xxh3Hash128;
9use std::hash::Hasher;
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())
287 .expect("XXH3 secret validation should have been done in XxHashConfig::with_secret")
288 } else {
289 Xxh3Hash64::with_seed(config.seed)
290 };
291 hasher.write(data);
292 hasher.finish().to_be_bytes().to_vec()
293 }
294 HashAlgorithm::XxHash3_128 => {
295 let mut hasher = if let Some(ref secret) = config.secret {
296 Xxh3Hash128::with_seed_and_secret(config.seed, secret.as_slice())
297 .expect("XXH3 secret validation should have been done in XxHashConfig::with_secret")
298 } else {
299 Xxh3Hash128::with_seed(config.seed)
300 };
301 hasher.write(data);
302 hasher.finish_128().to_be_bytes().to_vec()
303 }
304 }
305}
306
307#[cfg(test)]
308mod tests {
309 use super::*;
310
311 #[test]
312 fn test_md5() {
313 let data = b"hello world";
314 let hash = hash(data, HashAlgorithm::Md5);
315 assert_eq!(hash.len(), 16);
316 assert_eq!(hex::encode(&hash), "5eb63bbbe01eeed093cb22bb8f5acdc3");
318 }
319
320 #[test]
321 fn test_sha256() {
322 let data = b"hello world";
323 let hash = hash(data, HashAlgorithm::Sha256);
324 assert_eq!(hash.len(), 32);
325 assert_eq!(
327 hex::encode(&hash),
328 "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
329 );
330 }
331
332 #[test]
333 fn test_sha512() {
334 let data = b"hello world";
335 let hash = hash(data, HashAlgorithm::Sha512);
336 assert_eq!(hash.len(), 64);
337 }
338
339 #[test]
340 fn test_sha3_256() {
341 let data = b"hello world";
342 let hash = hash(data, HashAlgorithm::Sha3_256);
343 assert_eq!(hash.len(), 32);
344 }
345
346 #[test]
347 fn test_blake2b() {
348 let data = b"hello world";
349 let hash = hash(data, HashAlgorithm::Blake2b);
350 assert_eq!(hash.len(), 64);
351 }
352
353 #[test]
354 fn test_blake2s() {
355 let data = b"hello world";
356 let hash = hash(data, HashAlgorithm::Blake2s);
357 assert_eq!(hash.len(), 32);
358 }
359
360 #[test]
361 fn test_blake3() {
362 let data = b"hello world";
363 let hash = hash(data, HashAlgorithm::Blake3);
364 assert_eq!(hash.len(), 32);
365 }
366
367 #[test]
368 fn test_empty_input() {
369 let data = b"";
370 let hash = hash(data, HashAlgorithm::Sha256);
371 assert_eq!(hash.len(), 32);
372 assert_eq!(
374 hex::encode(&hash),
375 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
376 );
377 }
378
379 #[test]
380 fn test_output_sizes() {
381 assert_eq!(HashAlgorithm::Md5.output_size(), 16);
382 assert_eq!(HashAlgorithm::Sha256.output_size(), 32);
383 assert_eq!(HashAlgorithm::Sha512.output_size(), 64);
384 assert_eq!(HashAlgorithm::Blake3.output_size(), 32);
385 assert_eq!(HashAlgorithm::Crc16.output_size(), 2);
386 assert_eq!(HashAlgorithm::Crc32.output_size(), 4);
387 assert_eq!(HashAlgorithm::Crc64.output_size(), 8);
388 assert_eq!(HashAlgorithm::XxHash32.output_size(), 4);
389 assert_eq!(HashAlgorithm::XxHash64.output_size(), 8);
390 assert_eq!(HashAlgorithm::XxHash3_64.output_size(), 8);
391 assert_eq!(HashAlgorithm::XxHash3_128.output_size(), 16);
392 }
393
394 #[test]
395 fn test_crc32() {
396 let data = b"hello world";
397 let result = hash(data, HashAlgorithm::Crc32);
398 assert_eq!(result.len(), 4);
399 let result2 = hash(data, HashAlgorithm::Crc32);
401 assert_eq!(result, result2);
402 }
403
404 #[test]
405 fn test_crc32c() {
406 let data = b"hello world";
407 let result = hash(data, HashAlgorithm::Crc32c);
408 assert_eq!(result.len(), 4);
409 }
410
411 #[test]
412 fn test_crc16() {
413 let data = b"hello world";
414 let result = hash(data, HashAlgorithm::Crc16);
415 assert_eq!(result.len(), 2);
416 }
417
418 #[test]
419 fn test_crc64() {
420 let data = b"hello world";
421 let result = hash(data, HashAlgorithm::Crc64);
422 assert_eq!(result.len(), 8);
423 }
424
425 #[test]
426 fn test_xxhash32() {
427 let data = b"hello world";
428 let result = hash(data, HashAlgorithm::XxHash32);
429 assert_eq!(result.len(), 4);
430 let result2 = hash(data, HashAlgorithm::XxHash32);
432 assert_eq!(result, result2);
433 }
434
435 #[test]
436 fn test_xxhash64() {
437 let data = b"hello world";
438 let result = hash(data, HashAlgorithm::XxHash64);
439 assert_eq!(result.len(), 8);
440 }
441
442 #[test]
443 fn test_xxhash3_64() {
444 let data = b"hello world";
445 let result = hash(data, HashAlgorithm::XxHash3_64);
446 assert_eq!(result.len(), 8);
447 }
448
449 #[test]
450 fn test_xxhash3_128() {
451 let data = b"hello world";
452 let result = hash(data, HashAlgorithm::XxHash3_128);
453 assert_eq!(result.len(), 16);
454 }
455
456 #[test]
457 fn test_xxhash_config_default() {
458 let config = XxHashConfig::default();
459 assert_eq!(config.seed, 0);
460 assert!(config.secret.is_none());
461 }
462
463 #[test]
464 fn test_xxhash_config_secret_too_short() {
465 let result = XxHashConfig::with_secret(0, vec![0u8; 100]);
466 assert!(result.is_err());
467 assert!(result.unwrap_err().contains("136 bytes"));
468 }
469
470 #[test]
471 fn test_xxhash_config_secret_valid() {
472 let result = XxHashConfig::with_secret(42, vec![0u8; 136]);
473 assert!(result.is_ok());
474 let config = result.unwrap();
475 assert_eq!(config.seed, 42);
476 assert_eq!(config.secret.as_ref().unwrap().len(), 136);
477 }
478
479 #[test]
480 fn test_hash_seed_changes_output() {
481 let data = b"test";
482 let h1 = hash_with_config(data, HashAlgorithm::XxHash64, &XxHashConfig::with_seed(0));
483 let h2 = hash_with_config(data, HashAlgorithm::XxHash64, &XxHashConfig::with_seed(42));
484 assert_ne!(h1, h2);
485 }
486
487 #[test]
488 fn test_backward_compatibility() {
489 let data = b"test";
490 let old = hash(data, HashAlgorithm::XxHash64);
491 let new = hash_with_config(data, HashAlgorithm::XxHash64, &XxHashConfig::default());
492 assert_eq!(old, new);
493 }
494
495 #[test]
496 fn test_xxhash3_with_seed() {
497 let data = b"test data for secret hashing";
498
499 let h1 = hash_with_config(data, HashAlgorithm::XxHash3_64, &XxHashConfig::with_seed(0));
501 let h2 = hash_with_config(data, HashAlgorithm::XxHash3_64, &XxHashConfig::with_seed(123));
502 assert_ne!(h1, h2, "Different seeds should produce different hashes");
503
504 let h3 = hash_with_config(data, HashAlgorithm::XxHash3_64, &XxHashConfig::with_seed(123));
506 assert_eq!(h2, h3, "Same seed should produce same hash");
507 }
508
509 #[test]
510 fn test_xxhash32_with_seed() {
511 let data = b"test";
512 let h1 = hash_with_config(data, HashAlgorithm::XxHash32, &XxHashConfig::with_seed(0));
513 let h2 = hash_with_config(data, HashAlgorithm::XxHash32, &XxHashConfig::with_seed(999));
514 assert_ne!(h1, h2);
515 }
516}