Skip to content

Commit 07d370c

Browse files
authored
sha1: refactor backends selection (#808)
Replaces the `force-soft` crate feature with `sha1_backend` flags.
1 parent 7c7cb76 commit 07d370c

File tree

11 files changed

+107
-75
lines changed

11 files changed

+107
-75
lines changed

.github/workflows/sha1.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ jobs:
8787
- uses: RustCrypto/actions/cargo-hack-install@master
8888
- run: ${{ matrix.deps }}
8989
- run: cargo hack test --feature-powerset
90+
- run: cargo test --all-features
91+
env:
92+
RUSTFLAGS: -Dwarnings --cfg sha1_backend="soft"
9093

9194
# macOS tests
9295
macos:
@@ -108,6 +111,9 @@ jobs:
108111
- run: cargo test --no-default-features
109112
- run: cargo test
110113
- run: cargo test --all-features
114+
- run: cargo test --all-features
115+
env:
116+
RUSTFLAGS: -Dwarnings --cfg sha1_backend="aarch64-sha2"
111117

112118
# Windows tests
113119
windows:

sha1/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## 0.11.0 (UNRELEASED)
99
### Added
1010
- `alloc` crate feature ([#678])
11+
- `sha1_backend` configuration flag ([#808])
1112

1213
### Changed
1314
- Edition changed to 2024 and MSRV bumped to 1.85 ([#652])
@@ -19,11 +20,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1920
### Removed
2021
- `asm`, `loongarch64_asm`, and `compress` crate features [#542]
2122
- `std` crate feature ([#678])
23+
- `force-soft` crate feature ([#808])
2224

2325
[#542]: https://github.com/RustCrypto/hashes/pull/542
2426
[#652]: https://github.com/RustCrypto/hashes/pull/652
2527
[#678]: https://github.com/RustCrypto/hashes/pull/678
2628
[#716]: https://github.com/RustCrypto/hashes/pull/716
29+
[#808]: https://github.com/RustCrypto/hashes/pull/808
2730

2831
## 0.10.6 (2023-09-21)
2932
### Added

sha1/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ default = ["alloc", "oid"]
2828
alloc = ["digest/alloc"]
2929
oid = ["digest/oid"] # Enable OID support
3030
zeroize = ["digest/zeroize"]
31-
force-soft = [] # Force software implementation
31+
32+
[lints.rust]
33+
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(sha1_backend, values("aarch64-sha2", "x86-sha", "soft"))'] }
3234

3335
[package.metadata.docs.rs]
3436
all-features = true

sha1/src/block_api.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,17 @@ use digest::{
1313
#[cfg(feature = "zeroize")]
1414
use digest::zeroize::{Zeroize, ZeroizeOnDrop};
1515

16-
pub use crate::compress::compress;
16+
use crate::consts::{H0, State};
1717

18-
const STATE_LEN: usize = 5;
19-
const H0: [u32; STATE_LEN] = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0];
18+
/// SHA-1 compression function
19+
pub fn compress(state: &mut State, blocks: &[[u8; 64]]) {
20+
crate::compress::compress(state, blocks);
21+
}
2022

2123
/// Core SHA-1 hasher state.
2224
#[derive(Clone)]
2325
pub struct Sha1Core {
24-
h: [u32; STATE_LEN],
26+
h: State,
2527
block_len: u64,
2628
}
2729

@@ -123,7 +125,7 @@ impl SerializableState for Sha1Core {
123125
) -> Result<Self, DeserializeStateError> {
124126
let (serialized_h, serialized_block_len) = serialized_state.split::<U20>();
125127

126-
let mut h = [0; STATE_LEN];
128+
let mut h = State::default();
127129
for (val, chunk) in h.iter_mut().zip(serialized_h.chunks_exact(4)) {
128130
*val = u32::from_le_bytes(chunk.try_into().unwrap());
129131
}

sha1/src/compress.rs

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,64 @@
1-
const K: [u32; 4] = [0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6];
2-
31
cfg_if::cfg_if! {
4-
if #[cfg(feature = "force-soft")] {
5-
mod soft;
6-
use soft::compress as compress_inner;
7-
} else if #[cfg(all(target_arch = "aarch64"))] {
2+
if #[cfg(sha1_backend = "soft")] {
83
mod soft;
9-
mod aarch64;
10-
use aarch64::compress as compress_inner;
4+
pub(crate) use soft::compress;
5+
} else if #[cfg(sha1_backend = "aarch64-sha2")] {
6+
mod aarch64_sha2;
7+
8+
#[cfg(not(target_feature = "sha2"))]
9+
compile_error!("aarch64-sha2 backend requires sha2 target feature");
10+
11+
pub(crate) fn compress(state: &mut [u32; 5], blocks: &[[u8; 64]]) {
12+
// SAFETY: we checked above that the required target features are enabled
13+
unsafe { aarch64_sha2::compress(state, blocks) }
14+
}
15+
} else if #[cfg(sha1_backend = "x86-sha")] {
16+
mod x86_sha;
17+
18+
#[cfg(not(all(
19+
target_feature = "sha",
20+
target_feature = "sse2",
21+
target_feature = "ssse3",
22+
target_feature = "sse4.1",
23+
)))]
24+
compile_error!("x86-sha backend requires sha, sse2, ssse3, sse4.1 target features");
25+
26+
pub(crate) fn compress(state: &mut [u32; 5], blocks: &[[u8; 64]]) {
27+
// SAFETY: we checked above that the required target features are enabled
28+
unsafe { x86_sha::compress(state, blocks) }
29+
}
1130
} else if #[cfg(target_arch = "loongarch64")] {
1231
mod loongarch64_asm;
13-
use loongarch64_asm::compress as compress_inner;
14-
} else if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
15-
mod soft;
16-
mod x86;
17-
use x86::compress as compress_inner;
32+
pub(crate) use loongarch64_asm::compress;
1833
} else {
1934
mod soft;
20-
use soft::compress as compress_inner;
21-
}
22-
}
2335

24-
/// SHA-1 compression function
25-
pub fn compress(state: &mut [u32; 5], blocks: &[[u8; 64]]) {
26-
compress_inner(state, blocks);
36+
cfg_if::cfg_if! {
37+
if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
38+
mod x86_sha;
39+
cpufeatures::new!(shani_cpuid, "sha", "sse2", "ssse3", "sse4.1");
40+
} else if #[cfg(target_arch = "aarch64")] {
41+
mod aarch64_sha2;
42+
cpufeatures::new!(sha2_hwcap, "sha2");
43+
}
44+
}
45+
46+
pub(crate) fn compress(state: &mut [u32; 5], blocks: &[[u8; 64]]) {
47+
cfg_if::cfg_if! {
48+
if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
49+
if shani_cpuid::get() {
50+
// SAFETY: we checked that required target features are available
51+
return unsafe { x86_sha::compress(state, blocks) };
52+
}
53+
} else if #[cfg(target_arch = "aarch64")] {
54+
if sha2_hwcap::get() {
55+
// SAFETY: we checked that `sha2` target feature is available
56+
return unsafe { aarch64_sha2::compress(state, blocks) };
57+
}
58+
}
59+
}
60+
61+
soft::compress(state, blocks);
62+
}
63+
}
2764
}
Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
11
//! SHA-1 `aarch64` backend.
22
3-
use super::K;
3+
use crate::consts::K;
44

5-
// Per rustc target feature docs for `aarch64-unknown-linux-gnu` and
6-
// `aarch64-apple-darwin` platforms, the `sha2` target feature enables
7-
// SHA-1 as well:
8-
//
9-
// > Enable SHA1 and SHA256 support.
10-
cpufeatures::new!(sha1_hwcap, "sha2");
5+
#[cfg(not(target_arch = "aarch64"))]
6+
compile_error!("aarch64-sha2 backend can be used only aarch64 target arches");
117

128
// note that `sha2` implicitly enables `neon`
139
#[target_feature(enable = "sha2")]
1410
#[allow(unsafe_op_in_unsafe_fn)]
15-
unsafe fn compress_sha1_neon(state: &mut [u32; 5], blocks: &[[u8; 64]]) {
11+
pub(crate) unsafe fn compress(state: &mut [u32; 5], blocks: &[[u8; 64]]) {
1612
use core::arch::aarch64::*;
1713

1814
let mut abcd = vld1q_u32(state.as_ptr());
@@ -172,14 +168,3 @@ unsafe fn compress_sha1_neon(state: &mut [u32; 5], blocks: &[[u8; 64]]) {
172168
vst1q_u32(state.as_mut_ptr(), abcd);
173169
state[4] = e0;
174170
}
175-
176-
pub(super) fn compress(state: &mut [u32; 5], blocks: &[[u8; 64]]) {
177-
// TODO: Replace with https://github.com/rust-lang/rfcs/pull/2725 after stabilization
178-
if sha1_hwcap::get() {
179-
unsafe {
180-
compress_sha1_neon(state, blocks);
181-
}
182-
} else {
183-
super::soft::compress(state, blocks);
184-
}
185-
}

sha1/src/compress/loongarch64_asm.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
//! LoongArch64 assembly backend
22
3-
use super::K;
3+
use crate::consts::K;
44
use core::arch::asm;
55

6+
#[cfg(not(target_arch = "loongarch64"))]
7+
compile_error!("loongarch-asm backend can be used only on loongarch64 target arches");
8+
69
macro_rules! c {
710
($($l:expr)*) => {
811
concat!($($l ,)*)
@@ -101,7 +104,7 @@ macro_rules! roundtail {
101104
};
102105
}
103106

104-
pub(super) fn compress(state: &mut [u32; 5], blocks: &[[u8; 64]]) {
107+
pub(crate) fn compress(state: &mut [u32; 5], blocks: &[[u8; 64]]) {
105108
if blocks.is_empty() {
106109
return;
107110
}

sha1/src/compress/soft.rs

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#![allow(clippy::many_single_char_names)]
2-
use super::K;
2+
use crate::consts::K;
33

44
#[inline(always)]
55
fn add(a: [u32; 4], b: [u32; 4]) -> [u32; 4] {
@@ -193,7 +193,7 @@ macro_rules! schedule_rounds4 {
193193
}
194194

195195
#[inline(always)]
196-
fn sha1_digest_block_u32(state: &mut [u32; 5], block: &[u32; 16]) {
196+
fn digest_block(state: &mut [u32; 5], block: [u32; 16]) {
197197
let mut w0 = [block[0], block[1], block[2], block[3]];
198198
let mut w1 = [block[4], block[5], block[6], block[7]];
199199
let mut w2 = [block[8], block[9], block[10], block[11]];
@@ -242,16 +242,15 @@ fn sha1_digest_block_u32(state: &mut [u32; 5], block: &[u32; 16]) {
242242
state[4] = state[4].wrapping_add(e);
243243
}
244244

245-
pub(super) fn compress(state: &mut [u32; 5], blocks: &[[u8; 64]]) {
246-
let mut block_u32 = [0u32; 16];
247-
// since LLVM can't properly use aliasing yet it will make
248-
// unnecessary state stores without this copy
249-
let mut state_cpy = *state;
250-
for block in blocks.iter() {
251-
for (o, chunk) in block_u32.iter_mut().zip(block.chunks_exact(4)) {
252-
*o = u32::from_be_bytes(chunk.try_into().unwrap());
253-
}
254-
sha1_digest_block_u32(&mut state_cpy, &block_u32);
245+
fn read_block(block: &[u8; 64]) -> [u32; 16] {
246+
core::array::from_fn(|i| {
247+
let chunk = &block[4 * i..][..4];
248+
u32::from_be_bytes(chunk.try_into().unwrap())
249+
})
250+
}
251+
252+
pub(crate) fn compress(state: &mut [u32; 5], blocks: &[[u8; 64]]) {
253+
for block in blocks.iter().map(read_block) {
254+
digest_block(state, block);
255255
}
256-
*state = state_cpy;
257256
}
Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! SHA-1 `x86`/`x86_64` backend
22
3-
#![cfg(any(target_arch = "x86", target_arch = "x86_64"))]
3+
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
4+
compile_error!("x86-sha backend can be used only on x86 and x86_64 target arches");
45

56
#[cfg(target_arch = "x86")]
67
use core::arch::x86::*;
@@ -32,7 +33,7 @@ macro_rules! schedule_rounds4 {
3233

3334
#[target_feature(enable = "sha,sse2,ssse3,sse4.1")]
3435
#[allow(unsafe_op_in_unsafe_fn)]
35-
unsafe fn digest_blocks(state: &mut [u32; 5], blocks: &[[u8; 64]]) {
36+
pub(crate) unsafe fn compress(state: &mut [u32; 5], blocks: &[[u8; 64]]) {
3637
#[allow(non_snake_case)]
3738
let MASK: __m128i = _mm_set_epi64x(0x0001_0203_0405_0607, 0x0809_0A0B_0C0D_0E0F);
3839

@@ -89,17 +90,3 @@ unsafe fn digest_blocks(state: &mut [u32; 5], blocks: &[[u8; 64]]) {
8990
_mm_storeu_si128(state.as_mut_ptr().cast(), state_abcd);
9091
state[4] = _mm_extract_epi32(state_e, 3) as u32;
9192
}
92-
93-
cpufeatures::new!(shani_cpuid, "sha", "sse2", "ssse3", "sse4.1");
94-
95-
pub(super) fn compress(state: &mut [u32; 5], blocks: &[[u8; 64]]) {
96-
// TODO: Replace with https://github.com/rust-lang/rfcs/pull/2725
97-
// after stabilization
98-
if shani_cpuid::get() {
99-
unsafe {
100-
digest_blocks(state, blocks);
101-
}
102-
} else {
103-
super::soft::compress(state, blocks);
104-
}
105-
}

sha1/src/consts.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
pub(crate) const STATE_LEN: usize = 5;
2+
3+
pub(crate) type State = [u32; STATE_LEN];
4+
5+
pub(crate) const H0: State = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0];
6+
7+
pub(crate) const K: [u32; 4] = [0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6];

0 commit comments

Comments
 (0)