Commit graph

17 commits

Author SHA1 Message Date
c848a490ff verification: bind the statements, the specifications, and the model (P1-a)
Phases 3/3b establish what each certificate RESTS ON. Neither says what it
SAYS, nor what it is ABOUT. A certificate gutted to a tautology of the same
axiom cone passes both; so does one whose reference definition has been
redefined to BE the extracted code, at which point the theorem reads
`loop = loop` and every cone is byte-identical.

Phase 3c closes that. Proofs/Audit.lean emits a canonical block holding the
policy constants, every certificate's fully-elaborated statement (pp.all, so
implicit arguments, instances and universe levels are visible), and the body
of every specification constant transitively reachable from those statements.
Its SHA-256 is pinned in check.sh and the block itself is committed as
AUDIT-MANIFEST.txt, so a mismatch is DIFFED, not merely reported. 31
certificates, 68 specification constants per repository.

Two tiers, not one. These forks have an arithmetic tier that must stay
oracle-free and an apex tier carrying this fork's hash and wire-format axioms,
and the apex boundary genuinely differs per fork (dalek 8 extra names, anza 4,
risc0 and betrusted 5). One shared constant would have widened the arithmetic
tier to accept hash oracles, which is the most valuable property these repos
have. Each auditor is generated from its own repository's policy.

Phase 0b pins the extracted model. This was not a precaution: risc0 and
betrusted were observed emitting BYTE-IDENTICAL audit-manifest digests
(6c821b8e…) while shipping demonstrably different extracted models, their
point-doubling routines differing in operation order. A statement names an
extracted function; it does not contain that function's body. Binding
statements is not binding the subject. Membership derives from the filesystem,
so a new model file fails closed.

selftest-statements.sh attacks both phases with ten cases, each asserting a
specific diagnostic: an edited model body, an unlisted model file, a widened
policy, a hand-edited committed block, a certificate dropped from the auditor
WITH the digest refreshed to match, and a gutted statement whose cone is
unchanged. It lifts the phases out of check.sh at run time, so it attacks the
shipping gate rather than a copy.

Two bugs found and fixed during that testing, both mine: Phase 3c read `$0`
after `cd "$AENEAS_LEAN"`, and $0 is the caller's relative path; and the
axgate self-test compared the tree against a pristine checkout rather than
against how it found it. A third expectation was wrong rather than the code —
widening the apex boundary is caught by the exact-cone requirement before the
digest ever runs, which is a stronger rejection, and the test now says so.

All sixteen runs green at these commits: four main buttons, four axgate
self-tests, four binding self-tests, four scalar buttons.

TRUSTED-BASE.md records what this binds and, at equal length, what it does
not: a digest binds identity, not meaning; an author can rotate the pins in
one commit and is caught by review, not by the script; and pinning the model
says nothing about whether Charon and Aeneas translated the Rust faithfully.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-29 00:38:22 +02:00
ee52d50e20 PHASE 2 COMPLETE ON BETRUSTED: THE FULL POINT-LEVEL LIFT
(verify_accepts_iff_decompress, button-enforced)

Port of the risc0 decompress chain (identical v4 gen shape: same
instance naming, same RangeFrom+closure from_bytes extraction):

- source patch cee0e17: decompress step_2 negate-then-conditional-assign
  (the documented sqrt_ratio_i rewrite); extract.sh: decompress
  un-opaqued, re-extracted - the step_1/step_2 external axioms vanish
  from the template, decompress is transparent.
- Proofs/DecompressSpec.lean: dalek port, instance rename
  Shared0FieldElement51 -> SharedAFieldElement51.
- Proofs/FromBytesSpec.lean: the v4 closure-loader walk
  (range_from_index_spec + closure_call_spec; window/telescope math
  identical to dalek).
- Proofs/DecompressMain.lean: decompress_of_canonical (standard three)
  + verify_accepts_iff_decompress (corollary verbatim):

    accept  <=>  decompress(R) = [k]*(-A) + [s]*B   (as points).

check.sh: 4-tier Phase 3b; full-lift cone exactly [3 standard +
Signature + sha512_hash3 + to_bytes + Error + Error.new]. Full button
green fresh.

