Skip to content
Merged
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
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ 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"
Expand All @@ -28,3 +29,4 @@ wasm-bindgen-test = "0.3.33"
default = ["zero_hash_cache", "ring"]
zero_hash_cache = []
ring = ["dep:ring"]
sha2 = ["dep:sha2"]
46 changes: 31 additions & 15 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 = "sha2"))]
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 = "sha2"))]
Sha2,
#[cfg(feature = "ring")]
Ring,
Expand All @@ -123,27 +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")))]
{
compile_error!("Ring must be enabled on non-x86_64 architectures");
}

#[cfg(all(not(feature = "ring"), target_arch = "x86_64"))]
// If sha2 feature is enabled (non-x86_64), use sha2
#[cfg(all(feature = "sha2", not(target_arch = "x86_64")))]
{
Self::Sha2
}

// On x86_64 with ring: check for SHA extensions
#[cfg(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")))]
// On x86_64 without ring: always use sha2
#[cfg(all(not(feature = "ring"), target_arch = "x86_64"))]
{
Self::Sha2
}

// On non-x86_64 with ring but without sha2 feature: use ring
#[cfg(all(feature = "ring", not(target_arch = "x86_64"), not(feature = "sha2")))]
{
Self::Ring
}

// Compile error if no implementation available
#[cfg(all(
not(feature = "ring"),
not(target_arch = "x86_64"),
not(feature = "sha2")
))]
{
compile_error!(
"Either 'ring' or 'sha2' feature must be enabled on non-x86_64 architectures"
);
}
}
}

Expand All @@ -153,7 +169,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 = "sha2"))]
Self::Sha2 => Sha2CrateImpl.hash(input),
#[cfg(feature = "ring")]
Self::Ring => RingImpl.hash(input),
Expand All @@ -163,7 +179,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 = "sha2"))]
Self::Sha2 => Sha2CrateImpl.hash_fixed(input),
#[cfg(feature = "ring")]
Self::Ring => RingImpl.hash_fixed(input),
Expand All @@ -175,7 +191,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 = "sha2"))]
Sha2(sha2::Sha256),
#[cfg(feature = "ring")]
Ring(ring::digest::Context),
Expand All @@ -184,7 +200,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 = "sha2"))]
DynamicImpl::Sha2 => Self::Sha2(Sha256Context::new()),
#[cfg(feature = "ring")]
DynamicImpl::Ring => Self::Ring(Sha256Context::new()),
Expand All @@ -193,7 +209,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 = "sha2"))]
Self::Sha2(ctxt) => Sha256Context::update(ctxt, bytes),
#[cfg(feature = "ring")]
Self::Ring(ctxt) => Sha256Context::update(ctxt, bytes),
Expand All @@ -202,7 +218,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 = "sha2"))]
Self::Sha2(ctxt) => Sha256Context::finalize(ctxt),
#[cfg(feature = "ring")]
Self::Ring(ctxt) => Sha256Context::finalize(ctxt),
Expand Down
5 changes: 2 additions & 3 deletions src/sha2_impl.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// 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")]
// This implementation is compiled on x86_64 (always has sha2) or when the sha2 feature is enabled.
#![cfg(any(target_arch = "x86_64", feature = "sha2"))]

use crate::{Sha256, Sha256Context, HASH_LEN};
use sha2::Digest;
Expand Down
Loading