verify: discharge lemma_le64_injective#108
Merged
Merged
Conversation
Replaces the `assume(false)` in `lemma_le64_injective` with an explicit
four-step proof:
1. A single `assert(...) by(bit_vector)` proves the closed bit-vector
tautology that the eight `0xFF`-masked shifts of `a` and `b`
uniquely determine `a` and `b` — i.e. byte-wise LE equality implies
u64 equality.
2. Eight `<==>` bit-vector bridges show that comparing the masked
slices `as u8` is equivalent to comparing them as `u64`, since the
mask already truncates to [0, 0xFF].
3. Sixteen unfolding asserts expose `spec_le64(x)[i]` as the
corresponding `(x >> 8*i) & 0xFF as u8` slice, making the SMT
solver see each byte of the encoding as a function of the input.
4. A conditional contradiction block: under `sa == sb`, congruence on
`==` gives the eight byte equalities, which the bridges + bv lemma
force into `a == b`, contradicting `requires a != b`.
`cargo build --workspace --release` is clean (Verus annotations are inert
outside Bazel). The Bazel toolchain failed to download locally due to
disk pressure — tried to prove locally; needs CI verification. CI will
run `bazel build //src/lib/src/verus_proofs:wsc_merkle_proofs` with
`continue-on-error: true`, so the result is observable in logs without
breaking the gate.
Closes one open admit from audit C-1; the other relabeled
specifications remain. The Verus job in formal-verification.yml stays
masked with continue-on-error per the partial-closure plan until more
admits are discharged.
Refs: audit C-1
Verifies: CR-3 (parser injectivity property of PAE encoding)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 tasks
avrabe
added a commit
that referenced
this pull request
May 11, 2026
Patch release bundling four merged PRs: #107 — cargo-deny CI step hardening (closes #103) #108 — discharge lemma_le64_injective Verus admit (audit C-1 partial) #109 — repair fuzz_public_key target (audit follow-up from #98) #110 — clear 3 RUSTSEC advisories via dep bumps (fixes #102) Companion work on 0.8.2+next: #111 — criterion benches for signature verification (#89) #112 — lift Kani wasm_module mask; document merkle + format See CHANGELOG.md for the full release notes. Trace: skip Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
avrabe
added a commit
that referenced
this pull request
May 13, 2026
…116) Building on PR #108's discharge of lemma_le64_injective, this commit replaces the `assume(false)` in `theorem_pae_injective_on_types` with an explicit ~70-line proof. Structure: 1. Establish lengths of every prefix in `spec_pae`'s `Seq::add` chain (spec_le64 → length 8; Seq::add is length-additive). 2. Contrapositive on `pae1 == pae2`: a. Total-length equality → |type1| == |type2| b. For every index i, derive pae1[16+i] == type1[i] via the Seq::add indexing axiom, applied through the four `.add`s of spec_pae. Same chain for pae2. c. Extensionality (`=~=`) lifts byte-wise agreement to `type1 == type2`, contradicting the `requires type1 != type2`. This is the natural next-easiest admit after lemma_le64_injective — no SHA-256, no crypto, just structural reasoning over Seq::add. The proof has NOT been validated locally because Bazel/Verus tooling was unavailable in the agent's sandbox. The Verus CI job carries `continue-on-error: true` (audit C-1 mask) so the worst case is the proof doesn't discharge and the assert chain becomes diagnostic documentation rather than a hard verification gate. The structured `assert` chain in the proof body is itself useful for the next attempt regardless. Refs: audit C-1 (incremental closure progressing). Verifies (if Verus accepts): CR-3 (parser injectivity, payload-type component of PAE). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces the
assume(false)inlemma_le64_injective(src/lib/src/verus_proofs/dsse_proofs.rs) with an explicit four-step proof. Discharges one of the relabeledSPECIFICATION ONLYtheorems from audit C-1; the remaining admits indsse_proofs.rsandmerkle_proofs.rsare unchanged.Proof technique
Bit-vector tautology (Step 1). A single
assert(...) by(bit_vector)proves the closed quantifier-free goal: if the eight0xFF-masked shifts ofaagree with the corresponding shifts ofb, thena == b. This is a pure tautology inBitVec64— every bit ofaandblives in exactly one byte-slice.u8 <-> u64 bridge (Step 2). Eight concrete
<==>asserts (alsoby(bit_vector)) lift each(x & 0xFF) as u8 == (y & 0xFF) as u8to(x & 0xFF) == (y & 0xFF). The truncating cast is value-preserving because the mask already truncates to[0, 0xFF]. Concrete instantiations avoid trigger headaches.spec_le64unfolding (Step 3). Sixteen unfolding asserts exposespec_le64(x)[i] == (x >> 8*i) & 0xFF as u8for eachi in 0..8. These are pure macro/spec identities; they hold unconditionally.Conditional contradiction (Step 4). Under
sa == sb, congruence on==gives the eight byte equalitiessa[i] == sb[i]. The bridges (Step 2) lift those to u64 mask equalities; the bv tautology (Step 1) forcesa == b, contradictingrequires a != b. Outside the branch,sa != sbis the goal directly.Why this should work
spec_le64ispub open spec fn, so Verus sees through it.seq![x0..x7][i]reduces toxiby theseq!macro expansion.by(bit_vector)) reliably decides the closed Step-1 goal; it is small (eight 8-bit slices of a 64-bit value).if sa == sb { ... assert(a == b); }pattern is the standard Verus idiom for proof-by-contradiction over an equality goal.Honesty notes
bazel build //src/lib/src/verus_proofs:wsc_merkle_proofslocally — Bazel toolchain download failed (no space left on device). The proof must be validated by CI..github/workflows/formal-verification.ymlstill hascontinue-on-error: trueper the partial-closure plan (per the task brief, the CI mask should stay until more admits are discharged). So this PR's CI will not block on the Verus job, but the bazel build logs will reveal whether the proof actually verified.assert(...)Verus rejected — the Step-1 bv assert is the most likely friction point (slicing reasoning vs Z3's bv decision procedure performance), with a fallback being eight per-byte bv asserts joined explicitly.Test plan
cargo build --workspace --releaseclean (verified locally — Verus annotations are inert outside Bazel).bazel build //src/lib/src/verus_proofs:wsc_merkle_proofsreportsverification successfulforlemma_le64_injective. Other admits in the file remainassume(false)so the file as a whole still verifies (becauseassume(false)makes the obligation vacuous).continue-on-error: true) shows one fewer admitted lemma.Scope
src/lib/src/verus_proofs/dsse_proofs.rsis touched.formal-verification.ymlis intentionally left alone — CI mask removal is a follow-up after more admits land.Refs: audit C-1
Verifies: CR-3 (parser injectivity property of PAE encoding)
Generated with Claude Code