Commit graph

74 commits

Author SHA1 Message Date
a3ce8e8644 extract script: state what is actually guaranteed, and by what
Round-8 review found a tautological assert in this script — it compared a dict
against the comprehension that had just built it, so it could never fire, in the
very script written to fix a provenance-honesty defect. The first repair
(comparing kept[k] to t[k]) was tautological for the same reason; a negative test
confirmed it did not catch a tampered input.

No check inside a transformer can detect a corrupted input, because the
transformer is what defines the output from that input. Both vacuous checks are
removed and the lesson is recorded in the code. The header and the embedded
_provenance now say that fields are carried through BY CONSTRUCTION — a property
a reviewer reads — and name the guarantees that can actually fail: the pinned
upstream SOURCE_SHA256, the requirement that sk be present to be dropped, the
expected group and per-group test counts, and verify mode's byte-comparison of
the committed file. The last two were negative-tested: removing sk aborts with
'has no field sk to drop', deleting a test aborts with 'group 19 has 13 tests'.

The script is also now described as re-runnable EVIDENCE rather than a gate: it
needs network access, so nothing invokes it automatically.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 14:41:01 +02:00
c945821bf9 vectors: make the extraction re-derivable and the provenance claim literally true
Round-7 review (both reviewers, independently) found that the ACVP provenance
note said "the per-test private key `sk` dropped ... Nothing else altered" while
the extraction had in fact also dropped `additionalRandomness` and `deferred`
from all 42 tests, plus the top-level `isSample`. No field the tests consume was
affected and no verdict changed — but the provenance block is the audit trail a
third party diffs against, and as written it would have produced a false alarm
or taught the next reviewer to wave differences through.

Rather than reword the note, the transformation is now executable and pinned:
tests/nist_acvp_vectors/extract_sha2_128s.py re-derives the file, verifies the
upstream sha256 before doing anything, requires exactly 3 groups x 14 tests,
removes exactly ONE field (`sk`) and asserts that invariant, carries every other
per-test, group and top-level key through untouched, and writes canonical
output. Run with no arguments it VERIFIES the committed file against a fresh
extraction; --write regenerates. The file was regenerated with it, so
"only `sk` removed" is now literally true and machine-checkable.

Also from round 7, precision in the tests themselves:
- exact assertions replace floors: `points == 108` (was >= 100),
  `with_ctx == 9` (was > 0), and prehash `3/4/7` (was `checked > 0`). With 7 of
  14 prehash vectors skipped for unimplemented hash functions, a floor would
  have let real coverage fall from 3 to 1 while the total still summed to 14.
- the randomized bridge's doc comment claimed corruption "across the WHOLE
  signature"; measured, the schedule hits 72 distinct positions in 11..=7779,
  never bytes 0-10 or 7780-7855. Corrected to the measured statement.

Verified on stable Rust (rustc 1.95) as well as the pinned nightly: the bridge
needs no nightly feature, so a third party can run all of it with cargo alone.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 13:01:13 +02:00
3153988c4e tests: NIST ACVP SHA2-128s verification KATs + a real differential bridge
External review flagged across rounds 4-6 that the empirical evidence tying the
proved model to the deployed code had not moved by a single data point in six
rounds: the sole bridge was nine assertion points (3 rounds, one fixed seed,
corruption always at byte 100), and the ACVP vectors vendored here contain NO
SLH-DSA-SHA2-128s sigVer group at all — the one parameter set this verification
campaign is about had zero NIST known-answer verification coverage.

VECTORS. tests/nist_acvp_vectors/SLH-DSA-sigVer-FIPS205/sha2_128s_extracted.json
carries the three SHA2-128s sigVer groups extracted verbatim from the official
NIST ACVP-Server vector set (source URL, upstream file sha256 and extraction
method recorded in the file's own _provenance block; per-test private keys
dropped as unnecessary to verify). 42 tests: 2 valid and 12 negative per group,
the negatives spread over structurally distinct corruption sites — modified R,
modified SIGFORS, modified SIGHT, modified message, too-small and too-large
signatures.

