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 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}