Skip to content

Commit f77572e

Browse files
authored
ci: restore weekly assurance gates (#87)
Narrow Miri and test-only reachability so warning-denying builds no longer compile unused CRC, dispatch, tuning, Keccak, XXH3, or Linux ML-KEM assembly helpers. Keep the CT API inventory on the pinned stable compiler, discover emitted evidence across stable and nightly Cargo layouts, and record the six audited public API additions for every release-evidence target. Document CtDecision's redacted Debug output and cover the stable rustdoc JSON environment in the CT validation regression.
1 parent fec8c39 commit f77572e

11 files changed

Lines changed: 88 additions & 29 deletions

File tree

ct.toml

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2737,8 +2737,8 @@ name = "x86_64-unknown-linux-gnu"
27372737
group = "linux"
27382738
backend = "llvm"
27392739
linker = "platform-default-unpinned"
2740-
compiler_api_item_count = 2230
2741-
compiler_api_sha256 = "671da8ccb1855d450418dc6fc3e79c9b9a2d50c518f98761d8c0fda6abb7c3b3"
2740+
compiler_api_item_count = 2236
2741+
compiler_api_sha256 = "ec37f397970ac2d9bce6c5188c48c0e1dfbd9b1d321674bce13395acdd6781f8"
27422742
claim = "ct-intended"
27432743
physical_timing = "required"
27442744
binsec = "required"
@@ -2749,8 +2749,8 @@ name = "aarch64-unknown-linux-gnu"
27492749
group = "linux"
27502750
backend = "llvm"
27512751
linker = "platform-default-unpinned"
2752-
compiler_api_item_count = 2224
2753-
compiler_api_sha256 = "2bc45c4c1aa47db7c9786ca5aae449cfc7e4370899841c96efbaeb7b67a909ae"
2752+
compiler_api_item_count = 2230
2753+
compiler_api_sha256 = "2f64f9277c6acbb0510f9b6bb2753b6adcf862f6e8f2945831c5b7607d0a9240"
27542754
claim = "ct-intended"
27552755
physical_timing = "required"
27562756
binsec = "required"
@@ -2809,8 +2809,8 @@ name = "aarch64-apple-darwin"
28092809
group = "macos"
28102810
backend = "llvm"
28112811
linker = "apple-ld-unpinned"
2812-
compiler_api_item_count = 2224
2813-
compiler_api_sha256 = "2bc45c4c1aa47db7c9786ca5aae449cfc7e4370899841c96efbaeb7b67a909ae"
2812+
compiler_api_item_count = 2230
2813+
compiler_api_sha256 = "2f64f9277c6acbb0510f9b6bb2753b6adcf862f6e8f2945831c5b7607d0a9240"
28142814
claim = "ct-intended"
28152815
physical_timing = "required"
28162816
binsec = "unsupported"
@@ -2834,8 +2834,8 @@ name = "s390x-unknown-linux-gnu"
28342834
group = "ibm"
28352835
backend = "llvm"
28362836
linker = "platform-default-unpinned"
2837-
compiler_api_item_count = 2223
2838-
compiler_api_sha256 = "e54c2a119d506935fda2279f7d7f9f75506ce86fe5b0b6fc72fd08a7e8090883"
2837+
compiler_api_item_count = 2229
2838+
compiler_api_sha256 = "8c5e606f4792a7a5052da0e00093fbaa01f56f2223264b7df67bc3ada774d4d5"
28392839
claim = "ct-intended"
28402840
physical_timing = "required"
28412841
binsec = "unsupported"
@@ -2847,8 +2847,8 @@ name = "powerpc64le-unknown-linux-gnu"
28472847
group = "ibm"
28482848
backend = "llvm"
28492849
linker = "platform-default-unpinned"
2850-
compiler_api_item_count = 2223
2851-
compiler_api_sha256 = "e85c77d60d67a4648efd50cda101b8248026eb57d9008ceaae2511e319b502d9"
2850+
compiler_api_item_count = 2229
2851+
compiler_api_sha256 = "24448b0c3dc339211793edc2999bcb77b57956ddde4c1d1111247c52067b9c9f"
28522852
claim = "ct-intended"
28532853
physical_timing = "required"
28542854
binsec = "unsupported"
@@ -2860,8 +2860,8 @@ name = "riscv64gc-unknown-linux-gnu"
28602860
group = "linux"
28612861
backend = "llvm"
28622862
linker = "platform-default-unpinned"
2863-
compiler_api_item_count = 2223
2864-
compiler_api_sha256 = "119e6aef49beac534d7155cba8b15a7774c7a4fb6990c9e304b07998844a0858"
2863+
compiler_api_item_count = 2229
2864+
compiler_api_sha256 = "3d1b7cfa425696e8d060773d2f2778119ce27b8aac3f69daaef1c24ddfb6e677"
28652865
claim = "ct-intended"
28662866
physical_timing = "required"
28672867
binsec = "unsupported"

docs/constant-time.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,10 @@ opaque authentication success/failure result may leak.
8787
Secret-bearing fixed-size keys, shared secrets, authentication tags, keypairs,
8888
and keyed outputs do not implement `PartialEq` or `Eq`. Their inherent `ct_eq`
8989
methods return `CtDecision`, an opaque, non-`Copy` value with no public
90-
constructor, formatting, equality, or implicit boolean conversion. Decisions
91-
can be composed with bitwise `&`, `|`, and `!`; the consuming `declassify()`
92-
method is the only public route to a branchable equality bit.
90+
constructor, equality, or implicit boolean conversion. Its `Debug` output is
91+
the fixed redacted string `CtDecision(..)` and does not expose the decision.
92+
Decisions can be composed with bitwise `&`, `|`, and `!`; the consuming
93+
`declassify()` method is the only public route to a branchable equality bit.
9394

9495
Verification APIs keep that boundary inside the primitive and return one opaque
9596
`Result`. Public keys, nonces, signatures, and ciphertext containers are public

scripts/ct/artifacts.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ fi
114114
OUT_DIR="$ROOT/target/ct/$TARGET/$PROFILE"
115115
ARTIFACT_DIR="$OUT_DIR/artifacts"
116116
BUILD_TARGET_DIR="$ROOT/target/ct-build/$TARGET/$PROFILE"
117-
DEPS_DIR="$BUILD_TARGET_DIR/$TARGET/$PROFILE/deps"
117+
EMIT_ROOT="$BUILD_TARGET_DIR/$TARGET/$PROFILE"
118118
rm -rf "$OUT_DIR"
119119
rm -rf "$BUILD_TARGET_DIR"
120120
mkdir -p "$ARTIFACT_DIR"
@@ -138,7 +138,7 @@ LIB_EMITTED=()
138138
while IFS= read -r artifact; do
139139
LIB_EMITTED+=("$artifact")
140140
done < <(
141-
find "$DEPS_DIR" -maxdepth 1 -type f \
141+
find "$EMIT_ROOT" -type f \
142142
\( -name 'rscrypto_ct_harness*.ll' \
143143
-o -name 'rscrypto_ct_harness*.s' \
144144
-o -name 'rscrypto_ct_harness*.o' \
@@ -147,7 +147,7 @@ done < <(
147147
)
148148

149149
if [[ ${#LIB_EMITTED[@]} -eq 0 ]]; then
150-
echo "no CT harness emitted artifacts found in $DEPS_DIR" >&2
150+
echo "no CT harness emitted artifacts found under $EMIT_ROOT" >&2
151151
exit 1
152152
fi
153153
if [[ ${#LIB_EMITTED[@]} -ne 3 ]]; then
@@ -201,7 +201,7 @@ BIN_EMITTED=()
201201
while IFS= read -r artifact; do
202202
BIN_EMITTED+=("$artifact")
203203
done < <(
204-
find "$DEPS_DIR" -maxdepth 1 -type f \
204+
find "$EMIT_ROOT" -type f \
205205
\( -name 'rscrypto_ct_evidence*.ll' \
206206
-o -name 'rscrypto_ct_evidence*.s' \
207207
-o -name 'rscrypto_ct_evidence*.o' \

scripts/ct/evidence_validation_test.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,36 @@ def main() -> None:
142142
configure_target_environment("powerpc64le-unknown-linux-gnu", unrelated_environment)
143143
assert unrelated_environment == {}
144144

145+
captured_rustdoc: dict[str, object] = {}
146+
original_run = manifest_validation.subprocess.run
147+
148+
def capture_rustdoc(command, **kwargs):
149+
captured_rustdoc["command"] = command
150+
captured_rustdoc["env"] = kwargs.get("env")
151+
return subprocess.CompletedProcess(command, 1, "", "rustdoc unavailable")
152+
153+
manifest_validation.subprocess.run = capture_rustdoc
154+
try:
155+
with tempfile.TemporaryDirectory() as temporary:
156+
inventory_errors: list[str] = []
157+
assert (
158+
manifest_validation.compiler_public_api_snapshot(
159+
Path(temporary),
160+
"aarch64-apple-darwin",
161+
("rscrypto::auth",),
162+
inventory_errors,
163+
)
164+
is None
165+
)
166+
assert inventory_errors == ["compiler public-API inventory failed: rustdoc unavailable"]
167+
finally:
168+
manifest_validation.subprocess.run = original_run
169+
170+
rustdoc_env = captured_rustdoc["env"]
171+
assert isinstance(rustdoc_env, dict)
172+
assert rustdoc_env["RUSTC_BOOTSTRAP"] == "rscrypto"
173+
assert captured_rustdoc["command"][-4:] == ["-Z", "unstable-options", "--output-format", "json"]
174+
145175
commit = "a" * 40
146176
validate_exact_candidate("1.2.3", commit, "1.2.3", commit)
147177
expect_failure(lambda: validate_exact_candidate("1.2.3", commit, "1.2.4", commit))

scripts/ct/validate.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import argparse
77
import hashlib
88
import json
9+
import os
910
import re
1011
import subprocess
1112
import sys
@@ -242,7 +243,11 @@ def compiler_public_api_snapshot(
242243
"--output-format",
243244
"json",
244245
]
245-
completed = subprocess.run(command, cwd=root, capture_output=True, text=True, check=False)
246+
rustdoc_env = os.environ.copy()
247+
# rustdoc JSON is unstable even when the selected release compiler supports it. Scope the escape hatch to
248+
# rscrypto so the inventory describes the exact compiler used by this CT evidence lane.
249+
rustdoc_env["RUSTC_BOOTSTRAP"] = "rscrypto"
250+
completed = subprocess.run(command, cwd=root, capture_output=True, text=True, check=False, env=rustdoc_env)
246251
if completed.returncode != 0:
247252
detail = completed.stderr.strip().splitlines()
248253
suffix = f": {detail[-1]}" if detail else ""

src/auth/mlkem/portable/aarch64.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ unsafe extern "C" {
6262
input2: *const u8,
6363
caps: *const usize,
6464
) -> u64;
65-
#[cfg(any(test, feature = "diag"))]
65+
#[cfg(test)]
6666
fn rscrypto_mlkem_rej_uniform_3blocks_aarch64_linux(out: *mut u16, input: *const u8) -> usize;
67-
#[cfg(any(test, feature = "diag"))]
67+
#[cfg(test)]
6868
fn rscrypto_mlkem_basemul_accumulate_aarch64_linux(
6969
acc: *mut u16,
7070
a: *const u16,
@@ -187,7 +187,7 @@ pub(super) unsafe fn sample_ntt_rej_uniform_triple_block_bounded_asm(
187187
unpack_triple_counts(packed)
188188
}
189189

190-
#[cfg(all(any(test, feature = "diag"), target_os = "linux"))]
190+
#[cfg(all(test, target_os = "linux"))]
191191
#[inline]
192192
pub(super) unsafe fn sample_ntt_rej_uniform_3blocks_asm(out: *mut u16, input: *const u8) -> usize {
193193
// SAFETY: Linux aarch64 three-block SampleNTT rejection parser call because:
@@ -200,7 +200,7 @@ pub(super) unsafe fn sample_ntt_rej_uniform_3blocks_asm(out: *mut u16, input: *c
200200
}
201201

202202
#[inline]
203-
#[cfg(any(test, feature = "diag", target_os = "macos"))]
203+
#[cfg(any(test, target_os = "macos"))]
204204
/// # Safety
205205
///
206206
/// The active platform must provide the matching ML-KEM AArch64 assembly

src/checksum/common/tests.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ where
3333
/// Test that combine produces crc(A || B) from crc(A) and crc(B).
3434
///
3535
/// This is the fundamental combine property that enables parallel CRC computation.
36+
#[cfg(not(miri))]
3637
#[inline]
3738
pub(in crate::checksum) fn test_combine_property(data: &[u8], split: usize) {
3839
let split = split.checked_rem(data.len()).unwrap_or(0);
@@ -63,6 +64,7 @@ where
6364
}
6465

6566
/// Test combine with empty second part (identity case).
67+
#[cfg(not(miri))]
6668
#[inline]
6769
pub(in crate::checksum) fn test_combine_empty_suffix(data: &[u8]) {
6870
let crc_data = C::checksum(data);
@@ -73,6 +75,7 @@ where
7375
}
7476

7577
/// Test combine with empty first part.
78+
#[cfg(not(miri))]
7679
#[inline]
7780
pub(in crate::checksum) fn test_combine_empty_prefix(data: &[u8]) {
7881
let crc_empty = C::checksum(&[]);
@@ -98,6 +101,7 @@ where
98101
}
99102

100103
/// Test streaming with byte-at-a-time updates.
104+
#[cfg(not(miri))]
101105
#[inline]
102106
pub(in crate::checksum) fn test_streaming_byte_at_a_time(data: &[u8]) {
103107
let oneshot = C::checksum(data);
@@ -112,6 +116,7 @@ where
112116
}
113117

114118
/// Test streaming across a specific chunk size boundary.
119+
#[cfg(not(miri))]
115120
#[inline]
116121
pub(in crate::checksum) fn test_streaming_chunked(data: &[u8], chunk_size: usize) {
117122
if chunk_size == 0 {
@@ -167,6 +172,7 @@ where
167172
/// 1. Streaming first half
168173
/// 2. Computing second half separately
169174
/// 3. Combining the results
175+
#[cfg(not(miri))]
170176
#[inline]
171177
pub(in crate::checksum) fn test_streaming_and_combine(data: &[u8]) {
172178
if data.is_empty() {

src/checksum/crc64/portable.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ use crate::checksum::common::portable;
1010

1111
/// CRC-64-XZ slice-by-8 computation.
1212
#[inline]
13-
#[cfg(all(test, any(target_arch = "x86_64", target_arch = "aarch64")))]
13+
#[cfg(all(test, not(miri), any(target_arch = "x86_64", target_arch = "aarch64")))]
1414
pub(super) fn crc64_slice8_xz(crc: u64, data: &[u8]) -> u64 {
1515
crc64_slice8(crc, data, &kernel_tables::XZ_TABLES_8)
1616
}
1717

1818
/// CRC-64-NVME slice-by-8 computation.
1919
#[inline]
20-
#[cfg(all(test, any(target_arch = "x86_64", target_arch = "aarch64")))]
20+
#[cfg(all(test, not(miri), any(target_arch = "x86_64", target_arch = "aarch64")))]
2121
pub(super) fn crc64_slice8_nvme(crc: u64, data: &[u8]) -> u64 {
2222
crc64_slice8(crc, data, &kernel_tables::NVME_TABLES_8)
2323
}

src/hashes/crypto/keccak.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ fn read_unaligned_block_lane<const RATE: usize>(block: &[u8; RATE], lane: usize)
434434
///
435435
/// Since `RATE` is a const generic, `RATE / 8` is compile-time known and LLVM
436436
/// eliminates all `if lane < lanes` branches — the result is straight-line code.
437-
#[cfg(target_arch = "aarch64")]
437+
#[cfg(all(target_arch = "aarch64", not(miri)))]
438438
#[inline]
439439
fn keccakf_absorb_portable<const RATE: usize>(state: &mut [u64; 25], block: &[u8; RATE]) {
440440
debug_assert_eq!(RATE % 8, 0);

src/hashes/fast/xxh3/kernels.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ impl Xxh3KernelId {
4343

4444
/// Long-path-only entry for 64-bit hash (>240B, no ≤240B length checks).
4545
#[cfg(any(
46-
test,
46+
all(test, not(miri)),
4747
not(any(
4848
all(target_arch = "x86_64", any(target_feature = "avx512f", target_feature = "avx2")),
4949
all(target_arch = "aarch64", target_feature = "neon"),
@@ -69,7 +69,7 @@ pub(crate) fn hash64_long_fn(id: Xxh3KernelId) -> fn(&[u8], u64) -> u64 {
6969

7070
/// Long-path-only entry for 128-bit hash (>240B, no ≤240B length checks).
7171
#[cfg(any(
72-
test,
72+
all(test, not(miri)),
7373
not(any(
7474
all(target_arch = "x86_64", any(target_feature = "avx512f", target_feature = "avx2")),
7575
all(target_arch = "aarch64", target_feature = "neon"),

0 commit comments

Comments
 (0)