diff --git a/Cargo.toml b/Cargo.toml index 82a941e..6ca1bae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,8 +12,10 @@ categories = ["cryptography::cryptocurrencies"] [dependencies] lazy_static = { version = "1.1", optional = true } -cpufeatures = { version = "0.2", optional = true } ring = "0.17" + +[target.'cfg(target_arch = "x86_64")'.dependencies] +cpufeatures = "0.2" sha2 = "0.10" [dev-dependencies] @@ -23,6 +25,5 @@ rustc-hex = "2" wasm-bindgen-test = "0.3.33" [features] -default = ["zero_hash_cache", "detect-cpufeatures"] +default = ["zero_hash_cache"] zero_hash_cache = ["lazy_static"] -detect-cpufeatures = ["cpufeatures"] diff --git a/src/lib.rs b/src/lib.rs index 8dcbebf..5a99d6f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,8 +7,12 @@ //! 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. +mod sha2_impl; + pub use self::DynamicContext as Context; -use sha2::Digest; + +#[cfg(target_arch = "x86_64")] +use sha2_impl::Sha2CrateImpl; #[cfg(feature = "zero_hash_cache")] use lazy_static::lazy_static; @@ -54,35 +58,6 @@ pub trait Sha256 { 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; @@ -120,6 +95,7 @@ impl Sha256 for RingImpl { /// Default dynamic implementation that switches between available implementations. pub enum DynamicImpl { + #[cfg(target_arch = "x86_64")] Sha2, Ring, } @@ -127,15 +103,15 @@ pub enum DynamicImpl { // 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"))] +#[cfg(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"))] + #[cfg(target_arch = "x86_64")] return x86_sha_extensions::get(); - #[cfg(not(all(feature = "detect-cpufeatures", target_arch = "x86_64")))] + #[cfg(not(target_arch = "x86_64"))] return false; } @@ -143,11 +119,15 @@ impl DynamicImpl { /// Choose the best available implementation based on the currently executing CPU. #[inline(always)] pub fn best() -> Self { + #[cfg(target_arch = "x86_64")] if have_sha_extensions() { Self::Sha2 } else { Self::Ring } + + #[cfg(not(target_arch = "x86_64"))] + Self::Ring } } @@ -157,6 +137,7 @@ impl Sha256 for DynamicImpl { #[inline(always)] fn hash(&self, input: &[u8]) -> Vec { match self { + #[cfg(target_arch = "x86_64")] Self::Sha2 => Sha2CrateImpl.hash(input), Self::Ring => RingImpl.hash(input), } @@ -165,6 +146,7 @@ impl Sha256 for DynamicImpl { #[inline(always)] fn hash_fixed(&self, input: &[u8]) -> [u8; HASH_LEN] { match self { + #[cfg(target_arch = "x86_64")] Self::Sha2 => Sha2CrateImpl.hash_fixed(input), Self::Ring => RingImpl.hash_fixed(input), } @@ -175,6 +157,7 @@ impl Sha256 for DynamicImpl { /// /// This enum ends up being 8 bytes larger than the largest inner context. pub enum DynamicContext { + #[cfg(target_arch = "x86_64")] Sha2(sha2::Sha256), Ring(ring::digest::Context), } @@ -182,6 +165,7 @@ pub enum DynamicContext { impl Sha256Context for DynamicContext { fn new() -> Self { match DynamicImpl::best() { + #[cfg(target_arch = "x86_64")] DynamicImpl::Sha2 => Self::Sha2(Sha256Context::new()), DynamicImpl::Ring => Self::Ring(Sha256Context::new()), } @@ -189,6 +173,7 @@ impl Sha256Context for DynamicContext { fn update(&mut self, bytes: &[u8]) { match self { + #[cfg(target_arch = "x86_64")] Self::Sha2(ctxt) => Sha256Context::update(ctxt, bytes), Self::Ring(ctxt) => Sha256Context::update(ctxt, bytes), } @@ -196,6 +181,7 @@ impl Sha256Context for DynamicContext { fn finalize(self) -> [u8; HASH_LEN] { match self { + #[cfg(target_arch = "x86_64")] Self::Sha2(ctxt) => Sha256Context::finalize(ctxt), Self::Ring(ctxt) => Sha256Context::finalize(ctxt), } diff --git a/src/sha2_impl.rs b/src/sha2_impl.rs new file mode 100644 index 0000000..692c614 --- /dev/null +++ b/src/sha2_impl.rs @@ -0,0 +1,35 @@ +// This implementation should only be compiled on x86_64 due to its dependency on the `sha2` and +// `cpufeatures` crates which do not compile on some architectures like RISC-V. +#![cfg(target_arch = "x86_64")] + +use crate::{Sha256, Sha256Context, HASH_LEN}; +use sha2::Digest; + +/// Implementation of SHA256 using the `sha2` crate (fastest on x86_64 CPUs with SHA extensions). +pub 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() + } +}