1use crate::bases::*;
37use crate::rand::UnspecifiedRandError;
38#[cfg(target_arch = "wasm32")]
39use cryptoxide::{digest::Digest, sha2::Sha512};
40#[cfg(not(target_arch = "wasm32"))]
41use ring::digest;
42use std::{
43 fmt::{Debug, Display, Error, Formatter},
44 str::FromStr,
45};
46
47#[derive(
51 Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd, zerocopy::AsBytes, zerocopy::FromBytes,
52)]
53#[repr(transparent)]
54pub struct Hash64(pub [u8; 64]);
55
56impl AsRef<[u8]> for Hash64 {
57 fn as_ref(&self) -> &[u8] {
58 &self.0[..]
59 }
60}
61
62impl Display for Hash64 {
63 fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
64 write!(f, "{}", self.to_hex())
65 }
66}
67
68impl Debug for Hash64 {
69 fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
70 write!(f, "Hash64({})", self)
71 }
72}
73
74impl Default for Hash64 {
75 fn default() -> Hash64 {
76 Hash64([0; 64])
77 }
78}
79
80impl FromStr for Hash64 {
81 type Err = BaseConversionError;
82
83 fn from_str(s: &str) -> Result<Self, Self::Err> {
84 Hash64::from_hex(s)
85 }
86}
87
88impl Hash64 {
89 pub const SIZE_IN_BYTES: usize = 64;
91
92 #[inline]
94 pub fn random() -> Result<Self, UnspecifiedRandError> {
95 let mut random_bytes = [0u8; 64];
96 crate::rand::gen_random_bytes(&mut random_bytes).map_err(|_| UnspecifiedRandError)?;
97 Ok(Hash64(random_bytes))
98 }
99
100 #[cfg(target_arch = "wasm32")]
101 #[cfg(not(tarpaulin_include))]
102 pub fn sha512(data: &[u8]) -> Hash64 {
104 let mut hasher = Sha512::new();
105 hasher.input(data);
106 let mut hash_buffer = [0u8; 64];
107 hasher.result(&mut hash_buffer);
108 Hash64(hash_buffer)
109 }
110 #[cfg(not(target_arch = "wasm32"))]
111 pub fn sha512(datas: &[u8]) -> Hash64 {
113 let mut hash_buffer = [0u8; 64];
114 hash_buffer.copy_from_slice(digest::digest(&digest::SHA512, datas).as_ref());
115 Hash64(hash_buffer)
116 }
117
118 #[cfg(target_arch = "wasm32")]
119 #[cfg(not(tarpaulin_include))]
120 pub fn sha512_multipart(data_parts: &[&[u8]]) -> Hash64 {
122 let mut hasher = Sha512::new();
123 for data in data_parts {
124 hasher.input(data);
125 }
126 let mut hash_buffer = [0u8; 64];
127 hasher.result(&mut hash_buffer);
128 Hash64(hash_buffer)
129 }
130 #[cfg(not(target_arch = "wasm32"))]
131 pub fn sha512_multipart(data_parts: &[&[u8]]) -> Hash64 {
133 let mut ctx = digest::Context::new(&digest::SHA512);
134 for data in data_parts {
135 ctx.update(data);
136 }
137 let mut hash_buffer = [0u8; 64];
138 hash_buffer.copy_from_slice(ctx.finish().as_ref());
139 Hash64(hash_buffer)
140 }
141
142 pub fn to_bytes_vector(&self) -> Vec<u8> {
144 self.0.to_vec()
145 }
146
147 pub fn to_hex(&self) -> String {
149 let strings: Vec<String> = self.0.iter().map(|b| format!("{:02X}", b)).collect();
150
151 strings.join("")
152 }
153
154 #[inline]
159 pub fn from_hex(text: &str) -> Result<Hash64, BaseConversionError> {
160 Ok(Hash64(b16::str_hex_to_64bytes(text)?))
161 }
162
163 pub const fn max() -> Hash64 {
167 Hash64([255; 64])
168 }
169}
170
171#[cfg(test)]
172mod tests {
173
174 use super::*;
175 use unwrap::unwrap;
176
177 #[test]
178 fn test_hash_random() {
179 let hash1 = Hash64::random();
180 let hash2 = Hash64::random();
181 assert_ne!(hash1, hash2);
182 }
183
184 #[test]
185 fn test_hash_debug() {
186 assert_eq!(
187 "Hash64(00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000)".to_owned(),
188 format!("{:?}", Hash64::default()),
189 );
190 }
191
192 #[test]
193 fn test_hash_to_bytes() {
194 assert_eq!(
195 vec![
196 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
197 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
198 0, 0, 0, 0, 0, 0, 0, 0
199 ],
200 Hash64::default().to_bytes_vector(),
201 );
202 }
203
204 #[test]
205 fn test_hash_computation() {
206 assert_eq!(
207 unwrap!(Hash64::from_hex(
208 "9B71D224BD62F3785D96D46AD3EA3D73319BFBC2890CAADAE2DFF72519673CA72323C3D99BA5C11D7C7ACC6E14B8C5DA0C4663475C2E5C3ADEF46F73BCDEC043"
209 )),
210 Hash64::sha512(b"hello"),
211 );
212 }
213
214 #[test]
215 fn test_hash_from_hex() {
216 assert_eq!(
217 Ok(Hash64::default()),
218 Hash64::from_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
219 );
220 assert_eq!(
221 Err(BaseConversionError::InvalidLength {
222 expected: 128,
223 found: 130,
224 }),
225 Hash64::from_hex("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
226 );
227 assert_eq!(
228 Err(BaseConversionError::InvalidCharacter {
229 character: '_',
230 offset: 0,
231 }),
232 Hash64::from_hex("_0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
233 );
234 assert_eq!(
235 Err(BaseConversionError::InvalidCharacter {
236 character: '_',
237 offset: 1,
238 }),
239 Hash64::from_hex("0_000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
240 );
241 }
242}