Skip to main content

chuchi_crypto/hash/
mod.rs

1//! Contains structs used for hashing.
2//!
3//! ## Note
4//! **Do not** use this hasher for hashing password
5//! or other sensitive data since this hash does not
6//! use any salt, it is vulnerable to a rainbow table
7//! attack.
8
9#[cfg(feature = "b64")]
10use crate::error::DecodeError;
11use crate::error::TryFromError;
12
13use std::convert::{TryFrom, TryInto};
14use std::fmt;
15
16use blake2::{Blake2b512, Digest};
17
18#[cfg(feature = "b64")]
19use base64::engine::{Engine, general_purpose::URL_SAFE_NO_PAD};
20
21pub fn hash(data: impl AsRef<[u8]>) -> Hash {
22	Hasher::hash(data)
23}
24
25pub struct Hasher {
26	inner: Blake2b512,
27}
28
29impl Hasher {
30	pub fn new() -> Self {
31		Self {
32			inner: Blake2b512::new(),
33		}
34	}
35
36	pub fn update(&mut self, data: impl AsRef<[u8]>) {
37		self.inner.update(data);
38	}
39
40	pub fn finalize(self) -> Hash {
41		let arr = self.inner.finalize();
42		Hash { bytes: arr.into() }
43	}
44
45	pub fn hash(data: impl AsRef<[u8]>) -> Hash {
46		let mut hasher = Hasher::new();
47		hasher.update(data);
48		hasher.finalize()
49	}
50}
51
52#[derive(Clone, PartialEq, Eq)]
53pub struct Hash {
54	bytes: [u8; 64],
55}
56
57impl Hash {
58	pub const LEN: usize = 64;
59
60	/// ## Panics
61	/// if the slice is not 64 bytes long.
62	pub fn from_slice(slice: &[u8]) -> Self {
63		slice.try_into().unwrap()
64	}
65
66	pub fn to_bytes(&self) -> [u8; 64] {
67		self.bytes
68	}
69}
70
71#[cfg(not(feature = "b64"))]
72impl fmt::Debug for Hash {
73	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74		f.debug_tuple("Hash").field(&self.as_ref()).finish()
75	}
76}
77
78#[cfg(feature = "b64")]
79impl fmt::Debug for Hash {
80	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81		f.debug_tuple("Hash").field(&self.to_string()).finish()
82	}
83}
84
85#[cfg(feature = "b64")]
86impl fmt::Display for Hash {
87	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88		base64::display::Base64Display::new(self.as_ref(), &URL_SAFE_NO_PAD)
89			.fmt(f)
90	}
91}
92
93impl From<[u8; 64]> for Hash {
94	fn from(bytes: [u8; 64]) -> Self {
95		Self { bytes }
96	}
97}
98
99impl TryFrom<&[u8]> for Hash {
100	type Error = TryFromError;
101
102	fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
103		<[u8; 64]>::try_from(v)
104			.map_err(TryFromError::from_any)
105			.map(Self::from)
106	}
107}
108
109#[cfg(feature = "b64")]
110impl crate::FromStr for Hash {
111	type Err = DecodeError;
112
113	fn from_str(s: &str) -> Result<Self, Self::Err> {
114		if s.len() != crate::calculate_b64_len(Self::LEN) {
115			return Err(DecodeError::InvalidLength);
116		}
117
118		let mut bytes = [0u8; Self::LEN];
119		URL_SAFE_NO_PAD
120			.decode_slice_unchecked(s, &mut bytes)
121			.map_err(DecodeError::inv_bytes)
122			.and_then(|_| {
123				Self::try_from(bytes.as_ref()).map_err(DecodeError::inv_bytes)
124			})
125	}
126}
127
128impl AsRef<[u8]> for Hash {
129	fn as_ref(&self) -> &[u8] {
130		&self.bytes
131	}
132}
133
134#[cfg(all(feature = "b64", feature = "serde"))]
135mod impl_serde {
136
137	use super::*;
138
139	use std::borrow::Cow;
140	use std::str::FromStr;
141
142	use _serde::de::Error;
143	use _serde::{Deserialize, Deserializer, Serialize, Serializer};
144
145	impl Serialize for Hash {
146		fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
147		where
148			S: Serializer,
149		{
150			serializer.collect_str(&self)
151		}
152	}
153
154	impl<'de> Deserialize<'de> for Hash {
155		fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
156		where
157			D: Deserializer<'de>,
158		{
159			let s: Cow<'_, str> = Deserialize::deserialize(deserializer)?;
160			Self::from_str(s.as_ref()).map_err(D::Error::custom)
161		}
162	}
163}
164
165#[cfg(test)]
166mod tests {
167
168	use super::*;
169
170	#[test]
171	fn hash_something() {
172		let bytes: Vec<u8> = (0..=255).collect();
173
174		let hash = Hasher::hash(bytes);
175
176		let hash_bytes = [
177			30, 204, 137, 111, 52, 211, 249, 202, 196, 132, 199, 63, 117, 246,
178			165, 251, 88, 238, 103, 132, 190, 65, 179, 95, 70, 6, 123, 156,
179			101, 198, 58, 103, 148, 211, 215, 68, 17, 44, 101, 63, 115, 221,
180			125, 235, 102, 102, 32, 76, 90, 155, 250, 91, 70, 8, 31, 193, 15,
181			219, 231, 136, 79, 165, 203, 248,
182		];
183		assert_eq!(hash.to_bytes(), hash_bytes);
184	}
185
186	#[test]
187	#[cfg(feature = "b64")]
188	fn hash_b64() {
189		let bytes: Vec<u8> = (0..=255).collect();
190
191		let hash = Hasher::hash(bytes);
192
193		assert_eq!(
194			hash.to_string(),
195			"HsyJbzTT-crEhMc_dfal-1juZ4S-QbNfRgZ7nGXGOme\
196			U09dEESxlP3PdfetmZiBMWpv6W0YIH8EP2-eIT6XL-A"
197		);
198	}
199}