Skip to content

verify: discharge lemma_le64_injective#108

Merged
avrabe merged 1 commit into
mainfrom
verify/lemma-le64-injective
May 11, 2026
Merged

verify: discharge lemma_le64_injective#108
avrabe merged 1 commit into
mainfrom
verify/lemma-le64-injective

Conversation

@avrabe
Copy link
Copy Markdown
Contributor

@avrabe avrabe commented May 10, 2026

Summary

Replaces the assume(false) in lemma_le64_injective (src/lib/src/verus_proofs/dsse_proofs.rs) with an explicit four-step proof. Discharges one of the relabeled SPECIFICATION ONLY theorems from audit C-1; the remaining admits in dsse_proofs.rs and merkle_proofs.rs are unchanged.

Proof technique

  1. Bit-vector tautology (Step 1). A single assert(...) by(bit_vector) proves the closed quantifier-free goal: if the eight 0xFF-masked shifts of a agree with the corresponding shifts of b, then a == b. This is a pure tautology in BitVec64 — every bit of a and b lives in exactly one byte-slice.

  2. u8 <-> u64 bridge (Step 2). Eight concrete <==> asserts (also by(bit_vector)) lift each (x & 0xFF) as u8 == (y & 0xFF) as u8 to (x & 0xFF) == (y & 0xFF). The truncating cast is value-preserving because the mask already truncates to [0, 0xFF]. Concrete instantiations avoid trigger headaches.

  3. spec_le64 unfolding (Step 3). Sixteen unfolding asserts expose spec_le64(x)[i] == (x >> 8*i) & 0xFF as u8 for each i in 0..8. These are pure macro/spec identities; they hold unconditionally.

  4. Conditional contradiction (Step 4). Under sa == sb, congruence on == gives the eight byte equalities sa[i] == sb[i]. The bridges (Step 2) lift those to u64 mask equalities; the bv tautology (Step 1) forces a == b, contradicting requires a != b. Outside the branch, sa != sb is the goal directly.

Why this should work

  • spec_le64 is pub open spec fn, so Verus sees through it.
  • seq![x0..x7][i] reduces to xi by the seq! macro expansion.
  • Z3's bit-vector backend (via by(bit_vector)) reliably decides the closed Step-1 goal; it is small (eight 8-bit slices of a 64-bit value).
  • The if sa == sb { ... assert(a == b); } pattern is the standard Verus idiom for proof-by-contradiction over an equality goal.

Honesty notes

  • Could not run bazel build //src/lib/src/verus_proofs:wsc_merkle_proofs locally — Bazel toolchain download failed (no space left on device). The proof must be validated by CI.
  • The Verus job in .github/workflows/formal-verification.yml still has continue-on-error: true per 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.
  • If CI reports the proof did not discharge, the next iteration starts by inspecting which 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 --release clean (verified locally — Verus annotations are inert outside Bazel).
  • bazel build //src/lib/src/verus_proofs:wsc_merkle_proofs reports verification successful for lemma_le64_injective. Other admits in the file remain assume(false) so the file as a whole still verifies (because assume(false) makes the obligation vacuous).
  • CI's Verus job log (under continue-on-error: true) shows one fewer admitted lemma.

Scope

  • Only src/lib/src/verus_proofs/dsse_proofs.rs is touched.
  • formal-verification.yml is 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

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>
@avrabe avrabe merged commit 8c70ee1 into main May 11, 2026
15 of 16 checks passed
@avrabe avrabe deleted the verify/lemma-le64-injective branch May 11, 2026 03:56
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant