1use core::{fmt, mem};
2
3pub const DIGEST_LEN: usize = 32;
4
5pub trait HashingAlgorithm {
6 #[doc(hidden)]
7 fn hasher_type() -> sys::HasherType;
8}
9
10trait HashingAlgorithmExt: HashingAlgorithm {
11 fn init() -> sys::Hasher {
12 let mut hasher;
13 unsafe {
14 hasher = mem::zeroed();
15 sys::hasher_Init(&mut hasher, Self::hasher_type());
16 };
17 hasher
18 }
19 fn digest(data: &[u8]) -> [u8; DIGEST_LEN] {
20 let mut out = [0; DIGEST_LEN];
21 unsafe {
22 sys::hasher_Raw(
23 Self::hasher_type(),
24 data.as_ptr(),
25 data.len() as u64,
26 out.as_mut_ptr(),
27 )
28 }
29 out
30 }
31}
32
33impl<T> HashingAlgorithmExt for T where T: HashingAlgorithm {}
34
35macro_rules! hashing_algo {
36 ($name:ident, $ty:expr) => {
37 #[derive(Clone, Copy, Debug)]
38 pub struct $name;
39 impl HashingAlgorithm for $name {
40 fn hasher_type() -> sys::HasherType {
41 $ty
42 }
43 }
44 };
45}
46hashing_algo!(Blake, sys::HasherType_HASHER_BLAKE);
47hashing_algo!(Blake2b, sys::HasherType_HASHER_BLAKE2B);
48hashing_algo!(Blake2bPersonal, sys::HasherType_HASHER_BLAKE2B_PERSONAL);
49hashing_algo!(BlakeRipemd, sys::HasherType_HASHER_BLAKE_RIPEMD);
50hashing_algo!(Blaked, sys::HasherType_HASHER_BLAKED);
51hashing_algo!(GroestldTrunc, sys::HasherType_HASHER_GROESTLD_TRUNC);
52hashing_algo!(Sha2, sys::HasherType_HASHER_SHA2);
53hashing_algo!(Sha2Ripemd, sys::HasherType_HASHER_SHA2_RIPEMD);
54hashing_algo!(Sha2d, sys::HasherType_HASHER_SHA2D);
55hashing_algo!(Sha3, sys::HasherType_HASHER_SHA3);
56hashing_algo!(Sha3k, sys::HasherType_HASHER_SHA3K);
57hashing_algo!(NoHash, 0);
58
59pub struct Hasher {
60 hasher: sys::Hasher,
61}
62
63impl Hasher {
64 #[inline]
65 fn new(hasher: sys::Hasher) -> Self {
66 Self { hasher }
67 }
68 pub fn init<H: HashingAlgorithm>() -> Self {
69 Self::new(H::init())
70 }
71 pub fn update<D: AsRef<[u8]>>(&mut self, data: D) {
72 let data = data.as_ref();
73 unsafe { sys::hasher_Update(&mut self.hasher, data.as_ptr(), data.len() as u64) }
74 }
75 pub fn reset(&mut self) {
76 unsafe { sys::hasher_Reset(&mut self.hasher) }
77 }
78 pub fn finish(&mut self) -> Digest {
79 let mut out = [0; DIGEST_LEN];
80 unsafe {
81 sys::hasher_Final(&mut self.hasher, out.as_mut_ptr());
82 }
83 Digest::from_bytes(out)
84 }
85}
86
87impl fmt::Debug for Hasher {
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 f.debug_struct("Hasher").finish()
91 }
92}
93
94pub fn digest<H: HashingAlgorithm, D: AsRef<[u8]>>(data: D) -> Digest {
95 Digest::from_bytes(H::digest(data.as_ref()))
96}
97
98#[derive(Clone, Copy, PartialEq, Eq)]
99pub struct Digest {
100 bytes: [u8; DIGEST_LEN],
101}
102
103impl Digest {
104 #[inline]
105 pub fn from_bytes(bytes: [u8; DIGEST_LEN]) -> Self {
106 Self { bytes }
107 }
108 pub fn as_bytes(&self) -> &[u8; 32] {
109 &self.bytes
110 }
111}
112
113impl AsRef<[u8]> for Digest {
114 fn as_ref(&self) -> &[u8] {
115 self.as_bytes()
116 }
117}
118
119impl fmt::Debug for Digest {
120 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121 f.debug_tuple("Digest")
122 .field(&hex::encode(&self.bytes))
123 .finish()
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130
131 fn sha3_test_vector(input: impl AsRef<[u8]>, expected_hex: impl AsRef<str>) {
132 let digest = digest::<Sha3, _>(input);
133 assert_eq!(
134 hex::encode(digest.as_bytes()),
135 expected_hex.as_ref().to_lowercase()
136 )
137 }
138
139 #[test]
140 fn sha3_256_test_vectors() {
141 sha3_test_vector(
142 b"abc",
143 "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532",
144 );
145 sha3_test_vector(
146 b"",
147 "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a",
148 );
149 sha3_test_vector(
150 b"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
151 "41c0dba2a9d6240849100376a8235e2c82e1b9998a999e21db32dd97496d3376",
152 );
153 sha3_test_vector(
154 b"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
155 "916f6061fe879741ca6469b43971dfdb28b1a32dc36cb3254e812be27aad1d18"
156 );
157 }
158
159 #[test]
160 fn sha3_256_multi_threaded() {
161 let mut children = Vec::new();
162 for _ in 0..10 {
163 children.push(std::thread::spawn(|| {
164 sha3_test_vector(
165 b"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
166 "41c0dba2a9d6240849100376a8235e2c82e1b9998a999e21db32dd97496d3376",
167 );
168 }))
169 }
170 for child in children {
171 child.join().unwrap();
172 }
173 }
174}