1pub use self::DynamicContext as Context;
11use sha2::Digest;
12
13#[cfg(feature = "zero_hash_cache")]
14use lazy_static::lazy_static;
15
16pub const HASH_LEN: usize = 32;
18
19pub fn hash(input: &[u8]) -> Vec<u8> {
21 DynamicImpl::best().hash(input)
22}
23
24pub fn hash_fixed(input: &[u8]) -> [u8; HASH_LEN] {
28 DynamicImpl::best().hash_fixed(input)
29}
30
31pub fn hash32_concat(h1: &[u8], h2: &[u8]) -> [u8; 32] {
33 let mut ctxt = DynamicContext::new();
34 ctxt.update(h1);
35 ctxt.update(h2);
36 ctxt.finalize()
37}
38
39pub trait Sha256Context {
41 fn new() -> Self;
42
43 fn update(&mut self, bytes: &[u8]);
44
45 fn finalize(self) -> [u8; HASH_LEN];
46}
47
48pub trait Sha256 {
50 type Context: Sha256Context;
51
52 fn hash(&self, input: &[u8]) -> Vec<u8>;
53
54 fn hash_fixed(&self, input: &[u8]) -> [u8; HASH_LEN];
55}
56
57struct Sha2CrateImpl;
59
60impl Sha256Context for sha2::Sha256 {
61 fn new() -> Self {
62 sha2::Digest::new()
63 }
64
65 fn update(&mut self, bytes: &[u8]) {
66 sha2::Digest::update(self, bytes)
67 }
68
69 fn finalize(self) -> [u8; HASH_LEN] {
70 sha2::Digest::finalize(self).into()
71 }
72}
73
74impl Sha256 for Sha2CrateImpl {
75 type Context = sha2::Sha256;
76
77 fn hash(&self, input: &[u8]) -> Vec<u8> {
78 Self::Context::digest(input).into_iter().collect()
79 }
80
81 fn hash_fixed(&self, input: &[u8]) -> [u8; HASH_LEN] {
82 Self::Context::digest(input).into()
83 }
84}
85
86pub struct RingImpl;
88
89impl Sha256Context for ring::digest::Context {
90 fn new() -> Self {
91 Self::new(&ring::digest::SHA256)
92 }
93
94 fn update(&mut self, bytes: &[u8]) {
95 self.update(bytes)
96 }
97
98 fn finalize(self) -> [u8; HASH_LEN] {
99 let mut output = [0; HASH_LEN];
100 output.copy_from_slice(self.finish().as_ref());
101 output
102 }
103}
104
105impl Sha256 for RingImpl {
106 type Context = ring::digest::Context;
107
108 fn hash(&self, input: &[u8]) -> Vec<u8> {
109 ring::digest::digest(&ring::digest::SHA256, input)
110 .as_ref()
111 .into()
112 }
113
114 fn hash_fixed(&self, input: &[u8]) -> [u8; HASH_LEN] {
115 let mut ctxt = Self::Context::new(&ring::digest::SHA256);
116 ctxt.update(input);
117 ctxt.finalize()
118 }
119}
120
121pub enum DynamicImpl {
123 Sha2,
124 Ring,
125}
126
127#[cfg(target_arch = "x86_64")]
131cpufeatures::new!(x86_sha_extensions, "sha", "sse2", "ssse3", "sse4.1");
132
133#[inline(always)]
134pub fn have_sha_extensions() -> bool {
135 #[cfg(target_arch = "x86_64")]
136 return x86_sha_extensions::get();
137
138 #[cfg(not(target_arch = "x86_64"))]
139 return false;
140}
141
142impl DynamicImpl {
143 #[inline(always)]
145 pub fn best() -> Self {
146 if have_sha_extensions() {
147 Self::Sha2
148 } else {
149 Self::Ring
150 }
151 }
152}
153
154impl Sha256 for DynamicImpl {
155 type Context = DynamicContext;
156
157 #[inline(always)]
158 fn hash(&self, input: &[u8]) -> Vec<u8> {
159 match self {
160 Self::Sha2 => Sha2CrateImpl.hash(input),
161 Self::Ring => RingImpl.hash(input),
162 }
163 }
164
165 #[inline(always)]
166 fn hash_fixed(&self, input: &[u8]) -> [u8; HASH_LEN] {
167 match self {
168 Self::Sha2 => Sha2CrateImpl.hash_fixed(input),
169 Self::Ring => RingImpl.hash_fixed(input),
170 }
171 }
172}
173
174pub enum DynamicContext {
178 Sha2(sha2::Sha256),
179 Ring(ring::digest::Context),
180}
181
182impl Sha256Context for DynamicContext {
183 fn new() -> Self {
184 match DynamicImpl::best() {
185 DynamicImpl::Sha2 => Self::Sha2(Sha256Context::new()),
186 DynamicImpl::Ring => Self::Ring(Sha256Context::new()),
187 }
188 }
189
190 fn update(&mut self, bytes: &[u8]) {
191 match self {
192 Self::Sha2(ctxt) => Sha256Context::update(ctxt, bytes),
193 Self::Ring(ctxt) => Sha256Context::update(ctxt, bytes),
194 }
195 }
196
197 fn finalize(self) -> [u8; HASH_LEN] {
198 match self {
199 Self::Sha2(ctxt) => Sha256Context::finalize(ctxt),
200 Self::Ring(ctxt) => Sha256Context::finalize(ctxt),
201 }
202 }
203}
204
205#[cfg(feature = "zero_hash_cache")]
207pub const ZERO_HASHES_MAX_INDEX: usize = 48;
208
209#[cfg(feature = "zero_hash_cache")]
210lazy_static! {
211 pub static ref ZERO_HASHES: Vec<Vec<u8>> = {
213 let mut hashes = vec![vec![0; 32]; ZERO_HASHES_MAX_INDEX + 1];
214
215 for i in 0..ZERO_HASHES_MAX_INDEX {
216 hashes[i + 1] = hash32_concat(&hashes[i], &hashes[i])[..].to_vec();
217 }
218
219 hashes
220 };
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226 use rustc_hex::FromHex;
227
228 #[cfg(target_arch = "wasm32")]
229 use wasm_bindgen_test::*;
230
231 #[cfg_attr(not(target_arch = "wasm32"), test)]
232 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
233 fn test_hashing() {
234 let input: Vec<u8> = b"hello world".as_ref().into();
235
236 let output = hash(input.as_ref());
237 let expected_hex = "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9";
238 let expected: Vec<u8> = expected_hex.from_hex().unwrap();
239 assert_eq!(expected, output);
240 }
241
242 #[cfg(feature = "zero_hash_cache")]
243 mod zero_hash {
244 use super::*;
245
246 #[test]
247 fn zero_hash_zero() {
248 assert_eq!(ZERO_HASHES[0], vec![0; 32]);
249 }
250 }
251}