THE FULL POINT-LEVEL LIFT NOW HOLDS ON ALL FOUR PYRAMIDS.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 01:53:39 +02:00
c8540c3c9e THE POINT-LEVEL VERIFICATION EQUATION on the betrusted fork:
verify_accepts_iff_point_eq, button-enforced

Port of dalek's PointEqSpec (the encoding-injectivity mathematics is
fork-independent; compiled first try): for any valid on-curve point Q
whose canonical encoding is the signature's R bytes, the verifier
accepts IFF Q equals the recomputed point as denoted affine points -
the literal point-level EdDSA verification equation, no decompress
needed. enc_point_inj carries the standard three axioms; the equation
itself carries exactly this fork's enforced apex boundary, and Phase 3b
now audits all three tiers (byte apex, half-lift, point equation).
Full button green fresh.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 19:25:55 +02:00
81f6590670 PHASE-2 HALF-LIFT on the betrusted fork: verify_accepts_iff_point, button-enforced
Verbatim port of the risc0 fork's phase-2 files (identical v4 shape:
as_bytes serializers, inlined affine conversion, single sha512_hash3
oracle bind) - every proof compiled unchanged. verify_accepts_iff_point:
accept IFF the signature's R bytes are the canonical encoding of the
recomputed [k](-A) + [s]B over the certified model. Five new standard
certificates; Phase 3b enforces the hash3 + wire-format boundary on both
apex and half-lift. Full button green fresh.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 16:57:57 +02:00
9620cf5dd4 THE SIGNATURE APEX on the betrusted fork: verify_accepts_iff, button-enforced
Third pyramid capped. Identical shape to the risc0 fork (both are
sha2-0.10 stacks): the hash oracle is the single monomorphic
sha512_hash3(R, A, m) call, extraction runs --no-default-features.

- gen/CurveSig: extracted verify glue, definitionally welded to the
  proven CurveField model (every curve and scalar call resolves to a
  certified definition; only the hash and wire formats are opaque).
- Proofs/SigApexSpec.lean (unchanged from dalek): verify_loop_full with
  the standard three-axiom cone, and verify_accepts_iff — the verifier
  accepts IFF compress([s]B - [k]A) = R byte-for-byte.
- check.sh Phase 3b enforces the apex cone to be EXACTLY
  [propext, Classical.choice, Quot.sound, ed25519.Signature,
   verifying.sha512_hash3, ed25519.Signature.to_bytes,
   signature.error.Error, signature.error.Error.new].
- extract.sh gains the reproducible CurveSig stanza (same recipe
  verified byte-exact on the risc0 fork this session).

Full check.sh green: all standard certificates + the apex audit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 22:49:19 +02:00
b408df3765 Merge scalar into CurveField: one type universe, serial-only backend
Replicates dalek's gen merge: extract.sh co-extracts the Scalar52 backend
and scalar::from_bytes_mod_order[_wide] into the CurveField model, pins
RUSTFLAGS --cfg curve25519_dalek_backend="serial" (SIMD dispatch compiles
out; get_selected_backend becomes a real definition), and upgrades
unwrap_u8 to the documented Choice-model definition. ScalarDenote repoints
by one import line; both buttons green over the merged gen.

Prepares the verify-glue integration (CurveSig) on this fork.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 22:20:26 +02:00
112036eb0d NAF encoder proven end-to-end + the phase-1 double-scalar-mul apex
The complete non_adjacent_form(5) verification (four stages):
- `Proofs/DsmNafLoadSpec.lean` (generated) — the LE byte-to-word load.
- `Proofs/DsmNafMath.lean` — the digit loop's arithmetic core: window-read
  lemmas (single/cross-word), the exact ZZ invariant steps (Nat.mod_mul
  telescope), the carry-kill argument from V < 2^253, and the exit theorem.
- `Proofs/DsmNafLoopSpec.lean` — the w=5 digit loop by induction on the
  remaining-bits measure: per-step 64-bit window read (4-way word split),
  digit write via hcast/wrapping_sub (exact value window - 32*carry',
  oddness, |d| < 16), invariant carried through even/odd steps.
- `Proofs/DsmNafSpec.lean` — the public spec: both entry masserts
  DISCHARGED; the digits satisfy the NAF conditions and
      sum naf[k]*2^k = V   EXACTLY (integers, no modular slack)
  for any scalar whose LE byte value V is below 2^253.

