Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"]
56 changes: 43 additions & 13 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down Expand Up @@ -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,
Expand All @@ -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
}
Expand All @@ -153,7 +172,7 @@ impl Sha256 for DynamicImpl {
#[inline(always)]
fn hash(&self, input: &[u8]) -> Vec<u8> {
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),
Expand All @@ -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),
Expand All @@ -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),
Expand All @@ -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()),
Expand All @@ -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),
Expand All @@ -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),
Expand Down Expand Up @@ -245,6 +264,17 @@ mod tests {
assert_eq!(expected, output);
}

#[cfg(feature = "portable")]
#[test]
fn test_portable_hashing() {
let input: Vec<u8> = b"hello world".as_ref().into();

let output = hash(input.as_ref());
let expected_hex = "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9";
let expected: Vec<u8> = expected_hex.from_hex().unwrap();
assert_eq!(expected, output);
}

#[cfg(feature = "zero_hash_cache")]
mod zero_hash {
use super::*;
Expand Down
2 changes: 1 addition & 1 deletion src/sha2_impl.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
Loading