Skip to content

Commit 3d2bc57

Browse files
authored
sha2: refactor backends (#802)
This PR reorganizes backends structure and related configuration flags. Now backends like `x86_sha` expose `compress` function marked with `#[target_feature(enable = "...")]` which has to be called using `unsafe` after an appropriate run-time or compile-time check. Autodetection is now handled by the (default) fallback `cfg_if!` branch instead of doing it in the backend modules. `sha2_backend` now forces selection of the specified backend and if necessary checks at compile time whether target features required by the backend were properly enabled. `sha2_backend="soft-compact"` and `sha2_backend="riscv-zknh-compact"` configuration flags are replaced by `sha2_backend_soft="compact"` and `sha2_backend_riscv_zknh="compact"` flags respectively. Additionally `sha2_256_backend` and `sha2_512_backend` configuration flags are introduced for finer-grained control over selected backends.
1 parent faa55fb commit 3d2bc57

30 files changed

+791
-720
lines changed

.github/workflows/sha2.yml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ jobs:
8686
RUSTFLAGS: -Dwarnings --cfg sha2_backend="soft"
8787
- run: cargo test --all-features
8888
env:
89-
RUSTFLAGS: -Dwarnings --cfg sha2_backend="soft-compact"
89+
RUSTFLAGS: -Dwarnings --cfg sha2_backend="soft" --cfg sha2_backend_soft="compact"
9090

9191
# macOS tests
9292
macos:
@@ -112,7 +112,7 @@ jobs:
112112
RUSTFLAGS: -Dwarnings --cfg sha2_backend="soft"
113113
- run: cargo test --all-features
114114
env:
115-
RUSTFLAGS: -Dwarnings --cfg sha2_backend="soft-compact"
115+
RUSTFLAGS: -Dwarnings --cfg sha2_backend="soft" --cfg sha2_backend_soft="compact"
116116

117117
# Windows tests
118118
windows:
@@ -139,7 +139,7 @@ jobs:
139139
RUSTFLAGS: -Dwarnings --cfg sha2_backend="soft"
140140
- run: cargo test --all-features
141141
env:
142-
RUSTFLAGS: -Dwarnings --cfg sha2_backend="soft-compact"
142+
RUSTFLAGS: -Dwarnings --cfg sha2_backend="soft" --cfg sha2_backend_soft="compact"
143143

144144
# Cross-compiled tests
145145
cross:
@@ -186,16 +186,16 @@ jobs:
186186
toolchain: nightly
187187
- run: cross test --package sha2 --all-features --target riscv64gc-unknown-linux-gnu
188188
env:
189-
RUSTFLAGS: -Dwarnings --cfg sha2_backend="soft" -C target-feature=+zknh,+zbkb
189+
RUSTFLAGS: -Dwarnings --cfg sha2_backend="soft"
190190
- run: cross test --package sha2 --all-features --target riscv64gc-unknown-linux-gnu
191191
env:
192-
RUSTFLAGS: -Dwarnings --cfg sha2_backend="soft-compact" -C target-feature=+zknh,+zbkb
192+
RUSTFLAGS: -Dwarnings --cfg sha2_backend="soft" --cfg sha2_backend_soft="compact"
193193
- run: cross test --package sha2 --all-features --target riscv64gc-unknown-linux-gnu
194194
env:
195195
RUSTFLAGS: -Dwarnings --cfg sha2_backend="riscv-zknh" -C target-feature=+zknh,+zbkb
196196
- run: cross test --package sha2 --all-features --target riscv64gc-unknown-linux-gnu
197197
env:
198-
RUSTFLAGS: -Dwarnings --cfg sha2_backend="riscv-zknh-compact" -C target-feature=+zknh,+zbkb
198+
RUSTFLAGS: -Dwarnings --cfg sha2_backend="riscv-zknh" --cfg sha2_backend_riscv_zknh="compact" -C target-feature=+zknh,+zbkb
199199

200200
riscv32-zknh:
201201
runs-on: ubuntu-latest
@@ -208,16 +208,16 @@ jobs:
208208
components: rust-src
209209
- run: cargo build --all-features --target riscv32gc-unknown-linux-gnu -Z build-std
210210
env:
211-
RUSTFLAGS: -Dwarnings --cfg sha2_backend="soft" -C target-feature=+zknh,+zbkb
211+
RUSTFLAGS: -Dwarnings --cfg sha2_backend="soft"
212212
- run: cargo build --all-features --target riscv32gc-unknown-linux-gnu -Z build-std
213213
env:
214-
RUSTFLAGS: -Dwarnings --cfg sha2_backend="soft-compact" -C target-feature=+zknh,+zbkb
214+
RUSTFLAGS: -Dwarnings --cfg sha2_backend="soft" --cfg sha2_backend_soft="compact"
215215
- run: cargo build --all-features --target riscv32gc-unknown-linux-gnu -Z build-std
216216
env:
217217
RUSTFLAGS: -Dwarnings --cfg sha2_backend="riscv-zknh" -C target-feature=+zknh,+zbkb
218218
- run: cargo build --all-features --target riscv32gc-unknown-linux-gnu -Z build-std
219219
env:
220-
RUSTFLAGS: -Dwarnings --cfg sha2_backend="riscv-zknh-compact" -C target-feature=+zknh,+zbkb
220+
RUSTFLAGS: -Dwarnings --cfg sha2_backend="riscv-zknh" --cfg sha2_backend_riscv_zknh="compact" -C target-feature=+zknh,+zbkb
221221

222222
# wasmtime tests
223223
wasm:

sha2/CHANGELOG.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
- Replace type aliases with newtypes ([#678])
1414
- `compress256` and `compress512` are moved to the `block_api` module ([#678])
1515
- Implementation of the `SerializableState` trait ([#716])
16+
- Configuration flags for backend selection ([#614], [#615], [#686], [#802])
1617

1718
### Added
1819
- `alloc` crate feature ([#678])
19-
- RISC-V scalar crypto extension support gated behind `sha2_backend = "riscv-zknh"` or
20-
`sha2_backend = "riscv-zknh-compact"` configuration flags ([#614])
21-
- `sha2_backend = "soft"` configuration flag ([#615])
22-
- `sha2_backend = "soft-compact"` configuration flag ([#686])
20+
- RISC-V scalar crypto extension support ([#614])
2321

2422
### Removed
2523
- `asm`, `asm-aarch64`, `loongarch64_asm`, and `compress` crate features ([#542])
@@ -34,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3432
[#678]: https://github.com/RustCrypto/hashes/pull/678
3533
[#686]: https://github.com/RustCrypto/hashes/pull/686
3634
[#716]: https://github.com/RustCrypto/hashes/pull/716
35+
[#802]: https://github.com/RustCrypto/hashes/pull/802
3736

3837
## 0.10.9 (2025-04-30)
3938
### Added

sha2/Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,11 @@ oid = ["digest/oid"]
3535
[lints.rust.unexpected_cfgs]
3636
level = "warn"
3737
check-cfg = [
38-
'cfg(sha2_backend, values("soft", "soft-compact", "riscv-zknh", "riscv-zknh-compact"))',
38+
'cfg(sha2_backend, values("soft", "riscv-zknh"))',
39+
'cfg(sha2_256_backend, values("aarch64-sha2", "soft", "riscv-zknh", "x86-sha"))',
40+
'cfg(sha2_512_backend, values("aarch64-sha3", "soft", "riscv-zknh", "x86-avx2"))',
41+
'cfg(sha2_backend_soft, values("compact"))',
42+
'cfg(sha2_backend_riscv_zknh, values("compact"))',
3943
]
4044

4145
[package.metadata.docs.rs]

sha2/README.md

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -57,25 +57,51 @@ See the [`digest`] crate docs for additional examples.
5757

5858
## Backends
5959

60-
This crate supports the following backends:
61-
- `soft`: portable implementation with fully unrolled rounds
62-
- `soft-compact`: portable implementation which produces smaller binaries
63-
- `aarch64-sha2`: uses the AArch64 `sha2` extension, fallbacks to the `soft` backend
64-
if the extension is not available
60+
This crate supports a number of different backends.
61+
62+
SHA-256 and SHA-512 backends:
63+
- `soft`: portable software implementation
6564
- `loongarch64-asm`: `asm!`-based implementation for LoongArch64 targets
6665
- `riscv-zknh`: uses the RISC-V `Zknh` scalar crypto extension. Experimental,
6766
requires Nightly compiler and to enable `Zknh` and `Zbkb` (or `Zbb`)
6867
target features at compile time.
69-
- `riscv-zknh-compact`: same as `riscv-zknh` but does not unroll rounds.
70-
- `wasm32-simd`: uses the WASM `simd128` extension
71-
- `x86-shani`: uses the x86 SHA-NI extension, fallbacks to the `soft` backend
72-
if the extension is not available (SHA-256 only)
73-
- `x86-avx2`: uses the x86 AVX2 extension, fallbacks to the `soft` backend
74-
if the extension is not available (SHA-512 only)
75-
76-
You can force backend selection using the `sha2_backend` configuration flag. It can be enabled
77-
using either environment variable (e.g. `RUSTFLAGS='--cfg sha2_backend="soft"'`),
78-
or by modifying your `.cargo/config.toml` file.
68+
- `wasm32-simd128`: uses the WASM `simd128` extension.
69+
70+
SHA-256 only backends:
71+
- `aarch64-sha2`: uses the AArch64 `sha2` extension.
72+
- `x86-sha`: uses the x86 SHA-NI extension.
73+
74+
SHA-512 only backends:
75+
- `aarch64-sha3`: uses the AArch64 `sha3` extension.
76+
- `x86-avx2`: uses the x86 AVX2 extension.
77+
78+
By default the following backends are used:
79+
- `target_arch = "aarch64"`: use `aarch64-sha2` and `aarch64-sha3` when the required
80+
target features are detected at runtime; otherwise fall back to `soft`.
81+
- `any(target_arch = "x86", target_arch = "x86_64")`: use `x86-sha` and `x86-avx` when
82+
the required target features are detected at runtime; otherwise fall back to `soft`.
83+
- `target_arch = "loongarch64"`: use `loongarch64-asm`.
84+
- `all(target_arch = "wasm32", target_feature = "simd128")`: use `wasm32-simd128`.
85+
- All other targets: use `soft`.
86+
87+
You can force backend selection using the following configuration flags:
88+
- `sha2_backend`: select SHA-256 and SHA-512 backends. Supported values: `soft`, `riscv-zknh`.
89+
- `sha2_256_backend`: select SHA-256 backend. Supported values: `aarch64-sha2`, `soft`,
90+
`riscv-zknh`, `x86-sha`.
91+
- `sha2_512_backend`: select SHA-512 backend. Supported values: `aarch64-sha3`, `soft`,
92+
`riscv-zknh`, `x86-avx2`.
93+
94+
They can be enabled using either the `RUSTFLAGS` environment variable
95+
(e.g. `RUSTFLAGS='--cfg sha2_backend="soft"'`), or by modifying your `.cargo/config.toml` file.
96+
97+
Note that `sha2_backend` has higher priority than `sha2_256_backend` and `sha2_512_backend`.
98+
In other words, using `--cfg sha2_backend="soft" --cfg sha2_256_backend="x86_sha"` will result
99+
in selection of the software backend for SHA-256.
100+
101+
By default `soft` and `riscv-zknh` backends unroll round loops, which results in a better
102+
performance at the cost of a bigger resulting binary. You can disable unrolling in the backends
103+
by using `sha2_backend_soft = "compact"` and `sha2_backend_riscv_zknh = "compact"` configuration
104+
flags respectively.
79105

80106
## License
81107

sha2/src/lib.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,15 @@
77
#![cfg_attr(docsrs, feature(doc_cfg))]
88
#![warn(missing_docs, unreachable_pub)]
99
#![cfg_attr(
10-
any(sha2_backend = "riscv-zknh", sha2_backend = "riscv-zknh-compact"),
10+
any(
11+
sha2_backend = "riscv-zknh",
12+
sha2_256_backend = "riscv-zknh",
13+
sha2_256_backend = "riscv-zknh",
14+
),
1115
feature(riscv_ext_intrinsics)
1216
)]
1317
#![allow(clippy::needless_range_loop)]
1418

15-
#[cfg(all(
16-
any(sha2_backend = "riscv-zknh", sha2_backend = "riscv-zknh-compact"),
17-
not(any(any(target_arch = "riscv32", target_arch = "riscv64")))
18-
))]
19-
compile_error!("The Zknh backends can be enabled only for RISC-V targets");
20-
2119
pub use digest::{self, Digest};
2220

2321
use digest::{

sha2/src/sha256.rs

Lines changed: 65 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,45 @@
11
cfg_if::cfg_if! {
2-
if #[cfg(sha2_backend = "soft")] {
2+
if #[cfg(any(sha2_backend = "soft", sha2_256_backend = "soft"))] {
33
mod soft;
44
use soft::compress;
5-
} else if #[cfg(sha2_backend = "soft-compact")] {
6-
mod soft_compact;
7-
use soft_compact::compress;
8-
} else if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
9-
mod soft;
10-
mod x86_shani;
11-
use x86_shani::compress;
12-
} else if #[cfg(all(
13-
any(target_arch = "riscv32", target_arch = "riscv64"),
14-
sha2_backend = "riscv-zknh"
15-
))] {
5+
} else if #[cfg(any(sha2_backend = "riscv-zknh", sha2_256_backend = "riscv-zknh"))] {
166
mod riscv_zknh;
17-
mod riscv_zknh_utils;
18-
use riscv_zknh::compress;
19-
} else if #[cfg(all(
20-
any(target_arch = "riscv32", target_arch = "riscv64"),
21-
sha2_backend = "riscv-zknh-compact"
22-
))] {
23-
mod riscv_zknh_compact;
24-
mod riscv_zknh_utils;
25-
use riscv_zknh_compact::compress;
26-
} else if #[cfg(target_arch = "aarch64")] {
27-
mod soft;
7+
8+
#[cfg(not(all(
9+
target_feature = "zknh",
10+
any(target_feature = "zbb", target_feature = "zbkb")
11+
)))]
12+
compile_error!("riscv-zknh backend requires zknh and zbkb (or zbb) target features");
13+
14+
fn compress(state: &mut [u32; 8], blocks: &[[u8; 64]]) {
15+
// SAFETY: we checked above that the required target features are enabled
16+
unsafe { riscv_zknh::compress(state, blocks) }
17+
}
18+
} else if #[cfg(sha2_256_backend = "x86-sha")] {
19+
mod x86_sha;
20+
21+
#[cfg(not(all(
22+
target_feature = "sha",
23+
target_feature = "sse2",
24+
target_feature = "ssse3",
25+
target_feature = "sse4.1",
26+
)))]
27+
compile_error!("x86-sha backend requires sha, sse2, ssse3, sse4.1 target features");
28+
29+
fn compress(state: &mut [u32; 8], blocks: &[[u8; 64]]) {
30+
// SAFETY: we checked above that the required target features are enabled
31+
unsafe { x86_sha::compress(state, blocks) }
32+
}
33+
} else if #[cfg(sha2_256_backend = "aarch64-sha2")] {
2834
mod aarch64_sha2;
29-
use aarch64_sha2::compress;
35+
36+
#[cfg(not(target_feature = "sha2"))]
37+
compile_error!("aarch64-sha2 backend requires sha2 target feature");
38+
39+
fn compress(state: &mut [u64; 8], blocks: &[[u8; 128]]) {
40+
// SAFETY: we checked above that the required target features are enabled
41+
unsafe { aarch64_sha2::compress(state, blocks) }
42+
}
3043
} else if #[cfg(target_arch = "loongarch64")] {
3144
mod loongarch64_asm;
3245
use loongarch64_asm::compress;
@@ -35,17 +48,35 @@ cfg_if::cfg_if! {
3548
use wasm32_simd128::compress;
3649
} else {
3750
mod soft;
38-
use soft::compress;
39-
}
40-
}
4151