TESTS (all in src/verify_mono.rs, so they exercise the monomorphic path the Lean
certificates are about):

- mono_matches_nist_acvp_128s_internal — NIST's `internal` group carries M'
  directly, which is exactly what slh_verify_128s consumes, so these are true
  known-answer tests OF THE PROVED PATH: 10 executed, 4 attributed to
  deserialization (wrong-length signatures, rejected above the extraction root).
  Accounting is exact — all 14 are accounted for, nothing silently skipped.
- mono_matches_nist_acvp_128s_external_pure — builds M' the way lib.rs does and
  requires mono, the deployed verifier and NIST to agree: 10 executed, 9 of them
  with a NON-EMPTY context. This is the first empirical check of the
  domain-separator byte and context-length prefix that TRUSTED-BASE item 10
  declares outside every proof.
- deployed_matches_nist_acvp_128s_prehash — validates the deployed prehash path
  for 128s: 3 executed, 4 wrong-length, and 7 skipped because NIST exercises
  prehash functions (SHA3-*, truncated SHA2) this crate's `Ph` enum does not
  implement. Counted and reported rather than hidden.
- mono_matches_deployed_randomized — replaces the fixed-seed/fixed-byte bridge:
  12 rounds, varying message lengths including empty, corruption spread across
  the WHOLE 7856-byte signature, plus wrong-public-key and wrong-context cases
  that were never exercised before. 108 assertion points, each requiring mono
  and deployed to agree.

Bridge coverage: 9 assertion points -> 131, of which 20 are NIST known-answer
tests on the proved path where there were previously none.

No change to any verify-path function: this commit touches test code and test
data only, so the Charon/Aeneas extraction is unaffected (verified separately by
re-running extract.sh and diffing the generated model).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-28 09:42:56 +02:00
797b4ef263 reproducibility: commit Cargo.lock + pin rust-toolchain (review round 2)
External review round 2 (GPT B1.5) flagged that no Cargo.lock was committed
and the extraction toolchain was undocumented. Fixes:

- Un-ignore and commit Cargo.lock (this is a verification-pinned snapshot, not
  a library — a locked dependency graph is part of the reproducible artifact).
- Add rust-toolchain.toml pinning nightly-2026-06-01, the exact channel the
  Charon/Aeneas transpiler uses, so `charon cargo` extraction and the `cargo
  test` differential harness agree on one compiler.

Verified: re-running verification/extract.sh against this tree reproduces
gen/SlhVerify/{Types,Funs}.lean BYTE-IDENTICALLY (sha256 unchanged) — these
additions do not perturb the extracted Lean model.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 19:08:47 +02:00
bea105195b helpers: de-plumb round 2 — to_int & base_2b iterators to index loops
Removes the last two untranslatable iterator adapters on the verify path so
their loops extract to real definitions (no Take/IterMut axioms in any cone):

- to_int: `for item in x.iter().take(n)` -> `for i in 0..n { ... x[i] }`. The
  Take adapter was the LAST non-oracle, non-zeroize axiom in the model.
- base_2b: `for item in baseb.iter_mut()` -> `for out in 0..out_len { ...;
  baseb[out] = ... }`. The IterMut adapter carried a next_back write-back
  closure as loop state (a function-typed fixpoint), painful to reason about.

Both are semantics-identical for every FIPS 205 parameter set: the asserts
already pin x.len()==n and out_len==baseb.len(), so the index ranges visit
exactly the same elements/slots in the same order with the same values. The
inner `while bits < b` loop of base_2b was already clean and is untouched.

Validation: cargo test --features slh_dsa_sha2_128s --lib green — all 12
parameter-set round trips AND mono_matches_deployed_verify (mono == deployed
generic verify on valid / corrupted / wrong-message inputs).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 08:47:15 +02:00
6f6a9d65e7 verify_mono: de-plumb Result/iterator idioms (8 sites, semantics identical)
Aeneas translates the affected core-library instances only as axioms
(TryFrom/unwrap Result plumbing, iterator adapters, the &u32 Sub instance),
which would put transpiler axioms into certificate cones. Each site is
replaced by a construct that extracts to a real definition, with identical
semantics for every FIPS 205 parameter set:

