diff --git a/Cargo.toml b/Cargo.toml index 67b33d5..de8b86d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,10 +13,10 @@ rust-version = "1.80.0" [dependencies] ring = { version = "0.17", optional = true } +sha2 = { version = "0.10", optional = true } [target.'cfg(target_arch = "x86_64")'.dependencies] cpufeatures = "0.2" -sha2 = "0.10" [dev-dependencies] rustc-hex = "2" @@ -25,6 +25,7 @@ rustc-hex = "2" wasm-bindgen-test = "0.3.33" [features] -default = ["zero_hash_cache", "ring"] +default = ["zero_hash_cache", "portable"] zero_hash_cache = [] ring = ["dep:ring"] +portable = ["dep:sha2"] diff --git a/src/lib.rs b/src/lib.rs index b91b431..4d7c075 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,7 @@ mod sha2_impl; pub use self::DynamicContext as Context; -#[cfg(target_arch = "x86_64")] +#[cfg(any(target_arch = "x86_64", feature = "portable"))] use sha2_impl::Sha2CrateImpl; #[cfg(feature = "zero_hash_cache")] @@ -98,7 +98,7 @@ impl Sha256 for RingImpl { /// Default dynamic implementation that switches between available implementations. pub enum DynamicImpl { - #[cfg(target_arch = "x86_64")] + #[cfg(any(target_arch = "x86_64", feature = "portable"))] Sha2, #[cfg(feature = "ring")] Ring, @@ -123,24 +123,43 @@ impl DynamicImpl { /// Choose the best available implementation based on the currently executing CPU. #[inline(always)] pub fn best() -> Self { - #[cfg(all(not(feature = "ring"), not(target_arch = "x86_64")))] + #[cfg(feature = "portable")] + { + Self::Sha2 + } + + #[cfg(all( + not(feature = "portable"), + all(not(feature = "ring"), not(target_arch = "x86_64")) + ))] { - compile_error!("Ring must be enabled on non-x86_64 architectures"); + compile_error!( + "Ring must be enabled on non-x86_64 architectures. Alternatively, use the `portable` feature." + ); } - #[cfg(all(not(feature = "ring"), target_arch = "x86_64"))] + #[cfg(all( + not(feature = "portable"), + all(not(feature = "ring"), target_arch = "x86_64") + ))] { Self::Sha2 } - #[cfg(all(feature = "ring", target_arch = "x86_64"))] + #[cfg(all( + not(feature = "portable"), + all(feature = "ring", target_arch = "x86_64") + ))] if have_sha_extensions() { Self::Sha2 } else { Self::Ring } - #[cfg(all(feature = "ring", not(target_arch = "x86_64")))] + #[cfg(all( + not(feature = "portable"), + all(feature = "ring", not(target_arch = "x86_64")) + ))] { Self::Ring } @@ -153,7 +172,7 @@ impl Sha256 for DynamicImpl { #[inline(always)] fn hash(&self, input: &[u8]) -> Vec { match self { - #[cfg(target_arch = "x86_64")] + #[cfg(any(target_arch = "x86_64", feature = "portable"))] Self::Sha2 => Sha2CrateImpl.hash(input), #[cfg(feature = "ring")] Self::Ring => RingImpl.hash(input), @@ -163,7 +182,7 @@ impl Sha256 for DynamicImpl { #[inline(always)] fn hash_fixed(&self, input: &[u8]) -> [u8; HASH_LEN] { match self { - #[cfg(target_arch = "x86_64")] + #[cfg(any(target_arch = "x86_64", feature = "portable"))] Self::Sha2 => Sha2CrateImpl.hash_fixed(input), #[cfg(feature = "ring")] Self::Ring => RingImpl.hash_fixed(input), @@ -175,7 +194,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")] + #[cfg(any(target_arch = "x86_64", feature = "portable"))] Sha2(sha2::Sha256), #[cfg(feature = "ring")] Ring(ring::digest::Context), @@ -184,7 +203,7 @@ pub enum DynamicContext { impl Sha256Context for DynamicContext { fn new() -> Self { match DynamicImpl::best() { - #[cfg(target_arch = "x86_64")] + #[cfg(any(target_arch = "x86_64", feature = "portable"))] DynamicImpl::Sha2 => Self::Sha2(Sha256Context::new()), #[cfg(feature = "ring")] DynamicImpl::Ring => Self::Ring(Sha256Context::new()), @@ -193,7 +212,7 @@ impl Sha256Context for DynamicContext { fn update(&mut self, bytes: &[u8]) { match self { - #[cfg(target_arch = "x86_64")] + #[cfg(any(target_arch = "x86_64", feature = "portable"))] Self::Sha2(ctxt) => Sha256Context::update(ctxt, bytes), #[cfg(feature = "ring")] Self::Ring(ctxt) => Sha256Context::update(ctxt, bytes), @@ -202,7 +221,7 @@ impl Sha256Context for DynamicContext { fn finalize(self) -> [u8; HASH_LEN] { match self { - #[cfg(target_arch = "x86_64")] + #[cfg(any(target_arch = "x86_64", feature = "portable"))] Self::Sha2(ctxt) => Sha256Context::finalize(ctxt), #[cfg(feature = "ring")] Self::Ring(ctxt) => Sha256Context::finalize(ctxt), @@ -245,6 +264,17 @@ mod tests { assert_eq!(expected, output); } + #[cfg(feature = "portable")] + #[test] + fn test_portable_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::*; diff --git a/src/sha2_impl.rs b/src/sha2_impl.rs index 692c614..16e4eb5 100644 --- a/src/sha2_impl.rs +++ b/src/sha2_impl.rs @@ -1,6 +1,6 @@ // 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")] +#![cfg(any(target_arch = "x86_64", feature = "portable"))] use crate::{Sha256, Sha256Context, HASH_LEN}; use sha2::Digest;