hashes: Add cpufeatures for no_std SIMD detection
What changed, and why it matters
This change lets users of the library who don't use the standard Rust library ('no_std' environments, common in embedded or blockchain hardware) benefit from faster SHA-256 hashing via CPU-specific instructions. It adds an optional helper crate that detects CPU features in 'no_std' mode, similar to how the standard library already does it. There is no direct evidence in the commit that this fixes a security vulnerability; it is a performance and portability improvement.
No immediate security action required. Reviewers may want to verify that the `cpufeatures` crate's detection logic correctly identifies the required instruction-set features on target platforms and that the unsafe SIMD intrinsics remain sound under `no_std`.
Security signals we found
Adds optional runtime CPU feature detection for no_std builds
Uses `cpufeatures` crate, a widely-used Rust ecosystem crate for CPUID detection
Does not remove or weaken existing feature gates; extends them with an additional feature
No mention of security bug, vulnerability, or CVE in commit message or diff
No unsafe code added beyond existing SIMD intrinsics already present
Evidence from the diff
The commit adds the optional cpufeatures crate to hashes/Cargo.toml and updates hashes/src/sha256/crypto.rs so that SIMD-accelerated SHA-256 paths (x86 SHA-NI/SSE and ARM SHA2 crypto extensions) are compiled and used when either the std or the new cpufeatures feature is enabled. Previously these paths were gated behind feature = "std", excluding no_std builds. The change is additive and optional; the fallback software implementation remains available.
Changed components
hashes/Cargo.tomlhashes/src/sha256/crypto.rsCargo-minimal.lockCargo-recent.lockInspect captured patch +59 / −6
diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock
index c8a7abda..b7da4f9a 100644
--- a/Cargo-minimal.lock
+++ b/Cargo-minimal.lock
@@ -186,6 +186,7 @@ version = "0.19.0"
dependencies = [
"bitcoin-consensus-encoding",
"bitcoin-internals",
+ "cpufeatures",
"hex-conservative 0.3.0",
"serde",
"serde_test",
@@ -231,6 +232,15 @@ dependencies = [
"hex-conservative 0.3.0",
]
+[[package]]
+name = "cpufeatures"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "getrandom"
version = "0.3.0"
diff --git a/Cargo-recent.lock b/Cargo-recent.lock
index 2bfaff84..7d1d081d 100644
--- a/Cargo-recent.lock
+++ b/Cargo-recent.lock
@@ -185,6 +185,7 @@ version = "0.19.0"
dependencies = [
"bitcoin-consensus-encoding",
"bitcoin-internals",
+ "cpufeatures",
"hex-conservative 0.3.0",
"serde",
"serde_test",
@@ -227,6 +228,15 @@ dependencies = [
"hex-conservative 0.3.0",
]
+[[package]]
+name = "cpufeatures"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "getrandom"
version = "0.3.4"
diff --git a/hashes/Cargo.toml b/hashes/Cargo.toml
index 5cf08f6c..2d09535b 100644
--- a/hashes/Cargo.toml
+++ b/hashes/Cargo.toml
@@ -27,6 +27,7 @@ encoding = { package = "bitcoin-consensus-encoding", path = "../consensus_encodi
hex = { package = "hex-conservative", version = "0.3.0", default-features = false, optional = true }
serde = { version = "1.0.195", default-features = false, optional = true }
+cpufeatures = { version = "0.2", optional = true }
[dev-dependencies]
serde_test = "1.0.19"
diff --git a/hashes/src/sha256/crypto.rs b/hashes/src/sha256/crypto.rs
index f2b3881b..0887b4b6 100644
--- a/hashes/src/sha256/crypto.rs
+++ b/hashes/src/sha256/crypto.rs
@@ -1,16 +1,33 @@
// SPDX-License-Identifier: CC0-1.0
-#[cfg(all(feature = "std", target_arch = "aarch64"))]
-use core::arch::aarch64::*;
-#[cfg(all(feature = "std", target_arch = "x86"))]
+#[cfg(all(target_arch = "x86", any(feature = "std", feature = "cpufeatures")))]
use core::arch::x86::*;
-#[cfg(all(feature = "std", target_arch = "x86_64"))]
+#[cfg(all(target_arch = "x86_64", any(feature = "std", feature = "cpufeatures")))]
use core::arch::x86_64::*;
+#[cfg(all(target_arch = "aarch64", any(feature = "std", feature = "cpufeatures")))]
+use core::arch::aarch64::*;
use internals::slice::SliceExt;
use super::{HashEngine, Midstate, BLOCK_SIZE};
+#[cfg(all(feature = "cpufeatures", target_arch = "aarch64"))]
+// cpufeatures crate internally uses `u8::max_value()` which will be deprecated.
+// See: https://docs.rs/cpufeatures/0.2.17/src/cpufeatures/lib.rs.html#161
+#[allow(deprecated_in_future)]
+mod cpuid_sha256_aarch64 {
+ cpufeatures::new!(inner, "sha2");
+ pub fn get() -> bool { inner::get() }
+}
+#[cfg(all(feature = "cpufeatures", any(target_arch = "x86", target_arch = "x86_64")))]
+// cpufeatures crate internally uses `u8::max_value()` which will be deprecated.
+// See: https://docs.rs/cpufeatures/0.2.17/src/cpufeatures/lib.rs.html#161
+#[allow(deprecated_in_future)]
+mod cpuid_sha256_x86 {
+ cpufeatures::new!(inner, "sha", "sse2", "ssse3", "sse4.1");
+ pub fn get() -> bool { inner::get() }
+}
+
#[allow(non_snake_case)]
const fn Ch(x: u32, y: u32, z: u32) -> u32 { z ^ (x & (y ^ z)) }
#[allow(non_snake_case)]
@@ -262,6 +279,14 @@ impl HashEngine {
}
}
+ #[cfg(all(feature = "cpufeatures", any(target_arch = "x86", target_arch = "x86_64")))]
+ {
+ if cpuid_sha256_x86::get() {
+ return unsafe { self.process_block_simd_x86_intrinsics() };
+ }
+ }
+
+
#[cfg(all(feature = "std", target_arch = "aarch64"))]
{
if std::arch::is_aarch64_feature_detected!("sha2") {
@@ -269,11 +294,18 @@ impl HashEngine {
}
}
+ #[cfg(all(feature = "cpufeatures", target_arch = "aarch64"))]
+ {
+ if cpuid_sha256_aarch64::get() {
+ return unsafe { self.process_block_simd_arm_intrinsics() };
+ }
+ }
+
// fallback implementation without using any intrinsics
self.software_process_block()
}
- #[cfg(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))]
+ #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), any(feature = "std", feature = "cpufeatures")))]
#[target_feature(enable = "sha,sse2,ssse3,sse4.1")]
unsafe fn process_block_simd_x86_intrinsics(&mut self) {
// Code translated and based on from
@@ -532,7 +564,7 @@ impl HashEngine {
_mm_storeu_si128(self.h.as_mut_ptr().add(4).cast::<__m128i>(), state1);
}
- #[cfg(all(feature = "std", target_arch = "aarch64"))]
+ #[cfg(all(target_arch = "aarch64", any(feature = "std", feature = "cpufeatures")))]
#[target_feature(enable = "sha2")]
unsafe fn process_block_simd_arm_intrinsics(&mut self) {
// Code translated and based on from
Why this scored 19/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.