mirror of
https://github.com/saymrwulf/proof-aware-crypto-tooling-agent.git
synced 2026-09-03 19:53:43 +00:00
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>
49 lines
2.5 KiB
Diff
49 lines
2.5 KiB
Diff
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)
|
|
+}
|