42-
#[inline(always)]
43-
#[allow(dead_code)]
44-
fn to_u32s(block: &[u8; 64]) -> [u32; 16] {
45-
core::array::from_fn(|i| {
46-
let chunk = block[4 * i..][..4].try_into().unwrap();
47-
u32::from_be_bytes(chunk)
48-
})
52+
cfg_if::cfg_if! {
53+
if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
54+
mod x86_sha;
55+
cpufeatures::new!(shani_cpuid, "sha", "sse2", "ssse3", "sse4.1");
56+
} else if #[cfg(target_arch = "aarch64")] {
57+
mod aarch64_sha2;
58+
cpufeatures::new!(sha2_hwcap, "sha2");
59+
}
60+
}
61+
62+
fn compress(state: &mut [u32; 8], blocks: &[[u8; 64]]) {
63+
cfg_if::cfg_if! {
64+
if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
65+
if shani_cpuid::get() {
66+
// SAFETY: we checked that required target features are available
67+
return unsafe { x86_sha::compress(state, blocks) };
68+
}
69+
} else if #[cfg(target_arch = "aarch64")] {
70+
if sha2_hwcap::get() {
71+
// SAFETY: we checked that `sha2` target feature is available
72+
return unsafe { aarch64_sha2::compress(state, blocks) };
73+
}
74+
}
75+
}
76+
77+
soft::compress(state, blocks);
78+
}
79+
}
4980
}
5081

