Skip to main content

blazehash_core/
algorithm.rs

1use digest::Digest;
2use std::fmt;
3use std::str::FromStr;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
6pub enum Algorithm {
7    #[default]
8    Blake3,
9    Sha256,
10    Sha512,
11    Sha3_256,
12    Sha1,
13    Md5,
14    Tiger,
15    Whirlpool,
16    Ssdeep,
17    Tlsh,
18    Crc32c,
19    Xxh3,
20    Shake128,
21    Shake256,
22    Blake2b,
23    Blake2s,
24    Sm3,
25    Streebog256,
26    Streebog512,
27    Ripemd160,
28    Sha512_256,
29    Sha512_224,
30    K12,
31    Adler32,
32    Crc64,
33}
34
35impl Algorithm {
36    pub fn all() -> &'static [Algorithm] {
37        &[
38            Algorithm::Blake3,
39            Algorithm::Sha256,
40            Algorithm::Sha512,
41            Algorithm::Sha3_256,
42            Algorithm::Sha1,
43            Algorithm::Md5,
44            Algorithm::Tiger,
45            Algorithm::Whirlpool,
46        ]
47    }
48
49    pub fn hashdeep_name(&self) -> &'static str {
50        match self {
51            Algorithm::Blake3 => "blake3",
52            Algorithm::Sha256 => "sha256",
53            Algorithm::Sha512 => "sha512",
54            Algorithm::Sha3_256 => "sha3-256",
55            Algorithm::Sha1 => "sha1",
56            Algorithm::Md5 => "md5",
57            Algorithm::Tiger => "tiger",
58            Algorithm::Whirlpool => "whirlpool",
59            Algorithm::Ssdeep => "ssdeep",
60            Algorithm::Tlsh => "tlsh",
61            Algorithm::Crc32c => "crc32c",
62            Algorithm::Xxh3 => "xxh3",
63            Algorithm::Shake128 => "shake128",
64            Algorithm::Shake256 => "shake256",
65            Algorithm::Blake2b => "blake2b",
66            Algorithm::Blake2s => "blake2s",
67            Algorithm::Sm3 => "sm3",
68            Algorithm::Streebog256 => "streebog256",
69            Algorithm::Streebog512 => "streebog512",
70            Algorithm::Ripemd160 => "ripemd160",
71            Algorithm::Sha512_256 => "sha512-256",
72            Algorithm::Sha512_224 => "sha512-224",
73            Algorithm::K12 => "k12",
74            Algorithm::Adler32 => "adler32",
75            Algorithm::Crc64 => "crc64",
76        }
77    }
78
79    pub fn is_fuzzy(&self) -> bool {
80        matches!(self, Algorithm::Ssdeep | Algorithm::Tlsh)
81    }
82
83    pub fn is_non_cryptographic(&self) -> bool {
84        matches!(self, Algorithm::Crc32c | Algorithm::Xxh3 | Algorithm::Adler32 | Algorithm::Crc64)
85    }
86
87    /// Returns true for algorithms that require reading the full file into memory
88    /// before hashing — either because they are non-cryptographic (CRC32C, XXH3)
89    /// or because they are XOFs (SHAKE-128, SHAKE-256) that cannot stream via
90    /// the `DynHasher` trait, or because they use non-standard APIs.
91    pub fn needs_full_read(&self) -> bool {
92        matches!(
93            self,
94            Algorithm::Crc32c
95                | Algorithm::Xxh3
96                | Algorithm::Shake128
97                | Algorithm::Shake256
98                | Algorithm::K12
99                | Algorithm::Adler32
100                | Algorithm::Crc64
101        )
102    }
103}
104
105impl fmt::Display for Algorithm {
106    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107        f.write_str(self.hashdeep_name())
108    }
109}
110
111impl FromStr for Algorithm {
112    type Err = anyhow::Error;
113    fn from_str(s: &str) -> Result<Self, Self::Err> {
114        match s.to_lowercase().as_str() {
115            "blake3" => Ok(Algorithm::Blake3),
116            "sha256" | "sha-256" => Ok(Algorithm::Sha256),
117            "sha512" | "sha-512" => Ok(Algorithm::Sha512),
118            "sha3-256" | "sha3_256" => Ok(Algorithm::Sha3_256),
119            "sha1" | "sha-1" => Ok(Algorithm::Sha1),
120            "md5" => Ok(Algorithm::Md5),
121            "tiger" => Ok(Algorithm::Tiger),
122            "whirlpool" => Ok(Algorithm::Whirlpool),
123            "ssdeep" => Ok(Algorithm::Ssdeep),
124            "tlsh" => Ok(Algorithm::Tlsh),
125            "crc32c" => Ok(Algorithm::Crc32c),
126            "xxh3" => Ok(Algorithm::Xxh3),
127            "shake128" => Ok(Algorithm::Shake128),
128            "shake256" => Ok(Algorithm::Shake256),
129            "blake2b" => Ok(Algorithm::Blake2b),
130            "blake2s" => Ok(Algorithm::Blake2s),
131            "sm3" => Ok(Algorithm::Sm3),
132            "streebog256" => Ok(Algorithm::Streebog256),
133            "streebog512" => Ok(Algorithm::Streebog512),
134            "ripemd160" | "ripemd-160" => Ok(Algorithm::Ripemd160),
135            "sha512-256" | "sha512_256" => Ok(Algorithm::Sha512_256),
136            "sha512-224" | "sha512_224" => Ok(Algorithm::Sha512_224),
137            "k12" | "kangaroo12" | "kangarootwelve" => Ok(Algorithm::K12),
138            "adler32" => Ok(Algorithm::Adler32),
139            "crc64" => Ok(Algorithm::Crc64),
140            other => anyhow::bail!("unknown algorithm: {other}"),
141        }
142    }
143}
144
145pub fn hash_bytes(algo: Algorithm, data: &[u8]) -> String {
146    match algo {
147        Algorithm::Blake3 => blake3::hash(data).to_hex().to_string(),
148        Algorithm::Sha256 => hex_digest::<sha2::Sha256>(data),
149        Algorithm::Sha512 => hex_digest::<sha2::Sha512>(data),
150        Algorithm::Sha3_256 => hex_digest::<sha3::Sha3_256>(data),
151        Algorithm::Sha1 => hex_digest::<sha1::Sha1>(data),
152        Algorithm::Md5 => hex_digest::<md5::Md5>(data),
153        Algorithm::Tiger => hex_digest::<tiger::Tiger>(data),
154        Algorithm::Whirlpool => hex_digest::<whirlpool::Whirlpool>(data),
155        Algorithm::Ssdeep => crate::fuzzy::ssdeep::compute(data),
156        Algorithm::Tlsh => crate::fuzzy::tlsh::compute(data).unwrap_or_default(),
157        Algorithm::Crc32c => {
158            let checksum = crc32c::crc32c(data);
159            format!("{checksum:08x}")
160        }
161        Algorithm::Xxh3 => {
162            use xxhash_rust::xxh3::xxh3_128;
163            let hash = xxh3_128(data);
164            format!("{hash:032x}")
165        }
166        Algorithm::Shake128 => {
167            use sha3::digest::{ExtendableOutput, XofReader};
168            let mut h = sha3::Shake128::default();
169            sha3::digest::Update::update(&mut h, data);
170            let mut reader = h.finalize_xof();
171            let mut buf = [0u8; 32];
172            reader.read(&mut buf);
173            hex::encode(buf)
174        }
175        Algorithm::Shake256 => {
176            use sha3::digest::{ExtendableOutput, XofReader};
177            let mut h = sha3::Shake256::default();
178            sha3::digest::Update::update(&mut h, data);
179            let mut reader = h.finalize_xof();
180            let mut buf = [0u8; 64];
181            reader.read(&mut buf);
182            hex::encode(buf)
183        }
184        Algorithm::Blake2b => hex_digest::<blake2::Blake2b512>(data),
185        Algorithm::Blake2s => hex_digest::<blake2::Blake2s256>(data),
186        Algorithm::Sm3 => hex_digest::<sm3::Sm3>(data),
187        Algorithm::Streebog256 => hex_digest::<streebog::Streebog256>(data),
188        Algorithm::Streebog512 => hex_digest::<streebog::Streebog512>(data),
189        Algorithm::Ripemd160 => hex_digest::<ripemd::Ripemd160>(data),
190        Algorithm::Sha512_256 => hex_digest::<sha2::Sha512_256>(data),
191        Algorithm::Sha512_224 => hex_digest::<sha2::Sha512_224>(data),
192        Algorithm::K12 => {
193            use tiny_keccak::Hasher;
194            let mut h = tiny_keccak::KangarooTwelve::new(b"");
195            h.update(data);
196            let mut out = [0u8; 32];
197            h.finalize(&mut out);
198            hex::encode(out)
199        }
200        Algorithm::Adler32 => {
201            use adler::Adler32;
202            let mut h = Adler32::new();
203            h.write_slice(data);
204            format!("{:08x}", h.checksum())
205        }
206        Algorithm::Crc64 => {
207            let checksum = crc::Crc::<u64>::new(&crc::CRC_64_ECMA_182).checksum(data);
208            format!("{checksum:016x}")
209        }
210    }
211}
212
213fn hex_digest<D: Digest>(data: &[u8]) -> String {
214    let mut hasher = D::new();
215    hasher.update(data);
216    hex::encode(hasher.finalize())
217}