And the campaign's brick 4, `Proofs/DsmMulSpec.lean`:
- `run_basepoint` — the transpiled ED25519_BASEPOINT_POINT is the standard
  base point: valid extended coordinates (X*Y = Z*T) and the curve equation,
  kernel-checked via denominator-free 121666-scaled witnesses. Includes the
  generic witness lemmas fp_mul_eq_of_witness / onCurve_of_witness.
- `vartime_double_base_mul_spec` — THE PHASE-1 COMPUTATIONAL SPEC of
  vartime_double_base::mul: for canonical scalars and a valid on-curve A,
  the result is valid, on-curve, and denotes
      dsmFold (naf a) (naf b) (edPt A) edBasePt edId 256
  with both digit arrays proven exact NAF encodings. Phase 2 (group
  semantics [a]A + [b]B) requires Edwards associativity — deferred and
  documented; nothing assumes it.

Also: removed a vestigial pre-re-extraction axiom stub
(backend.serial.scalar_mul.vartime_double_base.mul) from FunsExternal —
a root-level leftover that shadowed the real namespaced definition during
name resolution in proof files. Never referenced by any certificate (the
#print-axioms audit guards against that); deleted for hygiene.

CERTS += naf_load_spec, naf_exit, naf_digit_loop_spec,
non_adjacent_form_spec, run_basepoint, vartime_double_base_mul_spec —
each audited to exactly [propext, Classical.choice, Quot.sound].
Full check.sh green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 16:52:10 +02:00
06a7c1e603 Double-scalar-mul proof campaign, bricks 1-3: table, digit step, loop
Three new proof files over the CurveField extraction, composing the proven
group-law layer (no new axioms, no associativity assumed — computational
layering over the abstract `edAdd`):

- `Proofs/DsmTableSpec.lean` — `NafLookupTable5::from(&A)`: the 8 entries
  are valid `ProjectiveNielsPoint` caches of valid on-curve points denoting
  the odd multiples A, 3A, ..., 15A as the `edOdd` double-and-add recursion.
  7 explicit loop peels over edwards_as_projective_niels_spec /
  add_projniels_law / compl_as_extended_law, seeded by edwards_double_law.
  `select`: both masserts (x odd, x < 16) DISCHARGED — panic-freedom is
  proven, not assumed; post enumerates all 8 digit cases.

- `Proofs/DsmStepSpec.lean` — `proj_double_law` (the projective doubling
  denotes `edAdd P P`; same Z^2-scaled linear_combination discipline as the
  extended-coordinate law), `compl_as_projective_law` ((X:Z),(Y:T) to
  (XT:YZ:ZT) preserves the point), `naf_select_entry` (digit-indexed lookup
  returns THE entry: NafEntryOf r A ((x-1)/2)), and `dsm_step_p_law` /
  `dsm_step_b_law`: the three-way NAF digit step denotes `edDigit` — add
  the d-th odd multiple, add its negation, or pass through.

- `Proofs/DsmLoopSpec.lean` — the 256-iteration Straus loop by GENUINE
  induction on the counter (one symbolic body walk, no unrolling):
  `dsm_loop_spec` — from the identity, the loop returns a valid on-curve
  point denoting `dsmFold ... edId 256`, the abstract double-and-add fold
  of both digit arrays over the table points. Digit and table hypotheses
  are exactly what the NAF spec and naf_table_spec provide (layering).

check.sh wired: PROOFS + AUDIT_IMPORTS + 7 new CERTS (naf_table_spec,
naf_select_spec, proj_double_law, compl_as_projective_law, dsm_step_p_law,
dsm_step_b_law, dsm_loop_spec), each `#print axioms`-audited to exactly
[propext, Classical.choice, Quot.sound]. Full check.sh green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 15:07:59 +02:00
77262e0ea0 Hash-to-scalar PROVEN: from_bytes_wide_spec - Scalar::from_hash's reduction is exact mod l
The apex brick of the scalar layer: for any 64 bytes (the opaque SHA-512
digest), [from_bytes_wide bytes] = (LE 512-bit value) mod l, with
canonical 52-bit-bounded output. Composition: bytes_unpack_spec (8x8
loops) -> split_words_lo/hi_spec (exact div/mod per limb, disjoint ORs
as additions) -> wide_split_telescope (isolated omega) -> montgomery_mul
by R and RR (R cancels as a unit, RR restores it) -> the canonical add.

The two kernel-capacity walls found and crossed en route (control repo
FAILURES.md updated):
- a montgomery_mul inside any walk motive replays its 400-line body at
  every kernel step (fix: named prefix functions in the pinned source);
- straight-line IndexMut closure chains make kernel defeq exponential in
  depth (fix: struct-literal construction - the split halves now build
  Scalar52([...]) directly). Full certificate: 77 s kernel-inclusive.

Regenerated gen (sources factor from_bytes_wide -> from_bytes_wide_parts
-> split_words_lo/hi; documented pure refactors, cargo-checked).
check-scalar.sh: 13 proof files, 13 kernel audits, all exactly
[propext, Classical.choice, Quot.sound]. Button pressed fresh: green.
2026-07-04 11:00:52 +02:00
8ef2d6eb91 Signature layer: the 64-byte unpack certificates (8x8 loops proven)
Toward Scalar::from_hash: bytes_unpack_spec proves the from_bytes_wide
word-unpack loops pack 64 little-endian bytes into 8 words exactly.

- Proofs/ScalarBytesSpec.lean (3308 lines): bytes_word_loop_spec_0..7,
  each split head/tail at j=4 (the 8-fold monolith grows exponentially
  in elaboration - METHOD 4). Disjoint-bit ORs become additions via
  core's Nat.two_pow_add_eq_or_of_lt with explicit calc bridges (the
  default simp set literalizes 2^8 -> 256 and breaks pow-form rewrites;
  simp only everywhere).
