1#![forbid(unsafe_code)]
2#![cfg_attr(
3 test,
4 allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)
5)]
6#[cfg(not(any(
62 feature = "sha2",
63 feature = "sha3",
64 feature = "blake3"
65)))]
66compile_error!(
67 "hsh-digest requires at least one algorithm feature: `sha2`, `sha3`, or `blake3`."
68);
69
70pub mod error;
71
72pub use error::DigestError;
73
74#[cfg(any(feature = "sha2", feature = "sha3"))]
80use digest::Digest as _;
81
82#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
86#[non_exhaustive]
87pub enum Algorithm {
88 #[cfg(feature = "sha2")]
90 Sha256,
91 #[cfg(feature = "sha2")]
93 Sha384,
94 #[cfg(feature = "sha2")]
96 Sha512,
97 #[cfg(feature = "sha3")]
99 Sha3_256,
100 #[cfg(feature = "sha3")]
102 Sha3_384,
103 #[cfg(feature = "sha3")]
105 Sha3_512,
106 #[cfg(feature = "blake3")]
108 Blake3,
109}
110
111impl Algorithm {
112 #[must_use]
114 pub const fn output_len(self) -> usize {
115 match self {
116 #[cfg(feature = "sha2")]
117 Self::Sha256 => 32,
118 #[cfg(feature = "sha2")]
119 Self::Sha384 => 48,
120 #[cfg(feature = "sha2")]
121 Self::Sha512 => 64,
122 #[cfg(feature = "sha3")]
123 Self::Sha3_256 => 32,
124 #[cfg(feature = "sha3")]
125 Self::Sha3_384 => 48,
126 #[cfg(feature = "sha3")]
127 Self::Sha3_512 => 64,
128 #[cfg(feature = "blake3")]
129 Self::Blake3 => 32,
130 }
131 }
132
133 #[must_use]
135 pub const fn id(self) -> &'static str {
136 match self {
137 #[cfg(feature = "sha2")]
138 Self::Sha256 => "sha256",
139 #[cfg(feature = "sha2")]
140 Self::Sha384 => "sha384",
141 #[cfg(feature = "sha2")]
142 Self::Sha512 => "sha512",
143 #[cfg(feature = "sha3")]
144 Self::Sha3_256 => "sha3-256",
145 #[cfg(feature = "sha3")]
146 Self::Sha3_384 => "sha3-384",
147 #[cfg(feature = "sha3")]
148 Self::Sha3_512 => "sha3-512",
149 #[cfg(feature = "blake3")]
150 Self::Blake3 => "blake3",
151 }
152 }
153}
154
155pub fn hash(
165 algorithm: Algorithm,
166 data: &[u8],
167) -> Result<Vec<u8>, DigestError> {
168 let mut h = Hasher::new(algorithm)?;
169 h.update(data);
170 Ok(h.finalize())
171}
172
173pub struct Hasher {
176 inner: HasherInner,
177}
178
179impl std::fmt::Debug for Hasher {
180 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
181 f.debug_struct("Hasher")
182 .field("algorithm", &self.algorithm())
183 .finish()
184 }
185}
186
187#[allow(clippy::large_enum_variant)]
192enum HasherInner {
193 #[cfg(feature = "sha2")]
194 Sha256(sha2::Sha256),
195 #[cfg(feature = "sha2")]
196 Sha384(sha2::Sha384),
197 #[cfg(feature = "sha2")]
198 Sha512(sha2::Sha512),
199 #[cfg(feature = "sha3")]
200 Sha3_256(sha3::Sha3_256),
201 #[cfg(feature = "sha3")]
202 Sha3_384(sha3::Sha3_384),
203 #[cfg(feature = "sha3")]
204 Sha3_512(sha3::Sha3_512),
205 #[cfg(feature = "blake3")]
206 Blake3(blake3::Hasher),
207}
208
209impl Hasher {
210 pub fn new(algorithm: Algorithm) -> Result<Self, DigestError> {
218 let inner = match algorithm {
219 #[cfg(feature = "sha2")]
220 Algorithm::Sha256 => {
221 HasherInner::Sha256(sha2::Sha256::new())
222 }
223 #[cfg(feature = "sha2")]
224 Algorithm::Sha384 => {
225 HasherInner::Sha384(sha2::Sha384::new())
226 }
227 #[cfg(feature = "sha2")]
228 Algorithm::Sha512 => {
229 HasherInner::Sha512(sha2::Sha512::new())
230 }
231 #[cfg(feature = "sha3")]
232 Algorithm::Sha3_256 => {
233 HasherInner::Sha3_256(sha3::Sha3_256::new())
234 }
235 #[cfg(feature = "sha3")]
236 Algorithm::Sha3_384 => {
237 HasherInner::Sha3_384(sha3::Sha3_384::new())
238 }
239 #[cfg(feature = "sha3")]
240 Algorithm::Sha3_512 => {
241 HasherInner::Sha3_512(sha3::Sha3_512::new())
242 }
243 #[cfg(feature = "blake3")]
244 Algorithm::Blake3 => {
245 HasherInner::Blake3(blake3::Hasher::new())
246 }
247 };
248 Ok(Self { inner })
249 }
250
251 #[must_use]
253 pub fn algorithm(&self) -> Algorithm {
254 match &self.inner {
255 #[cfg(feature = "sha2")]
256 HasherInner::Sha256(_) => Algorithm::Sha256,
257 #[cfg(feature = "sha2")]
258 HasherInner::Sha384(_) => Algorithm::Sha384,
259 #[cfg(feature = "sha2")]
260 HasherInner::Sha512(_) => Algorithm::Sha512,
261 #[cfg(feature = "sha3")]
262 HasherInner::Sha3_256(_) => Algorithm::Sha3_256,
263 #[cfg(feature = "sha3")]
264 HasherInner::Sha3_384(_) => Algorithm::Sha3_384,
265 #[cfg(feature = "sha3")]
266 HasherInner::Sha3_512(_) => Algorithm::Sha3_512,
267 #[cfg(feature = "blake3")]
268 HasherInner::Blake3(_) => Algorithm::Blake3,
269 }
270 }
271
272 pub fn update(&mut self, bytes: &[u8]) {
274 match &mut self.inner {
275 #[cfg(feature = "sha2")]
276 HasherInner::Sha256(h) => h.update(bytes),
277 #[cfg(feature = "sha2")]
278 HasherInner::Sha384(h) => h.update(bytes),
279 #[cfg(feature = "sha2")]
280 HasherInner::Sha512(h) => h.update(bytes),
281 #[cfg(feature = "sha3")]
282 HasherInner::Sha3_256(h) => h.update(bytes),
283 #[cfg(feature = "sha3")]
284 HasherInner::Sha3_384(h) => h.update(bytes),
285 #[cfg(feature = "sha3")]
286 HasherInner::Sha3_512(h) => h.update(bytes),
287 #[cfg(feature = "blake3")]
288 HasherInner::Blake3(h) => {
289 let _ = h.update(bytes);
290 }
291 }
292 }
293
294 #[must_use]
296 pub fn finalize(self) -> Vec<u8> {
297 match self.inner {
298 #[cfg(feature = "sha2")]
299 HasherInner::Sha256(h) => h.finalize().to_vec(),
300 #[cfg(feature = "sha2")]
301 HasherInner::Sha384(h) => h.finalize().to_vec(),
302 #[cfg(feature = "sha2")]
303 HasherInner::Sha512(h) => h.finalize().to_vec(),
304 #[cfg(feature = "sha3")]
305 HasherInner::Sha3_256(h) => h.finalize().to_vec(),
306 #[cfg(feature = "sha3")]
307 HasherInner::Sha3_384(h) => h.finalize().to_vec(),
308 #[cfg(feature = "sha3")]
309 HasherInner::Sha3_512(h) => h.finalize().to_vec(),
310 #[cfg(feature = "blake3")]
311 HasherInner::Blake3(h) => h.finalize().as_bytes().to_vec(),
312 }
313 }
314}
315
316#[must_use]
322pub fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
323 use subtle::ConstantTimeEq;
324 if a.len() != b.len() {
325 return false;
326 }
327 bool::from(a.ct_eq(b))
328}