From a3ce8e8644fe302019ed7ae271912333f1476de4 Mon Sep 17 00:00:00 2001 From: mrwulf Date: Tue, 28 Jul 2026 14:41:01 +0200 Subject: [PATCH] extract script: state what is actually guaranteed, and by what MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../sha2_128s_extracted.json | 2 +- .../extract_sha2_128s.cpython-314.pyc | Bin 0 -> 9947 bytes tests/nist_acvp_vectors/extract_sha2_128s.py | 51 +++++++++++++++--- 3 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 tests/nist_acvp_vectors/__pycache__/extract_sha2_128s.cpython-314.pyc diff --git a/tests/nist_acvp_vectors/SLH-DSA-sigVer-FIPS205/sha2_128s_extracted.json b/tests/nist_acvp_vectors/SLH-DSA-sigVer-FIPS205/sha2_128s_extracted.json index c782d3b..e16bf40 100644 --- a/tests/nist_acvp_vectors/SLH-DSA-sigVer-FIPS205/sha2_128s_extracted.json +++ b/tests/nist_acvp_vectors/SLH-DSA-sigVer-FIPS205/sha2_128s_extracted.json @@ -5,7 +5,7 @@ "source_url": "https://raw.githubusercontent.com/usnistgov/ACVP-Server/master/gen-val/json-files/SLH-DSA-sigVer-FIPS205/internalProjection.json", "source_sha256": "a013fc2104f4ed4799d96d51141f65b965969b2cf10646626a021b6d456ce792", "retrieved_utc": "2026-07-28", - "extraction": "Produced by tests/nist_acvp_vectors/extract_sha2_128s.py, which verifies the upstream sha256, selects every testGroup whose parameterSet == SLH-DSA-SHA2-128s, and removes exactly ONE per-test field: `sk` (the private key, not needed to verify a signature). Every other per-test field and all group and top-level metadata are carried through unchanged; the script asserts this and fails if it is not so.", + "extraction": "Produced by tests/nist_acvp_vectors/extract_sha2_128s.py, which verifies the upstream sha256, selects every testGroup whose parameterSet == SLH-DSA-SHA2-128s, and removes exactly ONE per-test field: `sk` (the private key, not needed to verify a signature). Every other per-test field and all group and top-level metadata are carried through unchanged by construction. The guarantees that can actually fail are: 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, which re-derives and byte-compares this committed file.", "note": "Test DATA only. Expected outcomes are NIST's `testPassed` field; `reason` records why a negative case must be rejected.", "regenerate": "python3 tests/nist_acvp_vectors/extract_sha2_128s.py --write" }, diff --git a/tests/nist_acvp_vectors/__pycache__/extract_sha2_128s.cpython-314.pyc b/tests/nist_acvp_vectors/__pycache__/extract_sha2_128s.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c6e35b6f8afe55f2b0fa3bd2b1f06db5e25aa662 GIT binary patch literal 9947 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 literal 0 HcmV?d00001 diff --git a/tests/nist_acvp_vectors/extract_sha2_128s.py b/tests/nist_acvp_vectors/extract_sha2_128s.py index 4084de1..42d7a18 100644 --- a/tests/nist_acvp_vectors/extract_sha2_128s.py +++ b/tests/nist_acvp_vectors/extract_sha2_128s.py @@ -15,10 +15,22 @@ removed. The transformation is now executable, pinned, and fails closed: * the upstream file's sha256 must match SOURCE_SHA256 exactly; * exactly EXPECTED_GROUPS groups must match the parameter set, each with EXPECTED_TESTS_PER_GROUP tests; - * exactly one field, `sk`, is removed, and it must be present to be removed; - * every other key present upstream is carried through untouched, and the - script asserts that afterwards; - * output is canonical (sorted keys off, fixed indent, trailing newline). + * exactly one field, `sk`, is removed, and it must be present to be removed + (so a schema change is caught, not silently transformed); + * every other key is carried through untouched — BY CONSTRUCTION, which a + reviewer verifies by reading `build()`, not by a self-check: no test inside a + transformer can detect a corrupted input, since the transformer is what + defines the output. The input is instead pinned by SOURCE_SHA256; + * verify mode re-derives and byte-compares the committed file, so a hand-edit + of the committed JSON IS caught; + * output is canonical (fixed indent, trailing newline). + +NOT A GATE — re-runnable EVIDENCE. Round-8 review noted the distinction: this +script needs network access, so nothing invokes it automatically (it is outside +`cargo test` and outside verification/check.sh). The committed JSON is still +trusted at review time; what this script provides is that a reviewer can +CHECK that trust cheaply and mechanically instead of taking a prose note's word. +A CI job running it in verify mode would close the remaining gap. Usage: python3 extract_sha2_128s.py # verify the committed file matches @@ -56,8 +68,11 @@ PROVENANCE = { f"parameterSet == {PARAMETER_SET}, and removes exactly ONE " "per-test field: `sk` (the private key, not needed to verify a " "signature). Every other per-test field and all group and " - "top-level metadata are carried through unchanged; the script " - "asserts this and fails if it is not so.", + "top-level metadata are carried through unchanged by construction. " + "The guarantees that can actually fail are: 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, which " + "re-derives and byte-compares this committed file.", "note": "Test DATA only. Expected outcomes are NIST's `testPassed` field; " "`reason` records why a negative case must be rejected.", "regenerate": "python3 tests/nist_acvp_vectors/extract_sha2_128s.py --write", @@ -90,9 +105,29 @@ def build(full: dict) -> dict: for f in DROP_FIELDS: if f not in t: sys.exit(f"FATAL: tcId {t.get('tcId')} has no field {f!r} to drop") + # Fields are carried through BY CONSTRUCTION: `kept` is `t` minus + # DROP_FIELDS, so every retained key holds the identical object. + # + # Round-8 review found a tautological `assert` here — it compared + # `kept` against the comprehension that had just built it, so it could + # never fire. The first attempt to repair it (comparing kept[k] to + # t[k]) was tautological for the same reason, and that is the lesson + # worth recording: NO check inside this function can detect a + # corrupted input, because this function is what defines the output + # from that input. Faithfulness here is a property of the two lines + # below, which a reviewer reads; it is not something the script can + # test about itself. + # + # What actually protects the result, and can fail: + # * SOURCE_SHA256 — the input is pinned, so upstream cannot drift + # or be substituted without an explicit, reviewed pin change; + # * the `f not in t` presence check above — `sk` must exist to be + # dropped, so a schema change is caught rather than silently + # producing a different transformation; + # * EXPECTED_GROUPS / EXPECTED_TESTS_PER_GROUP below; + # * verify mode, which re-derives and byte-compares the committed + # file, so a hand-edit of the committed JSON is caught. kept = {k: v for k, v in t.items() if k not in DROP_FIELDS} - # carried-through invariant, asserted rather than asserted-in-prose - assert set(kept) == set(t) - DROP_FIELDS, "unexpected per-test key change" tests.append(kept) if len(tests) != EXPECTED_TESTS_PER_GROUP: sys.exit(f"FATAL: group {g['tgId']} has {len(tests)} tests, "