Skip to content

Commit

Permalink
Enable RISC-V build (#8)
Browse files Browse the repository at this point in the history
* Add x86_64 dependency for cpufeature as it is only used for this plattform

* Update Cargo.toml

Co-authored-by: Leonardo Arias <[email protected]>

* Remove detect-cpufeatures feature

* Only compile sha2 dep on x86

* Fix compilation issue on x86

---------

Co-authored-by: Leonardo Arias <[email protected]>
Co-authored-by: Michael Sproul <[email protected]>
  • Loading branch information
3 people authored Jul 26, 2024
1 parent b6b5ff2 commit 3bfe7ec
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 36 deletions.
7 changes: 4 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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"]
52 changes: 19 additions & 33 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<u8> {
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;

Expand Down Expand Up @@ -120,34 +95,39 @@ impl Sha256 for RingImpl {

/// Default dynamic implementation that switches between available implementations.
pub enum DynamicImpl {
#[cfg(target_arch = "x86_64")]
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"))]
#[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;
}

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
}
}

Expand All @@ -157,6 +137,7 @@ impl Sha256 for DynamicImpl {
#[inline(always)]
fn hash(&self, input: &[u8]) -> Vec<u8> {
match self {
#[cfg(target_arch = "x86_64")]
Self::Sha2 => Sha2CrateImpl.hash(input),
Self::Ring => RingImpl.hash(input),
}
Expand All @@ -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),
}
Expand All @@ -175,27 +157,31 @@ 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),
}

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()),
}
}

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),
}
}

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),
}
Expand Down
35 changes: 35 additions & 0 deletions src/sha2_impl.rs
Original file line number Diff line number Diff line change
@@ -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<u8> {
Self::Context::digest(input).into_iter().collect()
}

fn hash_fixed(&self, input: &[u8]) -> [u8; HASH_LEN] {
Self::Context::digest(input).into()
}
}

0 comments on commit 3bfe7ec

Please sign in to comment.