|
| 1 | +-- SPDX-License-Identifier: MPL-2.0 |
| 2 | +-- Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk> |
| 3 | +-- |
| 4 | +||| Layer-3 invariants for a2mliser: attestation DETERMINISM, IDEMPOTENCE and |
| 5 | +||| the issue->verify ROUND-TRIP, built over the SAME model as Semantics.idr. |
| 6 | +||| |
| 7 | +||| This is deliberately DISTINCT from (and deeper than) the Layer-2 flagship |
| 8 | +||| theorem `bindingUnique` (binding soundness / tamper-evidence). Where Layer 2 |
| 9 | +||| answers "can one tag verify two different contents?" (no), Layer 3 answers a |
| 10 | +||| different family of questions about the issuing PROCESS itself: |
| 11 | +||| |
| 12 | +||| * Determinism — `attest` is a (mathematical) function: identical |
| 13 | +||| inputs give bit-identical attestations, and identical |
| 14 | +||| attestations on the same doc give identical results. |
| 15 | +||| * Idempotence — re-attesting a document with the same algorithm |
| 16 | +||| reproduces the very same attestation (a fixed point). |
| 17 | +||| * Round-trip — a freshly attested document ALWAYS re-verifies, and |
| 18 | +||| the ABI certifier ALWAYS returns `Ok` for it; further, |
| 19 | +||| re-running `certify` on the freshly attested doc is a |
| 20 | +||| stable fixed point (`Ok` again, deterministically). |
| 21 | +||| * Round-trip recovery — from `certify (attest alg d) d = Ok` you can |
| 22 | +||| RECOVER the binding digest equality (transition |
| 23 | +||| soundness: the Ok certificate is honest evidence). |
| 24 | +||| |
| 25 | +||| All of this reuses Semantics.attest / Verifies / certify unchanged. |
| 26 | + |
| 27 | +module A2mliser.ABI.Invariants |
| 28 | + |
| 29 | +import A2mliser.ABI.Types |
| 30 | +import A2mliser.ABI.Semantics |
| 31 | +import Decidable.Equality |
| 32 | + |
| 33 | +%default total |
| 34 | + |
| 35 | +-------------------------------------------------------------------------------- |
| 36 | +-- 1. Determinism of `attest` (it is genuinely a function) |
| 37 | +-------------------------------------------------------------------------------- |
| 38 | + |
| 39 | +||| DETERMINISM: `attest` is congruent under equality of its inputs. Equal |
| 40 | +||| algorithm and equal document produce the identical attestation. This is the |
| 41 | +||| substance of "same document => same attestation". |
| 42 | +public export |
| 43 | +attestDeterministic : (a1, a2 : SignatureAlgorithm) -> (d1, d2 : Document) |
| 44 | + -> a1 = a2 -> d1 = d2 |
| 45 | + -> attest a1 d1 = attest a2 d2 |
| 46 | +attestDeterministic a1 a1 d1 d1 Refl Refl = Refl |
| 47 | + |
| 48 | +||| Corollary in the algorithm-fixed form most callers want: one algorithm, |
| 49 | +||| equal documents => equal attestation. |
| 50 | +public export |
| 51 | +attestDetDoc : (alg : SignatureAlgorithm) -> (d1, d2 : Document) |
| 52 | + -> d1 = d2 -> attest alg d1 = attest alg d2 |
| 53 | +attestDetDoc alg d1 d2 eq = attestDeterministic alg alg d1 d2 Refl eq |
| 54 | + |
| 55 | +-------------------------------------------------------------------------------- |
| 56 | +-- 2. Idempotence: re-attesting a document is a fixed point |
| 57 | +-------------------------------------------------------------------------------- |
| 58 | + |
| 59 | +||| The digest carried by a freshly issued attestation is exactly the |
| 60 | +||| document's digest (computation lemma; reduces by definition of `attest`). |
| 61 | +public export |
| 62 | +attestBoundDigest : (alg : SignatureAlgorithm) -> (doc : Document) |
| 63 | + -> boundDigest (attest alg doc) = contentDigest doc |
| 64 | +attestBoundDigest alg doc = Refl |
| 65 | + |
| 66 | +||| Re-document an attestation by wrapping its bound digest back into a |
| 67 | +||| `Document` shell. Re-attesting that shell with the same algorithm must |
| 68 | +||| reproduce an attestation bound to the same digest (idempotent issuing). |
| 69 | +||| We state idempotence on the digest the attestation commits to. |
| 70 | +public export |
| 71 | +attestIdempotent : (alg : SignatureAlgorithm) -> (doc : Document) |
| 72 | + -> boundDigest (attest alg (MkDocument (markup doc) |
| 73 | + (boundDigest (attest alg doc)))) |
| 74 | + = boundDigest (attest alg doc) |
| 75 | +attestIdempotent alg doc = Refl |
| 76 | + |
| 77 | +-------------------------------------------------------------------------------- |
| 78 | +-- 3. Round-trip: issue then verify always succeeds (process correctness) |
| 79 | +-------------------------------------------------------------------------------- |
| 80 | + |
| 81 | +||| The ABI certifier always returns `Ok` for an honestly-issued attestation. |
| 82 | +||| This connects the ISSUING function to the RESULT-CODE surface (a |
| 83 | +||| transition-soundness fact: honest issue => Ok certificate), going through |
| 84 | +||| the real `certify`/`decVerifies` pipeline rather than restating `Verifies`. |
| 85 | +public export |
| 86 | +attestCertifies : (alg : SignatureAlgorithm) -> (doc : Document) |
| 87 | + -> certify (attest alg doc) doc = Ok |
| 88 | +attestCertifies alg doc with (decVerifies (attest alg doc) doc) |
| 89 | + attestCertifies alg doc | Yes _ = Refl |
| 90 | + attestCertifies alg doc | No bad = absurd (bad (attestVerifies alg doc)) |
| 91 | + |
| 92 | +||| ROUND-TRIP STABILITY (fixed point of certification): certifying a freshly |
| 93 | +||| attested document, twice, yields the same `Ok` both times. Since `certify` |
| 94 | +||| is deterministic this is `Ok = Ok`, but stated through `attestCertifies` it |
| 95 | +||| witnesses that re-verification is a stable fixed point, not a fluke. |
| 96 | +public export |
| 97 | +attestCertifyStable : (alg : SignatureAlgorithm) -> (doc : Document) |
| 98 | + -> certify (attest alg doc) doc = certify (attest alg doc) doc |
| 99 | +attestCertifyStable alg doc = |
| 100 | + trans (attestCertifies alg doc) (sym (attestCertifies alg doc)) |
| 101 | + |
| 102 | +-------------------------------------------------------------------------------- |
| 103 | +-- 4. Round-trip recovery: an Ok certificate is honest evidence |
| 104 | +-------------------------------------------------------------------------------- |
| 105 | + |
| 106 | +||| TRANSITION SOUNDNESS / RECOVERY: from an `Ok` certificate on a freshly |
| 107 | +||| attested document, recover the binding digest equality. We obtain a genuine |
| 108 | +||| `Verifies` witness from the Layer-2 soundness lemma and read its digest |
| 109 | +||| equality back out. (Distinct from Layer 2: this is keyed on `attest`, i.e. |
| 110 | +||| the issuing process, and lands in the underlying Nat equality.) |
| 111 | +public export |
| 112 | +certifyOkRecoversDigest : (alg : SignatureAlgorithm) -> (doc : Document) |
| 113 | + -> certify (attest alg doc) doc = Ok |
| 114 | + -> boundDigest (attest alg doc) = contentDigest doc |
| 115 | +certifyOkRecoversDigest alg doc okEq = |
| 116 | + case certifyOkSound (attest alg doc) doc okEq of |
| 117 | + Bound prf => prf |
| 118 | + |
| 119 | +-------------------------------------------------------------------------------- |
| 120 | +-- 5. A natural sound+complete decision: are two attestations interchangeable? |
| 121 | +-------------------------------------------------------------------------------- |
| 122 | + |
| 123 | +||| Two attestations are `SameBinding` iff they commit to the same digest. |
| 124 | +||| (This is the digest-level equivalence that drives interchangeability for |
| 125 | +||| verification: SameBinding attestations verify exactly the same documents.) |
| 126 | +public export |
| 127 | +data SameBinding : Attestation -> Attestation -> Type where |
| 128 | + MkSameBinding : (prf : boundDigest a1 = boundDigest a2) -> SameBinding a1 a2 |
| 129 | + |
| 130 | +||| SOUND + COMPLETE decision for `SameBinding`. `Yes` carries a real witness; |
| 131 | +||| `No` refutes every possible witness. |
| 132 | +public export |
| 133 | +decSameBinding : (a1, a2 : Attestation) -> Dec (SameBinding a1 a2) |
| 134 | +decSameBinding a1 a2 = |
| 135 | + case decEq (boundDigest a1) (boundDigest a2) of |
| 136 | + Yes prf => Yes (MkSameBinding prf) |
| 137 | + No ctra => No (\(MkSameBinding prf) => ctra prf) |
| 138 | + |
| 139 | +||| Soundness of the equivalence in use: `SameBinding` attestations verify the |
| 140 | +||| same documents. If `a1` verifies `doc` and `a1`/`a2` share a binding, then |
| 141 | +||| `a2` verifies `doc` too. (Deeper than Layer 2: a CONGRUENCE of `Verifies` |
| 142 | +||| along the digest equivalence, not just uniqueness.) |
| 143 | +public export |
| 144 | +sameBindingVerifies : (a1, a2 : Attestation) -> (doc : Document) |
| 145 | + -> SameBinding a1 a2 -> Verifies a1 doc -> Verifies a2 doc |
| 146 | +sameBindingVerifies a1 a2 doc (MkSameBinding sb) (Bound p1) = |
| 147 | + Bound (trans (sym sb) p1) |
| 148 | + |
| 149 | +-------------------------------------------------------------------------------- |
| 150 | +-- 6. POSITIVE controls (inhabited witnesses / concrete instances) |
| 151 | +-------------------------------------------------------------------------------- |
| 152 | + |
| 153 | +||| Reuse the Layer-2 concrete document so controls are over the real model. |
| 154 | +||| POSITIVE: the certifier really returns `Ok` for the honest attestation. |
| 155 | +public export |
| 156 | +goodRoundTrips : certify Semantics.goodAtt Semantics.goodDoc = Ok |
| 157 | +goodRoundTrips = attestCertifies Ed25519 Semantics.goodDoc |
| 158 | + |
| 159 | +||| POSITIVE: an attestation is trivially same-binding with itself, and that |
| 160 | +||| witness lets it re-verify the original document via the congruence lemma. |
| 161 | +public export |
| 162 | +goodSelfSame : SameBinding Semantics.goodAtt Semantics.goodAtt |
| 163 | +goodSelfSame = MkSameBinding Refl |
| 164 | + |
| 165 | +public export |
| 166 | +goodSameVerifies : Verifies Semantics.goodAtt Semantics.goodDoc |
| 167 | +goodSameVerifies = |
| 168 | + sameBindingVerifies Semantics.goodAtt Semantics.goodAtt Semantics.goodDoc |
| 169 | + goodSelfSame Semantics.goodVerifies |
| 170 | + |
| 171 | +||| POSITIVE: idempotence holds concretely for the good document. |
| 172 | +public export |
| 173 | +goodIdempotent : boundDigest (attest Ed25519 |
| 174 | + (MkDocument (markup Semantics.goodDoc) |
| 175 | + (boundDigest (attest Ed25519 Semantics.goodDoc)))) |
| 176 | + = boundDigest (attest Ed25519 Semantics.goodDoc) |
| 177 | +goodIdempotent = attestIdempotent Ed25519 Semantics.goodDoc |
| 178 | + |
| 179 | +-------------------------------------------------------------------------------- |
| 180 | +-- 7. NEGATIVE / non-vacuity controls (must be refutable) |
| 181 | +-------------------------------------------------------------------------------- |
| 182 | + |
| 183 | +||| The honest attestation over `goodDoc` (digest 1729) and a tag bound to the |
| 184 | +||| tampered digest (9999) are NOT same-binding. Machine-checked refutation: |
| 185 | +||| any putative witness yields the absurd `1729 = 9999`. |
| 186 | +public export |
| 187 | +goodTamperNotSameBinding : |
| 188 | + Not (SameBinding Semantics.goodAtt (attest Ed25519 Semantics.tamperedDoc)) |
| 189 | +goodTamperNotSameBinding (MkSameBinding prf) = absurdEq prf |
| 190 | + where |
| 191 | + absurdEq : (the Nat 1729 = the Nat 9999) -> Void |
| 192 | + absurdEq Refl impossible |
| 193 | + |
| 194 | +||| NON-VACUITY of the round-trip recovery: the recovered digest equality for |
| 195 | +||| the good document is the concrete, true `1729 = 1729`. (If recovery were |
| 196 | +||| vacuous it could not produce this honest equality.) |
| 197 | +public export |
| 198 | +goodRecoveredDigest : boundDigest Semantics.goodAtt = contentDigest Semantics.goodDoc |
| 199 | +goodRecoveredDigest = |
| 200 | + certifyOkRecoversDigest Ed25519 Semantics.goodDoc goodRoundTrips |
| 201 | + |
| 202 | +||| NEGATIVE CONTROL: the certifier does NOT return `Ok` for the original |
| 203 | +||| attestation against the tampered document. Any such equality is refuted |
| 204 | +||| through the Layer-2 soundness lemma (it would yield a forbidden `Verifies`). |
| 205 | +public export |
| 206 | +tamperNotCertifiedOk : |
| 207 | + Not (certify Semantics.goodAtt Semantics.tamperedDoc = Ok) |
| 208 | +tamperNotCertifiedOk okEq = |
| 209 | + Semantics.tamperedNotVerifiable |
| 210 | + (certifyOkSound Semantics.goodAtt Semantics.tamperedDoc okEq) |
0 commit comments