- Proofs/ScalarUnpackSpec.lean: bytes_unpack_spec composes the eight
  inner lemmas through the outer loop (iterator start needs a term-level
  equality rewrite per peel).

The from_bytes_wide main walk itself is proven at elaboration level
(fail-probe verified end to end) but its single-decl kernel certificate
replays >30min; it ships next as a phase-split (plan in the control
repo's method notes). check-scalar.sh: 12 proof files, 12 kernel audits,
all exactly [propext, Classical.choice, Quot.sound]. Button green.
2026-07-04 03:22:29 +02:00
a377ffc587 Signature layer, first bricks: canonicity closure + hash-to-scalar foundation
Canonicity pass (the layer is now closed under its own preconditions):
- sub_val_spec post carries the exact value equation
  (exists beta <= 1, scVal r + scVal b = scVal a + ell*beta, with the
  underflow guard beta = 1 -> scVal a < scVal b)
- add/montgomery_reduce/mul/aggregate posts all carry scVal r < ell:
  canonical inputs give canonical outputs everywhere. Needed because
  from_bytes_wide (hash-to-scalar) feeds Montgomery outputs into add.

Hash-to-scalar foundation (toward Scalar::from_hash / EdDSA verify):
- extraction scope + from_bytes_wide (brings constants::R); regenerated gen
- source repos carry a documented Aeneas-compat patch: the bare
  `hi[4] = words[7] >> 20` extracts ill-typed at pin bf13c42e; masked
  (semantic no-op, words[7] >> 20 < 2^44)
- Proofs/ScalarWideSpec.lean: R constant lemmas (R = 2^260 mod ell,
  witness 2^260 = R + 255*ell) and montgomery_mul_spec, the single
  Montgomery round: [r]*2^260 = [a]*[b], canonical bounded output

check-scalar.sh: 10 proof files, 11 kernel audits, all exactly
[propext, Classical.choice, Quot.sound]. Button pressed fresh: green.
2026-07-03 23:18:34 +02:00
1bbc70e5ce Scalar layer complete: Montgomery reduction + full mul ported, scalarImplementation aggregate
Port of the dalek Montgomery stack against THIS fork's own extraction
(gen sections byte-identical, proofs recompiled from scratch here):
- Proofs/ScalarMulSpec.lean      - mul_internal: 9 exact schoolbook columns
- Proofs/ScalarMontSpec.lean     - part1/part2 exact-division rounds
  (LFACTOR*L0 = -1 mod 2^52), head/tail telescopes, mont_bound, tail walk
- Proofs/ScalarReduceSpec.lean   - montgomery_reduce main walk:
  scDenote r * 2^260 = Z in ZMod ell + 52-bit output bounds
- Proofs/ScalarFullMulSpec.lean  - mul = double Montgomery round through
  RR = R^2 mod ell; R cancelled as a unit; post: [mul a b] = [a]*[b]
- Proofs/ScalarMain.lean         - scalarImplementation aggregate (ScBnd
  interfaces; canonical inputs discharge the Montgomery bound)

sub_val_spec/add_val_spec posts strengthened with result-limb bounds.
check-scalar.sh: 9 proof files, 10 kernel audits, all exactly
[propext, Classical.choice, Quot.sound]. Button pressed fresh: green.
2026-07-03 21:46:57 +02:00
422e91f270 scalar layer: add+sub fully proven mod l against THIS fork's v4 extraction
This v4.1.x fork implements Scalar52::sub with TWO loops (loop0 = borrow
chain; loop1 adds L &&& underflow_mask — the arithmetic-mask constant-time
conditional), a genuinely different code path from upstream v5's
subtle-based conditional_add_l. Verified per R2 against this fork's own gen:

- sub_loop_spec (= loop0): borrow chain, verbatim technique from dalek
- sub_loop1_zero_spec / sub_loop1_one_spec: the masked-L add, both mask
  values (0 / 2^64-1), full carry chains
- sub_val_spec: denote(sub a b) = denote a - denote b in ZMod l; the
  underflow mask um = ((borrow>>>63) XOR 1) - 1 resolved per case
- add_loop_spec + add_val_spec: denote(add a b) = denote a + denote b
  (v4 add_loop extracts token-identical to v5; composition through this
  fork's own sub_val_spec)

check-scalar.sh: full manifest, 5/5 kernel axiom audit
[propext, Classical.choice, Quot.sound], green at 300-400s/4096MB.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 18:45:17 +02:00
6120f9bb17 Add scalar-layer foundation (Scalar52 arithmetic mod ℓ)
Transpile the Scalar52 limb backend (backend::serial::u64::scalar
add/sub/mul/square/montgomery_*) from Rust to Lean via Charon/Aeneas,
scoped at the function level to the iterator-free arithmetic core.

  - verification/extract-scalar.sh: function-level Charon/Aeneas extraction
  - verification/gen/CurveScalar/{Types,Funs}.lean: transpiled model (27 defs).
    This fork (v4.1.2) implements Scalar52::sub's constant-time conditional add
    with a pure arithmetic mask (constants::L[i] & underflow_mask), so the
    extraction pulls in NO external functions or types (unlike v5 dalek, which
    routes sub through subtle, and v4.1.3, which uses a local black_box).
  - verification/gen/CurveScalar/{TypesExternal,FunsExternal}.lean: decl-free
    stub modules kept so the check manifest is uniform across forks.
  - verification/Proofs/ScalarDenote.lean: semantic foundation — Scalar52
    denotation into ℤ/ℓℤ, limb-bound invariant, and L_val (the transpiled
    constants::L denotes exactly the group order ℓ, kernel-checked).
  - verification/check-scalar.sh: guarded compile of the gen modules plus the
    denotation foundation.

check-scalar.sh passes: gen compiles; denotation + L = ℓ proven.
add/sub/mul remain in progress.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 21:27:33 +02:00
e4034e0880 group-law layer: complete twisted Edwards addition law proven
Extraction widened to backend::serial::curve_models + edwards (v4 Aeneas).
Ed* proof suite shared with the risc0 twin EXCEPT proj_double_spec: upstream
reordered ProjectivePoint::double's instructions between v4.1.2 (this fork)
and v4.1.3 (risc0), so the step sequence was resequenced to match THIS
fork's generated body — the proofs track each fork's actual code, not a
template. All 20 proofs pass under lean-guard; both certificates axiom-clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 17:11:28 +02:00
bdafbf94bf lean-guard: disable core dumps (no more apport popups on capped aborts)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 16:23:31 +02:00
c5b4930939 field layer: proofs pass, fieldImplementation axiom-clean
Ported from the locally verified Hermes working copy; FeQ and Square2Spec
(dead files in the published replica) now compile and are in the check
manifest. Basic.lean (never compiled under v4 Aeneas) removed rather than
shipped dead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 14:38:53 +02:00