diff --git a/.github/workflows/test-suite.yml b/.github/workflows/test-suite.yml index 1c1e345..72945a3 100644 --- a/.github/workflows/test-suite.yml +++ b/.github/workflows/test-suite.yml @@ -23,14 +23,22 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] + features: ["", "ring", "zero_hash_cache", "ring,zero_hash_cache"] + # Skip the ring-less builds on macOS ARM where ring is mandatory. + exclude: + - os: macos-latest + features: "" + - os: macos-latest + features: "zero_hash_cache" runs-on: ${{ matrix.os }} - name: test-${{ matrix.os }} + name: test-${{ matrix.os }}-feat-${{ matrix.features }} steps: - uses: actions/checkout@v3 - name: Get latest version of stable Rust run: rustup update stable - name: Run tests - run: cargo test --release + run: cargo test --release --no-default-features --features "${{ matrix.features }}" + # We use `--skip-clean` to aggregate the coverage from runs with different features. coverage: runs-on: ubuntu-latest name: cargo-tarpaulin @@ -40,8 +48,14 @@ jobs: run: rustup update stable - name: Install cargo-tarpaulin uses: taiki-e/install-action@cargo-tarpaulin - - name: Check code coverage with cargo-tarpaulin - run: cargo-tarpaulin --workspace --all-features --out xml + - name: Check code coverage with cargo-tarpaulin (no features) + run: cargo-tarpaulin --workspace --out xml --no-default-features + - name: Check code coverage with cargo-tarpaulin (just `ring` feature) + run: cargo-tarpaulin --workspace --out xml --skip-clean --no-default-features --features "ring" + - name: Check code coverage with cargo-tarpaulin (just `zero_hash_cache` feature) + run: cargo-tarpaulin --workspace --out xml --skip-clean --no-default-features --features "zero_hash_cache" + - name: Check code coverage with cargo-tarpaulin (all features) + run: cargo-tarpaulin --workspace --out xml --skip-clean --all-features - name: Upload to codecov.io uses: codecov/codecov-action@v3 with: diff --git a/Cargo.toml b/Cargo.toml index 6f23a8f..3d9131d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ categories = ["cryptography::cryptocurrencies"] rust-version = "1.80.0" [dependencies] -ring = "0.17" +ring = { version = "0.17", optional = true } [target.'cfg(target_arch = "x86_64")'.dependencies] cpufeatures = "0.2" @@ -25,5 +25,6 @@ rustc-hex = "2" wasm-bindgen-test = "0.3.33" [features] -default = ["zero_hash_cache"] +default = ["zero_hash_cache", "ring"] zero_hash_cache = [] +ring = ["dep:ring"] diff --git a/src/lib.rs b/src/lib.rs index 06e7e04..b91b431 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,8 +59,10 @@ pub trait Sha256 { } /// Implementation of SHA256 using the `ring` crate (fastest on CPUs without SHA extensions). +#[cfg(feature = "ring")] pub struct RingImpl; +#[cfg(feature = "ring")] impl Sha256Context for ring::digest::Context { fn new() -> Self { Self::new(&ring::digest::SHA256) @@ -77,6 +79,7 @@ impl Sha256Context for ring::digest::Context { } } +#[cfg(feature = "ring")] impl Sha256 for RingImpl { type Context = ring::digest::Context; @@ -97,6 +100,7 @@ impl Sha256 for RingImpl { pub enum DynamicImpl { #[cfg(target_arch = "x86_64")] Sha2, + #[cfg(feature = "ring")] Ring, } @@ -119,15 +123,27 @@ impl DynamicImpl { /// Choose the best available implementation based on the currently executing CPU. #[inline(always)] pub fn best() -> Self { - #[cfg(target_arch = "x86_64")] + #[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"))] + { + Self::Sha2 + } + + #[cfg(all(feature = "ring", target_arch = "x86_64"))] if have_sha_extensions() { Self::Sha2 } else { Self::Ring } - #[cfg(not(target_arch = "x86_64"))] - Self::Ring + #[cfg(all(feature = "ring", not(target_arch = "x86_64")))] + { + Self::Ring + } } } @@ -139,6 +155,7 @@ impl Sha256 for DynamicImpl { match self { #[cfg(target_arch = "x86_64")] Self::Sha2 => Sha2CrateImpl.hash(input), + #[cfg(feature = "ring")] Self::Ring => RingImpl.hash(input), } } @@ -148,6 +165,7 @@ impl Sha256 for DynamicImpl { match self { #[cfg(target_arch = "x86_64")] Self::Sha2 => Sha2CrateImpl.hash_fixed(input), + #[cfg(feature = "ring")] Self::Ring => RingImpl.hash_fixed(input), } } @@ -159,6 +177,7 @@ impl Sha256 for DynamicImpl { pub enum DynamicContext { #[cfg(target_arch = "x86_64")] Sha2(sha2::Sha256), + #[cfg(feature = "ring")] Ring(ring::digest::Context), } @@ -167,6 +186,7 @@ impl Sha256Context for DynamicContext { match DynamicImpl::best() { #[cfg(target_arch = "x86_64")] DynamicImpl::Sha2 => Self::Sha2(Sha256Context::new()), + #[cfg(feature = "ring")] DynamicImpl::Ring => Self::Ring(Sha256Context::new()), } } @@ -175,6 +195,7 @@ impl Sha256Context for DynamicContext { match self { #[cfg(target_arch = "x86_64")] Self::Sha2(ctxt) => Sha256Context::update(ctxt, bytes), + #[cfg(feature = "ring")] Self::Ring(ctxt) => Sha256Context::update(ctxt, bytes), } } @@ -183,6 +204,7 @@ impl Sha256Context for DynamicContext { match self { #[cfg(target_arch = "x86_64")] Self::Sha2(ctxt) => Sha256Context::finalize(ctxt), + #[cfg(feature = "ring")] Self::Ring(ctxt) => Sha256Context::finalize(ctxt), } }