2026-07-22 20:21:19 +00:00
|
|
|
|
/- ──────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
gen/SlhVerify/FunsExternal.lean — hand-maintained external functions.
|
|
|
|
|
|
|
|
|
|
|
|
TWO CLASSES of external, per honesty invariants H4/H5:
|
|
|
|
|
|
|
|
|
|
|
|
(1) THE CRYPTOGRAPHIC BOUNDARY — the deliberate opaque axioms.
|
|
|
|
|
|
The five SLH-DSA-SHA2-128s hash primitives, reached by name from the
|
|
|
|
|
|
monomorphic verify path (verify_mono::oracle):
|
|
|
|
|
|
· verify_mono.oracle.f — F (chain / FORS leaf)
|
|
|
|
|
|
· verify_mono.oracle.h — H (Merkle node)
|
|
|
|
|
|
· verify_mono.oracle.t_l — T_len (WOTS+ pk compression)
|
|
|
|
|
|
· verify_mono.oracle.t_len — T_k (FORS root compression)
|
|
|
|
|
|
· verify_mono.oracle.h_msg — H_msg (message digest)
|
|
|
|
|
|
These are SHA-256-based; their correctness against FIPS 180-4 is the
|
|
|
|
|
|
standing hash-oracle boundary (see TRUSTED-BASE.md). The apex
|
|
|
|
|
|
certificate will carry EXACTLY these five beyond Lean's three kernel
|
|
|
|
|
|
axioms — nothing else.
|
|
|
|
|
|
|
|
|
|
|
|
(2) TRANSPILER PLUMBING — core-library externals Aeneas emits for this
|
phase 2: FOURTH CERTIFICATE — hypertree layer walk (Algorithm 12) + de-plumbing
fips205.ht_loop_eq (Proofs/HtSpec.lean): the extracted ht_verify_free_loop
equals the explicit d-layer fold — at layer j: idx_leaf = idx_tree masked
to h' bits (mask+cast), idx_tree >>= h', layer address j, tree address to
the shifted index, node recomputed through xmss_pk_from_sig on the j-th
XMSS signature. Pins the hypertree layer schedule; the final node ==
pk_root comparison sits one bind above in ht_verify_free (apex material).
Exact cone: [propext, Classical.choice, Quot.sound, verify_mono.oracle.f,
verify_mono.oracle.h, verify_mono.oracle.t_l] — kernel-3 plus exactly the
three hash primitives the referenced WOTS+/XMSS machinery touches.
THE LAYER'S OBSTRUCTION (one per layer, on pattern) was not the proof but
the CONE: the first extraction of this loop carried Result-conversion
plumbing (try_from/is_err/unwrap; transitively a Take iterator and the
&u32 Sub instance) — all axioms, rightly rejected by the Phase-3 audit.
Fixed at SOURCE level (fips205-source 6f6a9d6, 8 sites, semantics
identical for every FIPS 205 parameter set, differential test re-run
green), then re-extracted: the loop body is now straight-line and the
proof is the plain chain/wots recipe (no branches; base case via
loop.eq_1; step lemma closes by rfl; induction = bind_congr ×12).
Also in this commit:
- gen/ regenerated from the patched snapshot (loop bodies of the three
prior certificates byte-identical modulo source line comments; all
three proofs recompiled unchanged and re-audited green).
- Dead-stub deletion (axiom-shadowing hygiene rule): the five obsoleted
plumbing axioms + vestigial take.default removed from FunsExternal, the
orphaned TryFromIntError type axiom removed from TypesExternal. The
model's external surface is now: 5 SHA-2 oracles (the boundary), the
Take iterator machinery used only by helpers::to_int (apex round's
de-plumbing item), 3 zeroize blanket impls (never on the verify path),
and the discharged-real u32 Step defs.
- check.sh: PROOFS += HtSpec, CERTS += fips205.ht_loop_eq, audit import
(self-test structure anchors untouched). README: four certificates +
the de-plumbing record.
Fidelity review at authorship (three-way): extracted body == Rust
ht_verify_free (verbatim from upstream hypertree.rs, calls -> *_free) ==
FIPS 205 Algorithm 12, incl. mask-then-shift order and layer-then-tree
address order.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 15:14:21 +00:00
|
|
|
|
extraction config. These carry NO cryptographic content. The u32
|
|
|
|
|
|
range Step machinery is DISCHARGED below with real definitions
|
|
|
|
|
|
(2026-07-22). The try_from / is_err / &u32-Sub / wots-Take /
|
|
|
|
|
|
Debug-fmt axioms were ELIMINATED at source level by the
|
|
|
|
|
|
fips205-source de-plumbing patch (8 sites, semantics identical,
|
|
|
|
|
|
differential-test-validated) and their declarations deleted here
|
|
|
|
|
|
(dead-stub rule, 2026-07-23). Remaining as axioms: the Take
|
|
|
|
|
|
iterator machinery used by helpers::to_int (slh_verify_internal's
|
|
|
|
|
|
digest split — the apex round's de-plumbing item) and the zeroize
|
|
|
|
|
|
blanket impls (never on the verify path). The #print axioms audit
|
|
|
|
|
|
confirms only class (1) survives in any certificate cone.
|
2026-07-22 20:21:19 +00:00
|
|
|
|
────────────────────────────────────────────────────────────────────────────── -/
|
|
|
|
|
|
-- This is a template file: rename it to "FunsExternal.lean" and fill the holes.
|
|
|
|
|
|
import Aeneas
|
|
|
|
|
|
import SlhVerify.Types
|
|
|
|
|
|
open Aeneas Aeneas.Std Result ControlFlow Error
|
|
|
|
|
|
set_option linter.dupNamespace false
|
|
|
|
|
|
set_option linter.hashCommand false
|
|
|
|
|
|
set_option linter.unusedVariables false
|
|
|
|
|
|
|
|
|
|
|
|
/- You can set the `maxHeartbeats` value with the `-max-heartbeats` CLI option -/
|
|
|
|
|
|
set_option maxHeartbeats 1000000
|
|
|
|
|
|
|
|
|
|
|
|
/- You can set the `maxRecDepth` value with the `-max-recdepth` CLI option -/
|
|
|
|
|
|
set_option maxRecDepth 2048
|
|
|
|
|
|
open fips205
|
|
|
|
|
|
|
phase 2: INPUT-PREP layer — to_int, to_byte, WOTS+ checksum (3 kernel-3 certs)
Three straight-line range-loop fidelity theorems (Proofs/InputPrepSpec.lean),
each #print axioms = EXACTLY [propext, Classical.choice, Quot.sound] — pure
byte/bit arithmetic, NO hash oracle enters (the cleanest cones in the campaign):
- fips205.to_int_loop_eq (Algorithm 2, toInt): the extracted big-endian
byte->u64 loop = the fold total <- (total<<8) + x[i].
- fips205.to_byte_loop_eq (Algorithm 3, toByte): the extracted u32->byte loop =
the fold writing s[n-1-i] and shifting total right by 8.
- fips205.wots_csum_loop_eq: the WOTS+ checksum loop = the fold
csum <- csum + (W-1-msg[i]).
All three are the straight-line recipe (hbody -> step lemma closed by rfl ->
induction with bind_congr per bind). to_int + checksum use the usize range
helpers (WotsSpec), to_byte the u32 range (ChainSpec); loop_unfold_bind reused.
Also in this commit — DE-PLUMBING ROUND 2 landed (source bea1051, separate
commit in fips205-source): to_int's iter().take() and base_2b's iter_mut()
became index loops, so both extract to real definitions. Consequently:
- gen/ regenerated (to_int_loop / base_2b_loop0 now clean StepUsize range
loops with Slice.index_usize / Slice.update; the six prior certificates
recompiled UNCHANGED and re-audited green against the new gen).
- The core::iter::adapters::take::Take::next AXIOM — the LAST non-oracle,
non-zeroize plumbing axiom on the verify path — is now unreferenced and was
DELETED from FunsExternal (dead-stub hygiene rule). The model's external
surface is now EXACTLY: the 5 SHA-2 oracles + 3 zeroize blanket impls (never
on the verify path) + the discharged-real u32 Step defs. Nothing else.
Fidelity review at authorship (three-way): extracted loop bodies (gen
Funs.lean) == Rust helpers.rs to_int/to_byte + verify_mono checksum (verbatim
FIPS 205 Alg 2/3) == the folds above.
check.sh: PROOFS += InputPrepSpec; CERTS += the 3 certs; audit imports it.
Green over ALL NINE certificates at default caps (400s/4096MB).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 06:55:34 +00:00
|
|
|
|
-- (the core::iter::adapters::take::Take::next axiom was here; DELETED
|
|
|
|
|
|
-- 2026-07-24 after de-plumbing round 2 removed the last Take iterator on the
|
|
|
|
|
|
-- verify path — to_int/base_2b now index-loop. dead-stub hygiene rule.)
|
2026-07-22 20:21:19 +00:00
|
|
|
|
|
|
|
|
|
|
/-- [core::iter::range::{impl core::iter::range::Step for u32}::backward_checked]:
|
|
|
|
|
|
Source: '/rustc/library/core/src/iter/range.rs', lines 290:16-290:74
|
|
|
|
|
|
Name pattern: [core::iter::range::{core::iter::range::Step<u32>}::backward_checked]
|
|
|
|
|
|
Visibility: public -/
|
2026-07-22 21:54:03 +00:00
|
|
|
|
-- DISCHARGED (2026-07-22, proof phase): Aeneas.Std ships a real `Step`
|
|
|
|
|
|
-- instance only for `usize` (StepUsize); u32 ranges therefore extracted as
|
|
|
|
|
|
-- opaque axioms. These are the FAITHFUL models of Rust's `impl Step for u32`
|
|
|
|
|
|
-- (core/src/iter/range.rs), mirroring StepUsize: forward/backward via
|
|
|
|
|
|
-- u32::try_from(n)-then-checked_{add,sub}; steps_between = saturating
|
|
|
|
|
|
-- difference. Real defs, axiom-clean — so the range-loop cones (chain, and
|
|
|
|
|
|
-- every layer above) carry no plumbing axiom, only the kernel three + the
|
|
|
|
|
|
-- five hash oracles. NOT the deployed hash boundary; ordinary loop control.
|
2026-07-22 20:21:19 +00:00
|
|
|
|
@[rust_fun
|
|
|
|
|
|
"core::iter::range::{core::iter::range::Step<u32>}::backward_checked"]
|
2026-07-22 21:54:03 +00:00
|
|
|
|
def U32.Insts.CoreIterRangeStep.backward_checked
|
|
|
|
|
|
: Std.U32 → Std.Usize → Result (Option Std.U32) :=
|
|
|
|
|
|
fun start n =>
|
|
|
|
|
|
if h : n.val < 2 ^ 32 then
|
|
|
|
|
|
ok (Std.U32.checked_sub start (Std.U32.ofNatCore n.val (by omega)))
|
|
|
|
|
|
else ok none
|
2026-07-22 20:21:19 +00:00
|
|
|
|
|
|
|
|
|
|
/-- [core::iter::range::{impl core::iter::range::Step for u32}::forward_checked]:
|
|
|
|
|
|
Source: '/rustc/library/core/src/iter/range.rs', lines 282:16-282:73
|
|
|
|
|
|
Name pattern: [core::iter::range::{core::iter::range::Step<u32>}::forward_checked]
|
|
|
|
|
|
Visibility: public -/
|
|
|
|
|
|
@[rust_fun
|
|
|
|
|
|
"core::iter::range::{core::iter::range::Step<u32>}::forward_checked"]
|
2026-07-22 21:54:03 +00:00
|
|
|
|
def U32.Insts.CoreIterRangeStep.forward_checked
|
|
|
|
|
|
: Std.U32 → Std.Usize → Result (Option Std.U32) :=
|
|
|
|
|
|
fun start n =>
|
|
|
|
|
|
if h : n.val < 2 ^ 32 then
|
|
|
|
|
|
ok (Std.U32.checked_add start (Std.U32.ofNatCore n.val (by omega)))
|
|
|
|
|
|
else ok none
|
2026-07-22 20:21:19 +00:00
|
|
|
|
|
|
|
|
|
|
/-- [core::iter::range::{impl core::iter::range::Step for u32}::steps_between]:
|
|
|
|
|
|
Source: '/rustc/library/core/src/iter/range.rs', lines 271:16-271:84
|
|
|
|
|
|
Name pattern: [core::iter::range::{core::iter::range::Step<u32>}::steps_between]
|
|
|
|
|
|
Visibility: public -/
|
|
|
|
|
|
@[rust_fun "core::iter::range::{core::iter::range::Step<u32>}::steps_between"]
|
2026-07-22 21:54:03 +00:00
|
|
|
|
def U32.Insts.CoreIterRangeStep.steps_between
|
|
|
|
|
|
: Std.U32 → Std.U32 → Result (Std.Usize × (Option Std.Usize)) :=
|
|
|
|
|
|
fun start end_ =>
|
|
|
|
|
|
if h : start.val > end_.val then ok (0#usize, none)
|
|
|
|
|
|
else
|
|
|
|
|
|
let steps := Std.Usize.ofNatCore (end_.val - start.val) (by scalar_tac)
|
|
|
|
|
|
ok (steps, some steps)
|
2026-07-22 20:21:19 +00:00
|
|
|
|
|
|
|
|
|
|
/-- [zeroize::{impl zeroize::Zeroize for Z}::zeroize]:
|
|
|
|
|
|
Source: '/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zeroize-1.9.0/src/lib.rs', lines 274:4-274:25
|
|
|
|
|
|
Name pattern: [zeroize::{zeroize::Zeroize<@Z>}::zeroize]
|
|
|
|
|
|
Visibility: public -/
|
|
|
|
|
|
@[rust_fun "zeroize::{zeroize::Zeroize<@Z>}::zeroize"]
|
|
|
|
|
|
axiom zeroize.Zeroize.Blanket.zeroize
|
|
|
|
|
|
{Z : Type} (DefaultIsZeroesInst : zeroize.DefaultIsZeroes Z) : Z → Result Z
|
|
|
|
|
|
|
|
|
|
|
|
/-- [zeroize::{impl zeroize::Zeroize for [Z; N]}::zeroize]:
|
|
|
|
|
|
Source: '/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zeroize-1.9.0/src/lib.rs', lines 346:4-346:25
|
|
|
|
|
|
Name pattern: [zeroize::{zeroize::Zeroize<[@Z; @N]>}::zeroize]
|
|
|
|
|
|
Visibility: public -/
|
|
|
|
|
|
@[rust_fun "zeroize::{zeroize::Zeroize<[@Z; @N]>}::zeroize"]
|
|
|
|
|
|
axiom Array.Insts.ZeroizeZeroize.zeroize
|
|
|
|
|
|
{Z : Type} {N : Std.Usize} (ZeroizeInst : zeroize.Zeroize Z) :
|
|
|
|
|
|
Array Z N → Result (Array Z N)
|
|
|
|
|
|
|
|
|
|
|
|
/-- [zeroize::__internal::{impl zeroize::__internal::AssertZeroize for T}::zeroize_or_on_drop]:
|
|
|
|
|
|
Source: '/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/zeroize-1.9.0/src/lib.rs', lines 846:8-846:40
|
|
|
|
|
|
Name pattern: [zeroize::__internal::{zeroize::__internal::AssertZeroize<@T>}::zeroize_or_on_drop]
|
|
|
|
|
|
Visibility: public -/
|
|
|
|
|
|
@[rust_fun
|
|
|
|
|
|
"zeroize::__internal::{zeroize::__internal::AssertZeroize<@T>}::zeroize_or_on_drop"]
|
|
|
|
|
|
axiom zeroize.__internal.AssertZeroize.Blanket.zeroize_or_on_drop
|
|
|
|
|
|
{T : Type} (ZeroizeInst : zeroize.Zeroize T) : T → Result T
|
|
|
|
|
|
|
|
|
|
|
|
/-- [fips205::verify_mono::oracle::f]:
|
|
|
|
|
|
Source: 'src/verify_mono.rs', lines 49:4-51:5 -/
|
|
|
|
|
|
axiom verify_mono.oracle.f
|
|
|
|
|
|
(N : Std.Usize) :
|
|
|
|
|
|
Slice Std.U8 → types.Adrs → Slice Std.U8 → Result (Array Std.U8 N)
|
|
|
|
|
|
|
|
|
|
|
|
/-- [fips205::verify_mono::oracle::h]:
|
|
|
|
|
|
Source: 'src/verify_mono.rs', lines 54:4-56:5 -/
|
|
|
|
|
|
axiom verify_mono.oracle.h
|
|
|
|
|
|
(N : Std.Usize) :
|
|
|
|
|
|
Slice Std.U8 → types.Adrs → Slice Std.U8 → Slice Std.U8 → Result
|
|
|
|
|
|
(Array Std.U8 N)
|
|
|
|
|
|
|
|
|
|
|
|
/-- [fips205::verify_mono::oracle::t_l]:
|
|
|
|
|
|
Source: 'src/verify_mono.rs', lines 60:4-64:5 -/
|
|
|
|
|
|
axiom verify_mono.oracle.t_l
|
|
|
|
|
|
{X : Std.Usize} {N : Std.Usize} :
|
|
|
|
|
|
Slice Std.U8 → types.Adrs → Array (Array Std.U8 N) X → Result (Array
|
|
|
|
|
|
Std.U8 N)
|
|
|
|
|
|
|
|
|
|
|
|
/-- [fips205::verify_mono::oracle::t_len]:
|
|
|
|
|
|
Source: 'src/verify_mono.rs', lines 69:4-73:5 -/
|
|
|
|
|
|
axiom verify_mono.oracle.t_len
|
|
|
|
|
|
{X : Std.Usize} {N : Std.Usize} :
|
|
|
|
|
|
Slice Std.U8 → types.Adrs → Array (Array Std.U8 N) X → Result (Array
|
|
|
|
|
|
Std.U8 N)
|
|
|
|
|
|
|
|
|
|
|
|
/-- [fips205::verify_mono::oracle::h_msg]:
|
|
|
|
|
|
Source: 'src/verify_mono.rs', lines 81:4-85:5 -/
|
|
|
|
|
|
axiom verify_mono.oracle.h_msg
|
|
|
|
|
|
(M : Std.Usize) :
|
|
|
|
|
|
Slice Std.U8 → Slice Std.U8 → Slice Std.U8 → Slice Std.U8 → Result
|
|
|
|
|
|
(Array Std.U8 M)
|
|
|
|
|
|
|