5182
/// Raw SHA-256 compression function.

sha2/src/sha256/aarch64_sha2.rs

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,16 @@
11
//! SHA-256 `aarch64` backend.
2+
//!
3+
//! Implementation adapted from mbedtls.
24
#![allow(unsafe_op_in_unsafe_fn)]
35

4-
// Implementation adapted from mbedtls.
5-
6-
use core::arch::aarch64::*;
6+
#[cfg(not(target_arch = "aarch64"))]
7+
compile_error!("aarch64-sha2 backend can be used only aarch64 target arches");
78

89
use crate::consts::K32;
9-
10-
cpufeatures::new!(sha2_hwcap, "sha2");
11-
12-
pub(super) fn compress(state: &mut [u32; 8], blocks: &[[u8; 64]]) {
13-
// TODO: Replace with https://github.com/rust-lang/rfcs/pull/2725
14-
// after stabilization
15-
if sha2_hwcap::get() {
16-
unsafe { sha256_compress(state, blocks) }
17-
} else {
18-
super::soft::compress(state, blocks);
19-
}
20-
}
10+
use core::arch::aarch64::*;
2111

2212
#[target_feature(enable = "sha2")]
23-
unsafe fn sha256_compress(state: &mut [u32; 8], blocks: &[[u8; 64]]) {
13+
pub(super) unsafe fn compress(state: &mut [u32; 8], blocks: &[[u8; 64]]) {
2414
// SAFETY: Requires the sha2 feature.
2515

2616
// Load state into vectors.

sha2/src/sha256/loongarch64_asm.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
//! LoongArch64 assembly backend
22
3+
#[cfg(not(target_arch = "loongarch64"))]
4+
compile_error!("loongarch-asm backend can be used only on loongarch64 target arches");
5+
36
macro_rules! c {
47
($($l:expr)*) => {
58
concat!($($l ,)*)

0 commit comments

Comments
 (0)