- wots/xmss/ht/fors/slh const-generic preps: u32::try_from(X).unwrap() ->
  X as u32 (every parameter <= 63; cast lossless).
- wots checksum loop: msg.iter().take(2*N) with &u32 subtraction -> index
  loop over 0..2*N with value reads (same iteration space, same values).
- ht per-layer idx_leaf and slh idx_leaf: u32::try_from + is_err/unwrap ->
  plain cast. Both values are pre-masked to hp' resp. h/d bits, and hp',
  h/d <= 9 for every FIPS 205 parameter set, so the conversion cannot
  fail: the removed error branch is dead code.

Validation: cargo test --features slh_dsa_sha2_128s --lib green — all 12
parameter-set round trips AND the differential test
mono_matches_deployed_verify (mono verify == deployed generic verify on
valid / corrupted / wrong-message inputs).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 17:04:52 +02:00
2d89ee3fd7 Aeneas-compat: single-slice M' + is_err idiom in mono verify
Two further transpiler-compat refinements found while extracting (Aeneas
reported one Unimplemented in slh_verify_internal_free):

- h_msg / slh_verify take the message-digest input M' as a SINGLE
  contiguous &[u8] instead of &[&[u8]] (nested slices are untranslatable;
  the dalek verify_sha512 single-message-slice lesson). H_msg hashes the
  byte concatenation, so a pre-concatenated M' is bit-identical — the
  differential test builds M' = toByte(0,1)||toByte(0,1)||<>||msg and
  still agrees with the deployed verifier.
- the one let-else (u32::try_from(idx_leaf) else return false) rewritten
  to the translatable is_err/unwrap idiom already used in ht_verify.

Result: charon + aeneas both exit 0 on the verify cone; the extracted
Lean model type-checks. Differential test still passes; default-feature
build still clean (additive).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 22:20:36 +02:00
b4e2293d96 Aeneas-compat: monomorphic SHA2-128s verify path (additive)
The generic verify path threads the six hash primitives through
crate::hashers::Hashers, a struct of fn() POINTERS, which the Aeneas
Rust->Lean transpiler cannot translate (the sole gate-0 obstruction).

This adds src/verify_mono.rs: the verify cone (chain, wots_pk_from_sig,
xmss_pk_from_sig, ht_verify, fors_pk_from_sig, slh_verify_internal)
reproduced with the hash suite reached through NAMED free functions in a
verify_mono::oracle module (the deliberate SHA-2 opaque boundary of the
proof) instead of fn-pointer dereferences, plus a monomorphic entry
slh_verify_128s fixing the SLH-DSA-SHA2-128s constants. Function bodies
are copied verbatim from wots/xmss/hypertree/fors/slh so the extracted
Lean model stays faithful to the deployed algorithm.

Additive and inert: gated behind feature slh_dsa_sha2_128s; the only
change to existing code is two lines declaring the module. All twelve
parameter sets build unchanged (cargo build default features: clean).

Fidelity is pinned by an in-crate differential unit test that pits
slh_verify_128s against the deployed Verifier::verify on freshly
generated signatures — valid (both accept), corrupted (both reject),
and wrong-message (both reject), across three keypairs. Passes.

