What changed, and why it matters
This commit adds a new performance optimization for SHA-256 hashing on modern x86 processors. It introduces a code path that processes four blocks at once using SSE4.1 instructions when available. There is no indication in the commit that this fixes a security vulnerability; it appears to be a routine performance improvement.
No security action required. Treat as a normal performance optimization. If reviewing for safety, verify that the existing sse41::sha256d_64_4way function correctly handles unaligned access and that the slice indexing/try_from conversions cannot panic under expected invariants.
Security signals we found
No security-relevant signals in commit message or diff
Change is a performance optimization (4-way SIMD dispatch)
Uses existing unsafe SSE4.1 implementation, does not introduce new unsafe code
Runtime CPU feature detection guards the optimized path
Evidence from the diff
The patch adds a 4-way SSE4.1 dispatcher to hashes/src/sha256/crypto/mod.rs. It detects SSE2/SSSE3/SSE4.1 support at runtime (via either std::is_x86_feature_detected! or the cpufeatures crate) and, when available, calls an existing sse41::sha256d_64_4way unsafe function to hash four 64-byte input blocks into four 32-byte outputs in parallel. The change removes the TODO comment for 4-way SSE4.1 and adds the corresponding dispatch logic after the existing 2-way SHA-NI path and before the scalar fallback. No unsafe code is newly introduced; the commit only wires up an existing unsafe implementation.
Changed components
hashes/src/sha256/crypto/mod.rsSHA-256 double-hash (sha256d) implementationx86/x86_64 SIMD dispatch logicInspect captured patch +37 / −1
diff --git a/hashes/src/sha256/crypto/mod.rs b/hashes/src/sha256/crypto/mod.rs
index ee2813d1..00d13d15 100644
--- a/hashes/src/sha256/crypto/mod.rs
+++ b/hashes/src/sha256/crypto/mod.rs
@@ -40,6 +40,13 @@ mod cpuid_sha256_x86 {
cpufeatures::new!(inner, "sha", "sse2", "ssse3", "sse4.1");
pub fn get() -> bool { inner::get() }
}
+#[cfg(feature = "cpufeatures")]
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+#[allow(deprecated_in_future)]
+mod cpuid_sse41_x86 {
+ cpufeatures::new!(inner, "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)) }
@@ -341,7 +348,6 @@ impl HashEngine {
let count = inputs.len();
// TODO: 8-way AVX2
- // TODO: 4-way SSE4.1
// 2-way x86 SHA-NI
#[cfg(feature = "std")]
@@ -401,6 +407,36 @@ impl HashEngine {
}
}
+ // 4-way SSE4.1
+ #[cfg(feature = "std")]
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ {
+ if std::is_x86_feature_detected!("sse4.1")
+ && std::is_x86_feature_detected!("sse2")
+ && std::is_x86_feature_detected!("ssse3")
+ {
+ while count - i >= 4 {
+ let out = <&mut [[u8; 32]; 4]>::try_from(&mut outputs[i..i + 4]).unwrap();
+ let inp = <&[[u8; 64]; 4]>::try_from(&inputs[i..i + 4]).unwrap();
+ unsafe { sse41::sha256d_64_4way(out, inp) };
+ i += 4;
+ }
+ }
+ }
+
+ #[cfg(feature = "cpufeatures")]
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ {
+ if cpuid_sse41_x86::get() {
+ while count - i >= 4 {
+ let out = <&mut [[u8; 32]; 4]>::try_from(&mut outputs[i..i + 4]).unwrap();
+ let inp = <&[[u8; 64]; 4]>::try_from(&inputs[i..i + 4]).unwrap();
+ unsafe { sse41::sha256d_64_4way(out, inp) };
+ i += 4;
+ }
+ }
+ }
+
// fallback
while i < count {
outputs[i] = sha256d::hash(&inputs[i]).to_byte_array();
Why this scored 17/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.