//! Optimized SHA256 for use in Ethereum 2.0. //! //! The initial purpose of this crate was to provide an abstraction over the hash function used in //! Ethereum 2.0. The hash function changed during the specification process, so defining it once in //! this crate made it easy to replace. //! //! Now this crate serves primarily as a wrapper over two SHA256 crates: `sha2` and `ring` – //! which it switches between at runtime based on the availability of SHA intrinsics. pub use self::DynamicContext as Context; use sha2::Digest; #[cfg(feature = "zero_hash_cache")] use lazy_static::lazy_static; /// Length of a SHA256 hash in bytes. pub const HASH_LEN: usize = 32; /// Returns the digest of `input` using the best available implementation. pub fn hash(input: &[u8]) -> Vec { DynamicImpl::best().hash(input) } /// Hash function returning a fixed-size array (to save on allocations). /// /// Uses the best available implementation based on CPU features. pub fn hash_fixed(input: &[u8]) -> [u8; HASH_LEN] { DynamicImpl::best().hash_fixed(input) } /// Compute the hash of two slices concatenated. pub fn hash32_concat(h1: &[u8], h2: &[u8]) -> [u8; 32] { let mut ctxt = DynamicContext::new(); ctxt.update(h1); ctxt.update(h2); ctxt.finalize() } /// Context trait for abstracting over implementation contexts. pub trait Sha256Context { fn new() -> Self; fn update(&mut self, bytes: &[u8]); fn finalize(self) -> [u8; HASH_LEN]; } /// Top-level trait implemented by both `sha2` and `ring` implementations. pub trait Sha256 { type Context: Sha256Context; fn hash(&self, input: &[u8]) -> Vec; fn hash_fixed(&self, input: &[u8]) -> [u8; HASH_LEN]; } /// Implementation of SHA256 using the `sha2` crate (fastest on CPUs with SHA extensions). struct Sha2CrateImpl; impl Sha256Context for sha2::Sha256 { fn new() -> Self { sha2::Digest::new() } fn update(&mut self, bytes: &[u8]) { sha2::Digest::update(self, bytes) } fn finalize(self) -> [u8; HASH_LEN] { sha2::Digest::finalize(self).into() } } impl Sha256 for Sha2CrateImpl { type Context = sha2::Sha256; fn hash(&self, input: &[u8]) -> Vec { Self::Context::digest(input).into_iter().collect() } fn hash_fixed(&self, input: &[u8]) -> [u8; HASH_LEN] { Self::Context::digest(input).into() } } /// Implementation of SHA256 using the `ring` crate (fastest on CPUs without SHA extensions). pub struct RingImpl; impl Sha256Context for ring::digest::Context { fn new() -> Self { Self::new(&ring::digest::SHA256) } fn update(&mut self, bytes: &[u8]) { self.update(bytes) } fn finalize(self) -> [u8; HASH_LEN] { let mut output = [0; HASH_LEN]; output.copy_from_slice(self.finish().as_ref()); output } } impl Sha256 for RingImpl { type Context = ring::digest::Context; fn hash(&self, input: &[u8]) -> Vec { ring::digest::digest(&ring::digest::SHA256, input) .as_ref() .into() } fn hash_fixed(&self, input: &[u8]) -> [u8; HASH_LEN] { let mut ctxt = Self::Context::new(&ring::digest::SHA256); ctxt.update(input); ctxt.finalize() } } /// Default dynamic implementation that switches between available implementations. pub enum DynamicImpl { Sha2, Ring, } // Runtime latch for detecting the availability of SHA extensions on x86_64. // // Inspired by the runtime switch within the `sha2` crate itself. #[cfg(all(feature = "detect-cpufeatures", target_arch = "x86_64"))] cpufeatures::new!(x86_sha_extensions, "sha", "sse2", "ssse3", "sse4.1"); #[inline(always)] pub fn have_sha_extensions() -> bool { #[cfg(all(feature = "detect-cpufeatures", target_arch = "x86_64"))] return x86_sha_extensions::get(); #[cfg(not(all(feature = "detect-cpufeatures", target_arch = "x86_64")))] return false; } impl DynamicImpl { /// Choose the best available implementation based on the currently executing CPU. #[inline(always)] pub fn best() -> Self { if have_sha_extensions() { Self::Sha2 } else { Self::Ring } } } impl Sha256 for DynamicImpl { type Context = DynamicContext; #[inline(always)] fn hash(&self, input: &[u8]) -> Vec { match self { Self::Sha2 => Sha2CrateImpl.hash(input), Self::Ring => RingImpl.hash(input), } } #[inline(always)] fn hash_fixed(&self, input: &[u8]) -> [u8; HASH_LEN] { match self { Self::Sha2 => Sha2CrateImpl.hash_fixed(input), Self::Ring => RingImpl.hash_fixed(input), } } } /// Context encapsulating all implemenation contexts. /// /// This enum ends up being 8 bytes larger than the largest inner context. pub enum DynamicContext { Sha2(sha2::Sha256), Ring(ring::digest::Context), } impl Sha256Context for DynamicContext { fn new() -> Self { match DynamicImpl::best() { DynamicImpl::Sha2 => Self::Sha2(Sha256Context::new()), DynamicImpl::Ring => Self::Ring(Sha256Context::new()), } } fn update(&mut self, bytes: &[u8]) { match self { Self::Sha2(ctxt) => Sha256Context::update(ctxt, bytes), Self::Ring(ctxt) => Sha256Context::update(ctxt, bytes), } } fn finalize(self) -> [u8; HASH_LEN] { match self { Self::Sha2(ctxt) => Sha256Context::finalize(ctxt), Self::Ring(ctxt) => Sha256Context::finalize(ctxt), } } } /// The max index that can be used with `ZERO_HASHES`. #[cfg(feature = "zero_hash_cache")] pub const ZERO_HASHES_MAX_INDEX: usize = 48; #[cfg(feature = "zero_hash_cache")] lazy_static! { /// Cached zero hashes where `ZERO_HASHES[i]` is the hash of a Merkle tree with 2^i zero leaves. pub static ref ZERO_HASHES: Vec> = { let mut hashes = vec![vec![0; 32]; ZERO_HASHES_MAX_INDEX + 1]; for i in 0..ZERO_HASHES_MAX_INDEX { hashes[i + 1] = hash32_concat(&hashes[i], &hashes[i])[..].to_vec(); } hashes }; } #[cfg(test)] mod tests { use super::*; use rustc_hex::FromHex; #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; #[cfg_attr(not(target_arch = "wasm32"), test)] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn test_hashing() { let input: Vec = b"hello world".as_ref().into(); let output = hash(input.as_ref()); let expected_hex = "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"; let expected: Vec = expected_hex.from_hex().unwrap(); assert_eq!(expected, output); } #[cfg(feature = "zero_hash_cache")] mod zero_hash { use super::*; #[test] fn zero_hash_zero() { assert_eq!(ZERO_HASHES[0], vec![0; 32]); } } }