eth2_hashing/
lib.rs

1//! Optimized SHA256 for use in Ethereum 2.0.
2//!
3//! The initial purpose of this crate was to provide an abstraction over the hash function used in
4//! Ethereum 2.0. The hash function changed during the specification process, so defining it once in
5//! this crate made it easy to replace.
6//!
7//! Now this crate serves primarily as a wrapper over two SHA256 crates: `sha2` and `ring` –
8//! which it switches between at runtime based on the availability of SHA intrinsics.
9
10pub use self::DynamicContext as Context;
11use sha2::Digest;
12
13#[cfg(feature = "zero_hash_cache")]
14use lazy_static::lazy_static;
15
16/// Length of a SHA256 hash in bytes.
17pub const HASH_LEN: usize = 32;
18
19/// Returns the digest of `input` using the best available implementation.
20pub fn hash(input: &[u8]) -> Vec<u8> {
21    DynamicImpl::best().hash(input)
22}
23
24/// Hash function returning a fixed-size array (to save on allocations).
25///
26/// Uses the best available implementation based on CPU features.
27pub fn hash_fixed(input: &[u8]) -> [u8; HASH_LEN] {
28    DynamicImpl::best().hash_fixed(input)
29}
30
31/// Compute the hash of two slices concatenated.
32pub 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
39/// Context trait for abstracting over implementation contexts.
40pub trait Sha256Context {
41    fn new() -> Self;
42
43    fn update(&mut self, bytes: &[u8]);
44
45    fn finalize(self) -> [u8; HASH_LEN];
46}
47
48/// Top-level trait implemented by both `sha2` and `ring` implementations.
49pub 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
57/// Implementation of SHA256 using the `sha2` crate (fastest on CPUs with SHA extensions).
58struct 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
86/// Implementation of SHA256 using the `ring` crate (fastest on CPUs without SHA extensions).
87pub 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
121/// Default dynamic implementation that switches between available implementations.
122pub enum DynamicImpl {
123    Sha2,
124    Ring,
125}
126
127// Runtime latch for detecting the availability of SHA extensions on x86_64.
128//
129// Inspired by the runtime switch within the `sha2` crate itself.
130#[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    /// Choose the best available implementation based on the currently executing CPU.
144    #[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
174/// Context encapsulating all implemenation contexts.
175///
176/// This enum ends up being 8 bytes larger than the largest inner context.
177pub 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/// The max index that can be used with `ZERO_HASHES`.
206#[cfg(feature = "zero_hash_cache")]
207pub const ZERO_HASHES_MAX_INDEX: usize = 48;
208
209#[cfg(feature = "zero_hash_cache")]
210lazy_static! {
211    /// Cached zero hashes where `ZERO_HASHES[i]` is the hash of a Merkle tree with 2^i zero leaves.
212    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}