From ad4f8472f6596dde624afc6d2cc57263d87fe587 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 28 Sep 2026 11:15:57 +0000 Subject: [PATCH 1/9] CI: test on ppc64le, on a ppc64le runner as pyca/cryptography does The job runs in pyca's ppc64le runner image (ghcr.io/pyca/cryptography-runner-ubuntu-rolling:ppc64le) on the ubuntu-24.04-ppc64le runner, the same pair pyca/cryptography's distros job uses. It runs on stable only: inline assembly on PowerPC, which the verified code needs for naked_asm!, was stabilized in Rust 1.95, after the crate's minimum of 1.88. Refs #55 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Fw6RULWHCeVgMDGc3wRrwE --- .github/workflows/ci.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b430f6e0c..75cbc33c4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -181,9 +181,24 @@ jobs: container: image: ghcr.io/pyca/cryptography-runner-ubuntu-rolling:armv7l options: --env RUSTUP_HOME=/root/.rustup + # PPC64LE: pyca's ppc64le runner image, on a ppc64le runner (as + # pyca/cryptography does). + - os: ubuntu-24.04-ppc64le + container: + image: ghcr.io/pyca/cryptography-runner-ubuntu-rolling:ppc64le + options: --env RUSTUP_HOME=/root/.rustup - os: macos-latest - os: windows-latest rust: [stable, "1.88"] + exclude: + # Inline assembly (and so `naked_asm!`) on PowerPC was stabilized + # in Rust 1.95, after the crate's minimum of 1.88. + - platform: + os: ubuntu-24.04-ppc64le + container: + image: ghcr.io/pyca/cryptography-runner-ubuntu-rolling:ppc64le + options: --env RUSTUP_HOME=/root/.rustup + rust: "1.88" # One job each for the upcoming toolchains, to catch regressions (and # new warnings, which are errors here) before they reach stable. include: From 1c089b9fc113289691f34ed6c72acced05fbd554 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 28 Sep 2026 22:04:32 +0000 Subject: [PATCH 2/9] CPU detection: allow it to go unused on PPC64LE No PPC64LE module chooses among implementations yet, so with -D warnings the ppc64le build rejected cpu.rs's detection as dead code. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Fw6RULWHCeVgMDGc3wRrwE --- src/cpu.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/cpu.rs b/src/cpu.rs index ec8991b27..64442e82b 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -19,6 +19,13 @@ //! this library knows: anything else panics, rather than quietly testing //! another configuration. +// No PPC64LE module chooses among implementations yet, so detection is only +// used by the tests there. +#![cfg_attr( + all(target_arch = "powerpc64", target_endian = "little"), + allow(dead_code) +)] + use core::sync::atomic::{AtomicU32, Ordering}; /// The features detection knows, by their Rust `target_feature` names: bit From 2ff8cc8d2321510de4e2178527bc8eb78e197041 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 28 Sep 2026 22:05:27 +0000 Subject: [PATCH 3/9] Benchmarks: PPC64LE assembly needs no benchmark run PPC64LE is not benchmarked, and a change to its generated assembly only fell through to the shared src/ rule, which benchmarked every module on every other architecture. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Fw6RULWHCeVgMDGc3wRrwE --- ci/bench_arches.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ci/bench_arches.py b/ci/bench_arches.py index f989c2efe..a3c10618c 100644 --- a/ci/bench_arches.py +++ b/ci/bench_arches.py @@ -79,8 +79,10 @@ def need(arch, module): for path in changed: asm, api, family = ASM.match(path), API.match(path), FAMILY.match(path) - if asm and asm[1] in PLATFORMS: - if asm[2] != "mod": + if asm: + # The assembly of an architecture that is not benchmarked (e.g. + # PPC64LE) needs no benchmark. + if asm[1] in PLATFORMS and asm[2] != "mod": need(asm[1], asm[2]) elif api: for a in PLATFORMS: From 107f35fe2d9ab157f5732fa44292d4cd1d957939 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 29 Sep 2026 22:44:26 +0000 Subject: [PATCH 4/9] Arch gates: PPC64LE is an architecture; gate what doesn't run there ci/check_arch_gates.py's ARCHES (all architectures, for a file with no cfg) gets powerpc64. The RFC 1321 tests and the benchmarks of ChaCha20, HMAC-SHA-256, MD5, SHA-1, SHA-256 and SHA-512, which named no architecture, now name the four their library code runs on, so the tests build on ppc64le and the check holds; each algorithm's PR for PPC64LE widens its gate again. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Fw6RULWHCeVgMDGc3wRrwE --- bench/benches/primitives/chacha20.rs | 14 ++++++++++++++ bench/benches/primitives/hmac_sha256.rs | 14 ++++++++++++++ bench/benches/primitives/md5.rs | 14 ++++++++++++++ bench/benches/primitives/sha1.rs | 14 ++++++++++++++ bench/benches/primitives/sha256.rs | 14 ++++++++++++++ bench/benches/primitives/sha512.rs | 14 ++++++++++++++ ci/check_arch_gates.py | 2 +- tests/rfc1321/main.rs | 7 +++++++ 8 files changed, 92 insertions(+), 1 deletion(-) diff --git a/bench/benches/primitives/chacha20.rs b/bench/benches/primitives/chacha20.rs index ea5c52211..9b657b380 100644 --- a/bench/benches/primitives/chacha20.rs +++ b/bench/benches/primitives/chacha20.rs @@ -12,6 +12,20 @@ use crate::{OPENSSL, SIZES, VG}; /// `ci/bench_arches.py`). pub const USES: &[&str] = &["chacha20"]; +#[cfg(not(any( + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "arm", + target_arch = "x86" +)))] +pub fn bench(_: &mut Criterion) {} + +#[cfg(any( + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "arm", + target_arch = "x86" +))] pub fn bench(c: &mut Criterion) { let key = [0x42; 32]; let nonce = [0x24; 16]; diff --git a/bench/benches/primitives/hmac_sha256.rs b/bench/benches/primitives/hmac_sha256.rs index a223a223f..2b0132e9d 100644 --- a/bench/benches/primitives/hmac_sha256.rs +++ b/bench/benches/primitives/hmac_sha256.rs @@ -9,6 +9,20 @@ use verified_garbage::hmac::Hmac; /// `ci/bench_arches.py`): this one and those it calls. pub const USES: &[&str] = &["hmac_sha256", "sha256"]; +#[cfg(not(any( + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "arm", + target_arch = "x86" +)))] +pub fn bench(_: &mut Criterion) {} + +#[cfg(any( + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "arm", + target_arch = "x86" +))] pub fn bench(c: &mut Criterion) { crate::hmac_group( c, diff --git a/bench/benches/primitives/md5.rs b/bench/benches/primitives/md5.rs index 6ca8ae330..dff39b21f 100644 --- a/bench/benches/primitives/md5.rs +++ b/bench/benches/primitives/md5.rs @@ -10,6 +10,20 @@ use crate::hash_group; /// `ci/bench_arches.py`). pub const USES: &[&str] = &["md5"]; +#[cfg(not(any( + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "arm", + target_arch = "x86" +)))] +pub fn bench(_: &mut Criterion) {} + +#[cfg(any( + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "arm", + target_arch = "x86" +))] pub fn bench(c: &mut Criterion) { hash_group(c, "md5", Md5::digest, MessageDigest::md5()); } diff --git a/bench/benches/primitives/sha1.rs b/bench/benches/primitives/sha1.rs index b3f9de7af..a6fea0181 100644 --- a/bench/benches/primitives/sha1.rs +++ b/bench/benches/primitives/sha1.rs @@ -10,6 +10,20 @@ use crate::hash_group; /// `ci/bench_arches.py`). pub const USES: &[&str] = &["sha1"]; +#[cfg(not(any( + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "arm", + target_arch = "x86" +)))] +pub fn bench(_: &mut Criterion) {} + +#[cfg(any( + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "arm", + target_arch = "x86" +))] pub fn bench(c: &mut Criterion) { hash_group(c, "sha1", Sha1::digest, MessageDigest::sha1()); } diff --git a/bench/benches/primitives/sha256.rs b/bench/benches/primitives/sha256.rs index 37dfbfe3a..e3f0f776d 100644 --- a/bench/benches/primitives/sha256.rs +++ b/bench/benches/primitives/sha256.rs @@ -10,6 +10,20 @@ use crate::hash_group; /// `ci/bench_arches.py`). pub const USES: &[&str] = &["sha256"]; +#[cfg(not(any( + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "arm", + target_arch = "x86" +)))] +pub fn bench(_: &mut Criterion) {} + +#[cfg(any( + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "arm", + target_arch = "x86" +))] pub fn bench(c: &mut Criterion) { hash_group(c, "sha256", Sha256::digest, MessageDigest::sha256()); } diff --git a/bench/benches/primitives/sha512.rs b/bench/benches/primitives/sha512.rs index 179fd2cca..f54a3d2ac 100644 --- a/bench/benches/primitives/sha512.rs +++ b/bench/benches/primitives/sha512.rs @@ -10,6 +10,20 @@ use crate::hash_group; /// `ci/bench_arches.py`). pub const USES: &[&str] = &["sha512"]; +#[cfg(not(any( + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "arm", + target_arch = "x86" +)))] +pub fn bench(_: &mut Criterion) {} + +#[cfg(any( + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "arm", + target_arch = "x86" +))] pub fn bench(c: &mut Criterion) { hash_group(c, "sha512", Sha512::digest, MessageDigest::sha512()); } diff --git a/ci/check_arch_gates.py b/ci/check_arch_gates.py index 5d13212b3..193c003eb 100644 --- a/ci/check_arch_gates.py +++ b/ci/check_arch_gates.py @@ -24,7 +24,7 @@ import sys ROOT = pathlib.Path(__file__).resolve().parent.parent -ARCHES = frozenset({"x86_64", "aarch64", "arm", "x86"}) +ARCHES = frozenset({"x86_64", "aarch64", "arm", "x86", "powerpc64"}) INNER_CFG = re.compile(r"^#!\[cfg\((.*?)\)\]$", re.MULTILINE | re.DOTALL) BENCH_CFG = re.compile(r"^#\[cfg\(((?!not\().*?)\)\]\npub fn bench\(", re.MULTILINE | re.DOTALL) diff --git a/tests/rfc1321/main.rs b/tests/rfc1321/main.rs index 6171791b6..2d5c96de6 100644 --- a/tests/rfc1321/main.rs +++ b/tests/rfc1321/main.rs @@ -5,6 +5,13 @@ //! always run. Every vector of the suite is checked, in one call and split //! into pieces at every position. +#![cfg(any( + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "arm", + target_arch = "x86" +))] + use verified_garbage::hashes::HashFunction; use verified_garbage::hashes::md5::Md5; From 7e237492715db9469d1c28a021f137bdd8931b3f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 1 Oct 2026 01:48:22 +0000 Subject: [PATCH 5/9] Arch gates: gate the ML-DSA ACVP test macros tests/acvp/mldsa.rs (new on main) names no architecture, so with powerpc64 in check_arch_gates.py's ARCHES it would be built for PPC64LE, where hashes::sha3 (which its macro uses) is not. Gate it on SHA-3's architectures; the parameter sets' files that expand it are gated already. Refs #55 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Fw6RULWHCeVgMDGc3wRrwE --- tests/acvp/mldsa.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/acvp/mldsa.rs b/tests/acvp/mldsa.rs index 7861b22d5..64efc8258 100644 --- a/tests/acvp/mldsa.rs +++ b/tests/acvp/mldsa.rs @@ -7,6 +7,13 @@ //! by verifying its signatures, and against Wycheproof's seed vectors //! (`tests/wycheproof/mldsa*.rs`). +#![cfg(any( + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "arm", + target_arch = "x86" +))] + /// Defines the tests of the parameter set named `$name` (`"ML-DSA-44"`), of /// the module `$module` and its key types. // Used by the parameter sets' files, on the architectures they support. From 5b9345f527cf9d17fe2c56723ba3f0077e6ec669 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 1 Oct 2026 13:13:53 +0000 Subject: [PATCH 6/9] CI: install the ppc64le (and ARMv7) Rust toolchains into a fresh rustup home Since Rust 1.99.0 (2026-10-01), `rustup toolchain install stable` fails in pyca's runner images: updating the image's preinstalled toolchain removes its previous components, and the image's installation lacks files their manifests list ("failure removing component 'cargo-...', directory does not exist: 'share/man/man1/cargo.1'"). The ARMv7 job on main fails the same way; #458 fixes it by pointing RUSTUP_HOME at a fresh directory, so that the toolchain is installed rather than upgraded. Do the same for the ppc64le platform (and its 1.88 exclusion, which must match it), and carry #458's ARMv7 change so that this PR's CI is green whichever lands first. Refs #55 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Fw6RULWHCeVgMDGc3wRrwE --- .github/workflows/ci.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 75cbc33c4..a988825de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -180,13 +180,15 @@ jobs: - os: ubuntu-24.04-arm container: image: ghcr.io/pyca/cryptography-runner-ubuntu-rolling:armv7l - options: --env RUSTUP_HOME=/root/.rustup + # A fresh home avoids upgrading the image's incomplete Rust installation. + options: --env RUSTUP_HOME=/tmp/verified-garbage-rustup # PPC64LE: pyca's ppc64le runner image, on a ppc64le runner (as # pyca/cryptography does). - os: ubuntu-24.04-ppc64le container: image: ghcr.io/pyca/cryptography-runner-ubuntu-rolling:ppc64le - options: --env RUSTUP_HOME=/root/.rustup + # A fresh home avoids upgrading the image's incomplete Rust installation. + options: --env RUSTUP_HOME=/tmp/verified-garbage-rustup - os: macos-latest - os: windows-latest rust: [stable, "1.88"] @@ -197,7 +199,7 @@ jobs: os: ubuntu-24.04-ppc64le container: image: ghcr.io/pyca/cryptography-runner-ubuntu-rolling:ppc64le - options: --env RUSTUP_HOME=/root/.rustup + options: --env RUSTUP_HOME=/tmp/verified-garbage-rustup rust: "1.88" # One job each for the upcoming toolchains, to catch regressions (and # new warnings, which are errors here) before they reach stable. From 98ac904dc96ffc3c212bcdd34865234aa79dc62a Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 1 Oct 2026 13:17:35 +0000 Subject: [PATCH 7/9] CI: install the Thumb job's Rust toolchain into a fresh rustup home The Thumb job (new on main) runs in the same ARMv7 image, and fails to upgrade its preinstalled stable toolchain to 1.99.0 in the same way; #458 gives it a fresh RUSTUP_HOME too. Carry that change here as well, so this PR's CI is green whichever lands first. Refs #55 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Fw6RULWHCeVgMDGc3wRrwE --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 65218266d..6b8d06cab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -360,7 +360,8 @@ jobs: runs-on: ubuntu-24.04-arm container: image: ghcr.io/pyca/cryptography-runner-ubuntu-rolling:armv7l - options: --env RUSTUP_HOME=/root/.rustup + # A fresh home avoids upgrading the image's incomplete Rust installation. + options: --env RUSTUP_HOME=/tmp/verified-garbage-rustup env: RUSTUP_TOOLCHAIN: ${{ matrix.rust }} # For every cargo command, with the container's native linker. From b854b3949354453eeea8f729a4e71d4bb7d0965d Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 1 Oct 2026 13:18:17 +0000 Subject: [PATCH 8/9] Benchmarks: install the ARMv7 Rust toolchain into a fresh rustup home The ARMv7 benchmark job runs in the same image as ci.yml's ARMv7 jobs, and fails to upgrade its preinstalled stable toolchain to 1.99.0 in the same way ("failure removing component 'cargo-...', directory does not exist: 'share/man/man1/cargo.1'"). Point it at a fresh RUSTUP_HOME too. Refs #55 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Fw6RULWHCeVgMDGc3wRrwE --- ci/bench_arches.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ci/bench_arches.py b/ci/bench_arches.py index b634d6ed5..921f9eb5c 100644 --- a/ci/bench_arches.py +++ b/ci/bench_arches.py @@ -47,7 +47,9 @@ "arm": { "os": "ubuntu-24.04-arm", "image": "ghcr.io/pyca/cryptography-runner-ubuntu-rolling:armv7l", - "options": "--env RUSTUP_HOME=/root/.rustup", + # A fresh home avoids upgrading the image's incomplete Rust + # installation (as in ci.yml). + "options": "--env RUSTUP_HOME=/tmp/verified-garbage-rustup", }, } From 7d7eb38b161339c7d8c7878e4237c8d84f99d69c Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 1 Oct 2026 23:12:52 +0000 Subject: [PATCH 9/9] Gate the SHA-224 benchmark on the architectures of SHA-224 It landed on main without a gate, which ci/check_arch_gates.py requires once powerpc64 is an architecture: SHA-224 is not on PPC64LE yet. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Fw6RULWHCeVgMDGc3wRrwE --- bench/benches/primitives/sha224.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/bench/benches/primitives/sha224.rs b/bench/benches/primitives/sha224.rs index 67000f877..0c6e93e70 100644 --- a/bench/benches/primitives/sha224.rs +++ b/bench/benches/primitives/sha224.rs @@ -10,6 +10,20 @@ use crate::hash_group; /// `ci/bench_arches.py`). pub const USES: &[&str] = &["sha224", "sha256"]; +#[cfg(not(any( + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "arm", + target_arch = "x86" +)))] +pub fn bench(_: &mut Criterion) {} + +#[cfg(any( + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "arm", + target_arch = "x86" +))] pub fn bench(c: &mut Criterion) { hash_group(c, "sha224", Sha224::digest, MessageDigest::sha224()); }