Same pattern as the curve25519-dalek-source verify_sha512 shim. This is
a frozen snapshot for the formal-verification campaign; no affiliation
with, and no changes proposed to, the upstream project.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 22:12:04 +02:00
5dca0db7e9 snapshot hygiene: strip upstream CI workflows
This is a frozen snapshot for the SLH-DSA verification campaign, pinned
at upstream 30bac08. Upstream's CI does not belong in this namespace
(and must not run here); removing .github is the only deviation from
verbatim. Upstream history is fully preserved beneath this commit.
No affiliation with, and no changes proposed to, the upstream project.
2026-07-22 20:58:59 +02:00
integritychain
30bac08580
Update package.json 2025-09-01 18:34:31 -05:00
eschorn1
a9b02e92cd 0.4.1 RC1 2024-12-22 20:46:43 -06:00
eschorn1
6f0f48eaee fuzz up 2024-12-22 18:32:27 -06:00
eschorn1
e77284a10d v0.4.0 RC1 2024-10-04 11:02:10 -05:00
eschorn1
5f8a96c02b docs, bench stats 2024-10-03 14:48:53 -05:00
eschorn1
49bebd533e audit/coverage/ffi 2024-10-03 12:39:25 -05:00
eschorn1
59314dc043 dudect, workflows 2024-10-03 12:24:28 -05:00
eschorn1
e68fb16221 much tighter clippy 2024-10-02 16:02:02 -05:00
eschorn1
834e75b603 align comments with released spec 2024-10-02 14:50:54 -05:00
eschorn1
f0576386fc hashing 2024-10-01 17:17:27 -05:00
eschorn1
899179a1e1 clippy/benches: shaky, shaky 2024-10-01 14:56:50 -05:00
eschorn1
ab0f62369a shaky, shaky 2024-10-01 14:29:06 -05:00
eschorn1
8c03d1f9c3 prep update 2024-10-01 09:30:20 -05:00
integritychain
fca844d470
Merge pull request #3 from jonmon6691/add-acvp-tests
Add test module for ACVP known answer tests
2024-09-29 19:01:56 -05:00
eschorn1
628656a3a0 clippy: starting update 2024-09-29 18:26:45 -05:00
eschorn1
7542bb2d80 starting update 2024-09-29 18:05:16 -05:00
Jon Wallace
2c027b3f6d Add test module for ACVP known answer tests 2024-09-15 19:55:08 -07:00
eschorn1
bf62d98c42 python binding 2024-03-15 11:36:47 -05:00
eschorn1
4a8c3d215f python binding 2024-03-15 10:17:14 -05:00
eschorn1
8e4ebfec1a rust ffi 2024-03-14 13:35:26 -05:00
eschorn1
20a8263d42 wasm 2024-03-14 08:46:53 -05:00
eschorn1
98fc10359f benchmarks 2024-03-10 18:07:57 -05:00
eschorn1
bdd49221e4 minor clippy 2024-03-10 08:15:56 -05:00
eschorn1
f4bd328bf1 unwound generic-array 2024-03-09 15:58:29 -06:00
eschorn1
aaf3b57066 clippy 2024-03-08 18:25:57 -06:00
eschorn1
db92cdfabd msrv 1.70 2024-03-08 17:22:43 -06:00
eschorn1
b8b3a192dd gitignore nit 2024-02-10 14:44:09 -06:00
eschorn1
6bab551ffb dudect 2024-02-10 14:26:37 -06:00
eschorn1
6cec1182c7 cargo deny2 2024-02-10 09:26:34 -06:00
eschorn1
19d280b50e cargo deny 2024-02-10 09:03:01 -06:00
eschorn1
f1991a2282 ga2 2024-02-09 18:32:05 -06:00
eschorn1
d8bdfbb883 ga 2024-02-09 18:22:35 -06:00
eschorn1
6b94ff1aa0 msrv 1.73 2024-02-09 17:50:05 -06:00
eschorn1
b0213416c8 fips205 prep 2024-02-09 17:43:59 -06:00
eschorn1
ddfabeb6ac source split 2024-02-09 16:31:05 -06:00
eschorn1
6c449f3e36 working traits 2024-02-09 15:28:59 -06:00
eschorn1
69f94c55fe clippy and test clean 2024-02-08 06:42:28 -06:00
eschorn1
569374a055 polish 2024-02-07 17:22:26 -06:00
eschorn1
4f8f03546c diet1 2024-02-07 16:30:42 -06:00
eschorn1
a6ff2130de not 2024-02-07 15:31:17 -06:00
eschorn1
7bb838973b vectors passing 2024-02-07 12:17:59 -06:00