proof-aware-crypto-tooling-.../dogfood/quorum/verify-slhdsa/expose-mono.patch

50 lines
2.5 KiB
Diff
Raw Normal View History

quorum: pacta-verify-slhdsa — the SLH-DSA head-checker built from the proven source Fifth quorum member, first post-quantum one: verifies an SLH-DSA-SHA2-128s signature by calling slh_verify_128s, the extraction root the eleven fips205 certificates cover (apex fips205.slh_verify_128s_accepts_iff). Verify-only like the other four: quorum members judge, they never sign. Build discipline, because "built from the proven source" is a claim that has to survive a hostile reader: build-verify-slhdsa.sh REFUSES to build if the pinned checkout is dirty or at any commit other than a3ce8e8, exports the pinned commit via git archive (never a working copy), applies expose-mono.patch to that scratch copy, and then DIFFS the patched verify_mono.rs against the pinned one, aborting if any existing line changed rather than being appended. The patch is a visibility keyword plus its doc comment (the crate denies missing_docs, so pub mod alone does not compile) and one appended argument-assembly function whose body is the crate's own test helper. The extraction root is provably untouched. A provenance sidecar lands beside the binary: source commit, patch hash, main.rs hash, rustc, and a not_covered field naming what no certificate reaches — M-prime assembly (including the pure/prehash domain-separator byte), hex/file IO, the compiler; signing and keygen out of scope entirely. Demonstrated against OpenSSL 3.5.5 on a throwaway key: valid signature OK both ways, wrong message INVALID, corrupted signature INVALID. The agreement is itself a finding — this binary assembles M' = 0x00 || 0x00 || payload (pure variant, empty context) and OpenSSL evidently does the same. Convention matches the other members: template + main.rs + patch + build script tracked; rendered Cargo.toml, lock, target/ and the .build-slhdsa scratch tree ignored. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-06 19:52:56 +00:00
Expose the proven verify root so a quorum binary can call it.
APPLIED TO A COPY of fips205-source at the pinned commit, never to the pinned
checkout itself. Two hunks, and the reason each is the smallest possible:
1. `mod verify_mono;` -> `pub mod verify_mono;`
A visibility keyword. Rust's `src/bin/` and `examples/` targets are
SEPARATE crates, so neither can reach a `pub(crate)` item; the module has
to be public for any binary to call into it at all.
2. A new `verify_mono_bytes` function, appended.
It only assembles arguments: split the 32-byte public key into pk_seed and
pk_root, deserialize the 7856-byte signature, call `slh_verify_128s`. The
body is copied from the crate's OWN test helper `internal_inputs`, so the
conversion is the one the crate already trusts rather than one invented
here. Exposing the argument types and their fields instead would have
meant four more visibility changes across two files.
WHAT THIS DOES NOT CHANGE. No existing line's semantics. The extraction root
`slh_verify_128s` is untouched -- same body, same callees. Module visibility and
an added sibling function do not alter the MIR of an existing function, so the
code the certificates cover compiles to what it compiled to before. What IS
true and must be said: the binary is built from `pinned commit + this patch`,
not from the pinned commit alone, and the diff below is the whole of the
difference.
--- a/src/lib.rs
+++ b/src/lib.rs
@@
-mod verify_mono; // Aeneas-compat monomorphic verify path (formal-verification campaign; additive)
+pub mod verify_mono; // Aeneas-compat monomorphic verify path (formal-verification campaign; additive)
--- a/src/verify_mono.rs
+++ b/src/verify_mono.rs
@@ (appended after slh_verify_128s)
+/// Byte-level entry to the PROVEN root, for out-of-crate callers.
+///
+/// Assembles arguments only. `mprime` is FIPS 205's M' and is built by the
+/// CALLER -- its construction is outside every certificate (TRUSTED-BASE item
+/// 10), which is exactly why it is a parameter here and not computed inside.
+pub fn verify_mono_bytes(mprime: &[u8], sig_bytes: &[u8; 7856], pk_bytes: &[u8; 32]) -> bool {
+ let mut pk_seed = [0u8; 16];
+ let mut pk_root = [0u8; 16];
+ pk_seed.copy_from_slice(&pk_bytes[0..16]);
+ pk_root.copy_from_slice(&pk_bytes[16..32]);
+ let pk = SlhPublicKey { pk_seed, pk_root };
+ let sig = SlhDsaSig::<12, 7, 9, 14, 35, 16>::deserialize(sig_bytes);
+ slh_verify_128s(mprime, &sig, &pk)
+}