diff --git a/README.md b/README.md
index c6b06ad75..e5e261893 100644
--- a/README.md
+++ b/README.md
@@ -297,7 +297,7 @@ yours to keep:
✅ |
-❌ |
+✅ |
diff --git a/bench/benches/primitives/hmac_sha256.rs b/bench/benches/primitives/hmac_sha256.rs
index c7d5d223b..e961805cc 100644
--- a/bench/benches/primitives/hmac_sha256.rs
+++ b/bench/benches/primitives/hmac_sha256.rs
@@ -9,20 +9,6 @@ use verified_garbage::hmac::Hmac;
/// `ci/bench_arches.py`): this one and those it calls.
pub const USES: &[&str] = &["hmac_sha256", "sha256"];
-#[cfg(not(any(
- target_arch = "x86_64",
- target_arch = "aarch64",
- target_arch = "arm",
- target_arch = "x86"
-)))]
-pub fn bench(_: &mut Criterion) {}
-
-#[cfg(any(
- target_arch = "x86_64",
- target_arch = "aarch64",
- target_arch = "arm",
- target_arch = "x86"
-))]
pub fn bench(c: &mut Criterion) {
crate::hmac_group(
c,
diff --git a/lean/VerifiedGarbage/Artifacts/Ct/PPC64LE.lean b/lean/VerifiedGarbage/Artifacts/Ct/PPC64LE.lean
new file mode 100644
index 000000000..09d336390
--- /dev/null
+++ b/lean/VerifiedGarbage/Artifacts/Ct/PPC64LE.lean
@@ -0,0 +1,14 @@
+import VerifiedGarbage.Proof.Ct.PPC64LE
+
+namespace VG.Artifacts.Ct.PPC64LE
+open VG.PPC64LE
+
+def artifacts : List Artifact := [
+ { Spec.Ct.eqApi with
+ target := target
+ doc := Spec.Ct.eqApi.doc
+ code := Impl.Ct.PPC64LE.eq
+ contract := Spec.Ct.eqContract abi
+ verified := Proof.Ct.PPC64LE.verified
+ spSafe := Code.all_of_forall (fun _ => rfl) _ }]
+end VG.Artifacts.Ct.PPC64LE
diff --git a/lean/VerifiedGarbage/Artifacts/HmacSha256/PPC64LE.lean b/lean/VerifiedGarbage/Artifacts/HmacSha256/PPC64LE.lean
new file mode 100644
index 000000000..33a13aaa5
--- /dev/null
+++ b/lean/VerifiedGarbage/Artifacts/HmacSha256/PPC64LE.lean
@@ -0,0 +1,37 @@
+import VerifiedGarbage.TCB.PPC64LE.Target
+import VerifiedGarbage.Proof.Hmac.PPC64LE.Shared
+
+/-!
+# HMAC-SHA-256 (RFC 2104) on PPC64LE
+
+A registration file (see `TCB/Emit.lean`): the artifacts it lists are
+emitted. **Review note**: `sig` and `doc` are trusted, as they tie the Rust
+caller to the contract; check them against the contract's `pre`/`post`. An
+artifact made from a function's `Api` (in `Spec/`, reviewed with the
+contract) takes them from there, and this file adds only notes on the
+implementation. The emitter adds the `# Safety` items that depend on the
+target (`Sig.layoutDoc`), from `stack` and `writeArgs`, which `ofSig` checks
+against the contract.
+-/
+
+namespace VG.Artifacts.HmacSha256.PPC64LE
+
+def artifacts : List Artifact := [
+ { Spec.Hmac.initSha256Api with
+ target := PPC64LE.target
+ doc := Spec.Hmac.initSha256Api.doc
+ code := Impl.Hmac.PPC64LE.init
+ contract := Spec.Hmac.initSha256Contract PPC64LE.abi 48
+ stack := 48
+ verified := Proof.Hmac.PPC64LE.Shared.init
+ spSafe := Code.all_of_forall (fun _ => rfl) _ },
+ { Spec.Hmac.finalizeSha256Api with
+ target := PPC64LE.target
+ doc := Spec.Hmac.finalizeSha256Api.doc
+ code := Impl.Hmac.PPC64LE.finalize
+ contract := Spec.Hmac.finalizeSha256Contract PPC64LE.abi 96
+ stack := 96
+ verified := Proof.Hmac.PPC64LE.Shared.finalize
+ spSafe := Code.all_of_forall (fun _ => rfl) _ }]
+
+end VG.Artifacts.HmacSha256.PPC64LE
diff --git a/lean/VerifiedGarbage/Impl/Ct/PPC64LE.lean b/lean/VerifiedGarbage/Impl/Ct/PPC64LE.lean
new file mode 100644
index 000000000..355ab1037
--- /dev/null
+++ b/lean/VerifiedGarbage/Impl/Ct/PPC64LE.lean
@@ -0,0 +1,32 @@
+import VerifiedGarbage.TCB.PPC64LE.Isa
+
+/-!
+# Constant-time byte comparison on PPC64LE
+
+`vg_ct_eq(a = r3, a_len = r4, b = r5, b_len = r6)`: the same algorithm as
+the AArch64 implementation (`VG.Impl.Ct.AArch64`). The lengths are compared
+first; if they are equal, the XORs of the bytes at each offset are ORed
+together into `r7`, and the result is `(r7 - 1) >> 63`, which is 1 iff `r7`
+is zero. The branches are on the lengths only.
+-/
+
+namespace VG.Impl.Ct.PPC64LE
+open VG.PPC64LE
+
+def step : List Instr := [
+ .add .r10 .r3 .r8, .lbz .r11 .r10 0,
+ .add .r10 .r5 .r8, .lbz .r12 .r10 0,
+ .logic .xor .r11 .r11 .r12, .logic .or .r7 .r7 .r11,
+ .addi .r8 .r8 1, .sub .r9 .r8 .r4]
+
+def finish : Prog isa := .block [.subi .r7 .r7 1, .lsr .d .r3 .r7 63]
+
+def equal : Prog isa :=
+ .seq (.block [.li .r8 0])
+ (.seq (.ite (.zero .d .r4) (.block []) (.loop (.block step) (.nonzero .d .r9))) finish)
+
+def eq : Prog isa :=
+ .seq (.block [.li .r7 0, .sub .r9 .r4 .r6])
+ (.ite (.zero .d .r9) equal (.block [.li .r3 0]))
+
+end VG.Impl.Ct.PPC64LE
diff --git a/lean/VerifiedGarbage/Impl/Hmac/PPC64LE.lean b/lean/VerifiedGarbage/Impl/Hmac/PPC64LE.lean
new file mode 100644
index 000000000..93f04448b
--- /dev/null
+++ b/lean/VerifiedGarbage/Impl/Hmac/PPC64LE.lean
@@ -0,0 +1,119 @@
+import VerifiedGarbage.Impl.Sha256.PPC64LE.Stream
+
+/-!
+# HMAC-SHA-256: PPC64LE implementation
+
+The same algorithm as on AArch64 (`VG.Impl.Hmac.AArch64`): two SHA-256
+streaming states (`inner`, `outer`; see `VG.Spec.Hmac`).
+
+* `init(inner = r3, outer = r4, key = r5, key_len = r6, scratch = r7)`
+ stores `H⁽⁰⁾` in both states, the block `K₀ ⊕ ipad` in the inner buffer
+ and `K₀ ⊕ opad` in the outer one, and compresses both (calling
+ `vg_sha256_compress`).
+* `finalize(inner = r3, outer = r4, count = r5, scratch = r6)` finalizes the
+ inner state (calling `vg_sha256_finalize`), makes the inner state
+ represent `(K₀ ⊕ opad) ‖ digest` (96 bytes) from the outer hash value and
+ that digest, and finalizes it again, leaving the MAC in
+ `scratch[176..208)`.
+
+Both move the link register to `r0` and save it in a frame around the
+whole function, as the streaming SHA-256 functions do.
+-/
+
+namespace VG.Impl.Hmac.PPC64LE
+
+open VG.PPC64LE
+open VG.Impl.Sha256.PPC64LE.Stream (mov compressAt save restore)
+
+/-! ## `init`
+
+As in the streaming SHA-256 `update`, the call of the compression function
+(`compressAt`: the block at `r4` into the hash value at `r26`, with scratch
+space `r27`) preserves `r14`–`r31`, so our variables live in `r26`–`r31`,
+and our caller's values of those are saved in `scratch[112..160)`.
+
+Registers: `r26` = the state being compressed (`inner`, then `outer`), `r27`
+= `scratch`, `r28` = `outer`, `r29` = the next key byte, `r30` = key bytes
+left, `r31` = the byte index, `r12` = `0x36` (`ipad`), `r0` = `0x5c`
+(`opad`). Byte `r31` of a buffer is addressed as `32(r11)` with
+`r11 = state + r31`. -/
+
+/-- `H⁽⁰⁾` into the state at `b`. -/
+def h0 (b : Reg) : List Instr :=
+ (List.range 8).flatMap fun k =>
+ [.lis .r8 (Spec.Sha256.H0[k]!.extractLsb' 16 16),
+ .ori .r8 .r8 (Spec.Sha256.H0[k]!.extractLsb' 0 16),
+ .store .w .r8 b (4 * k)]
+
+/-- The key bytes, XORed with `ipad` into the inner buffer and `opad` into the outer one. -/
+def keyLoop : Prog isa :=
+ .loop (.block [.lbz .r8 .r29 0,
+ .logic .xor .r9 .r8 .r12, .add .r11 .r26 .r31, .stb .r9 .r11 32,
+ .logic .xor .r9 .r8 .r0, .add .r11 .r28 .r31, .stb .r9 .r11 32,
+ .addi .r29 .r29 1, .addi .r31 .r31 1, .subi .r30 .r30 1]) (.nonzero .d .r30)
+
+/-- The zero bytes after the key (`r10` of them), XORed likewise. -/
+def padLoop : Prog isa :=
+ .loop (.block [.add .r11 .r26 .r31, .stb .r12 .r11 32, .add .r11 .r28 .r31, .stb .r0 .r11 32,
+ .addi .r31 .r31 1, .subi .r10 .r10 1]) (.nonzero .d .r10)
+
+/-- `init`, but for saving the link register. -/
+def initMain : Prog isa :=
+ .seq (.block (save .r7 ++ [mov .r26 .r3, mov .r27 .r7, mov .r28 .r4, mov .r29 .r5, mov .r30 .r6] ++
+ h0 .r26 ++ h0 .r28 ++ [.li .r12 0x36, .li .r0 0x5c, .li .r31 0]))
+ (.seq (.ite (.zero .d .r30) (.block []) keyLoop)
+ (.seq (.block [.li .r10 64, .sub .r10 .r10 .r31])
+ (.seq (.ite (.zero .d .r10) (.block []) padLoop)
+ (.seq (.block [.addi .r4 .r26 32])
+ (.seq compressAt
+ (.seq (.block [mov .r26 .r28, .addi .r4 .r26 32])
+ (.seq compressAt
+ (.block restore))))))))
+
+def init : Prog isa :=
+ .seq (.block [.mflr .r0]) (.seq (.frame (.push .r0) initMain (.pop .r0)) (.block [.mtlr .r0]))
+
+/-! ## `finalize`
+
+The MAC is left in `scratch[176..208)`. The outer hash value is first copied
+to `scratch[208..240)`; the inner state is finalized into
+`scratch[176..208)`; then the inner state is overwritten with the outer hash
+value and that digest, so that it represents `(K₀ ⊕ opad) ‖ digest`, and
+finalized again.
+
+`inner` and `scratch` are kept in `r24` and `r25`, which the calls of
+`vg_sha256_finalize` preserve and never even write (so the taint analysis
+knows they are still public after them); our caller's values of those are
+saved in `scratch[160..176)`, and our return address in a stack frame. -/
+
+/-- Copying 32-bit word `k` from `o₁(src)` to `o₂(dst)`. -/
+def cp32 (src dst : Reg) (o₁ o₂ k : Nat) : List Instr :=
+ [.load .w .r8 src (o₁ + 4 * k), .store .w .r8 dst (o₂ + 4 * k)]
+
+/-- Copying 64-bit word `k` from `o₁(src)` to `o₂(dst)`. -/
+def cp64 (src dst : Reg) (o₁ o₂ k : Nat) : List Instr :=
+ [.load .d .r8 src (o₁ + 8 * k), .store .d .r8 dst (o₂ + 8 * k)]
+
+/-- The outer hash value into `scratch[208..240)`. -/
+def saveOuter : List Instr := (List.range 8).flatMap (cp32 .r4 .r6 0 208)
+
+/-- The outer hash value and the first digest into the inner state. -/
+def loadOuter : List Instr :=
+ (List.range 8).flatMap (cp32 .r25 .r24 208 0) ++ (List.range 4).flatMap (cp64 .r25 .r24 176 32)
+
+/-- A call of `vg_sha256_finalize`. -/
+def sha256Finalize : Prog isa := .call "vg_sha256_finalize" Impl.Sha256.PPC64LE.Stream.finalize
+
+/-- `finalize`, but for saving the link register. -/
+def finalizeMain : Prog isa :=
+ .seq (.block ([.store .d .r24 .r6 160, .store .d .r25 .r6 168, mov .r24 .r3, mov .r25 .r6] ++ saveOuter ++
+ [mov .r4 .r5, .addi .r5 .r6 176]))
+ (.seq sha256Finalize
+ (.seq (.block (loadOuter ++ [mov .r3 .r24, .li .r4 96, .addi .r5 .r25 176, mov .r6 .r25]))
+ (.seq sha256Finalize
+ (.block [.load .d .r24 .r25 160, .load .d .r25 .r25 168]))))
+
+def finalize : Prog isa :=
+ .seq (.block [.mflr .r0]) (.seq (.frame (.push .r0) finalizeMain (.pop .r0)) (.block [.mtlr .r0]))
+
+end VG.Impl.Hmac.PPC64LE
diff --git a/lean/VerifiedGarbage/Proof/Ct/PPC64LE.lean b/lean/VerifiedGarbage/Proof/Ct/PPC64LE.lean
new file mode 100644
index 000000000..8329eccec
--- /dev/null
+++ b/lean/VerifiedGarbage/Proof/Ct/PPC64LE.lean
@@ -0,0 +1,200 @@
+import VerifiedGarbage.Impl.Ct.PPC64LE
+import VerifiedGarbage.Proof.Ct.Common
+import VerifiedGarbage.Proof.Framework.PPC64LE.Run
+import VerifiedGarbage.Proof.Framework.PPC64LE.Inline
+import VerifiedGarbage.Proof.Framework.PPC64LE.Taint
+import VerifiedGarbage.Proof.Framework.Contract
+import VerifiedGarbage.Proof.Framework.Offset
+import VerifiedGarbage.Spec.Ct.Contract
+import VerifiedGarbage.TCB.PPC64LE.Target
+
+/-!
+# Constant-time byte comparison on PPC64LE
+
+Untrusted: everything here is checked by Lean. The same structure as the
+AArch64 proof (`VG.Proof.Ct.AArch64`).
+-/
+
+namespace VG.Proof.Ct.PPC64LE
+open VG VG.PPC64LE VG.Impl.Ct.PPC64LE
+
+/-- The registers the code writes. -/
+def scratch : List Reg := [.r3, .r7, .r8, .r9, .r10, .r11, .r12]
+
+/-- The comparison changes only `scratch` (all but `r3` before `finish`) and
+not its permissions. -/
+def Keep (s t : State) : Prop :=
+ (∀ r, r ∉ scratch → t.gpr r = s.gpr r) ∧ t.rd = s.rd ∧ t.wr = s.wr
+
+theorem keep {c : Prog isa} {s : State} {Q : State → Prop}
+ (h : WP isa c s Q)
+ (hc : c.allInstrs (fun i => match dstOf i with
+ | none => true | some r => scratch.contains r) = true)
+ (hn : c.noCalls = true := by decide +kernel) :
+ WP isa c s fun t => Q t ∧ Keep s t := by
+ obtain ⟨tr, t, he, hq⟩ := h
+ refine ⟨tr, t, he, hq, fun r hr => Exec.gpr (fun i hi => ?_) he (.inl hn),
+ (Exec.rdwr he).1, (Exec.rdwr he).2.1⟩
+ rw [Code.allInstrs_eq, List.all_eq_true] at hc
+ have hh := hc i hi
+ intro hd
+ rw [hd] at hh
+ exact hr (List.contains_iff_mem.mp hh)
+
+theorem Keep.trans {a b c : State} (h : Keep a b) (k : Keep b c) : Keep a c :=
+ ⟨fun r hr => (k.1 r hr).trans (h.1 r hr), k.2.1.trans h.2.1, k.2.2.trans h.2.2⟩
+
+def contract : Contract isa where
+ pre s := s.rd = [⟨s.gpr .r3, (s.gpr .r4).toNat⟩, ⟨s.gpr .r5, (s.gpr .r6).toNat⟩] ∧ s.wr = []
+ post s t := (t.gpr .r3).setWidth 32 = if Spec.Ct.eq
+ (Spec.Ct.bytesAt s.mem (s.gpr .r3) (s.gpr .r4).toNat)
+ (Spec.Ct.bytesAt s.mem (s.gpr .r5) (s.gpr .r6).toNat) then 1 else 0
+ pub s t := s.gpr .r3 = t.gpr .r3 ∧ s.gpr .r4 = t.gpr .r4 ∧
+ s.gpr .r5 = t.gpr .r5 ∧ s.gpr .r6 = t.gpr .r6 ∧ s.sp = t.sp
+
+def Inv (s₀ : State) (i : Nat) (s : State) : Prop :=
+ Keep s₀ s ∧ s.gpr .r3 = s₀.gpr .r3 ∧ s.mem = s₀.mem ∧ s.gpr .r8 = BitVec.ofNat 64 i ∧
+ s.gpr .r7 = (diff s₀.mem (s₀.gpr .r3) (s₀.gpr .r5) i).setWidth 64
+
+theorem step_ok (s₀ s : State) (hp : contract.pre s₀)
+ (hlen : s₀.gpr .r6 = s₀.gpr .r4) (i : Nat) (hi : i < (s₀.gpr .r4).toNat)
+ (h : Inv s₀ i s) :
+ WP isa (.block step) s fun t => Inv s₀ (i + 1) t ∧
+ t.gpr .r9 = BitVec.ofNat 64 (i + 1) - s₀.gpr .r4 := by
+ obtain ⟨hk, h3, hm, hx, ha⟩ := h
+ have h4 := hk.1 .r4 (by decide)
+ have h5 := hk.1 .r5 (by decide)
+ have hn := (s₀.gpr .r4).isLt
+ have hA : InRegions (s.rd ++ s.wr) (s₀.gpr .r3 + BitVec.ofNat 64 i) 1 := by
+ rw [hk.2.1, hk.2.2, hp.1, hp.2, List.append_nil]
+ exact ⟨⟨s₀.gpr .r3, (s₀.gpr .r4).toNat⟩, by simp, Offset.contains_base _ (by omega) (by omega)⟩
+ have hB : InRegions (s.rd ++ s.wr) (s₀.gpr .r5 + BitVec.ofNat 64 i) 1 := by
+ rw [hk.2.1, hk.2.2, hp.1, hp.2, hlen, List.append_nil]
+ exact ⟨⟨s₀.gpr .r5, (s₀.gpr .r4).toNat⟩, by simp, Offset.contains_base _ (by omega) (by omega)⟩
+ have hadd : BitVec.ofNat 64 i + 1#64 = BitVec.ofNat 64 (i + 1) := by rw [BitVec.ofNat_add]
+ unfold step
+ refine WP.mono (keep (Q := fun t => t.gpr .r3 = s₀.gpr .r3 ∧ t.mem = s₀.mem ∧
+ t.gpr .r8 = BitVec.ofNat 64 (i + 1) ∧
+ t.gpr .r7 = (diff s₀.mem (s₀.gpr .r3) (s₀.gpr .r5) (i + 1)).setWidth 64 ∧
+ t.gpr .r9 = BitVec.ofNat 64 (i + 1) - s₀.gpr .r4) ?_ (by rfl)) ?_
+ · crun [h3, h4, h5, hx, ha, hm, hA, hB, hadd, ← BitVec.setWidth_or, ← BitVec.setWidth_xor]
+ rfl
+ · intro t ⟨⟨h3', hm', hx', ha', hz⟩, hk'⟩
+ exact ⟨⟨hk.trans hk', h3', hm', hx', ha'⟩, hz⟩
+
+theorem sub_zero (a b : BitVec 64) : (a - b == 0) = (a == b) := by
+ apply Bool.eq_iff_iff.mpr
+ simp only [beq_iff_eq]
+ bv_omega
+
+theorem loop_ok (s₀ : State) (hp : contract.pre s₀) (hlen : s₀.gpr .r6 = s₀.gpr .r4)
+ (s : State) (i : Nat) (hi : i < (s₀.gpr .r4).toNat) (h : Inv s₀ i s) :
+ WP isa (.loop (.block step) (.nonzero .d .r9)) s (Inv s₀ (s₀.gpr .r4).toNat) := by
+ let n := (s₀.gpr .r4).toNat
+ apply WP.loop (M := isa) (fun rem t => ∃ j, j < n ∧ rem = n - j ∧ Inv s₀ j t) ?_ (n - i) s
+ ⟨i, hi, rfl, h⟩
+ intro rem t ⟨j, hj, hr, hinv⟩
+ refine WP.mono (step_ok s₀ t hp hlen j hj hinv) fun t' ⟨hout, hz⟩ => ?_
+ by_cases he : j + 1 = n
+ · left
+ refine ⟨?_, (by simpa only [he] using hout)⟩
+ simp [VG.PPC64LE.eval, State.read, hz, he, n]
+ · right
+ have hne : BitVec.ofNat 64 (j + 1) ≠ s₀.gpr .r4 := by
+ intro hh
+ have := congrArg BitVec.toNat hh
+ rw [BitVec.toNat_ofNat, Nat.mod_eq_of_lt (by have := (s₀.gpr .r4).isLt; dsimp [n] at *; omega)] at this
+ exact he this
+ refine ⟨?_, n - (j + 1), by omega, j + 1, by omega, rfl, hout⟩
+ simp only [VG.PPC64LE.eval, State.read, BitVec.setWidth_eq, hz, bne, sub_zero,
+ beq_eq_false_iff_ne.mpr hne]
+ rfl
+
+theorem finish_ok (s : State) (d : Byte) (ha : s.gpr .r7 = d.setWidth 64) :
+ WP isa finish s fun t => t.mem = s.mem ∧
+ (t.gpr .r3).setWidth 32 = if d = 0 then 1 else 0 := by
+ unfold finish
+ crun [ha]
+ exact result64 d
+
+theorem equal_ok (s₀ s : State) (hp : contract.pre s₀)
+ (hlen : s₀.gpr .r6 = s₀.gpr .r4)
+ (hk : Keep s₀ s) (h3 : s.gpr .r3 = s₀.gpr .r3) (hm : s.mem = s₀.mem) (ha : s.gpr .r7 = 0) :
+ WP isa equal s fun t => t.mem = s₀.mem ∧ contract.post s₀ t := by
+ have start : WP isa (.block [.li .r8 0]) s (Inv s₀ 0) := by
+ refine WP.mono (keep (Q := fun t => t.gpr .r3 = s₀.gpr .r3 ∧ t.mem = s₀.mem ∧
+ t.gpr .r8 = 0 ∧ t.gpr .r7 = 0) ?_ (by rfl)) ?_
+ · crun [h3, hm, ha]
+ · intro t ⟨⟨h3', hm', hx', ha'⟩, hk'⟩
+ exact ⟨hk.trans hk', h3', hm', hx', ha'⟩
+ unfold equal
+ refine WP.seq (WP.mono start fun t hinv => WP.seq ?_)
+ have h4 := hinv.1.1 .r4 (by decide)
+ have after : WP isa (.ite (.zero .d .r4) (.block []) (.loop (.block step) (.nonzero .d .r9))) t
+ (Inv s₀ (s₀.gpr .r4).toNat) := by
+ by_cases he : s₀.gpr .r4 = 0
+ · refine WP.ite true (by simp [VG.PPC64LE.eval, State.read, h4, he]) (fun _ => ?_) (by simp)
+ apply WP.block_nil
+ simpa only [he, (show (0 : BitVec 64).toNat = 0 from rfl)] using hinv
+ · refine WP.ite false (by simp only [VG.PPC64LE.eval, State.read, BitVec.setWidth_eq, h4,
+ beq_eq_false_iff_ne.mpr he]) (by simp) (fun _ => ?_)
+ exact loop_ok s₀ hp hlen t 0 (by bv_omega) hinv
+ refine WP.mono after fun u hu => ?_
+ refine WP.mono (finish_ok u _ hu.2.2.2.2) fun v ⟨hmv, hv⟩ => ?_
+ refine ⟨hmv.trans hu.2.2.1, ?_⟩
+ change (v.gpr .r3).setWidth 32 = _
+ rw [hlen, diff_spec]
+ simpa only [decide_eq_true_eq] using hv
+
+theorem correct (s₀ : State) (hp : contract.pre s₀) :
+ WP isa eq s₀ fun t => t.mem = s₀.mem ∧ contract.post s₀ t := by
+ have start : WP isa (.block [.li .r7 0, .sub .r9 .r4 .r6]) s₀
+ (fun t => Keep s₀ t ∧ t.gpr .r3 = s₀.gpr .r3 ∧ t.mem = s₀.mem ∧ t.gpr .r7 = 0 ∧
+ t.gpr .r9 = s₀.gpr .r4 - s₀.gpr .r6) := by
+ refine WP.mono (keep (Q := fun t => t.gpr .r3 = s₀.gpr .r3 ∧ t.mem = s₀.mem ∧
+ t.gpr .r7 = 0 ∧ t.gpr .r9 = s₀.gpr .r4 - s₀.gpr .r6) ?_ (by rfl)) ?_
+ · crun
+ · intro t ⟨h, hk⟩; exact ⟨hk, h⟩
+ unfold eq
+ refine WP.seq (WP.mono start fun t ⟨hk, h3, hm, ha, hz⟩ => ?_)
+ by_cases he : s₀.gpr .r4 = s₀.gpr .r6
+ · exact WP.ite true (by simp [VG.PPC64LE.eval, State.read, hz, he])
+ (fun _ => equal_ok s₀ t hp he.symm hk h3 hm ha) (by simp)
+ · refine WP.ite false (by simp only [VG.PPC64LE.eval, State.read, BitVec.setWidth_eq, hz,
+ sub_zero, beq_eq_false_iff_ne.mpr he]) (by simp) (fun _ => ?_)
+ have hn : (s₀.gpr .r4).toNat ≠ (s₀.gpr .r6).toNat := fun h => he (BitVec.eq_of_toNat_eq h)
+ crun [hm, contract, lengths_ne _ _ _ hn]
+
+def sat : State where
+ gpr _ := 0
+ lr := 0
+ sp := 0x4000
+ mem _ := 0
+ rd := [⟨0, 0⟩, ⟨0, 0⟩]
+ wr := []
+
+theorem verified : Verified target eq (Spec.Ct.eqContract abi) := by
+ apply Verified.of_correct (k := contract)
+ · intro s hp
+ obtain ⟨tr, t, he, hm, ho⟩ := correct s hp
+ refine ⟨tr, t, he, ⟨?_, Exec.sp he, Exec.lr he (by decide +kernel)
+ (by rw [← Code.allInstrs_eq]; decide +kernel)⟩, ho⟩
+ intro r hr
+ have hc : ((instrs eq).all fun i => preserved.all fun r => dstOf i != some r) = true := by
+ rw [← Code.allInstrs_eq]; decide +kernel
+ apply Exec.gpr (fun i hi => ?_) he
+ simpa using List.all_eq_true.mp (List.all_eq_true.mp hc i hi) r hr
+ · refine VG.Taint.constantTime (A := taint) (Taint.ofRegs [.r3, .r4, .r5, .r6]) ?_
+ (by taint_decide)
+ intro s t _ _ h
+ refine ⟨h.2.2.2.2, ?_⟩
+ intro r hr
+ simp only [Taint.mem_ofRegs, List.mem_cons, List.not_mem_nil, or_false] at hr
+ rcases hr with rfl | rfl | rfl | rfl
+ · exact h.1
+ · exact h.2.1
+ · exact h.2.2.1
+ · exact h.2.2.2.1
+ · sig_implies [Spec.Ct.eqContract, Spec.Ct.eqSig, contract, abi, argRegs] [sat] using sat
+
+end VG.Proof.Ct.PPC64LE
diff --git a/lean/VerifiedGarbage/Proof/Hmac/PPC64LE/Common.lean b/lean/VerifiedGarbage/Proof/Hmac/PPC64LE/Common.lean
new file mode 100644
index 000000000..5c3ebd721
--- /dev/null
+++ b/lean/VerifiedGarbage/Proof/Hmac/PPC64LE/Common.lean
@@ -0,0 +1,90 @@
+import VerifiedGarbage.Proof.Sha256.PPC64LE.Stream.Finalize
+import VerifiedGarbage.Proof.Hmac.Common
+import VerifiedGarbage.Impl.Hmac.PPC64LE
+
+/-!
+# HMAC-SHA-256 on PPC64LE: common lemmas
+
+Untrusted: everything here is checked by Lean. Words copied between memory
+regions; the memory lemmas themselves are target-independent and shared with
+the other targets (`VG.Proof.Hmac.Common`).
+-/
+
+namespace VG.Proof.Hmac.PPC64LE
+open VG VG.PPC64LE
+open VG.Proof.Sha256.Stream (writeBytes writeBytes_nil)
+open VG.Proof.Hmac.Common (copy_mem)
+open VG.Spec.Sha256 (bytesAt)
+open VG.Impl.Hmac.PPC64LE (cp32 cp64)
+open VG.Proof.Sha256.PPC64LE.Stream (Upd Mupd wp_lwz wp_stw wp_ld wp_std)
+
+theorem add_off (p : Addr) (o j : Nat) :
+ p + BitVec.ofNat 64 (o + j) = p + BitVec.ofNat 64 o + BitVec.ofNat 64 j := by
+ rw [BitVec.ofNat_add, BitVec.add_assoc]
+
+theorem copy32_ok {src dst : Reg} (hs : src ≠ .r8) (hd : dst ≠ .r8) (hs0 : src ≠ .r0) (hd0 : dst ≠ .r0) (o₁ o₂ : Nat) (n : Nat)
+ (ho : o₁ % 4 = 0 ∧ o₂ % 4 = 0) (hb : o₁ + 4 * n ≤ 2 ^ 15 ∧ o₂ + 4 * n ≤ 2 ^ 15) :
+ ∀ (rest : List Instr) (s : State) (Q : State → Prop),
+ (∀ k < n, InRegions (s.rd ++ s.wr) (s.gpr src + BitVec.ofNat 64 o₁ + BitVec.ofNat 64 (4 * k)) 4) →
+ (∀ k < n, InRegions s.wr (s.gpr dst + BitVec.ofNat 64 o₂ + BitVec.ofNat 64 (4 * k)) 4) →
+ Mem.Sep (s.gpr src + BitVec.ofNat 64 o₁) (4 * n) (s.gpr dst + BitVec.ofNat 64 o₂) (4 * n) →
+ (∀ s', (∀ r, r ≠ .r8 → s'.gpr r = s.gpr r) → s'.rd = s.rd → s'.wr = s.wr → s'.sp = s.sp →
+ s'.mem = writeBytes s.mem (s.gpr dst + BitVec.ofNat 64 o₂)
+ (bytesAt s.mem (s.gpr src + BitVec.ofNat 64 o₁) (4 * n)) → WP isa (.block rest) s' Q) →
+ WP isa (.block ((List.range n).flatMap (cp32 src dst o₁ o₂) ++ rest)) s Q := by
+ induction n with
+ | zero =>
+ intro rest s Q _ _ _ k
+ exact k s (fun _ _ => rfl) rfl rfl rfl (by rw [Nat.mul_zero, VG.Proof.Hmac.Common.bytesAt_zero, writeBytes_nil])
+ | succ n ih =>
+ intro rest s Q hin hout hsep k
+ rw [List.range_succ, List.flatMap_append, List.flatMap_singleton, List.append_assoc]
+ refine ih ⟨by omega, by omega⟩ _ s Q (fun j hj => hin j (by omega))
+ (fun j hj => hout j (by omega)) (fun x hx hy => hsep x (by omega) (by omega))
+ fun s₁ g₁ rd₁ wr₁ sp₁ m₁ => ?_
+ simp only [cp32, List.cons_append, List.nil_append]
+ refine wp_lwz (a := s.gpr src + BitVec.ofNat 64 o₁ + BitVec.ofNat 64 (4 * n)) hs0
+ (by omega) (by rw [g₁ _ hs, add_off]) (by rw [rd₁, wr₁]; exact hin n (by omega))
+ fun s₂ u₂ => ?_
+ refine wp_stw (a := s.gpr dst + BitVec.ofNat 64 o₂ + BitVec.ofNat 64 (4 * n)) hd0
+ (by omega) (by rw [u₂.other _ hd, g₁ _ hd, add_off])
+ (by rw [u₂.wr, wr₁]; exact hout n (by omega))
+ fun s₃ u₃ => k s₃ (fun r hr => by rw [u₃.gpr, u₂.other r hr, g₁ r hr])
+ (by rw [u₃.rd, u₂.rd, rd₁]) (by rw [u₃.wr, u₂.wr, wr₁]) (by rw [u₃.sp, u₂.sp, sp₁]) ?_
+ rw [u₃.mem, u₂.gpr, u₂.mem, m₁, BitVec.setWidth_setWidth_of_le _ (by decide), BitVec.setWidth_eq,
+ Nat.mul_succ]
+ exact copy_mem s.mem _ _ n 4 (by rwa [← Nat.mul_succ]) (by omega)
+
+theorem copy64_ok {src dst : Reg} (hs : src ≠ .r8) (hd : dst ≠ .r8) (hs0 : src ≠ .r0) (hd0 : dst ≠ .r0) (o₁ o₂ : Nat) (n : Nat)
+ (ho : o₁ % 8 = 0 ∧ o₂ % 8 = 0) (hb : o₁ + 8 * n ≤ 2 ^ 15 ∧ o₂ + 8 * n ≤ 2 ^ 15) :
+ ∀ (rest : List Instr) (s : State) (Q : State → Prop),
+ (∀ k < n, InRegions (s.rd ++ s.wr) (s.gpr src + BitVec.ofNat 64 o₁ + BitVec.ofNat 64 (8 * k)) 8) →
+ (∀ k < n, InRegions s.wr (s.gpr dst + BitVec.ofNat 64 o₂ + BitVec.ofNat 64 (8 * k)) 8) →
+ Mem.Sep (s.gpr src + BitVec.ofNat 64 o₁) (8 * n) (s.gpr dst + BitVec.ofNat 64 o₂) (8 * n) →
+ (∀ s', (∀ r, r ≠ .r8 → s'.gpr r = s.gpr r) → s'.rd = s.rd → s'.wr = s.wr → s'.sp = s.sp →
+ s'.mem = writeBytes s.mem (s.gpr dst + BitVec.ofNat 64 o₂)
+ (bytesAt s.mem (s.gpr src + BitVec.ofNat 64 o₁) (8 * n)) → WP isa (.block rest) s' Q) →
+ WP isa (.block ((List.range n).flatMap (cp64 src dst o₁ o₂) ++ rest)) s Q := by
+ induction n with
+ | zero =>
+ intro rest s Q _ _ _ k
+ exact k s (fun _ _ => rfl) rfl rfl rfl (by rw [Nat.mul_zero, VG.Proof.Hmac.Common.bytesAt_zero, writeBytes_nil])
+ | succ n ih =>
+ intro rest s Q hin hout hsep k
+ rw [List.range_succ, List.flatMap_append, List.flatMap_singleton, List.append_assoc]
+ refine ih ⟨by omega, by omega⟩ _ s Q (fun j hj => hin j (by omega))
+ (fun j hj => hout j (by omega)) (fun x hx hy => hsep x (by omega) (by omega))
+ fun s₁ g₁ rd₁ wr₁ sp₁ m₁ => ?_
+ simp only [cp64, List.cons_append, List.nil_append]
+ refine wp_ld (a := s.gpr src + BitVec.ofNat 64 o₁ + BitVec.ofNat 64 (8 * n)) hs0
+ ⟨by omega, by omega⟩ (by rw [g₁ _ hs, add_off]) (by rw [rd₁, wr₁]; exact hin n (by omega))
+ fun s₂ u₂ => ?_
+ refine wp_std (a := s.gpr dst + BitVec.ofNat 64 o₂ + BitVec.ofNat 64 (8 * n)) hd0
+ ⟨by omega, by omega⟩ (by rw [u₂.other _ hd, g₁ _ hd, add_off])
+ (by rw [u₂.wr, wr₁]; exact hout n (by omega))
+ fun s₃ u₃ => k s₃ (fun r hr => by rw [u₃.gpr, u₂.other r hr, g₁ r hr])
+ (by rw [u₃.rd, u₂.rd, rd₁]) (by rw [u₃.wr, u₂.wr, wr₁]) (by rw [u₃.sp, u₂.sp, sp₁]) ?_
+ rw [u₃.mem, u₂.gpr, u₂.mem, m₁, Nat.mul_succ]
+ exact copy_mem s.mem _ _ n 8 (by rwa [← Nat.mul_succ]) (by omega)
+
+end VG.Proof.Hmac.PPC64LE
diff --git a/lean/VerifiedGarbage/Proof/Hmac/PPC64LE/Contract.lean b/lean/VerifiedGarbage/Proof/Hmac/PPC64LE/Contract.lean
new file mode 100644
index 000000000..a29aa0b46
--- /dev/null
+++ b/lean/VerifiedGarbage/Proof/Hmac/PPC64LE/Contract.lean
@@ -0,0 +1,93 @@
+import VerifiedGarbage.Spec.Hmac
+import VerifiedGarbage.Proof.Sha256.PPC64LE.Contract
+
+/-!
+# HMAC-SHA-256: the PPC64LE contracts
+
+**Untrusted**: the contracts the proofs are written against; the artifacts are emitted with the shared contracts of `Spec/`, which imply these (`Contract.Implies`). The same functions as on x86-64
+(`VerifiedGarbage/Spec/Hmac/Contract.lean`), with the same Rust signatures: an
+HMAC-SHA-256 computation is two SHA-256 streaming states
+(`VG.Spec.Sha256.Repr`), the inner one, which absorbs `(K₀ ⊕ ipad) ‖ text`,
+and the outer one, which holds `K₀ ⊕ opad`. `vg_hmac_sha256_init` sets them
+up from the key, the text is absorbed into the inner state with
+`vg_sha256_update` (`VG.Proof.Sha256.updatePPC64LE`), and
+`vg_hmac_sha256_finalize` computes the MAC.
+
+The arguments are in `r3`–`r7` (ELFv2), and `bl` leaves the return address
+in the link register rather than on the stack, so unlike on x86-64 there is
+no return address for the regions to avoid.
+-/
+
+namespace VG.Proof.Hmac
+
+open Spec.Hmac
+
+open Spec.Sha256 (Repr bytesAt)
+
+open PPC64LE in
+/-- PPC64LE contract for
+`vg_hmac_sha256_init(inner: *mut [u8; 96], outer: *mut [u8; 96], key: *const u8, key_len: usize, scratch: *mut [u64; 20])`,
+for a key of at most 64 bytes (the SHA-256 block size): makes the streaming
+state at `inner` represent `K₀ ⊕ ipad` and the one at `outer` represent
+`K₀ ⊕ opad`, for the key `K₀` made of the `key_len` bytes at `key`.
+
+The code may read `key` (`key_len` bytes) and read and write `inner` and
+`outer` (96 bytes each) and `scratch` (160 bytes, whose contents on exit are
+unspecified). These may not overlap each other, nor the 48 bytes below the
+stack pointer (the frame saving the link register), which do not wrap around. The
+pointers and `key_len` are public; the key is secret. -/
+def initSha256PPC64LE : Contract PPC64LE.isa where
+ pre s :=
+ let inner : Region := ⟨s.gpr .r3, 96⟩
+ let outer : Region := ⟨s.gpr .r4, 96⟩
+ let key : Region := ⟨s.gpr .r5, (s.gpr .r6).toNat⟩
+ let scratch : Region := ⟨s.gpr .r7, 160⟩
+ let stack : Region := ⟨s.sp - 48, 48⟩
+ (s.gpr .r6).toNat ≤ 64 ∧ s.rd = [key] ∧ s.wr = [inner, outer, scratch] ∧
+ inner.Disjoint outer ∧ inner.Disjoint scratch ∧ outer.Disjoint scratch ∧
+ key.Disjoint inner ∧ key.Disjoint outer ∧ key.Disjoint scratch ∧
+ 48 ≤ s.sp.toNat ∧ stack.Disjoint inner ∧ stack.Disjoint outer ∧ stack.Disjoint key ∧
+ stack.Disjoint scratch
+ post s s' :=
+ let k0 := blockKey sha256 (bytesAt s.mem (s.gpr .r5) (s.gpr .r6).toNat)
+ Repr s'.mem (s.gpr .r3) (xorPad k0 ipad) ∧ Repr s'.mem (s.gpr .r4) (xorPad k0 opad)
+ pub s₁ s₂ :=
+ s₁.gpr .r3 = s₂.gpr .r3 ∧ s₁.gpr .r4 = s₂.gpr .r4 ∧ s₁.gpr .r5 = s₂.gpr .r5 ∧
+ s₁.gpr .r6 = s₂.gpr .r6 ∧ s₁.gpr .r7 = s₂.gpr .r7 ∧ s₁.sp = s₂.sp
+
+open PPC64LE in
+/-- PPC64LE contract for
+`vg_hmac_sha256_finalize(inner: *mut [u8; 96], outer: *const [u8; 96], count: u64, scratch: *mut [u64; 30])`:
+if, for a 64-byte key `K₀` and a text, the streaming state at `inner`
+represents `(K₀ ⊕ ipad) ‖ text`, of `count` bytes (modulo 2⁶⁴), and the one at
+`outer` represents `K₀ ⊕ opad`, leaves the HMAC-SHA-256 of the text under
+`K₀` in bytes 176 to 207 of `scratch`.
+
+The MAC is left in `scratch`, as on x86-64, so that both targets share one
+Rust signature (and one Rust wrapper).
+
+The code may read `outer` (96 bytes), and read and write `inner` (96 bytes,
+whose contents on exit are unspecified) and `scratch` (240 bytes, whose
+contents on exit are unspecified apart from the MAC). These may not overlap
+each other, nor the 96 bytes below the stack pointer (the frames saving the link
+register here and in `vg_sha256_finalize`), which do not wrap around. The pointers
+and `count` are public; the states are secret. -/
+def finalizeSha256PPC64LE : Contract PPC64LE.isa where
+ pre s :=
+ let inner : Region := ⟨s.gpr .r3, 96⟩
+ let outer : Region := ⟨s.gpr .r4, 96⟩
+ let scratch : Region := ⟨s.gpr .r6, 240⟩
+ let stack : Region := ⟨s.sp - 96, 96⟩
+ s.rd = [outer] ∧ s.wr = [inner, scratch] ∧
+ inner.Disjoint outer ∧ inner.Disjoint scratch ∧ outer.Disjoint scratch ∧
+ 96 ≤ s.sp.toNat ∧ stack.Disjoint inner ∧ stack.Disjoint outer ∧ stack.Disjoint scratch
+ post s s' := ∀ k0 text, k0.length = 64 →
+ Repr s.mem (s.gpr .r3) (xorPad k0 ipad ++ text) →
+ s.gpr .r5 = BitVec.ofNat 64 (64 + text.length) →
+ Repr s.mem (s.gpr .r4) (xorPad k0 opad) →
+ bytesAt s'.mem (s.gpr .r6 + 176) 32 = hmacBlockKey sha256 k0 text
+ pub s₁ s₂ :=
+ s₁.gpr .r3 = s₂.gpr .r3 ∧ s₁.gpr .r4 = s₂.gpr .r4 ∧ s₁.gpr .r5 = s₂.gpr .r5 ∧
+ s₁.gpr .r6 = s₂.gpr .r6 ∧ s₁.sp = s₂.sp
+
+end VG.Proof.Hmac
diff --git a/lean/VerifiedGarbage/Proof/Hmac/PPC64LE/Finalize.lean b/lean/VerifiedGarbage/Proof/Hmac/PPC64LE/Finalize.lean
new file mode 100644
index 000000000..b99c015fc
--- /dev/null
+++ b/lean/VerifiedGarbage/Proof/Hmac/PPC64LE/Finalize.lean
@@ -0,0 +1,501 @@
+import VerifiedGarbage.Proof.Hmac.PPC64LE.Common
+import VerifiedGarbage.Proof.Hmac.Common
+import VerifiedGarbage.Proof.Hmac.PPC64LE.Contract
+
+/-!
+# HMAC-SHA-256 on PPC64LE: `finalize`
+
+Untrusted: everything here is checked by Lean. The same structure as the
+x86-64 proof (`VG.Proof.Hmac.Common.Finalize`). The two SHA-256
+finalizations are calls of `vg_sha256_finalize`, used as a black box through
+its proof (`WP.callF`), inside the frame saving the link register
+(`WP.frameReg`).
+-/
+
+namespace VG.Proof.Hmac.PPC64LE.Finalize
+
+open VG VG.PPC64LE VG.Impl.Hmac.PPC64LE
+open VG.Impl.Sha256.PPC64LE.Stream (mov)
+open VG.Proof.Hmac.PPC64LE
+open VG.Proof.Hmac.Common (writeBytes_at writeBytes_other bytesAt_getD' bytesAt_length
+ bytesAt_writeBytes_self bytesAt_writeBytes_sep stateAt_eq_of_bytes)
+open VG.Proof.Hmac.Common (xorPad_length repr_outer)
+open VG.Proof.Sha256.Stream (writeBytes writeBytes_frame)
+open VG.Proof.Sha256.PPC64LE (contains_offset sub_offset toNat_ofNat_lt)
+open VG.Proof.Sha256.PPC64LE.Stream (Upd Mupd wp_mov wp_li wp_addi wp_std wp_ld frame_bytes
+ write_frame_bytes readW_writeW_save frame_sub)
+open VG.Proof.Sha256.PPC64LE.Stream.WP (cons)
+open VG.Spec.Sha256 (bytesAt stateAt Repr)
+open VG.Spec.Hmac (xorPad ipad opad hmacBlockKey sha256)
+
+/-! ## The precondition -/
+
+section
+variable (s₀ : State)
+
+abbrev inn : Addr := s₀.gpr .r3
+abbrev out : Addr := s₀.gpr .r4
+abbrev scr : Addr := s₀.gpr .r6
+abbrev inR : Region := ⟨inn s₀, 96⟩
+abbrev outR : Region := ⟨out s₀, 96⟩
+abbrev scR : Region := ⟨scr s₀, 240⟩
+/-- Where the finalizations may write (besides their frames). -/
+abbrev finW : List Region := [inR s₀, ⟨scr s₀ + 176, 32⟩, ⟨scr s₀, 160⟩]
+
+end
+
+structure Pre (s₀ : State) : Prop where
+ rd : s₀.rd = [outR s₀]
+ wr : s₀.wr = [inR s₀, scR s₀]
+ i_o : (inR s₀).Disjoint (outR s₀)
+ i_s : (inR s₀).Disjoint (scR s₀)
+ o_s : (outR s₀).Disjoint (scR s₀)
+
+/-- The frame a call of `vg_sha256_finalize` pushes, below the stack pointer
+`s` has, is disjoint from the buffers. -/
+structure Stack (s : State) : Prop where
+ sp48 : 48 ≤ s.sp.toNat
+ i : (below s.sp 48).Disjoint (inR s)
+ o : (below s.sp 48).Disjoint (outR s)
+ s : (below s.sp 48).Disjoint (scR s)
+
+/-- On entry: our frame and the callee's are in the 96 bytes below the stack
+pointer, disjoint from the buffers. -/
+structure Stack₀ (s₀ : State) : Prop where
+ sp96 : 96 ≤ s₀.sp.toNat
+ i : (below s₀.sp 96).Disjoint (inR s₀)
+ o : (below s₀.sp 96).Disjoint (outR s₀)
+ s : (below s₀.sp 96).Disjoint (scR s₀)
+
+theorem pre_of {s₀ : State} (h : Proof.Hmac.finalizeSha256PPC64LE.pre s₀) : Pre s₀ ∧ Stack₀ s₀ := by
+ obtain ⟨h1, h2, h3, h4, h5, h6, h7, h8, h9⟩ := h
+ exact ⟨⟨h1, h2, h3, h4, h5⟩, ⟨h6, h7, h8, h9⟩⟩
+
+/-! ## The calls of `vg_sha256_finalize` -/
+
+theorem fin_exec : ∀ s, Proof.Sha256.finalizePPC64LE.pre s → ∃ t s',
+ Exec isa Impl.Sha256.PPC64LE.Stream.finalize s t s' ∧ abiPreserved s s' ∧
+ Proof.Sha256.finalizePPC64LE.post s s' := by
+ intro s hs
+ have h := Proof.Sha256.PPC64LE.Stream.Finalize.pre_of hs
+ obtain ⟨t, s', he, h₁, h₂⟩ := Proof.Sha256.PPC64LE.Stream.Finalize.correct h.1 h.2
+ exact ⟨t, s', he, h₁, h₂⟩
+
+theorem fin_fdepth : Impl.Sha256.PPC64LE.Stream.finalize.fdepth = 1 := by decide +kernel
+
+theorem sub176 (s₀ : State) : Region.Sub ⟨scr s₀ + 176, 32⟩ (scR s₀) :=
+ sub_offset (off := 176) (by omega) (by omega)
+
+theorem sub160 (s₀ : State) : Region.Sub ⟨scr s₀, 160⟩ (scR s₀) := Region.sub_prefix (by omega)
+
+/-- A call of `vg_sha256_finalize` on the inner state, with its digest at
+`scratch[176..208)` and `scratch[0..160)` as its scratch space. -/
+theorem fin_ok {s₀ : State} (hp : Pre s₀) (hs : Stack s₀) {s : State} (hrd : s.rd = s₀.rd)
+ (hwr : s.wr = s₀.wr) (hsp : s.sp = s₀.sp)
+ (h0 : s.gpr .r3 = inn s₀) (h3 : s.gpr .r6 = scr s₀) (h2 : s.gpr .r5 = scr s₀ + 176)
+ {Q : State → Prop}
+ (hQ : ∀ s', s'.rd = s.rd → s'.wr = s.wr → s'.sp = s.sp →
+ Frame (finW s₀ ++ [below s₀.sp 48]) s.mem s'.mem →
+ (∀ r ∈ preserved, s'.gpr r = s.gpr r) →
+ (∀ m, Repr s.mem (inn s₀) m → s.gpr .r4 = BitVec.ofNat 64 m.length →
+ bytesAt s'.mem (scr s₀ + 176) 32 = Spec.Sha256.hash m) → Q s') :
+ WP isa sha256Finalize s Q := by
+ have c0 : s.callEntry.gpr .r3 = inn s₀ := (State.callEntry_gpr _ (by decide)).trans h0
+ have c2 : s.callEntry.gpr .r5 = scr s₀ + 176 := (State.callEntry_gpr _ (by decide)).trans h2
+ have c3 : s.callEntry.gpr .r6 = scr s₀ := (State.callEntry_gpr _ (by decide)).trans h3
+ refine WP.callF (k := Proof.Sha256.finalizePPC64LE) fin_exec (rd := []) (wr := finW s₀) ?_ ?_ ?_ ?_
+ (by rw [fin_fdepth]; decide)
+ · simp only [Proof.Sha256.finalizePPC64LE, State.withRegions_gpr, State.withRegions_rd,
+ State.withRegions_wr, State.withRegions_sp, State.callEntry_sp, c0, c2, c3, hsp]
+ refine ⟨trivial, trivial, ?_, ?_, ?_, hs.sp48, hs.i, ?_, ?_⟩
+ · exact hp.i_s.sub_right (sub176 s₀)
+ · exact hp.i_s.sub_right (sub160 s₀)
+ · intro a h₁ h₂; simp only [Region.Contains] at h₁ h₂; bv_omega
+ · exact hs.s.sub_right (sub176 s₀)
+ · exact hs.s.sub_right (sub160 s₀)
+ · rw [hrd, hwr, hp.rd, hp.wr]
+ apply Covers.of_sub
+ intro r hr
+ simp only [List.nil_append, List.mem_cons, List.not_mem_nil, or_false] at hr
+ rcases hr with rfl | rfl | rfl
+ · exact ⟨inR s₀, by simp, 0, by simp, by simp⟩
+ · exact ⟨scR s₀, by simp, 176, rfl, by simp⟩
+ · exact ⟨scR s₀, by simp, 0, by simp, by simp⟩
+ · rw [hwr, hp.wr]
+ apply Covers.of_sub
+ intro r hr
+ simp only [List.mem_cons, List.not_mem_nil, or_false] at hr
+ rcases hr with rfl | rfl | rfl
+ · exact ⟨inR s₀, by simp, 0, by simp, by simp⟩
+ · exact ⟨scR s₀, by simp, 176, rfl, by simp⟩
+ · exact ⟨scR s₀, by simp, 0, by simp, by simp⟩
+ · intro s' h₁ h₂ h₃ h₄ hcs hpost
+ simp only [Proof.Sha256.finalizePPC64LE, State.withRegions_gpr, State.withRegions_mem,
+ State.callEntry_mem, c0, c2] at hpost
+ rw [fin_fdepth, hsp] at h₄
+ exact hQ s' h₁ h₂ h₃ h₄ hcs fun m hr hc => hpost Spec.Sha256.H0 m hr hc
+
+/-! ## Saving `r24`, `r25` and the outer hash value -/
+
+/-- The memory after saving our caller's `r24` and `r25` in `scratch[160..176)`. -/
+abbrev svMem (s₀ : State) : Mem :=
+ (s₀.mem.writeW (scr s₀ + BitVec.ofNat 64 160) (s₀.gpr .r24)).writeW (scr s₀ + BitVec.ofNat 64 168)
+ (s₀.gpr .r25)
+
+/-- After the prologue: our caller's `r24` and `r25` are in `scratch[160..176)`,
+and the outer hash value's bytes in `scratch[208..240)`. -/
+structure Saved (s₀ s : State) : Prop where
+ rd : s.rd = s₀.rd
+ wr : s.wr = s₀.wr
+ r3 : s.gpr .r3 = inn s₀
+ r6 : s.gpr .r6 = scr s₀
+ r24 : s.gpr .r24 = inn s₀
+ r25 : s.gpr .r25 = scr s₀
+ r4 : s.gpr .r4 = s₀.gpr .r5
+ r5 : s.gpr .r5 = scr s₀ + 176
+ cs : ∀ r ∈ preserved, r ≠ .r24 → r ≠ .r25 → s.gpr r = s₀.gpr r
+ sp : s.sp = s₀.sp
+ mem : s.mem = writeBytes (svMem s₀) (scr s₀ + BitVec.ofNat 64 208) (bytesAt s₀.mem (out s₀) 32)
+
+theorem not_pres {r : Reg} (hr : r ∈ preserved) :
+ r ≠ .r3 ∧ r ≠ .r4 ∧ r ≠ .r5 ∧ r ≠ .r6 ∧ r ≠ .r8 :=
+ (by decide : ∀ r ∈ preserved, r ≠ .r3 ∧ r ≠ .r4 ∧ r ≠ .r5 ∧ r ≠ .r6 ∧ r ≠ .r8) r hr
+
+theorem svMem_frame (s₀ : State) : Frame [scR s₀] s₀.mem (svMem s₀) :=
+ ((Frame.refl _ _).writeW (List.mem_singleton_self _) _ (contains_offset (by omega) (by omega))).writeW
+ (List.mem_singleton_self _) _ (contains_offset (by omega) (by omega))
+
+theorem svMem_out {s₀ : State} (hp : Pre s₀) :
+ bytesAt (svMem s₀) (out s₀) 32 = bytesAt s₀.mem (out s₀) 32 :=
+ Proof.Sha256.Stream.bytesAt_congr fun i hi =>
+ frame_bytes (svMem_frame s₀) (R := outR s₀) (by simpa using hp.o_s) (by simp) (show i < 96 by omega)
+
+/-- Everything the prologue writes is in the scratch space. -/
+theorem Saved.frame {s₀ s : State} (h : Saved s₀ s) : Frame [scR s₀] s₀.mem s.mem := by
+ rw [h.mem]
+ refine (svMem_frame s₀).trans (writeBytes_frame _ _ _ ?_)
+ rw [bytesAt_length]; exact contains_offset (by omega) (by omega)
+
+theorem Saved.sv_eq {s₀ s : State} (h : Saved s₀ s) {o : Nat} (ho : 160 ≤ o ∧ o + 8 ≤ 176) :
+ s.mem.readW (scr s₀ + BitVec.ofNat 64 o) 64 = (svMem s₀).readW (scr s₀ + BitVec.ofNat 64 o) 64 := by
+ rw [h.mem]
+ refine (writeBytes_frame (R := ⟨scr s₀ + BitVec.ofNat 64 208, 32⟩) _ _ _
+ (by rw [bytesAt_length]; exact Region.contains_self _ _)).readW (Region.contains_self _ _) ?_ (by decide)
+ simp only [List.mem_singleton]; rintro r rfl
+ intro a h₁ h₂; simp only [Region.Contains] at h₁ h₂
+ have : (BitVec.ofNat 64 o).toNat = o := toNat_ofNat_lt (by omega)
+ bv_omega
+
+theorem Saved.sv25 {s₀ s : State} (h : Saved s₀ s) :
+ s.mem.readW (scr s₀ + BitVec.ofNat 64 160) 64 = s₀.gpr .r24 := by
+ rw [h.sv_eq (by omega), svMem, readW_writeW_save _ _ _ (by omega) (by omega) (by omega),
+ Mem.readW_writeW_self64]
+
+theorem Saved.sv26 {s₀ s : State} (h : Saved s₀ s) :
+ s.mem.readW (scr s₀ + BitVec.ofNat 64 168) 64 = s₀.gpr .r25 := by
+ rw [h.sv_eq (by omega), svMem, Mem.readW_writeW_self64]
+
+theorem prologue_ok {s₀ : State} (hp : Pre s₀) :
+ WP isa (.block (([.store .d .r24 .r6 160, .store .d .r25 .r6 168, mov .r24 .r3, mov .r25 .r6] :
+ List Instr) ++ saveOuter ++ ([mov .r4 .r5, .addi .r5 .r6 176] : List Instr))) s₀ (Saved s₀) := by
+ unfold saveOuter
+ have h0 : out s₀ + BitVec.ofNat 64 0 = out s₀ := by simp
+ simp only [List.cons_append, List.nil_append]
+ refine wp_std (a := scr s₀ + BitVec.ofNat 64 160) (by decide) ⟨by omega, by omega⟩ rfl
+ ⟨scR s₀, by simp [hp.wr], contains_offset (by omega) (by omega)⟩ fun sa ua => ?_
+ refine wp_std (a := scr s₀ + BitVec.ofNat 64 168) (by decide) ⟨by omega, by omega⟩ (by rw [ua.gpr])
+ ⟨scR s₀, by simp [ua.wr, hp.wr], contains_offset (by omega) (by omega)⟩ fun sb ub => ?_
+ refine wp_mov fun s₁ u₁ => wp_mov fun s₂ u₂ => ?_
+ have e1 : s₂.gpr .r4 = out s₀ := by
+ rw [u₂.other _ (by decide), u₁.other _ (by decide), ub.gpr, ua.gpr]
+ have e3 : s₂.gpr .r6 = scr s₀ := by
+ rw [u₂.other _ (by decide), u₁.other _ (by decide), ub.gpr, ua.gpr]
+ have rd₂ : s₂.rd = s₀.rd := by rw [u₂.rd, u₁.rd, ub.rd, ua.rd]
+ have wr₂ : s₂.wr = s₀.wr := by rw [u₂.wr, u₁.wr, ub.wr, ua.wr]
+ have m₂ : s₂.mem = svMem s₀ := by rw [u₂.mem, u₁.mem, ub.mem, ua.gpr, ua.mem]
+ have k₂ : ∀ r, r ≠ .r24 → r ≠ .r25 → s₂.gpr r = s₀.gpr r := fun r a b => by
+ rw [u₂.other _ b, u₁.other _ a, ub.gpr, ua.gpr]
+ refine copy32_ok (by decide) (by decide) (by decide) (by decide) 0 208 8 ⟨rfl, rfl⟩ ⟨by omega, by omega⟩ _ s₂ _
+ (fun k hk => ?_) (fun k hk => ?_) ?_
+ fun s₃ g₃ rd₃ wr₃ sp₃ m₃ => wp_mov fun s₄ u₄ => wp_addi (imm := 176) (by decide) (by omega) fun s₅ u₅ =>
+ WP.block_nil ⟨?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, fun r hr h25 h26 => ?_, ?_, ?_⟩
+ · rw [e1, h0, rd₂, wr₂]
+ exact ⟨outR s₀, by simp [hp.rd], contains_offset (by omega) (by omega)⟩
+ · rw [e3, wr₂]
+ refine ⟨scR s₀, by simp [hp.wr], ?_⟩
+ rw [BitVec.add_assoc, ← BitVec.ofNat_add]
+ exact contains_offset (by omega) (by omega)
+ · rw [e1, e3, h0]
+ exact hp.o_s.sep (a := out s₀) (n := 32) (by simp [Region.Contains]) (contains_offset (by omega) (by omega))
+ · rw [u₅.rd, u₄.rd, rd₃, rd₂]
+ · rw [u₅.wr, u₄.wr, wr₃, wr₂]
+ · rw [u₅.other _ (by decide), u₄.other _ (by decide), g₃ _ (by decide), k₂ _ (by decide) (by decide)]
+ · rw [u₅.other _ (by decide), u₄.other _ (by decide), g₃ _ (by decide), e3]
+ · rw [u₅.other _ (by decide), u₄.other _ (by decide), g₃ _ (by decide), u₂.other _ (by decide), u₁.gpr,
+ ub.gpr, ua.gpr]
+ · rw [u₅.other _ (by decide), u₄.other _ (by decide), g₃ _ (by decide), u₂.gpr, u₁.other _ (by decide),
+ ub.gpr, ua.gpr]
+ · rw [u₅.other _ (by decide), u₄.gpr, g₃ _ (by decide), k₂ _ (by decide) (by decide)]
+ · rw [u₅.gpr, u₄.other _ (by decide), g₃ _ (by decide), e3]; rfl
+ · have := not_pres hr
+ rw [u₅.other _ this.2.2.1, u₄.other _ this.2.1, g₃ _ this.2.2.2.2, k₂ _ h25 h26]
+ · rw [u₅.sp, u₄.sp, sp₃, u₂.sp, u₁.sp, ub.sp, ua.sp]
+ · rw [u₅.mem, u₄.mem, m₃, m₂, e1, e3, h0]
+ exact congrArg _ (svMem_out hp)
+
+/-! ## Loading the outer hash value and the inner digest -/
+
+/-- After the middle block, from `s`: the inner state holds the hash value from
+`scratch[208..240)` and, in its buffer, the digest from `scratch[176..208)`. -/
+structure Loaded (s₀ s s' : State) : Prop where
+ rd : s'.rd = s.rd
+ wr : s'.wr = s.wr
+ r3 : s'.gpr .r3 = inn s₀
+ r6 : s'.gpr .r6 = scr s₀
+ r4 : s'.gpr .r4 = BitVec.ofNat 64 (64 + 32)
+ r5 : s'.gpr .r5 = scr s₀ + 176
+ cs : ∀ r ∈ preserved, s'.gpr r = s.gpr r
+ sp : s'.sp = s.sp
+ state : stateAt s'.mem (inn s₀) = stateAt s.mem (scr s₀ + BitVec.ofNat 64 208)
+ buf : bytesAt s'.mem (inn s₀ + 32) 32 = bytesAt s.mem (scr s₀ + 176) 32
+ frame : Frame [inR s₀] s.mem s'.mem
+
+theorem load_ok {s₀ : State} (hp : Pre s₀) {s : State} (hwr : s.wr = s₀.wr)
+ (h25 : s.gpr .r24 = inn s₀) (h26 : s.gpr .r25 = scr s₀) :
+ WP isa (.block (loadOuter ++ [mov .r3 .r24, .li .r4 96, .addi .r5 .r25 176, mov .r6 .r25]))
+ s (Loaded s₀ s) := by
+ unfold loadOuter
+ rw [List.append_assoc]
+ have hin : ∀ {o k : Nat}, o + k ≤ 240 → InRegions (s.rd ++ s.wr) (scr s₀ + BitVec.ofNat 64 o) k :=
+ fun h => ⟨scR s₀, by simp [hwr, hp.wr], contains_offset h (by omega)⟩
+ have hout : ∀ {o k : Nat}, o + k ≤ 96 → InRegions s.wr (inn s₀ + BitVec.ofNat 64 o) k :=
+ fun h => ⟨inR s₀, by simp [hwr, hp.wr], contains_offset h (by omega)⟩
+ have add : ∀ (p : Addr) (a b : Nat), p + BitVec.ofNat 64 a + BitVec.ofNat 64 b = p + BitVec.ofNat 64 (a + b) :=
+ fun p a b => by rw [BitVec.add_assoc, ← BitVec.ofNat_add]
+ refine copy32_ok (src := .r25) (dst := .r24) (by decide) (by decide) (by decide) (by decide) 208 0 8 ⟨rfl, rfl⟩
+ ⟨by omega, by omega⟩ _ s _ ?_ ?_ ?_ fun s₁ g₁ rd₁ wr₁ sp₁ m₁ => ?_
+ · intro k hk; rw [h26, add]; exact hin (by omega)
+ · intro k hk; rw [h25, add]; exact hout (by omega)
+ · rw [h26, h25]
+ exact hp.i_s.symm.sep (contains_offset (by omega) (by omega)) (contains_offset (by omega) (by omega))
+ have e₁ : s₁.gpr .r25 = scr s₀ := by rw [g₁ _ (by decide), h26]
+ have d₁ : s₁.gpr .r24 = inn s₀ := by rw [g₁ _ (by decide), h25]
+ refine copy64_ok (src := .r25) (dst := .r24) (by decide) (by decide) (by decide) (by decide) 176 32 4 ⟨rfl, rfl⟩
+ ⟨by omega, by omega⟩ _ s₁ _ ?_ ?_ ?_ fun s₂ g₂ rd₂ wr₂ sp₂ m₂ =>
+ wp_mov fun s₃ u₃ => wp_li (imm := 96) (by omega) fun s₄ u₄ => wp_addi (imm := 176) (by decide) (by omega) fun s₅ u₅ =>
+ wp_mov fun s₆ u₆ => WP.block_nil ?_
+ · intro k hk; rw [e₁, add, rd₁, wr₁]; exact hin (by omega)
+ · intro k hk; rw [d₁, add, wr₁]; exact hout (by omega)
+ · rw [e₁, d₁]
+ exact hp.i_s.symm.sep (contains_offset (by omega) (by omega)) (contains_offset (by omega) (by omega))
+ rw [h26, h25, show inn s₀ + BitVec.ofNat 64 0 = inn s₀ by simp] at m₁
+ rw [e₁, d₁] at m₂
+ have c₀ : (inR s₀).Contains (inn s₀) 32 := by simp [Region.Contains]
+ have hm : s₆.mem = s₂.mem := by rw [u₆.mem, u₅.mem, u₄.mem, u₃.mem]
+ have k₂ : ∀ r, r ≠ .r8 → s₂.gpr r = s.gpr r := fun r h => by rw [g₂ r h, g₁ r h]
+ have hbuf : bytesAt s₂.mem (inn s₀ + 32) 32 = bytesAt s.mem (scr s₀ + 176) 32 := by
+ rw [m₂, show inn s₀ + 32 = inn s₀ + BitVec.ofNat 64 32 from rfl]
+ have := bytesAt_writeBytes_self s₁.mem (inn s₀ + BitVec.ofNat 64 32)
+ (bytesAt s₁.mem (scr s₀ + BitVec.ofNat 64 176) (8 * 4)) (by simp [bytesAt])
+ rw [bytesAt_length] at this
+ rw [this, m₁, bytesAt_writeBytes_sep]
+ · rfl
+ · rw [bytesAt_length]
+ exact hp.i_s.symm.sep (contains_offset (by omega) (by omega)) c₀
+ · omega
+ have hst : stateAt s₂.mem (inn s₀) = stateAt s.mem (scr s₀ + BitVec.ofNat 64 208) := by
+ refine stateAt_eq_of_bytes fun i hi => ?_
+ rw [m₂, writeBytes_other _ _ _ (by rw [bytesAt_length]; bv_omega), m₁,
+ writeBytes_at _ _ _ (by rw [bytesAt_length]; omega) (by rw [bytesAt_length]; omega),
+ bytesAt_getD' _ _ (by omega)]
+ have hfr : Frame [inR s₀] s.mem s₂.mem := by
+ rw [m₂, m₁]
+ refine (writeBytes_frame (R := inR s₀) _ _ _ ?_).trans (writeBytes_frame (R := inR s₀) _ _ _ ?_)
+ · rw [bytesAt_length]; exact c₀
+ · rw [bytesAt_length]; exact contains_offset (by omega) (by omega)
+ refine ⟨by rw [u₆.rd, u₅.rd, u₄.rd, u₃.rd, rd₂, rd₁], by rw [u₆.wr, u₅.wr, u₄.wr, u₃.wr, wr₂, wr₁],
+ ?_, ?_, ?_, ?_, fun r hr => ?_, by rw [u₆.sp, u₅.sp, u₄.sp, u₃.sp, sp₂, sp₁],
+ by rw [hm, hst], by rw [hm, hbuf], by rw [hm]; exact hfr⟩
+ · rw [u₆.other _ (by decide), u₅.other _ (by decide), u₄.other _ (by decide), u₃.gpr, k₂ _ (by decide), h25]
+ · rw [u₆.gpr, u₅.other _ (by decide), u₄.other _ (by decide), u₃.other _ (by decide), k₂ _ (by decide), h26]
+ · rw [u₆.other _ (by decide), u₅.other _ (by decide), u₄.gpr]
+ · rw [u₆.other _ (by decide), u₅.gpr, u₄.other _ (by decide), u₃.other _ (by decide), k₂ _ (by decide), h26]
+ rfl
+ · have := not_pres hr
+ rw [u₆.other _ this.2.2.2.1, u₅.other _ this.2.2.1, u₄.other _ this.2.1, u₃.other _ this.1,
+ k₂ _ this.2.2.2.2]
+
+/-! ## Correctness -/
+
+/-- The calls of `vg_sha256_finalize` write outside `scratch[160..176)` and
+`scratch[208..240)`. -/
+theorem not_finW {s₀ : State} (hp : Pre s₀) (hs : Stack s₀) {o : Nat}
+ (ho : (160 ≤ o ∧ o < 176) ∨ (208 ≤ o ∧ o < 240)) :
+ ∀ r ∈ finW s₀ ++ [below s₀.sp 48], (⟨scr s₀ + BitVec.ofNat 64 o, 1⟩ : Region).Disjoint r := by
+ have hsub : Region.Sub ⟨scr s₀ + BitVec.ofNat 64 o, 1⟩ (scR s₀) := sub_offset (by omega) (by omega)
+ have ht : (BitVec.ofNat 64 o).toNat = o := toNat_ofNat_lt (by omega)
+ intro r hr
+ simp only [finW, List.cons_append, List.nil_append, List.mem_cons, List.not_mem_nil, or_false] at hr
+ rcases hr with rfl | rfl | rfl | rfl
+ · exact hp.i_s.symm.sub_left hsub
+ · intro a h₁ h₂; simp only [Region.Contains] at h₁ h₂; bv_omega
+ · intro a h₁ h₂; simp only [Region.Contains] at h₁ h₂; bv_omega
+ · exact hs.s.symm.sub_left hsub
+
+/-- A byte of `scratch[160..176)` or `scratch[208..240)` survives a call. -/
+theorem kept {s₀ : State} (hp : Pre s₀) (hs : Stack s₀) {m m' : Mem}
+ (hf : Frame (finW s₀ ++ [below s₀.sp 48]) m m') {o : Nat}
+ (ho : (160 ≤ o ∧ o < 176) ∨ (208 ≤ o ∧ o < 240)) :
+ m' (scr s₀ + BitVec.ofNat 64 o) = m (scr s₀ + BitVec.ofNat 64 o) := by
+ have := frame_bytes hf (not_finW hp hs ho) Nat.one_le_two_pow (i := 0) Nat.one_pos
+ simpa using this
+
+/-- A byte of `scratch[160..176)` or `scratch[208..240)` survives loading the
+inner state. -/
+theorem kept_load {s₀ : State} (hp : Pre s₀) {m m' : Mem} (hf : Frame [inR s₀] m m') {o : Nat}
+ (ho : o < 240) : m' (scr s₀ + BitVec.ofNat 64 o) = m (scr s₀ + BitVec.ofNat 64 o) := by
+ have hsub : Region.Sub ⟨scr s₀ + BitVec.ofNat 64 o, 1⟩ (scR s₀) := sub_offset (by omega) (by omega)
+ have := frame_bytes hf (R := ⟨scr s₀ + BitVec.ofNat 64 o, 1⟩)
+ (by simp only [List.mem_singleton]; rintro r rfl; exact hp.i_s.symm.sub_left hsub) Nat.one_le_two_pow
+ (i := 0) Nat.one_pos
+ simpa using this
+
+/-- A saved register survives the calls and the load. -/
+theorem sv_kept {s₀ : State} (hp : Pre s₀) (hs : Stack s₀) {m₁ m₂ m₃ m₄ : Mem}
+ (f₂ : Frame (finW s₀ ++ [below s₀.sp 48]) m₁ m₂) (f₃ : Frame [inR s₀] m₂ m₃)
+ (f₄ : Frame (finW s₀ ++ [below s₀.sp 48]) m₃ m₄) {o : Nat} (ho : o = 160 ∨ o = 168) :
+ m₄.readW (scr s₀ + BitVec.ofNat 64 o) 64 = m₁.readW (scr s₀ + BitVec.ofNat 64 o) 64 := by
+ simp only [Mem.readW]
+ refine congrArg _ (Mem.read_congr fun i hi => ?_)
+ have e : scr s₀ + BitVec.ofNat 64 o + BitVec.ofNat 64 i = scr s₀ + BitVec.ofNat 64 (o + i) := by
+ rw [BitVec.add_assoc, ← BitVec.ofNat_add]
+ have hi' : i < 8 := hi
+ rw [e, kept hp hs f₄ (by omega), kept_load hp f₃ (by omega), kept hp hs f₂ (by omega)]
+
+theorem sub48 (sp : Addr) : Region.Sub ⟨sp - 48, 48⟩ (below sp 96) := by
+ intro x hx
+ simp only [Region.Contains] at hx ⊢
+ bv_omega
+
+/-- `finalize` without its frame. -/
+theorem correctMain {s₀ : State} (hp : Pre s₀) (hs : Stack s₀) :
+ WP isa finalizeMain s₀ fun s' => (∀ r ∈ preserved, s'.gpr r = s₀.gpr r) ∧
+ s'.sp = s₀.sp ∧ Proof.Hmac.finalizeSha256PPC64LE.post s₀ s' := by
+ unfold finalizeMain
+ refine WP.seq (WP.mono (prologue_ok hp) fun s₁ h₁ => ?_)
+ refine WP.seq (fin_ok hp hs h₁.rd h₁.wr h₁.sp h₁.r3 h₁.r6 h₁.r5
+ fun s₂ rd₂ wr₂ sp₂ fr₂ cs₂ post₂ => ?_)
+ have r24₂ : s₂.gpr .r24 = inn s₀ := by rw [cs₂ _ (by decide), h₁.r24]
+ have r25₂ : s₂.gpr .r25 = scr s₀ := by rw [cs₂ _ (by decide), h₁.r25]
+ refine WP.seq (WP.mono (load_ok hp (s := s₂) (wr₂.trans h₁.wr) r24₂ r25₂) fun s₃ h₃ => ?_)
+ refine WP.seq (fin_ok hp hs (h₃.rd.trans (rd₂.trans h₁.rd)) (h₃.wr.trans (wr₂.trans h₁.wr))
+ (h₃.sp.trans (sp₂.trans h₁.sp)) h₃.r3 h₃.r6 h₃.r5
+ fun s₄ rd₄ wr₄ sp₄ fr₄ cs₄ post₄ => ?_)
+ -- Restoring `r24` and `r25`.
+ have r25₄ : s₄.gpr .r25 = scr s₀ := by
+ rw [cs₄ _ (by decide), h₃.cs _ (by decide), r25₂]
+ have hin : ∀ o, o + 8 ≤ 240 → InRegions (s₄.rd ++ s₄.wr) (scr s₀ + BitVec.ofNat 64 o) 8 := fun o h =>
+ ⟨scR s₀, by simp [wr₄, h₃.wr, wr₂, h₁.wr, hp.wr], contains_offset h (by omega)⟩
+ have sv : ∀ o, o = 160 ∨ o = 168 → s₄.mem.readW (scr s₀ + BitVec.ofNat 64 o) 64 =
+ s₁.mem.readW (scr s₀ + BitVec.ofNat 64 o) 64 :=
+ fun o ho => sv_kept hp hs fr₂ h₃.frame fr₄ ho
+ refine wp_ld (a := scr s₀ + BitVec.ofNat 64 160) (by decide) ⟨by omega, by omega⟩ (by rw [r25₄])
+ (hin 160 (by omega))
+ fun s₅ u₅ => ?_
+ refine wp_ld (a := scr s₀ + BitVec.ofNat 64 168) (by decide) ⟨by omega, by omega⟩
+ (by rw [u₅.other _ (by decide), r25₄])
+ (by rw [u₅.rd, u₅.wr]; exact hin 168 (by omega)) fun s₆ u₆ => WP.block_nil ⟨fun r hr => ?_,
+ by rw [u₆.sp, u₅.sp, sp₄, h₃.sp, sp₂, h₁.sp], ?_⟩
+ · by_cases h26 : r = .r25
+ · subst h26; rw [u₆.gpr, u₅.mem, sv _ (.inr rfl), h₁.sv26]
+ by_cases h25 : r = .r24
+ · subst h25; rw [u₆.other _ (by decide), u₅.gpr, sv _ (.inl rfl), h₁.sv25]
+ rw [u₆.other _ h26, u₅.other _ h25, cs₄ _ hr, h₃.cs _ hr, cs₂ _ hr, h₁.cs _ hr h25 h26]
+ · intro k0 text hk hin hcnt hout
+ -- The inner digest.
+ have hin₁ : Repr s₁.mem (inn s₀) (xorPad k0 ipad ++ text) :=
+ Proof.Sha256.Stream.repr_congr (fun i hi => frame_bytes h₁.frame (R := inR s₀)
+ (by simpa using hp.i_s) (by simp) hi) hin
+ have hd := post₂ _ hin₁ (by rw [h₁.r4, hcnt, List.length_append, xorPad_length, hk])
+ -- The outer state.
+ have hst : stateAt s₃.mem (inn s₀) = Spec.Sha256.compressList Spec.Sha256.H0 (xorPad k0 opad) 1 := by
+ rw [h₃.state]
+ have e : stateAt s₂.mem (scr s₀ + BitVec.ofNat 64 208) = stateAt s₀.mem (out s₀) := by
+ refine stateAt_eq_of_bytes fun i hi => ?_
+ rw [show scr s₀ + BitVec.ofNat 64 208 + BitVec.ofNat 64 i = scr s₀ + BitVec.ofNat 64 (208 + i) by
+ rw [BitVec.add_assoc, ← BitVec.ofNat_add],
+ kept hp hs fr₂ (by omega), BitVec.ofNat_add,
+ ← BitVec.add_assoc, h₁.mem,
+ writeBytes_at _ _ _ (by rw [bytesAt_length]; omega) (by rw [bytesAt_length]; omega),
+ bytesAt_getD' _ _ (by omega)]
+ rw [e, hout.1, xorPad_length, hk]
+ have hrepr := repr_outer hk (by rw [bytesAt_length]) hst h₃.buf
+ have := post₄ _ hrepr (by rw [h₃.r4, List.length_append, xorPad_length, hk, bytesAt_length])
+ rw [hd] at this
+ rw [u₆.mem, u₅.mem]
+ simpa [hmacBlockKey, sha256] using this
+
+/-- The state `finalizeMain` starts in: the link register moved to `r0`,
+then pushed in a frame. -/
+abbrev inner (s₀ : State) : State := framed .r0 (s₀.write .r0 s₀.lr)
+
+theorem correct {s₀ : State} (hp : Pre s₀) (hs : Stack₀ s₀) :
+ WP isa finalize s₀ fun s' => abiPreserved s₀ s' ∧ Proof.Hmac.finalizeSha256PPC64LE.post s₀ s' := by
+ have hsp := hs.sp96
+ have hb : Region.Sub (below (s₀.sp - 48) 48) (below s₀.sp 96) := below_body s₀.sp 48
+ have hf : Region.Sub ⟨s₀.sp - 48, 48⟩ (below s₀.sp 96) := sub48 s₀.sp
+ have hsi : Stack (inner s₀) :=
+ ⟨by show 48 ≤ (s₀.sp - 48).toNat; bv_omega, hs.i.sub_left hb, hs.o.sub_left hb, hs.s.sub_left hb⟩
+ refine WP.seq (cons exec_mflr (WP.block_nil (WP.seq ?_)))
+ refine WP.frameReg (by show 48 ≤ s₀.sp.toNat; omega) (fun R hR => ?_)
+ (WP.mono (correctMain (s₀ := inner s₀) ⟨hp.rd, hp.wr, hp.i_o, hp.i_s, hp.o_s⟩ hsi)
+ fun s' ⟨hk, _, hpost⟩ => ?_)
+ · rw [show (s₀.write .r0 s₀.lr).wr = s₀.wr from rfl, hp.wr] at hR
+ simp only [List.mem_cons, List.not_mem_nil, or_false] at hR
+ rcases hR with rfl | rfl
+ · exact (hs.i.sub_left hf).sub_left (frame_sub _)
+ · exact (hs.s.sub_left hf).sub_left (frame_sub _)
+ · refine cons exec_mtlr (WP.block_nil ⟨⟨fun r hr => ?_, rfl, ?_⟩, fun k0 text hk hin hcnt hout => ?_⟩)
+ · have h0 : r ≠ .r0 := by revert r hr; decide
+ simp only [State.write, h0, ite_false]
+ rw [hk r hr]
+ simp only [framed, State.write, h0, ite_false]
+ · simp [State.write]
+ · have c : ∀ p : Addr, Region.Disjoint ⟨s₀.sp - 48, 48⟩ ⟨p, 96⟩ → ∀ l,
+ Repr s₀.mem p l → Repr (inner s₀).mem p l := fun p hd l h =>
+ Proof.Sha256.Stream.repr_congr (fun i hi => write_frame_bytes (R := ⟨p, 96⟩) hd (show 96 < 2 ^ 64 by omega) hi) h
+ have := hpost k0 text hk (c _ (hs.i.sub_left hf) _ hin) hcnt (c _ (hs.o.sub_left hf) _ hout)
+ exact this
+
+/-! ## `Verified` -/
+
+/-- The initial taint: only the arguments are public. -/
+theorem agree₀ {s₁ s₂ : State} (hpub : Proof.Hmac.finalizeSha256PPC64LE.pub s₁ s₂) :
+ VG.PPC64LE.Taint.Agree (VG.PPC64LE.Taint.ofRegs [.r3, .r4, .r5, .r6]) s₁ s₂ := by
+ obtain ⟨p1, p2, p3, p4, hsp⟩ := hpub
+ refine ⟨hsp, fun r hr => ?_⟩
+ simp only [VG.PPC64LE.Taint.mem_ofRegs, List.mem_cons, List.not_mem_nil, or_false] at hr
+ rcases hr with rfl | rfl | rfl | rfl <;> assumption
+
+/-- A state satisfying the precondition. -/
+def sat : State where
+ gpr r := match r with
+ | .r3 => 0x1000 | .r4 => 0x2000 | .r6 => 0x3000 | _ => 0
+ lr := 0
+ sp := 0x4000
+ mem _ := 0
+ rd := [⟨0x2000, 96⟩]
+ wr := [⟨0x1000, 96⟩, ⟨0x3000, 240⟩]
+
+theorem finalize_verified : Verified PPC64LE.target finalize Proof.Hmac.finalizeSha256PPC64LE := by
+ refine ⟨fun s hs => ?_, ?_, ?_⟩
+ · obtain ⟨t, s', he, h⟩ := correct (pre_of hs).1 (pre_of hs).2
+ exact ⟨t, s', he, h⟩
+ · exact VG.Taint.constantTime (A := taint) (Taint.ofRegs [.r3, .r4, .r5, .r6]) (fun _ _ _ _ hp => agree₀ hp)
+ (by taint_decide)
+ · refine ⟨sat, rfl, rfl, ?_, ?_, ?_, by decide, ?_, ?_, ?_⟩ <;>
+ · intro a h₁ h₂
+ simp only [Region.Contains, sat] at h₁ h₂
+ bv_omega
+
+end VG.Proof.Hmac.PPC64LE.Finalize
diff --git a/lean/VerifiedGarbage/Proof/Hmac/PPC64LE/Init.lean b/lean/VerifiedGarbage/Proof/Hmac/PPC64LE/Init.lean
new file mode 100644
index 000000000..3ea4afcfd
--- /dev/null
+++ b/lean/VerifiedGarbage/Proof/Hmac/PPC64LE/Init.lean
@@ -0,0 +1,783 @@
+import VerifiedGarbage.Proof.Hmac.PPC64LE.Common
+import VerifiedGarbage.Proof.Hmac.Common
+import VerifiedGarbage.Proof.Sha256.PPC64LE.Stream.Init
+import VerifiedGarbage.Proof.Hmac.PPC64LE.Contract
+
+/-!
+# HMAC-SHA-256 on PPC64LE: `init`
+
+Untrusted: everything here is checked by Lean. The same structure as the
+x86-64 proof (`VG.Proof.Hmac.Common.Init`).
+-/
+
+namespace VG.Proof.Hmac.PPC64LE.Init
+
+
+open VG VG.PPC64LE VG.Impl.Hmac.PPC64LE
+open VG.Impl.Sha256.PPC64LE.Stream (mov save restore compressAt saved)
+open VG.Proof.Hmac.Common (bytesAt_length)
+open VG.Proof.Hmac.Common (bytesAt_snoc repr_block)
+open VG.Proof.Sha256.Stream (writeBytes repr_congr)
+open VG.Proof.Sha256.PPC64LE (writeState stateAt_writeState contains_offset sub_offset toNat_ofNat_lt)
+open VG.Proof.Sha256.PPC64LE.Stream (Upd Mupd wp_mov wp_li wp_addi wp_subi wp_sub wp_add wp_lbz
+ wp_stb compressAt_ok saveMem saveMem_saved saveMem_frame save_ok restore_ok frame_bytes untouched
+ eval_zero eval_nonzero ofNat_beq_zero sub_ofNat ofNat_succ nvRegs nv_pres pushMem frame_sub)
+open VG.Spec.Sha256 (bytesAt stateAt Repr H0)
+open VG.Spec.Hmac (xorPad ipad opad blockKey sha256)
+
+/-! ## The precondition -/
+
+section
+variable (s₀ : State)
+
+abbrev inn : Addr := s₀.gpr .r3
+abbrev out : Addr := s₀.gpr .r4
+abbrev kp : Addr := s₀.gpr .r5
+abbrev kl : Nat := (s₀.gpr .r6).toNat
+abbrev scr : Addr := s₀.gpr .r7
+abbrev inR : Region := ⟨inn s₀, 96⟩
+abbrev outR : Region := ⟨out s₀, 96⟩
+abbrev kR : Region := ⟨kp s₀, kl s₀⟩
+abbrev scR : Region := ⟨scr s₀, 160⟩
+
+/-- The key, padded with zeros to a block. -/
+def K0 : List Byte := bytesAt s₀.mem (kp s₀) (kl s₀) ++ List.replicate (64 - kl s₀) 0
+
+/-- The caller's registers are saved in the scratch space. -/
+def Saved (m : Mem) : Prop :=
+ ∀ p ∈ saved, m.readW (scr s₀ + BitVec.ofNat 64 p.2) 64 = s₀.gpr p.1
+
+end
+
+structure Pre (s₀ : State) : Prop where
+ kl_le : kl s₀ ≤ 64
+ rd : s₀.rd = [kR s₀]
+ wr : s₀.wr = [inR s₀, outR s₀, scR s₀]
+ i_o : (inR s₀).Disjoint (outR s₀)
+ i_s : (inR s₀).Disjoint (scR s₀)
+ o_s : (outR s₀).Disjoint (scR s₀)
+ k_i : (kR s₀).Disjoint (inR s₀)
+ k_o : (kR s₀).Disjoint (outR s₀)
+ k_s : (kR s₀).Disjoint (scR s₀)
+
+/-- The frame saving the link register, below the stack pointer. -/
+abbrev stkR (s₀ : State) : Region := ⟨s₀.sp - 48, 48⟩
+
+/-- The frame is below the stack pointer, and disjoint from the buffers. -/
+structure Stack (s₀ : State) : Prop where
+ sp48 : 48 ≤ s₀.sp.toNat
+ i : (stkR s₀).Disjoint (inR s₀)
+ o : (stkR s₀).Disjoint (outR s₀)
+ k : (stkR s₀).Disjoint (kR s₀)
+ s : (stkR s₀).Disjoint (scR s₀)
+
+theorem pre_of {s₀ : State} (h : Proof.Hmac.initSha256PPC64LE.pre s₀) : Pre s₀ ∧ Stack s₀ := by
+ obtain ⟨h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11, h12, h13⟩ := h
+ exact ⟨⟨h0, h1, h2, h3, h4, h5, h6, h7, h8⟩, ⟨h9, h10, h11, h12, h13⟩⟩
+
+theorem K0_length (s₀ : State) (hp : Pre s₀) : (K0 s₀).length = 64 := by
+ simp [K0, bytesAt_length]; have := hp.kl_le; omega
+
+theorem blockKey_eq {s₀ : State} (hp : Pre s₀) :
+ blockKey sha256 (bytesAt s₀.mem (kp s₀) (kl s₀)) = K0 s₀ := by
+ have := hp.kl_le
+ simp [blockKey, sha256, K0, bytesAt_length, show ¬ (64 < kl s₀) by omega]
+
+/-! ## `H⁽⁰⁾` -/
+
+open VG.Proof.Sha256.PPC64LE.Stream.WP (cons)
+
+/-- The three instructions storing the 32-bit word `x` at `off(b)`. -/
+def word (b : Reg) (x : BitVec 32) (off : Nat) : List Instr :=
+ [.lis .r8 (x.extractLsb' 16 16), .ori .r8 .r8 (x.extractLsb' 0 16), .store .w .r8 b off]
+
+theorem h0_eq (b : Reg) : h0 b = word b H0[0] 0 ++ word b H0[1] 4 ++ word b H0[2] 8 ++ word b H0[3] 12 ++
+ word b H0[4] 16 ++ word b H0[5] 20 ++ word b H0[6] 24 ++ word b H0[7] 28 := rfl
+
+theorem word_ok {b : Reg} (hb : b ≠ .r8) (hb0 : b ≠ .r0) {x : BitVec 32} {off : Nat} (ho : off < 2 ^ 15)
+ {rest : List Instr} {s : State} {Q : State → Prop}
+ (hout : InRegions s.wr (s.gpr b + BitVec.ofNat 64 off) 4)
+ (k : ∀ s', (∀ r, r ≠ .r8 → s'.gpr r = s.gpr r) → s'.rd = s.rd → s'.wr = s.wr → s'.sp = s.sp →
+ s'.mem = s.mem.writeW (s.gpr b + BitVec.ofNat 64 off) x → WP isa (.block rest) s' Q) :
+ WP isa (.block (word b x off ++ rest)) s Q := by
+ simp only [word, List.cons_append, List.nil_append]
+ refine cons exec_lis (cons exec_ori (cons (exec_store_w hb0 ho ?_) (k _ ?_ rfl rfl rfl ?_)))
+ · simpa [State.write, hb] using hout
+ · intro r hr; simp [State.write, hr]
+ · simp only [State.write, ite_true, hb, ite_false]
+ congr 1
+ exact lis_ori x
+
+/-- `H⁽⁰⁾` stored at `b`. -/
+theorem h0_ok {b : Reg} (hb : b ≠ .r8) (hb0 : b ≠ .r0) {s : State} {rest : List Instr} {Q : State → Prop}
+ (o : ∀ k < 8, InRegions s.wr (s.gpr b + BitVec.ofNat 64 (4 * k)) 4)
+ (k : ∀ s', (∀ r, r ≠ .r8 → s'.gpr r = s.gpr r) → s'.rd = s.rd → s'.wr = s.wr → s'.sp = s.sp →
+ s'.mem = writeState s.mem (s.gpr b) H0 → WP isa (.block rest) s' Q) :
+ WP isa (.block (h0 b ++ rest)) s Q := by
+ rw [h0_eq]
+ simp only [List.append_assoc]
+ refine word_ok hb hb0 (by omega) (o 0 (by omega)) fun s1 g1 _ wr1 sp1 m1 => ?_
+ have k1 : s1.gpr b = s.gpr b := g1 _ hb
+ refine word_ok hb hb0 (by omega) (by rw [wr1, k1]; exact o 1 (by omega)) fun s2 g2 _ wr2 sp2 m2 => ?_
+ have k2 : s2.gpr b = s.gpr b := by rw [g2 _ hb, k1]
+ have w2 : s2.wr = s.wr := by rw [wr2, wr1]
+ refine word_ok hb hb0 (by omega) (by rw [w2, k2]; exact o 2 (by omega)) fun s3 g3 _ wr3 sp3 m3 => ?_
+ have k3 : s3.gpr b = s.gpr b := by rw [g3 _ hb, k2]
+ have w3 : s3.wr = s.wr := by rw [wr3, w2]
+ refine word_ok hb hb0 (by omega) (by rw [w3, k3]; exact o 3 (by omega)) fun s4 g4 _ wr4 sp4 m4 => ?_
+ have k4 : s4.gpr b = s.gpr b := by rw [g4 _ hb, k3]
+ have w4 : s4.wr = s.wr := by rw [wr4, w3]
+ refine word_ok hb hb0 (by omega) (by rw [w4, k4]; exact o 4 (by omega)) fun s5 g5 _ wr5 sp5 m5 => ?_
+ have k5 : s5.gpr b = s.gpr b := by rw [g5 _ hb, k4]
+ have w5 : s5.wr = s.wr := by rw [wr5, w4]
+ refine word_ok hb hb0 (by omega) (by rw [w5, k5]; exact o 5 (by omega)) fun s6 g6 _ wr6 sp6 m6 => ?_
+ have k6 : s6.gpr b = s.gpr b := by rw [g6 _ hb, k5]
+ have w6 : s6.wr = s.wr := by rw [wr6, w5]
+ refine word_ok hb hb0 (by omega) (by rw [w6, k6]; exact o 6 (by omega)) fun s7 g7 _ wr7 sp7 m7 => ?_
+ have k7 : s7.gpr b = s.gpr b := by rw [g7 _ hb, k6]
+ have w7 : s7.wr = s.wr := by rw [wr7, w6]
+ refine word_ok hb hb0 (by omega) (by rw [w7, k7]; exact o 7 (by omega)) fun s8 g8 rd8 wr8 sp8 m8 => ?_
+ rename_i rd1 rd2 rd3 rd4 rd5 rd6 rd7
+ refine k s8 (fun r h => by rw [g8 r h, g7 r h, g6 r h, g5 r h, g4 r h, g3 r h, g2 r h, g1 r h])
+ (by rw [rd8, rd7, rd6, rd5, rd4, rd3, rd2, rd1]) (by rw [wr8, w7])
+ (by rw [sp8, sp7, sp6, sp5, sp4, sp3, sp2, sp1]) ?_
+ rw [m8, m7, m6, m5, m4, m3, m2, m1, k7, k6, k5, k4, k3, k2, k1]
+ rfl
+
+/-- Writing a hash value stays within its 32 bytes. -/
+theorem writeState_frame (m : Mem) (p : Addr) (v : Spec.Sha256.HashValue) :
+ Frame [⟨p, 32⟩] m (writeState m p v) := by
+ have c : ∀ k, k < 8 → (⟨p, 32⟩ : Region).Contains (p + BitVec.ofNat 64 (4 * k)) (32 / 8) :=
+ fun k hk => contains_offset (by omega) (by omega)
+ simp only [writeState]
+ refine (((((((((Frame.refl _ _).writeW ?_ _ (c 0 ?_)).writeW ?_ _ (c 1 ?_)).writeW ?_ _
+ (c 2 ?_)).writeW ?_ _ (c 3 ?_)).writeW ?_ _ (c 4 ?_)).writeW ?_ _ (c 5 ?_)).writeW ?_ _
+ (c 6 ?_)).writeW ?_ _ (c 7 ?_)) <;> simp
+
+/-! ## The key block -/
+
+/-- The memory while building the two buffers: `j` bytes of `K₀ ⊕ ipad` and
+`K₀ ⊕ opad` are written. -/
+structure BufMem (s₀ : State) (j : Nat) (m : Mem) : Prop where
+ stI : stateAt m (inn s₀) = H0
+ stO : stateAt m (out s₀) = H0
+ bufI : bytesAt m (inn s₀ + 32) j = ((K0 s₀).take j).map (· ^^^ ipad)
+ bufO : bytesAt m (out s₀ + 32) j = ((K0 s₀).take j).map (· ^^^ opad)
+ saved : Saved s₀ m
+ frame : Frame [inR s₀, outR s₀, scR s₀] s₀.mem m
+
+/-- The registers while building the two buffers (`r31` = `j`). -/
+structure Buf (s₀ : State) (j : Nat) (s : State) : Prop where
+ j_le : j ≤ 64
+ rd : s.rd = s₀.rd
+ wr : s.wr = s₀.wr
+ sp : s.sp = s₀.sp
+ r26 : s.gpr .r26 = inn s₀
+ r27 : s.gpr .r27 = scr s₀
+ r28 : s.gpr .r28 = out s₀
+ r31 : s.gpr .r31 = BitVec.ofNat 64 j
+ r12 : s.gpr .r12 = BitVec.ofNat 64 0x36
+ r0 : s.gpr .r0 = BitVec.ofNat 64 0x5c
+ mem : BufMem s₀ j s.mem
+ nv : ∀ r ∈ nvRegs, s.gpr r = s₀.gpr r
+
+/-- In the key loop: `r29` points at key byte `j`, and `r30` counts the key bytes left. -/
+structure Key (s₀ : State) (j : Nat) (s : State) : Prop extends Buf s₀ j s where
+ r29 : s.gpr .r29 = kp s₀ + BitVec.ofNat 64 j
+ r30 : s.gpr .r30 = BitVec.ofNat 64 (kl s₀ - j)
+
+/-- In the pad loop: `r10` counts the bytes left. -/
+structure Pad (s₀ : State) (j : Nat) (s : State) : Prop extends Buf s₀ j s where
+ r10 : s.gpr .r10 = BitVec.ofNat 64 (64 - j)
+
+theorem sub32 (p : Addr) : Region.Sub ⟨p, 32⟩ ⟨p, 96⟩ := Region.sub_prefix (by omega)
+
+theorem save_sub (s₀ : State) : Region.Sub ⟨scr s₀ + BitVec.ofNat 64 112, 48⟩ (scR s₀) :=
+ sub_offset (by omega) (by omega)
+
+/-- `Saved` survives a write outside the save area `scratch[112..160)`. -/
+theorem saved_frame {s₀ : State} {m m' : Mem} (h : Saved s₀ m) {rs : List Region} (hf : Frame rs m m')
+ (hd : ∀ r ∈ rs, Region.Disjoint ⟨scr s₀ + BitVec.ofNat 64 112, 48⟩ r) : Saved s₀ m' := by
+ intro p hp'
+ rw [← h p hp']
+ refine hf.readW (r := ⟨scr s₀ + BitVec.ofNat 64 p.2, 8⟩) (Region.contains_self _ _) ?_ (by decide)
+ intro r hr
+ refine (hd r hr).sub_left ?_
+ simp only [saved, List.mem_cons, List.not_mem_nil, or_false] at hp'
+ rcases hp' with rfl | rfl | rfl | rfl | rfl | rfl <;>
+ · intro a ha; simp only [Region.Contains] at *; bv_omega
+
+/-- `Saved` survives a write outside the scratch space. -/
+theorem saved_frame' {s₀ : State} {m m' : Mem} (h : Saved s₀ m) {rs : List Region} (hf : Frame rs m m')
+ (hd : ∀ r ∈ rs, (scR s₀).Disjoint r) : Saved s₀ m' :=
+ saved_frame h hf fun r hr => (hd r hr).sub_left (save_sub s₀)
+
+theorem prologue_ok {s₀ : State} (hp : Pre s₀) :
+ WP isa (.block (save .r7 ++
+ ([mov .r26 .r3, mov .r27 .r7, mov .r28 .r4, mov .r29 .r5, mov .r30 .r6] : List Instr) ++
+ h0 .r26 ++ h0 .r28 ++ ([.li .r12 0x36, .li .r0 0x5c, .li .r31 0] : List Instr))) s₀
+ (Key s₀ 0) := by
+ simp only [List.append_assoc]
+ refine save_ok (by decide) (fun d hd₁ hd₂ => ⟨scR s₀, by simp [hp.wr], contains_offset hd₂ (by omega)⟩)
+ fun s₁ g₁ rd₁ wr₁ sp₁ m₁ => ?_
+ simp only [List.cons_append, List.nil_append]
+ refine wp_mov fun s₂ u₂ => wp_mov fun s₃ u₃ => wp_mov fun s₄ u₄ => wp_mov fun s₅ u₅ =>
+ wp_mov fun s₆ u₆ => ?_
+ have h19 : s₆.gpr .r26 = inn s₀ := by
+ rw [u₆.other _ (by decide), u₅.other _ (by decide), u₄.other _ (by decide), u₃.other _ (by decide),
+ u₂.gpr, g₁]
+ have h20 : s₆.gpr .r27 = scr s₀ := by
+ rw [u₆.other _ (by decide), u₅.other _ (by decide), u₄.other _ (by decide), u₃.gpr,
+ u₂.other _ (by decide), g₁]
+ have h21 : s₆.gpr .r28 = out s₀ := by
+ rw [u₆.other _ (by decide), u₅.other _ (by decide), u₄.gpr, u₃.other _ (by decide),
+ u₂.other _ (by decide), g₁]
+ have h22 : s₆.gpr .r29 = kp s₀ := by
+ rw [u₆.other _ (by decide), u₅.gpr, u₄.other _ (by decide), u₃.other _ (by decide),
+ u₂.other _ (by decide), g₁]
+ have h23 : s₆.gpr .r30 = s₀.gpr .r6 := by
+ rw [u₆.gpr, u₅.other _ (by decide), u₄.other _ (by decide), u₃.other _ (by decide),
+ u₂.other _ (by decide), g₁]
+ have m₆ : s₆.mem = saveMem s₀.mem (scr s₀) s₀.gpr := by
+ rw [u₆.mem, u₅.mem, u₄.mem, u₃.mem, u₂.mem, m₁]
+ have rd₆ : s₆.rd = s₀.rd := by rw [u₆.rd, u₅.rd, u₄.rd, u₃.rd, u₂.rd, rd₁]
+ have wr₆ : s₆.wr = s₀.wr := by rw [u₆.wr, u₅.wr, u₄.wr, u₃.wr, u₂.wr, wr₁]
+ have sp₆ : s₆.sp = s₀.sp := by rw [u₆.sp, u₅.sp, u₄.sp, u₃.sp, u₂.sp, sp₁]
+ refine h0_ok (by decide) (by decide) (fun k hk => ⟨inR s₀, by simp [wr₆, hp.wr], by
+ rw [h19]; exact contains_offset (by omega) (by omega)⟩) fun s₇ g₇ rd₇ wr₇ sp₇ m₇ => ?_
+ refine h0_ok (by decide) (by decide) (fun k hk => ⟨outR s₀, by simp [wr₇, wr₆, hp.wr], by
+ rw [g₇ _ (by decide), h21]; exact contains_offset (by omega) (by omega)⟩)
+ fun s₈ g₈ rd₈ wr₈ sp₈ m₈ => ?_
+ refine wp_li (by decide) fun s₉ u₉ => wp_li (by decide) fun s₁₀ u₁₀ => wp_li (by decide) fun s₁₁ u₁₁ => WP.block_nil ?_
+ have k : ∀ r, r ≠ .r8 → r ≠ .r12 → r ≠ .r0 → r ≠ .r31 → s₁₁.gpr r = s₆.gpr r :=
+ fun r a b c d => by rw [u₁₁.other r d, u₁₀.other r c, u₉.other r b, g₈ r a, g₇ r a]
+ rw [h19] at m₇
+ rw [g₇ _ (by decide), h21] at m₈
+ have hm : s₁₁.mem = writeState (writeState (saveMem s₀.mem (scr s₀) s₀.gpr) (inn s₀) H0) (out s₀) H0 := by
+ rw [u₁₁.mem, u₁₀.mem, u₉.mem, m₈, m₇, m₆]
+ have fS := saveMem_frame s₀.mem (scr s₀) s₀.gpr
+ have fI := writeState_frame (saveMem s₀.mem (scr s₀) s₀.gpr) (inn s₀) H0
+ have fO := writeState_frame (writeState (saveMem s₀.mem (scr s₀) s₀.gpr) (inn s₀) H0) (out s₀) H0
+ have ds : ∀ p : Addr, ∀ r : Region, r.Disjoint ⟨p, 96⟩ → r.Disjoint ⟨p, 32⟩ :=
+ fun p r h => h.sub_right (sub32 p)
+ refine ⟨⟨by omega, by rw [u₁₁.rd, u₁₀.rd, u₉.rd, rd₈, rd₇, rd₆],
+ by rw [u₁₁.wr, u₁₀.wr, u₉.wr, wr₈, wr₇, wr₆], by rw [u₁₁.sp, u₁₀.sp, u₉.sp, sp₈, sp₇, sp₆],
+ by rw [k _ (by decide) (by decide) (by decide) (by decide), h19],
+ by rw [k _ (by decide) (by decide) (by decide) (by decide), h20],
+ by rw [k _ (by decide) (by decide) (by decide) (by decide), h21], by rw [u₁₁.gpr],
+ by rw [u₁₁.other _ (by decide), u₁₀.other _ (by decide), u₉.gpr],
+ by rw [u₁₁.other _ (by decide), u₁₀.gpr],
+ ⟨?_, ?_, by simp [bytesAt], by simp [bytesAt], ?_, ?_⟩, fun r hr => ?_⟩, ?_, ?_⟩
+ · rw [hm]
+ refine (Proof.Sha256.Stream.stateAt_congr fun i hi => ?_).trans
+ (stateAt_writeState (saveMem s₀.mem (scr s₀) s₀.gpr) _ _)
+ exact frame_bytes fO (R := ⟨inn s₀, 32⟩) (by simpa using ds _ _ (hp.i_o.sub_left (sub32 _))) (by simp) hi
+ · rw [hm, stateAt_writeState]
+ · rw [hm]
+ refine saved_frame' (saved_frame' (saveMem_saved _ _ _) fI ?_) fO ?_ <;> simp only [List.mem_singleton] <;>
+ rintro r rfl
+ · exact ds _ _ hp.i_s.symm
+ · exact ds _ _ hp.o_s.symm
+ · rw [hm]
+ refine ((fS.mono ?_).trans (fI.sub ?_)).trans (fO.sub ?_)
+ · simp
+ · simp only [List.mem_singleton]; rintro r rfl; exact ⟨inR s₀, by simp, sub32 _⟩
+ · simp only [List.mem_singleton]; rintro r rfl; exact ⟨outR s₀, by simp, sub32 _⟩
+ · have ne : r ≠ .r8 ∧ r ≠ .r12 ∧ r ≠ .r0 ∧ r ≠ .r31 ∧ r ≠ .r26 ∧ r ≠ .r27 ∧ r ≠ .r28 ∧ r ≠ .r29 ∧
+ r ≠ .r30 := by revert r hr; decide
+ rw [k r ne.1 ne.2.1 ne.2.2.1 ne.2.2.2.1, u₆.other r ne.2.2.2.2.2.2.2.2, u₅.other r ne.2.2.2.2.2.2.2.1,
+ u₄.other r ne.2.2.2.2.2.2.1, u₃.other r ne.2.2.2.2.2.1, u₂.other r ne.2.2.2.2.1, g₁]
+ · rw [k _ (by decide) (by decide) (by decide) (by decide), h22]; simp
+ · rw [k _ (by decide) (by decide) (by decide) (by decide), h23]; simp
+
+/-- A byte written right after `j` bytes of a buffer, in both buffers. -/
+theorem buf_write {s₀ : State} (hp : Pre s₀) {j : Nat} {m : Mem} (h : BufMem s₀ j m) (hj : j < 64) :
+ BufMem s₀ (j + 1) ((m.writeW (inn s₀ + 32 + BitVec.ofNat 64 j)
+ ((K0 s₀)[j]'(by rw [K0_length s₀ hp]; omega) ^^^ ipad)).writeW
+ (out s₀ + 32 + BitVec.ofNat 64 j) ((K0 s₀)[j]'(by rw [K0_length s₀ hp]; omega) ^^^ opad)) := by
+ have hl : j < (K0 s₀).length := by rw [K0_length s₀ hp]; omega
+ set x := (K0 s₀)[j] ^^^ ipad
+ set y := (K0 s₀)[j] ^^^ opad
+ let bI : Region := ⟨inn s₀ + 32, 64⟩
+ let bO : Region := ⟨out s₀ + 32, 64⟩
+ have sI : Region.Sub bI (inR s₀) := sub_offset (off := 32) (by omega) (by omega)
+ have sO : Region.Sub bO (outR s₀) := sub_offset (off := 32) (by omega) (by omega)
+ have f₁ : Frame [bI] m (m.writeW (inn s₀ + 32 + BitVec.ofNat 64 j) x) :=
+ (Frame.refl _ _).writeW (List.mem_singleton_self _) x (contains_offset (by omega) (by omega))
+ have f₂ : Frame [bO] (m.writeW (inn s₀ + 32 + BitVec.ofNat 64 j) x)
+ ((m.writeW (inn s₀ + 32 + BitVec.ofNat 64 j) x).writeW (out s₀ + 32 + BitVec.ofNat 64 j) y) :=
+ (Frame.refl _ _).writeW (List.mem_singleton_self _) y (contains_offset (by omega) (by omega))
+ have F := (f₁.mono (rs' := [bI, bO]) (by simp)).trans (f₂.mono (by simp))
+ have dIO : bI.Disjoint bO := (hp.i_o.sub_left sI).sub_right sO
+ have st : ∀ p : Addr, (∀ r ∈ [bI, bO], Region.Disjoint ⟨p, 32⟩ r) →
+ stateAt ((m.writeW (inn s₀ + 32 + BitVec.ofNat 64 j) x).writeW (out s₀ + 32 + BitVec.ofNat 64 j) y) p =
+ stateAt m p :=
+ fun p hd => Proof.Sha256.Stream.stateAt_congr fun i hi => frame_bytes F (R := ⟨p, 32⟩) hd (by simp) hi
+ have self : ∀ q : Addr, Region.Disjoint ⟨q, 32⟩ ⟨q + 32, 64⟩ := fun q a h₁ h₂ => by
+ simp only [Region.Contains] at h₁ h₂; bv_omega
+ refine ⟨?_, ?_, ?_, ?_, saved_frame' h.saved F ?_, h.frame.trans (F.sub ?_)⟩
+ · rw [st _ ?_, h.stI]
+ simp only [List.mem_cons, List.not_mem_nil, or_false]
+ rintro r (rfl | rfl)
+ · exact self _
+ · exact (hp.i_o.sub_left (sub32 _)).sub_right sO
+ · rw [st _ ?_, h.stO]
+ simp only [List.mem_cons, List.not_mem_nil, or_false]
+ rintro r (rfl | rfl)
+ · exact (hp.i_o.symm.sub_left (sub32 _)).sub_right sI
+ · exact self _
+ · rw [Proof.Sha256.Stream.bytesAt_congr
+ (fun i hi => frame_bytes f₂ (R := ⟨inn s₀ + 32, j + 1⟩) ?_ (by simp; omega) hi),
+ bytesAt_snoc _ _ (by omega), h.bufI, List.take_succ_eq_append_getElem hl, List.map_append]
+ · rfl
+ · simp only [List.mem_singleton]; rintro r rfl
+ exact dIO.sub_left (Region.sub_prefix (by omega))
+ · rw [bytesAt_snoc _ _ (by omega),
+ Proof.Sha256.Stream.bytesAt_congr
+ (fun i hi => frame_bytes f₁ (R := ⟨out s₀ + 32, j⟩) ?_ (by simp; omega) hi),
+ h.bufO, List.take_succ_eq_append_getElem hl, List.map_append]
+ · rfl
+ · simp only [List.mem_singleton]; rintro r rfl
+ exact dIO.symm.sub_left (Region.sub_prefix (by omega))
+ · simp only [List.mem_cons, List.not_mem_nil, or_false]
+ rintro r (rfl | rfl)
+ · exact hp.i_s.symm.sub_right sI
+ · exact hp.o_s.symm.sub_right sO
+ · simp only [List.mem_cons, List.not_mem_nil, or_false]
+ rintro r (rfl | rfl)
+ · exact ⟨inR s₀, by simp, sI⟩
+ · exact ⟨outR s₀, by simp, sO⟩
+
+/-! ## The key and pad loops -/
+
+theorem wp_eor {is : List Instr} {s : State} {Q : State → Prop} {d n m : Reg}
+ (k : ∀ s', Upd s s' d (s.gpr n ^^^ s.gpr m) → WP isa (.block is) s' Q) :
+ WP isa (.block (.logic .xor d n m :: is)) s Q :=
+ cons exec_logic (k _ (Proof.Sha256.PPC64LE.Stream.Upd.write _ _ _))
+
+theorem xor_byte (b : Byte) (v : Nat) :
+ (b.setWidth 64 ^^^ BitVec.ofNat 64 v).setWidth 8 = b ^^^ BitVec.ofNat 8 v := by
+ ext i hi
+ simp [BitVec.getElem_xor]
+
+theorem K0_lt {s₀ : State} {j : Nat} (hj : j < kl s₀) (h : j < (K0 s₀).length) :
+ (K0 s₀)[j] = s₀.mem (kp s₀ + BitVec.ofNat 64 j) := by
+ simp only [K0]
+ rw [List.getElem_append_left (by rw [bytesAt_length]; exact hj)]
+ simp [bytesAt]
+
+theorem K0_ge {s₀ : State} {j : Nat} (hj : kl s₀ ≤ j) (h : j < (K0 s₀).length) : (K0 s₀)[j] = 0 := by
+ simp only [K0]
+ rw [List.getElem_append_right (by rw [bytesAt_length]; exact hj)]
+ simp
+
+def keyBody : List Instr :=
+ [.lbz .r8 .r29 0,
+ .logic .xor .r9 .r8 .r12, .add .r11 .r26 .r31, .stb .r9 .r11 32,
+ .logic .xor .r9 .r8 .r0, .add .r11 .r28 .r31, .stb .r9 .r11 32,
+ .addi .r29 .r29 1, .addi .r31 .r31 1, .subi .r30 .r30 1]
+
+theorem keyLoop_eq : keyLoop = .loop (.block keyBody) (.nonzero .d .r30) := rfl
+
+theorem buf_in {s₀ : State} (hp : Pre s₀) {s : State} (hwr : s.wr = s₀.wr) {j : Nat} (hj : j < 64)
+ {p : Addr} (hpR : p = inn s₀ ∨ p = out s₀) : InRegions s.wr (p + 32 + BitVec.ofNat 64 j) 1 := by
+ refine ⟨⟨p, 96⟩, by rcases hpR with rfl | rfl <;> simp [hwr, hp.wr], ?_⟩
+ rw [BitVec.add_assoc, show (32 : Addr) + BitVec.ofNat 64 j = BitVec.ofNat 64 (32 + j) by
+ rw [BitVec.ofNat_add]; rfl]
+ exact contains_offset (by omega) (by omega)
+
+theorem key_step {s₀ : State} (hp : Pre s₀) {j : Nat} (hj : j < kl s₀) {s : State} (h : Key s₀ j s) :
+ WP isa (.block keyBody) s (Key s₀ (j + 1)) := by
+ have hkl := hp.kl_le
+ have hl : j < (K0 s₀).length := by rw [K0_length s₀ hp]; omega
+ have hin : InRegions (s.rd ++ s.wr) (kp s₀ + BitVec.ofNat 64 j) 1 :=
+ ⟨kR s₀, by simp [h.rd, hp.rd], contains_offset (by omega) (by omega)⟩
+ have hbyte : s.mem (kp s₀ + BitVec.ofNat 64 j) = (K0 s₀)[j] := by
+ rw [K0_lt hj hl]
+ refine frame_bytes h.mem.frame (R := kR s₀) ?_ (by show kl s₀ ≤ 2 ^ 64; omega) hj
+ simp only [List.mem_cons, List.not_mem_nil, or_false]
+ rintro r (rfl | rfl | rfl)
+ · exact hp.k_i
+ · exact hp.k_o
+ · exact hp.k_s
+ unfold keyBody
+ refine wp_lbz (a := kp s₀ + BitVec.ofNat 64 j) (by decide) (by omega) (by rw [h.r29]; simp) hin fun s₁ u₁ => ?_
+ refine wp_eor fun s₂ u₂ => wp_add fun s₃ u₃ => ?_
+ refine wp_stb (a := inn s₀ + 32 + BitVec.ofNat 64 j) (by decide) (by omega) ?_
+ (by rw [u₃.wr, u₂.wr, u₁.wr]; exact buf_in hp h.wr (by omega) (.inl rfl)) fun s₄ u₄ => ?_
+ · simp (config := {decide := true}) only [u₃.gpr, u₂.other, u₁.other, h.r26, h.r31]; bv_omega
+ refine wp_eor fun s₅ u₅ => wp_add fun s₆ u₆ => ?_
+ refine wp_stb (a := out s₀ + 32 + BitVec.ofNat 64 j) (by decide) (by omega) ?_
+ (by rw [u₆.wr, u₅.wr, u₄.wr, u₃.wr, u₂.wr, u₁.wr]; exact buf_in hp h.wr (by omega) (.inr rfl))
+ fun s₇ u₇ => ?_
+ · simp (config := {decide := true}) only [u₆.gpr, u₅.other, u₄.gpr, u₃.other, u₂.other, u₁.other,
+ h.r28, h.r31]
+ bv_omega
+ refine wp_addi (by decide) (by omega) fun s₈ u₈ => wp_addi (by decide) (by omega) fun s₉ u₉ =>
+ wp_subi (by decide) (by omega) fun s₁₀ u₁₀ => WP.block_nil ?_
+ have k : ∀ r, r ≠ .r8 → r ≠ .r9 → r ≠ .r11 → r ≠ .r29 → r ≠ .r30 → r ≠ .r31 → s₁₀.gpr r = s.gpr r :=
+ fun r a b c d e f => by
+ rw [u₁₀.other r e, u₉.other r f, u₈.other r d, u₇.gpr, u₆.other r c, u₅.other r b, u₄.gpr,
+ u₃.other r c, u₂.other r b, u₁.other r a]
+ have v₁ : (s₃.gpr .r9).setWidth 8 = (K0 s₀)[j] ^^^ ipad := by
+ simp (config := {decide := true}) only [u₃.other, u₂.gpr, u₁.gpr, u₁.other, h.r12, xor_byte, hbyte]
+ rfl
+ have v₂ : (s₆.gpr .r9).setWidth 8 = (K0 s₀)[j] ^^^ opad := by
+ simp (config := {decide := true}) only [u₆.other, u₅.gpr, u₄.gpr, u₃.other, u₂.other, u₁.gpr,
+ u₁.other, h.r0, xor_byte, hbyte]
+ rfl
+ have hm : s₁₀.mem = (s.mem.writeW (inn s₀ + 32 + BitVec.ofNat 64 j) ((K0 s₀)[j] ^^^ ipad)).writeW
+ (out s₀ + 32 + BitVec.ofNat 64 j) ((K0 s₀)[j] ^^^ opad) := by
+ rw [u₁₀.mem, u₉.mem, u₈.mem, u₇.mem, v₂, u₆.mem, u₅.mem, u₄.mem, v₁, u₃.mem, u₂.mem, u₁.mem]
+ refine ⟨⟨by omega, by rw [u₁₀.rd, u₉.rd, u₈.rd, u₇.rd, u₆.rd, u₅.rd, u₄.rd, u₃.rd, u₂.rd, u₁.rd, h.rd],
+ by rw [u₁₀.wr, u₉.wr, u₈.wr, u₇.wr, u₆.wr, u₅.wr, u₄.wr, u₃.wr, u₂.wr, u₁.wr, h.wr],
+ by rw [u₁₀.sp, u₉.sp, u₈.sp, u₇.sp, u₆.sp, u₅.sp, u₄.sp, u₃.sp, u₂.sp, u₁.sp, h.sp],
+ by rw [k _ (by decide) (by decide) (by decide) (by decide) (by decide) (by decide), h.r26],
+ by rw [k _ (by decide) (by decide) (by decide) (by decide) (by decide) (by decide), h.r27],
+ by rw [k _ (by decide) (by decide) (by decide) (by decide) (by decide) (by decide), h.r28], ?_,
+ by rw [k _ (by decide) (by decide) (by decide) (by decide) (by decide) (by decide), h.r12],
+ by rw [k _ (by decide) (by decide) (by decide) (by decide) (by decide) (by decide), h.r0],
+ by rw [hm]; exact buf_write hp h.mem (by omega), fun r hr => by
+ rw [k r (by revert r hr; decide) (by revert r hr; decide) (by revert r hr; decide)
+ (by revert r hr; decide) (by revert r hr; decide) (by revert r hr; decide)]; exact h.nv r hr⟩,
+ ?_, ?_⟩
+ · simp (config := {decide := true}) only [u₁₀.other, u₉.gpr, u₈.other, u₇.gpr, u₆.other, u₅.other,
+ u₄.gpr, u₃.other, u₂.other, u₁.other, h.r31]
+ rw [BitVec.ofNat_add]
+ · simp (config := {decide := true}) only [u₁₀.other, u₉.other, u₈.gpr, u₇.gpr, u₆.other, u₅.other,
+ u₄.gpr, u₃.other, u₂.other, u₁.other, h.r29]
+ rw [BitVec.ofNat_add, BitVec.add_assoc]
+ · simp (config := {decide := true}) only [u₁₀.gpr, u₉.other, u₈.other, u₇.gpr, u₆.other, u₅.other,
+ u₄.gpr, u₃.other, u₂.other, u₁.other, h.r30]
+ rw [show (BitVec.ofNat 64 1 : BitVec 64) = 1 from rfl, ← show kl s₀ - j - 1 = kl s₀ - (j + 1) by omega,
+ ← sub_ofNat (a := kl s₀ - j) (b := 1) (by omega)]
+ rfl
+
+def padBody : List Instr :=
+ [.add .r11 .r26 .r31, .stb .r12 .r11 32, .add .r11 .r28 .r31, .stb .r0 .r11 32,
+ .addi .r31 .r31 1, .subi .r10 .r10 1]
+
+theorem padLoop_eq : padLoop = .loop (.block padBody) (.nonzero .d .r10) := rfl
+
+theorem ipad_byte : (BitVec.ofNat 64 0x36).setWidth 8 = (0 : Byte) ^^^ ipad := by decide
+theorem opad_byte : (BitVec.ofNat 64 0x5c).setWidth 8 = (0 : Byte) ^^^ opad := by decide
+
+theorem pad_step {s₀ : State} (hp : Pre s₀) {j : Nat} (hj : kl s₀ ≤ j) (hj' : j < 64) {s : State}
+ (h : Pad s₀ j s) : WP isa (.block padBody) s (Pad s₀ (j + 1)) := by
+ have hl : j < (K0 s₀).length := by rw [K0_length s₀ hp]; omega
+ unfold padBody
+ refine wp_add fun s₁ u₁ => ?_
+ refine wp_stb (a := inn s₀ + 32 + BitVec.ofNat 64 j) (by decide) (by omega) ?_
+ (by rw [u₁.wr]; exact buf_in hp h.wr hj' (.inl rfl)) fun s₂ u₂ => ?_
+ · rw [u₁.gpr, h.r26, h.r31]; bv_omega
+ refine wp_add fun s₃ u₃ => ?_
+ refine wp_stb (a := out s₀ + 32 + BitVec.ofNat 64 j) (by decide) (by omega) ?_
+ (by rw [u₃.wr, u₂.wr, u₁.wr]; exact buf_in hp h.wr hj' (.inr rfl)) fun s₄ u₄ => ?_
+ · simp (config := {decide := true}) only [u₃.gpr, u₂.gpr, u₁.other, h.r28, h.r31]; bv_omega
+ refine wp_addi (by decide) (by omega) fun s₅ u₅ => wp_subi (by decide) (by omega) fun s₆ u₆ => WP.block_nil ?_
+ have k : ∀ r, r ≠ .r10 → r ≠ .r11 → r ≠ .r31 → s₆.gpr r = s.gpr r := fun r a b c => by
+ rw [u₆.other r a, u₅.other r c, u₄.gpr, u₃.other r b, u₂.gpr, u₁.other r b]
+ have hm : s₆.mem = (s.mem.writeW (inn s₀ + 32 + BitVec.ofNat 64 j) ((K0 s₀)[j] ^^^ ipad)).writeW
+ (out s₀ + 32 + BitVec.ofNat 64 j) ((K0 s₀)[j] ^^^ opad) := by
+ simp (config := {decide := true}) only [u₆.mem, u₅.mem, u₄.mem, u₃.mem, u₂.mem, u₁.mem, u₃.other,
+ u₂.gpr, u₁.other, h.r12, h.r0, K0_ge hj hl, ipad_byte, opad_byte]
+ refine ⟨⟨by omega, by rw [u₆.rd, u₅.rd, u₄.rd, u₃.rd, u₂.rd, u₁.rd, h.rd],
+ by rw [u₆.wr, u₅.wr, u₄.wr, u₃.wr, u₂.wr, u₁.wr, h.wr],
+ by rw [u₆.sp, u₅.sp, u₄.sp, u₃.sp, u₂.sp, u₁.sp, h.sp],
+ by rw [k _ (by decide) (by decide) (by decide), h.r26],
+ by rw [k _ (by decide) (by decide) (by decide), h.r27],
+ by rw [k _ (by decide) (by decide) (by decide), h.r28], ?_,
+ by rw [k _ (by decide) (by decide) (by decide), h.r12],
+ by rw [k _ (by decide) (by decide) (by decide), h.r0],
+ by rw [hm]; exact buf_write hp h.mem hj', fun r hr => by
+ rw [k r (by revert r hr; decide) (by revert r hr; decide) (by revert r hr; decide)]
+ exact h.nv r hr⟩, ?_⟩
+ · simp (config := {decide := true}) only [u₆.other, u₅.gpr, u₄.gpr, u₃.other, u₂.gpr, u₁.other, h.r31]
+ rw [BitVec.ofNat_add]
+ · simp (config := {decide := true}) only [u₆.gpr, u₅.other, u₄.gpr, u₃.other, u₂.gpr, u₁.other, h.r10]
+ rw [show 64 - (j + 1) = 64 - j - 1 by omega, ← sub_ofNat (a := 64 - j) (b := 1) (by omega)]
+
+theorem key_loop_ok {s₀ : State} (hp : Pre s₀) {s : State} (h : Key s₀ 0 s) (hk : 0 < kl s₀) :
+ WP isa keyLoop s (Buf s₀ (kl s₀)) := by
+ have := hp.kl_le
+ rw [keyLoop_eq]
+ refine WP.loop (M := isa) (fun n s => ∃ j, n = kl s₀ - j ∧ j < kl s₀ ∧ Key s₀ j s) ?_ (kl s₀) s
+ ⟨0, rfl, hk, h⟩
+ rintro n s ⟨j, rfl, hj, hb⟩
+ refine WP.mono (key_step hp hj hb) fun s' hb' => ?_
+ have hz : isa.eval (.nonzero .d .r30) s' = some (decide (kl s₀ - (j + 1) ≠ 0)) := by
+ show VG.PPC64LE.eval (.nonzero .d .r30) s' = _
+ rw [eval_nonzero, hb'.r30, bne, ofNat_beq_zero (by omega)]
+ simp
+ by_cases hl : kl s₀ - (j + 1) = 0
+ · refine .inl ⟨by rw [hz, decide_eq_false fun h => h hl], ?_⟩
+ rw [show kl s₀ = j + 1 by omega]; exact hb'.toBuf
+ · exact .inr ⟨by rw [hz, decide_eq_true hl], _, by omega, j + 1, rfl, by omega, hb'⟩
+
+theorem pad_loop_ok {s₀ : State} (hp : Pre s₀) {s : State} (h : Pad s₀ (kl s₀) s) (hk : kl s₀ < 64) :
+ WP isa padLoop s (Buf s₀ 64) := by
+ rw [padLoop_eq]
+ refine WP.loop (M := isa) (fun n s => ∃ j, n = 64 - j ∧ kl s₀ ≤ j ∧ j < 64 ∧ Pad s₀ j s) ?_
+ (64 - kl s₀) s ⟨kl s₀, rfl, le_rfl, hk, h⟩
+ rintro n s ⟨j, rfl, hj, hj', hb⟩
+ refine WP.mono (pad_step hp hj hj' hb) fun s' hb' => ?_
+ have hz : isa.eval (.nonzero .d .r10) s' = some (decide (64 - (j + 1) ≠ 0)) := by
+ show VG.PPC64LE.eval (.nonzero .d .r10) s' = _
+ rw [eval_nonzero, hb'.r10, bne, ofNat_beq_zero (by omega)]
+ simp
+ by_cases hl : 64 - (j + 1) = 0
+ · refine .inl ⟨by rw [hz, decide_eq_false fun h => h hl], ?_⟩
+ rw [show (64 : Nat) = j + 1 by omega]; exact hb'.toBuf
+ · exact .inr ⟨by rw [hz, decide_eq_true hl], _, by omega, j + 1, rfl, by omega, by omega, hb'⟩
+
+/-! ## The two compressions -/
+
+/-- The inlined compression of the block in the buffer of the state at `p`
+(the inner or the outer one). -/
+theorem compress_ok {s₀ : State} (hp : Pre s₀) {p : Addr} (hpR : p = inn s₀ ∨ p = out s₀) {s : State}
+ (hrd : s.rd = s₀.rd) (hwr : s.wr = s₀.wr) (h19 : s.gpr .r26 = p) (h20 : s.gpr .r27 = scr s₀)
+ (h1 : s.gpr .r4 = p + 32) {Q : State → Prop}
+ (hQ : ∀ s', s'.rd = s.rd → s'.wr = s.wr → (∀ r ∈ preserved, s'.gpr r = s.gpr r) →
+ s'.sp = s.sp →
+ Frame [⟨p, 32⟩, ⟨scr s₀, 112⟩] s.mem s'.mem →
+ stateAt s'.mem p = Spec.Sha256.compress (stateAt s.mem p) (Spec.Sha256.blockAt s.mem (p + 32)) →
+ Q s') :
+ WP isa compressAt s Q := by
+ have hs : Region.Disjoint ⟨p, 96⟩ (scR s₀) ∧ ⟨p, 96⟩ ∈ s₀.wr := by
+ rcases hpR with rfl | rfl
+ · exact ⟨hp.i_s, by simp [hp.wr]⟩
+ · exact ⟨hp.o_s, by simp [hp.wr]⟩
+ obtain ⟨d, hm⟩ := hs
+ have e32 : Region.Sub ⟨p, 32⟩ ⟨p, 96⟩ := Region.sub_prefix (by omega)
+ have eb : Region.Sub ⟨p + 32, 64⟩ ⟨p, 96⟩ := sub_offset (off := 32) (by omega) (by omega)
+ have e112 : Region.Sub ⟨scr s₀, 112⟩ (scR s₀) := Region.sub_prefix (by omega)
+ refine compressAt_ok h19 h20 h1 ((d.sub_left e32).sub_right e112) ?_ ((d.sub_left eb).sub_right e112)
+ ?_ ?_ hQ
+ · intro a h₁ h₂; simp only [Region.Contains] at h₁ h₂; bv_omega
+ · rw [hrd, hwr]
+ apply Covers.of_sub
+ intro r hr
+ simp only [List.mem_cons, List.not_mem_nil, or_false] at hr
+ rcases hr with rfl | rfl | rfl
+ · exact ⟨⟨p, 96⟩, by simp [hm], 32, rfl, by simp⟩
+ · exact ⟨⟨p, 96⟩, by simp [hm], 0, by simp, by simp⟩
+ · exact ⟨scR s₀, by simp [hp.wr], 0, by simp, by simp⟩
+ · rw [hwr]
+ apply Covers.of_sub
+ intro r hr
+ simp only [List.mem_cons, List.not_mem_nil, or_false] at hr
+ rcases hr with rfl | rfl
+ · exact ⟨⟨p, 96⟩, hm, 0, by simp, by simp⟩
+ · exact ⟨scR s₀, by simp [hp.wr], 0, by simp, by simp⟩
+
+/-- A state that a write outside it keeps. -/
+theorem state_frame {rs : List Region} {m m' : Mem} (hf : Frame rs m m') {p : Addr}
+ (hd : ∀ r ∈ rs, Region.Disjoint ⟨p, 96⟩ r) :
+ stateAt m' p = stateAt m p ∧ bytesAt m' (p + 32) 64 = bytesAt m (p + 32) 64 := by
+ refine ⟨Proof.Sha256.Stream.stateAt_congr fun i hi =>
+ frame_bytes hf (R := ⟨p, 32⟩) (fun r hr => (hd r hr).sub_left (sub32 p)) (by simp) hi,
+ Proof.Sha256.Stream.bytesAt_congr fun i hi =>
+ frame_bytes hf (R := ⟨p + 32, 64⟩)
+ (fun r hr => (hd r hr).sub_left (sub_offset (off := 32) (by omega) (by omega))) (by simp) hi⟩
+
+/-! ## Epilogue -/
+
+/-- The epilogue's postcondition. -/
+def Post (s₀ s' : State) : Prop :=
+ (∀ p ∈ saved, s'.gpr p.1 = s₀.gpr p.1) ∧ (∀ r ∈ nvRegs, s'.gpr r = s₀.gpr r) ∧ s'.sp = s₀.sp ∧
+ Proof.Hmac.initSha256PPC64LE.post s₀ s'
+
+theorem epilogue_ok {s₀ : State} (hp : Pre s₀) {s : State} (hrd : s.rd = s₀.rd) (hwr : s.wr = s₀.wr)
+ (h20 : s.gpr .r27 = scr s₀) (hsp : s.sp = s₀.sp) (hsv : Saved s₀ s.mem)
+ (hnv : ∀ r ∈ nvRegs, s.gpr r = s₀.gpr r)
+ (hI : Repr s.mem (inn s₀) (xorPad (K0 s₀) ipad)) (hO : Repr s.mem (out s₀) (xorPad (K0 s₀) opad)) :
+ WP isa (.block restore) s (Post s₀) := by
+ refine restore_ok (scr := scr s₀) h20
+ (fun d hd₁ hd₂ => ⟨scR s₀, by simp [hrd, hwr, hp.wr], contains_offset hd₂ (by omega)⟩) s₀.gpr
+ hsv fun s' hs ho hmem _ _ hsp' => ⟨hs, fun r hr => by
+ rw [ho r (by revert r hr; decide)]; exact hnv r hr, by rw [hsp', hsp], ?_⟩
+ simp only [Proof.Hmac.initSha256PPC64LE]
+ rw [blockKey_eq hp, hmem]
+ exact ⟨hI, hO⟩
+
+/-- No instruction of `init` writes the callee-saved registers it does not save. -/
+theorem untouched_ok : ∀ r ∈ untouched, ∀ i ∈ instrs initMain, dstOf i ≠ some r := by
+ have : ((instrs initMain).all fun i => untouched.all fun r => dstOf i != some r) = true := by
+ rw [← Code.allInstrs_eq]; decide +kernel
+ intro r hr i hi
+ have := List.all_eq_true.mp (List.all_eq_true.mp this i hi) r hr
+ simpa using this
+
+/-! ## Correctness -/
+
+theorem buf_full {s₀ : State} (hp : Pre s₀) {m : Mem} (h : BufMem s₀ 64 m) :
+ bytesAt m (inn s₀ + 32) 64 = xorPad (K0 s₀) ipad ∧ bytesAt m (out s₀ + 32) 64 = xorPad (K0 s₀) opad := by
+ rw [h.bufI, h.bufO, List.take_of_length_le (by rw [K0_length s₀ hp])]
+ exact ⟨rfl, rfl⟩
+
+/-- `init` without its frame: the callee-saved registers are kept. -/
+theorem correctMain {s₀ : State} (hp : Pre s₀) :
+ WP isa initMain s₀ fun s' => (∀ r ∈ preserved, s'.gpr r = s₀.gpr r) ∧
+ s'.sp = s₀.sp ∧ Proof.Hmac.initSha256PPC64LE.post s₀ s' := by
+ have hkl := hp.kl_le
+ refine WP.mono (Proof.Sha256.PPC64LE.Stream.WP.gprs (Q := Post s₀) ?_ untouched_ok)
+ fun s' ⟨⟨hsv, hnv, hsp, hpost⟩, hu⟩ => ⟨fun r hr => ?_, hsp, hpost⟩
+ · unfold initMain
+ refine WP.seq (WP.mono (prologue_ok hp) fun s₁ h₁ => ?_)
+ -- The key.
+ refine WP.seq (WP.mono (Q := Buf s₀ (kl s₀)) ?_ fun s₂ h₂ => ?_)
+ · refine WP.ite (decide (kl s₀ = 0))
+ (by show VG.PPC64LE.eval (.zero .d .r30) s₁ = _
+ rw [eval_zero, h₁.r30, Nat.sub_zero, ofNat_beq_zero (by omega)])
+ (fun hb => WP.block_nil ?_) (fun hb => key_loop_ok hp h₁ ?_)
+ · simp only [decide_eq_true_eq] at hb; rw [hb]; exact h₁.toBuf
+ · simp only [decide_eq_false_iff_not] at hb; omega
+ -- The padding.
+ refine WP.seq (wp_li (by decide) fun s₃ u₃ => wp_sub fun s₄ u₄ => WP.block_nil ?_)
+ have hP : Pad s₀ (kl s₀) s₄ := by
+ refine ⟨⟨h₂.j_le, by rw [u₄.rd, u₃.rd, h₂.rd], by rw [u₄.wr, u₃.wr, h₂.wr],
+ by rw [u₄.sp, u₃.sp, h₂.sp], ?_, ?_, ?_, ?_, ?_, ?_, by rw [u₄.mem, u₃.mem]; exact h₂.mem,
+ fun r hr => by
+ rw [u₄.other r (by revert r hr; decide), u₃.other r (by revert r hr; decide)]
+ exact h₂.nv r hr⟩, ?_⟩
+ all_goals simp (config := {decide := true}) only [u₄.gpr, u₄.other, u₃.gpr, u₃.other, h₂.r26,
+ h₂.r27, h₂.r28, h₂.r31, h₂.r12, h₂.r0]
+ rw [← sub_ofNat hkl]
+ refine WP.seq (WP.mono (Q := Buf s₀ 64) ?_ fun s₆ h₆ => ?_)
+ · refine WP.ite (decide (64 - kl s₀ = 0))
+ (by show VG.PPC64LE.eval (.zero .d .r10) s₄ = _
+ rw [eval_zero, hP.r10, ofNat_beq_zero (by omega)])
+ (fun hb => WP.block_nil ?_) (fun hb => pad_loop_ok hp hP ?_)
+ · simp only [decide_eq_true_eq] at hb
+ exact (show kl s₀ = 64 by omega) ▸ hP.toBuf
+ · simp only [decide_eq_false_iff_not] at hb; omega
+ obtain ⟨bI, bO⟩ := buf_full hp h₆.mem
+ -- The inner block.
+ refine WP.seq (wp_addi (by decide) (by omega) fun s₇ u₇ => WP.block_nil ?_)
+ refine WP.seq (compress_ok hp (.inl rfl) (by rw [u₇.rd, h₆.rd]) (by rw [u₇.wr, h₆.wr])
+ (by rw [u₇.other _ (by decide), h₆.r26]) (by rw [u₇.other _ (by decide), h₆.r27])
+ (by rw [u₇.gpr, h₆.r26]; rfl) fun s₈ rd₈ wr₈ cs₈ sp₈ fr₈ st₈ => ?_)
+ have hI₈ : Repr s₈.mem (inn s₀) (xorPad (K0 s₀) ipad) :=
+ repr_block (by rw [u₇.mem]; exact h₆.mem.stI) (by rw [u₇.mem]; exact bI)
+ (by simp [xorPad, K0_length s₀ hp]) st₈
+ have dO : ∀ r ∈ [(⟨inn s₀, 32⟩ : Region), ⟨scr s₀, 112⟩], Region.Disjoint ⟨out s₀, 96⟩ r := by
+ simp only [List.mem_cons, List.not_mem_nil, or_false]
+ rintro r (rfl | rfl)
+ · exact hp.i_o.symm.sub_right (sub32 _)
+ · exact hp.o_s.sub_right (Region.sub_prefix (by omega))
+ obtain ⟨sO₈, bO₈⟩ := state_frame fr₈ dO
+ have sv₈ : Saved s₀ s₈.mem := by
+ refine saved_frame (by rw [u₇.mem]; exact h₆.mem.saved) fr₈ ?_
+ simp only [List.mem_cons, List.not_mem_nil, or_false]
+ rintro r (rfl | rfl)
+ · exact (hp.i_s.symm.sub_left (save_sub s₀)).sub_right (sub32 _)
+ · intro a h₁ h₂; simp only [Region.Contains] at h₁ h₂; bv_omega
+ -- The outer block.
+ refine WP.seq (wp_mov fun s₉ u₉ => wp_addi (by decide) (by omega) fun s₁₀ u₁₀ => WP.block_nil ?_)
+ have x21₈ : s₈.gpr .r28 = out s₀ := by
+ rw [cs₈ _ (by decide), u₇.other _ (by decide), h₆.r28]
+ have x20₈ : s₈.gpr .r27 = scr s₀ := by
+ rw [cs₈ _ (by decide), u₇.other _ (by decide), h₆.r27]
+ have m₁₀ : s₁₀.mem = s₈.mem := by rw [u₁₀.mem, u₉.mem]
+ have nv₁₀ : ∀ r ∈ nvRegs, s₁₀.gpr r = s₀.gpr r := fun r hr => by
+ rw [u₁₀.other r (by revert r hr; decide), u₉.other r (by revert r hr; decide),
+ cs₈ r (nv_pres r hr), u₇.other r (by revert r hr; decide)]
+ exact h₆.nv r hr
+ refine WP.seq (compress_ok hp (.inr rfl) (by rw [u₁₀.rd, u₉.rd, rd₈, u₇.rd, h₆.rd])
+ (by rw [u₁₀.wr, u₉.wr, wr₈, u₇.wr, h₆.wr])
+ (by rw [u₁₀.other _ (by decide), u₉.gpr, x21₈])
+ (by rw [u₁₀.other _ (by decide), u₉.other _ (by decide), x20₈])
+ (by rw [u₁₀.gpr, u₉.gpr, x21₈]; rfl)
+ fun s₁₁ rd₁₁ wr₁₁ cs₁₁ sp₁₁ fr₁₁ st₁₁ => ?_)
+ have hO : Repr s₁₁.mem (out s₀) (xorPad (K0 s₀) opad) :=
+ repr_block (by rw [m₁₀, sO₈, u₇.mem]; exact h₆.mem.stO) (by rw [m₁₀, bO₈, u₇.mem]; exact bO)
+ (by simp [xorPad, K0_length s₀ hp]) st₁₁
+ have hI : Repr s₁₁.mem (inn s₀) (xorPad (K0 s₀) ipad) := by
+ refine repr_congr (fun i hi => frame_bytes fr₁₁ (R := inR s₀) ?_ (by simp) hi) (m₁₀ ▸ hI₈)
+ simp only [List.mem_cons, List.not_mem_nil, or_false]
+ rintro r (rfl | rfl)
+ · exact hp.i_o.sub_right (sub32 _)
+ · exact hp.i_s.sub_right (Region.sub_prefix (by omega))
+ refine epilogue_ok hp (by rw [rd₁₁, u₁₀.rd, u₉.rd, rd₈, u₇.rd, h₆.rd])
+ (by rw [wr₁₁, u₁₀.wr, u₉.wr, wr₈, u₇.wr, h₆.wr])
+ (by rw [cs₁₁ _ (by decide), u₁₀.other _ (by decide), u₉.other _ (by decide), x20₈])
+ (by rw [sp₁₁, u₁₀.sp, u₉.sp, sp₈, u₇.sp, h₆.sp]) ?_
+ (fun r hr => by rw [cs₁₁ r (nv_pres r hr)]; exact nv₁₀ r hr) hI hO
+ refine saved_frame (by rw [m₁₀]; exact sv₈) fr₁₁ ?_
+ simp only [List.mem_cons, List.not_mem_nil, or_false]
+ rintro r (rfl | rfl)
+ · exact (hp.o_s.symm.sub_left (save_sub s₀)).sub_right (sub32 _)
+ · intro a h₁ h₂; simp only [Region.Contains] at h₁ h₂; bv_omega
+ · have key : ∀ r ∈ preserved, r ∈ untouched ∨ r ∈ nvRegs ∨ r ∈ saved.map Prod.fst := by decide
+ rcases key r hr with hr' | hr' | hr'
+ · exact hu r hr'
+ · exact hnv r hr'
+ · obtain ⟨p, hp', rfl⟩ := List.mem_map.mp hr'
+ exact hsv p hp'
+
+/-- The state `initMain` starts in: the link register moved to `r0`, then
+pushed in a frame. -/
+abbrev inner (s₀ : State) : State := framed .r0 (s₀.write .r0 s₀.lr)
+
+theorem correct {s₀ : State} (hp : Pre s₀) (hs : Stack s₀) :
+ WP isa init s₀ fun s' => abiPreserved s₀ s' ∧ Proof.Hmac.initSha256PPC64LE.post s₀ s' := by
+ have hpi : Pre (inner s₀) :=
+ ⟨hp.kl_le, hp.rd, hp.wr, hp.i_o, hp.i_s, hp.o_s, hp.k_i, hp.k_o, hp.k_s⟩
+ refine WP.seq (cons exec_mflr (WP.block_nil (WP.seq ?_)))
+ refine WP.frameReg (by exact hs.sp48) (fun R hR => ?_) (WP.mono (correctMain hpi)
+ fun s' ⟨hk, hsp, hpost⟩ => ?_)
+ · rw [show (s₀.write .r0 s₀.lr).wr = s₀.wr from rfl, hp.wr] at hR
+ simp only [List.mem_cons, List.not_mem_nil, or_false] at hR
+ rcases hR with rfl | rfl | rfl
+ · exact hs.i.sub_left (frame_sub _)
+ · exact hs.o.sub_left (frame_sub _)
+ · exact hs.s.sub_left (frame_sub _)
+ · refine cons exec_mtlr (WP.block_nil ⟨⟨fun r hr => ?_, rfl, ?_⟩, ?_⟩)
+ · have h0 : r ≠ .r0 := by revert r hr; decide
+ simp only [State.write, h0, ite_false]
+ rw [hk r hr]
+ simp only [framed, State.write, h0, ite_false]
+ · simp [State.write]
+ · have e : bytesAt (inner s₀).mem (s₀.gpr .r5) (s₀.gpr .r6).toNat =
+ bytesAt s₀.mem (s₀.gpr .r5) (s₀.gpr .r6).toNat :=
+ Proof.Sha256.Stream.bytesAt_congr fun i hi =>
+ Proof.Sha256.PPC64LE.Stream.write_frame_bytes (R := kR s₀) hs.k (s₀.gpr .r6).isLt hi
+ change Repr s'.mem (s₀.gpr .r3) (xorPad (blockKey sha256
+ (bytesAt (inner s₀).mem (s₀.gpr .r5) (s₀.gpr .r6).toNat)) ipad) ∧
+ Repr s'.mem (s₀.gpr .r4) (xorPad (blockKey sha256
+ (bytesAt (inner s₀).mem (s₀.gpr .r5) (s₀.gpr .r6).toNat)) opad) at hpost
+ rw [e] at hpost
+ exact hpost
+
+/-! ## `Verified` -/
+
+/-- The initial taint: only the arguments are public. -/
+theorem agree₀ {s₁ s₂ : State} (hpub : Proof.Hmac.initSha256PPC64LE.pub s₁ s₂) :
+ VG.PPC64LE.Taint.Agree (VG.PPC64LE.Taint.ofRegs [.r3, .r4, .r5, .r6, .r7]) s₁ s₂ := by
+ obtain ⟨p1, p2, p3, p4, p5, hsp⟩ := hpub
+ refine ⟨hsp, fun r hr => ?_⟩
+ simp only [VG.PPC64LE.Taint.mem_ofRegs, List.mem_cons, List.not_mem_nil, or_false] at hr
+ rcases hr with rfl | rfl | rfl | rfl | rfl <;> assumption
+
+/-- A state satisfying the precondition (with an empty key). -/
+def sat : State where
+ gpr r := match r with
+ | .r3 => 0x1000 | .r4 => 0x2000 | .r5 => 0x3000 | .r7 => 0x4000 | _ => 0
+ lr := 0
+ sp := 0x5000
+ mem _ := 0
+ rd := [⟨0x3000, 0⟩]
+ wr := [⟨0x1000, 96⟩, ⟨0x2000, 96⟩, ⟨0x4000, 160⟩]
+
+theorem init_verified : Verified PPC64LE.target init Proof.Hmac.initSha256PPC64LE := by
+ refine ⟨fun s hs => ?_, ?_, ?_⟩
+ · obtain ⟨t, s', he, h⟩ := correct (pre_of hs).1 (pre_of hs).2
+ exact ⟨t, s', he, h⟩
+ · exact VG.Taint.constantTime (A := taint) (Taint.ofRegs [.r3, .r4, .r5, .r6, .r7]) (fun _ _ _ _ hp => agree₀ hp)
+ (by taint_decide)
+ · refine ⟨sat, by decide, rfl, rfl, ?_, ?_, ?_, ?_, ?_, ?_, by decide, ?_, ?_, ?_, ?_⟩ <;>
+ · intro a h₁ h₂
+ simp only [Region.Contains, sat] at h₁ h₂
+ bv_omega
+
+end VG.Proof.Hmac.PPC64LE.Init
diff --git a/lean/VerifiedGarbage/Proof/Hmac/PPC64LE/Shared.lean b/lean/VerifiedGarbage/Proof/Hmac/PPC64LE/Shared.lean
new file mode 100644
index 000000000..c0021a2b2
--- /dev/null
+++ b/lean/VerifiedGarbage/Proof/Hmac/PPC64LE/Shared.lean
@@ -0,0 +1,114 @@
+import VerifiedGarbage.Proof.Framework.Contract
+import VerifiedGarbage.Proof.Framework.PPC64LE.Inline
+import VerifiedGarbage.Proof.Hmac.PPC64LE.Finalize
+import VerifiedGarbage.Proof.Hmac.PPC64LE.Init
+import VerifiedGarbage.Spec.Hmac.Contract
+
+/-!
+# Hmac on PPC64LE: the shared contracts
+
+Untrusted: everything here is checked by Lean. The proofs are written against
+per-target contracts (`Proof/Hmac/PPC64LE/Contract.lean`); these theorems move
+them to the shared contracts of `Spec/Hmac/Contract.lean`, which the
+artifacts are emitted with.
+
+The shared contracts give the functions more scratch than these ones use (608
+bytes for `init` and 688 for `finalize`, sized for the x86-64 AVX2 compression
+function): the per-target contracts are first widened to that scratch
+(`Verified.widen`, the same code running with the same trace and result), then
+moved to the shared ones.
+-/
+
+namespace VG.Proof.Hmac.PPC64LE.Shared
+
+open _root_.VG.PPC64LE
+
+/-- `initSha256PPC64LE` with 608 bytes of scratch, of which the code uses 160. -/
+def initWide : Contract PPC64LE.isa :=
+ { Proof.Hmac.initSha256PPC64LE with
+ pre := fun s =>
+ let inner : Region := ⟨s.gpr .r3, 96⟩
+ let outer : Region := ⟨s.gpr .r4, 96⟩
+ let key : Region := ⟨s.gpr .r5, (s.gpr .r6).toNat⟩
+ let scratch : Region := ⟨s.gpr .r7, 608⟩
+ let stack : Region := ⟨s.sp - 48, 48⟩
+ (s.gpr .r6).toNat ≤ 64 ∧ s.rd = [key] ∧ s.wr = [inner, outer, scratch] ∧
+ inner.Disjoint outer ∧ inner.Disjoint scratch ∧ outer.Disjoint scratch ∧
+ key.Disjoint inner ∧ key.Disjoint outer ∧ key.Disjoint scratch ∧
+ 48 ≤ s.sp.toNat ∧ stack.Disjoint inner ∧ stack.Disjoint outer ∧ stack.Disjoint key ∧
+ stack.Disjoint scratch }
+
+/-- The regions `initSha256PPC64LE` lets the code write. -/
+def initNarrowWr (s : State) : List Region :=
+ [⟨s.gpr .r3, 96⟩, ⟨s.gpr .r4, 96⟩, ⟨s.gpr .r7, 160⟩]
+
+theorem initWide_pre (s : State) (h : initWide.pre s) :
+ Proof.Hmac.initSha256PPC64LE.pre (s.withRegions s.rd (initNarrowWr s)) :=
+ let ⟨h₁, h₂, _, h₄, h₅, h₆, h₇, h₈, h₉, h₁₀, h₁₁, h₁₂, h₁₃, h₁₄⟩ := h
+ ⟨h₁, h₂, rfl, h₄, h₅.sub_right (Region.sub_of_ble rfl), h₆.sub_right (Region.sub_of_ble rfl), h₇,
+ h₈, h₉.sub_right (Region.sub_of_ble rfl), h₁₀, h₁₁, h₁₂, h₁₃,
+ h₁₄.sub_right (Region.sub_of_ble rfl)⟩
+
+/-- A state satisfying `initWide.pre`. -/
+def initWideSat : State :=
+ { Proof.Hmac.PPC64LE.Init.sat with wr := [⟨0x1000, 96⟩, ⟨0x2000, 96⟩, ⟨0x4000, 608⟩] }
+
+theorem initWide_implies : initWide.Implies (Spec.Hmac.initSha256Contract PPC64LE.abi 48) := by
+ sig_implies [Spec.Hmac.initSha256Contract, Spec.Hmac.initSha256Sig, initWide,
+ Proof.Hmac.initSha256PPC64LE, PPC64LE.abi, PPC64LE.argRegs]
+ [initWideSat, Proof.Hmac.PPC64LE.Init.sat] using initWideSat
+
+theorem init :
+ Verified PPC64LE.target Impl.Hmac.PPC64LE.init (Spec.Hmac.initSha256Contract PPC64LE.abi 48) :=
+ have hsat := initWide_implies.sat_left
+ (Verified.widen Proof.Hmac.PPC64LE.Init.init_verified initNarrowWr initWide_pre
+ (fun _ h => by
+ obtain ⟨_, _, h₃, _⟩ := h
+ rw [h₃]
+ exact .cons (Region.prefix_of_ble rfl) (.cons (Region.prefix_of_ble rfl)
+ (.cons (Region.prefix_of_ble rfl) .nil)))
+ (fun _ _ _ h => h) (fun _ _ _ _ h => h) hsat).of_implies initWide_implies
+
+/-- `finalizeSha256PPC64LE` with 688 bytes of scratch, of which the code uses
+240 (the MAC stays at byte 176). -/
+def finalizeWide : Contract PPC64LE.isa :=
+ { Proof.Hmac.finalizeSha256PPC64LE with
+ pre := fun s =>
+ let inner : Region := ⟨s.gpr .r3, 96⟩
+ let outer : Region := ⟨s.gpr .r4, 96⟩
+ let scratch : Region := ⟨s.gpr .r6, 688⟩
+ let stack : Region := ⟨s.sp - 96, 96⟩
+ s.rd = [outer] ∧ s.wr = [inner, scratch] ∧
+ inner.Disjoint outer ∧ inner.Disjoint scratch ∧ outer.Disjoint scratch ∧
+ 96 ≤ s.sp.toNat ∧ stack.Disjoint inner ∧ stack.Disjoint outer ∧ stack.Disjoint scratch }
+
+/-- The regions `finalizeSha256PPC64LE` lets the code write. -/
+def finalizeNarrowWr (s : State) : List Region := [⟨s.gpr .r3, 96⟩, ⟨s.gpr .r6, 240⟩]
+
+theorem finalizeWide_pre (s : State) (h : finalizeWide.pre s) :
+ Proof.Hmac.finalizeSha256PPC64LE.pre (s.withRegions s.rd (finalizeNarrowWr s)) :=
+ let ⟨h₁, _, h₃, h₄, h₅, h₆, h₇, h₈, h₉⟩ := h
+ ⟨h₁, rfl, h₃, h₄.sub_right (Region.sub_of_ble rfl), h₅.sub_right (Region.sub_of_ble rfl), h₆, h₇,
+ h₈, h₉.sub_right (Region.sub_of_ble rfl)⟩
+
+/-- A state satisfying `finalizeWide.pre`. -/
+def finalizeWideSat : State :=
+ { Proof.Hmac.PPC64LE.Finalize.sat with wr := [⟨0x1000, 96⟩, ⟨0x3000, 688⟩] }
+
+theorem finalizeWide_implies :
+ finalizeWide.Implies (Spec.Hmac.finalizeSha256Contract PPC64LE.abi 96) := by
+ sig_implies [Spec.Hmac.finalizeSha256Contract, Spec.Hmac.finalizeSha256Sig, finalizeWide,
+ Proof.Hmac.finalizeSha256PPC64LE, PPC64LE.abi, PPC64LE.argRegs]
+ [finalizeWideSat, Proof.Hmac.PPC64LE.Finalize.sat] using finalizeWideSat
+
+theorem finalize :
+ Verified PPC64LE.target Impl.Hmac.PPC64LE.finalize
+ (Spec.Hmac.finalizeSha256Contract PPC64LE.abi 96) :=
+ have hsat := finalizeWide_implies.sat_left
+ (Verified.widen Proof.Hmac.PPC64LE.Finalize.finalize_verified finalizeNarrowWr finalizeWide_pre
+ (fun _ h => by
+ obtain ⟨_, h₂, _⟩ := h
+ rw [h₂]; exact .cons (Region.prefix_of_ble rfl) (.cons (Region.prefix_of_ble rfl) .nil))
+ (fun _ _ _ h => h) (fun _ _ _ _ h => h) hsat).of_implies finalizeWide_implies
+
+end VG.Proof.Hmac.PPC64LE.Shared
diff --git a/src/asm/powerpc64le/ct.rs b/src/asm/powerpc64le/ct.rs
new file mode 100644
index 000000000..017082d00
--- /dev/null
+++ b/src/asm/powerpc64le/ct.rs
@@ -0,0 +1,46 @@
+// @generated from lean/VerifiedGarbage/Artifacts.lean by lean/Emit.lean. DO NOT EDIT.
+//! Verified `ct` functions for `powerpc64le`.
+#![allow(dead_code)]
+
+/// Compares two byte strings in constant time: returns 1 if the `a_len` bytes at `a` are the `b_len` bytes at `b` (so byte strings of different lengths are unequal), and 0 otherwise. Writes no memory.
+///
+/// Contract: `VG.Spec.Ct.eqContract`. Constant time: only the pointers, `a_len` and `b_len` may affect timing, not the bytes compared; only the result depends on them.
+///
+/// # Safety
+///
+/// * `a` must be valid for reads of `a_len` bytes.
+/// * `b` must be valid for reads of `b_len` bytes.
+/// * Neither `a` nor `b` may wrap around the end of the address space (no Rust object does).
+#[unsafe(naked)]
+pub(crate) unsafe extern "C" fn vg_ct_eq(a: *const u8, a_len: usize, b: *const u8, b_len: usize) -> u32 {
+ core::arch::naked_asm!(
+ "li %r7, 0",
+ "subf %r9, %r6, %r4",
+ "cmpldi %cr0, %r9, 0",
+ "beq %cr0, 20f",
+ "li %r3, 0",
+ "b 21f",
+ "20:",
+ "li %r8, 0",
+ "cmpldi %cr0, %r4, 0",
+ "beq %cr0, 22f",
+ "24:",
+ "add %r10, %r3, %r8",
+ "lbz %r11, 0(%r10)",
+ "add %r10, %r5, %r8",
+ "lbz %r12, 0(%r10)",
+ "xor %r11, %r11, %r12",
+ "or %r7, %r7, %r11",
+ "addi %r8, %r8, 1",
+ "subf %r9, %r4, %r8",
+ "cmpldi %cr0, %r9, 0",
+ "bne %cr0, 24b",
+ "b 23f",
+ "22:",
+ "23:",
+ "addi %r7, %r7, -1",
+ "rldicl %r3, %r7, 1, 63",
+ "21:",
+ "blr",
+ )
+}
diff --git a/src/asm/powerpc64le/hmac_sha256.rs b/src/asm/powerpc64le/hmac_sha256.rs
new file mode 100644
index 000000000..9f6c0c405
--- /dev/null
+++ b/src/asm/powerpc64le/hmac_sha256.rs
@@ -0,0 +1,225 @@
+// @generated from lean/VerifiedGarbage/Artifacts.lean by lean/Emit.lean. DO NOT EDIT.
+//! Verified `hmac_sha256` functions for `powerpc64le`.
+#![allow(dead_code)]
+
+/// Starts an HMAC-SHA-256 computation with a key of at most 64 bytes: makes the SHA-256 streaming state `*inner` represent `K₀ ⊕ ipad` and `*outer` represent `K₀ ⊕ opad`, where `K₀` is the `key_len` bytes at `key` padded with zeros to 64 bytes (FIPS 198-1). The text is then absorbed with `vg_sha256_update` on `*inner` (its `count` starting at 64), and the MAC computed with `vg_hmac_sha256_finalize`.
+///
+/// Contract: `VG.Spec.Hmac.initSha256Contract`. Constant time: only the pointers and `key_len` may affect timing, not the key.
+///
+/// # Safety
+///
+/// * `inner` must be valid for reads and writes of 96 bytes.
+/// * `outer` must be valid for reads and writes of 96 bytes.
+/// * `key` must be valid for reads of `key_len` bytes.
+/// * `scratch` must be valid for reads and writes of 608 bytes.
+/// * `key_len` must be at most 64.
+/// * The contents of `scratch` on return are unspecified.
+/// * `inner`, `outer` and `scratch` must not overlap each other or `key` (distinct Rust objects never do).
+/// * None of `inner`, `outer`, `key` and `scratch` may overlap the 48 bytes of stack below the stack pointer, or wrap around the end of the address space (no Rust object does).
+#[unsafe(naked)]
+pub(crate) unsafe extern "C" fn vg_hmac_sha256_init(inner: *mut [u8; 96], outer: *mut [u8; 96], key: *const u8, key_len: usize, scratch: *mut [u64; 76]) {
+ core::arch::naked_asm!(
+ "mflr %r0",
+ "stdu %r1, -48(%r1)",
+ "std %r0, 32(%r1)",
+ "std %r26, 112(%r7)",
+ "std %r27, 120(%r7)",
+ "std %r28, 128(%r7)",
+ "std %r29, 136(%r7)",
+ "std %r30, 144(%r7)",
+ "std %r31, 152(%r7)",
+ "addi %r26, %r3, 0",
+ "addi %r27, %r7, 0",
+ "addi %r28, %r4, 0",
+ "addi %r29, %r5, 0",
+ "addi %r30, %r6, 0",
+ "lis %r8, 27145",
+ "ori %r8, %r8, 58983",
+ "stw %r8, 0(%r26)",
+ "lis %r8, -17561",
+ "ori %r8, %r8, 44677",
+ "stw %r8, 4(%r26)",
+ "lis %r8, 15470",
+ "ori %r8, %r8, 62322",
+ "stw %r8, 8(%r26)",
+ "lis %r8, -23217",
+ "ori %r8, %r8, 62778",
+ "stw %r8, 12(%r26)",
+ "lis %r8, 20750",
+ "ori %r8, %r8, 21119",
+ "stw %r8, 16(%r26)",
+ "lis %r8, -25851",
+ "ori %r8, %r8, 26764",
+ "stw %r8, 20(%r26)",
+ "lis %r8, 8067",
+ "ori %r8, %r8, 55723",
+ "stw %r8, 24(%r26)",
+ "lis %r8, 23520",
+ "ori %r8, %r8, 52505",
+ "stw %r8, 28(%r26)",
+ "lis %r8, 27145",
+ "ori %r8, %r8, 58983",
+ "stw %r8, 0(%r28)",
+ "lis %r8, -17561",
+ "ori %r8, %r8, 44677",
+ "stw %r8, 4(%r28)",
+ "lis %r8, 15470",
+ "ori %r8, %r8, 62322",
+ "stw %r8, 8(%r28)",
+ "lis %r8, -23217",
+ "ori %r8, %r8, 62778",
+ "stw %r8, 12(%r28)",
+ "lis %r8, 20750",
+ "ori %r8, %r8, 21119",
+ "stw %r8, 16(%r28)",
+ "lis %r8, -25851",
+ "ori %r8, %r8, 26764",
+ "stw %r8, 20(%r28)",
+ "lis %r8, 8067",
+ "ori %r8, %r8, 55723",
+ "stw %r8, 24(%r28)",
+ "lis %r8, 23520",
+ "ori %r8, %r8, 52505",
+ "stw %r8, 28(%r28)",
+ "li %r12, 54",
+ "li %r0, 92",
+ "li %r31, 0",
+ "cmpldi %cr0, %r30, 0",
+ "beq %cr0, 20f",
+ "22:",
+ "lbz %r8, 0(%r29)",
+ "xor %r9, %r8, %r12",
+ "add %r11, %r26, %r31",
+ "stb %r9, 32(%r11)",
+ "xor %r9, %r8, %r0",
+ "add %r11, %r28, %r31",
+ "stb %r9, 32(%r11)",
+ "addi %r29, %r29, 1",
+ "addi %r31, %r31, 1",
+ "addi %r30, %r30, -1",
+ "cmpldi %cr0, %r30, 0",
+ "bne %cr0, 22b",
+ "b 21f",
+ "20:",
+ "21:",
+ "li %r10, 64",
+ "subf %r10, %r31, %r10",
+ "cmpldi %cr0, %r10, 0",
+ "beq %cr0, 23f",
+ "25:",
+ "add %r11, %r26, %r31",
+ "stb %r12, 32(%r11)",
+ "add %r11, %r28, %r31",
+ "stb %r0, 32(%r11)",
+ "addi %r31, %r31, 1",
+ "addi %r10, %r10, -1",
+ "cmpldi %cr0, %r10, 0",
+ "bne %cr0, 25b",
+ "b 24f",
+ "23:",
+ "24:",
+ "addi %r4, %r26, 32",
+ "addi %r3, %r26, 0",
+ "li %r5, 1",
+ "addi %r6, %r27, 0",
+ "bl {vg_sha256_compress}",
+ "addi %r26, %r28, 0",
+ "addi %r4, %r26, 32",
+ "addi %r3, %r26, 0",
+ "li %r5, 1",
+ "addi %r6, %r27, 0",
+ "bl {vg_sha256_compress}",
+ "ld %r26, 112(%r27)",
+ "ld %r28, 128(%r27)",
+ "ld %r29, 136(%r27)",
+ "ld %r30, 144(%r27)",
+ "ld %r31, 152(%r27)",
+ "ld %r27, 120(%r27)",
+ "ld %r0, 32(%r1)",
+ "addi %r1, %r1, 48",
+ "mtlr %r0",
+ "blr",
+ vg_sha256_compress = sym super::sha256::vg_sha256_compress,
+ )
+}
+
+/// Finishes an HMAC-SHA-256 computation: if, for a 64-byte key `K₀` and a text, the SHA-256 streaming state `*inner` represents `(K₀ ⊕ ipad) ‖ text`, of `count` bytes (modulo 2⁶⁴), and `*outer` represents `K₀ ⊕ opad`, leaves the HMAC-SHA-256 of the text under `K₀` in bytes 176 to 207 of `*scratch`.
+///
+/// Contract: `VG.Spec.Hmac.finalizeSha256Contract`. Constant time: only the pointers and `count` may affect timing, not the states.
+///
+/// # Safety
+///
+/// * `inner` must be valid for reads and writes of 96 bytes.
+/// * `outer` must be valid for reads of 96 bytes.
+/// * `scratch` must be valid for reads and writes of 688 bytes.
+/// * The contents of `inner` on return are unspecified.
+/// * The contents of `scratch` on return are unspecified, apart from the MAC.
+/// * `inner` and `scratch` must not overlap each other or `outer` (distinct Rust objects never do).
+/// * None of `inner`, `outer` and `scratch` may overlap the 96 bytes of stack below the stack pointer, or wrap around the end of the address space (no Rust object does).
+#[unsafe(naked)]
+pub(crate) unsafe extern "C" fn vg_hmac_sha256_finalize(inner: *mut [u8; 96], outer: *const [u8; 96], count: u64, scratch: *mut [u64; 86]) {
+ core::arch::naked_asm!(
+ "mflr %r0",
+ "stdu %r1, -48(%r1)",
+ "std %r0, 32(%r1)",
+ "std %r24, 160(%r6)",
+ "std %r25, 168(%r6)",
+ "addi %r24, %r3, 0",
+ "addi %r25, %r6, 0",
+ "lwz %r8, 0(%r4)",
+ "stw %r8, 208(%r6)",
+ "lwz %r8, 4(%r4)",
+ "stw %r8, 212(%r6)",
+ "lwz %r8, 8(%r4)",
+ "stw %r8, 216(%r6)",
+ "lwz %r8, 12(%r4)",
+ "stw %r8, 220(%r6)",
+ "lwz %r8, 16(%r4)",
+ "stw %r8, 224(%r6)",
+ "lwz %r8, 20(%r4)",
+ "stw %r8, 228(%r6)",
+ "lwz %r8, 24(%r4)",
+ "stw %r8, 232(%r6)",
+ "lwz %r8, 28(%r4)",
+ "stw %r8, 236(%r6)",
+ "addi %r4, %r5, 0",
+ "addi %r5, %r6, 176",
+ "bl {vg_sha256_finalize}",
+ "lwz %r8, 208(%r25)",
+ "stw %r8, 0(%r24)",
+ "lwz %r8, 212(%r25)",
+ "stw %r8, 4(%r24)",
+ "lwz %r8, 216(%r25)",
+ "stw %r8, 8(%r24)",
+ "lwz %r8, 220(%r25)",
+ "stw %r8, 12(%r24)",
+ "lwz %r8, 224(%r25)",
+ "stw %r8, 16(%r24)",
+ "lwz %r8, 228(%r25)",
+ "stw %r8, 20(%r24)",
+ "lwz %r8, 232(%r25)",
+ "stw %r8, 24(%r24)",
+ "lwz %r8, 236(%r25)",
+ "stw %r8, 28(%r24)",
+ "ld %r8, 176(%r25)",
+ "std %r8, 32(%r24)",
+ "ld %r8, 184(%r25)",
+ "std %r8, 40(%r24)",
+ "ld %r8, 192(%r25)",
+ "std %r8, 48(%r24)",
+ "ld %r8, 200(%r25)",
+ "std %r8, 56(%r24)",
+ "addi %r3, %r24, 0",
+ "li %r4, 96",
+ "addi %r5, %r25, 176",
+ "addi %r6, %r25, 0",
+ "bl {vg_sha256_finalize}",
+ "ld %r24, 160(%r25)",
+ "ld %r25, 168(%r25)",
+ "ld %r0, 32(%r1)",
+ "addi %r1, %r1, 48",
+ "mtlr %r0",
+ "blr",
+ vg_sha256_finalize = sym super::sha256::vg_sha256_finalize,
+ )
+}
diff --git a/src/asm/powerpc64le/mod.rs b/src/asm/powerpc64le/mod.rs
index e13f02637..38352c428 100644
--- a/src/asm/powerpc64le/mod.rs
+++ b/src/asm/powerpc64le/mod.rs
@@ -4,6 +4,12 @@
#[rustfmt::skip]
pub(crate) mod chacha20;
+#[rustfmt::skip]
+pub(crate) mod ct;
+
+#[rustfmt::skip]
+pub(crate) mod hmac_sha256;
+
#[rustfmt::skip]
pub(crate) mod sha256;
diff --git a/src/ct.rs b/src/ct.rs
index 1c05bc6f4..9f3b91708 100644
--- a/src/ct.rs
+++ b/src/ct.rs
@@ -5,7 +5,8 @@
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm",
- target_arch = "x86"
+ target_arch = "x86",
+ all(target_arch = "powerpc64", target_endian = "little")
))]
use crate::arch::ct::vg_ct_eq;
diff --git a/src/hmac/mod.rs b/src/hmac/mod.rs
index 61e9d12b4..95f36e23a 100644
--- a/src/hmac/mod.rs
+++ b/src/hmac/mod.rs
@@ -12,7 +12,8 @@
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm",
- target_arch = "x86"
+ target_arch = "x86",
+ all(target_arch = "powerpc64", target_endian = "little")
))]
use crate::hashes::HashFunction;
diff --git a/src/hmac/sha256.rs b/src/hmac/sha256.rs
index 6dcf8ff28..369502596 100644
--- a/src/hmac/sha256.rs
+++ b/src/hmac/sha256.rs
@@ -12,18 +12,24 @@
//! `vg_sha256_finalize_shani`, or the `_avx2` ones. On AArch64, the `_sha2`
//! variants use the SHA-256 instructions through the same generic code.
//!
-//! On ARMv7 and x86, their contracts are `VG.Spec.Hmac.initSha256Contract`
-//! and `VG.Spec.Hmac.finalizeSha256Contract` (or `finalizeSha256OutContract`
-//! on the 32-bit targets), with `VG.Spec.Sha256.updateContract`.
+//! On ARMv7, x86 and PPC64LE, their contracts are
+//! `VG.Spec.Hmac.initSha256Contract` and `VG.Spec.Hmac.finalizeSha256Contract`
+//! (PPC64LE, which leaves the MAC in `scratch`) or `finalizeSha256OutContract`
+//! (the 32-bit targets), with `VG.Spec.Sha256.updateContract`.
#![cfg(any(
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm",
- target_arch = "x86"
+ target_arch = "x86",
+ all(target_arch = "powerpc64", target_endian = "little")
))]
-#[cfg(any(target_arch = "arm", target_arch = "x86"))]
+#[cfg(any(
+ target_arch = "arm",
+ target_arch = "x86",
+ all(target_arch = "powerpc64", target_endian = "little")
+))]
use super::{HmacHash, sealed};
#[cfg(target_arch = "x86_64")]
use crate::arch::hmac_sha256::{
@@ -77,7 +83,11 @@ impl super::Hmac {
/// An HMAC-SHA-256 computation: the SHA-256 streaming states for the inner
/// hash, which represents `(K₀ ⊕ ipad) ‖ text`, and the outer one, which
/// represents `K₀ ⊕ opad`, and the length of the inner message.
-#[cfg(any(target_arch = "arm", target_arch = "x86"))]
+#[cfg(any(
+ target_arch = "arm",
+ target_arch = "x86",
+ all(target_arch = "powerpc64", target_endian = "little")
+))]
#[doc(hidden)]
#[derive(Clone)]
pub struct Sha256HmacState {
@@ -89,7 +99,11 @@ pub struct Sha256HmacState {
backend: Sha256Backend,
}
-#[cfg(any(target_arch = "arm", target_arch = "x86"))]
+#[cfg(any(
+ target_arch = "arm",
+ target_arch = "x86",
+ all(target_arch = "powerpc64", target_endian = "little")
+))]
impl Drop for Sha256HmacState {
/// Wipes the streaming states, which represent the key.
fn drop(&mut self) {
@@ -98,10 +112,18 @@ impl Drop for Sha256HmacState {
}
}
-#[cfg(any(target_arch = "arm", target_arch = "x86"))]
+#[cfg(any(
+ target_arch = "arm",
+ target_arch = "x86",
+ all(target_arch = "powerpc64", target_endian = "little")
+))]
impl sealed::Sealed for Sha256 {}
-#[cfg(any(target_arch = "arm", target_arch = "x86"))]
+#[cfg(any(
+ target_arch = "arm",
+ target_arch = "x86",
+ all(target_arch = "powerpc64", target_endian = "little")
+))]
impl HmacHash for Sha256 {
type State = Sha256HmacState;
@@ -155,6 +177,27 @@ impl HmacHash for Sha256 {
state.count = state.count.wrapping_add(data.len() as u64);
}
+ #[cfg(all(target_arch = "powerpc64", target_endian = "little"))]
+ fn hmac_finalize(mut state: Sha256HmacState) -> [u8; 32] {
+ let mut scratch = [0u64; 86];
+ // SAFETY: `state.inner` is valid for reads and writes of 96 bytes,
+ // `state.outer` for reads of 96 bytes and `scratch` for reads and
+ // writes of 688 bytes; they are distinct objects, so they do not
+ // overlap each other or the call's stack frame, nor wrap around the
+ // address space. `state.inner` represents `(K₀ ⊕ ipad) ‖ text`, of
+ // `state.count` bytes, and `state.outer` represents `K₀ ⊕ opad`.
+ unsafe {
+ vg_hmac_sha256_finalize(&mut state.inner, &state.outer, state.count, &mut scratch)
+ };
+ // The MAC is in bytes 176 to 207 of `scratch`.
+ let mut mac = [0u8; 32];
+ for (out, word) in mac.as_chunks_mut::<8>().0.iter_mut().zip(&scratch[22..26]) {
+ *out = word.to_ne_bytes();
+ }
+ mac
+ }
+
+ #[cfg(any(target_arch = "arm", target_arch = "x86"))]
fn hmac_finalize(mut state: Sha256HmacState) -> [u8; 32] {
let mut mac = [0u8; 32];
let mut scratch = [0u64; 86];
diff --git a/tests/wycheproof/hmac.rs b/tests/wycheproof/hmac.rs
index 84bff2363..ff98a5e6b 100644
--- a/tests/wycheproof/hmac.rs
+++ b/tests/wycheproof/hmac.rs
@@ -5,7 +5,8 @@
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm",
- target_arch = "x86"
+ target_arch = "x86",
+ all(target_arch = "powerpc64", target_endian = "little")
))]
use serde::Deserialize;
diff --git a/tests/wycheproof/hmac_sha256.rs b/tests/wycheproof/hmac_sha256.rs
index 8b5f1809e..3f468e04a 100644
--- a/tests/wycheproof/hmac_sha256.rs
+++ b/tests/wycheproof/hmac_sha256.rs
@@ -4,7 +4,8 @@
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm",
- target_arch = "x86"
+ target_arch = "x86",
+ all(target_arch = "powerpc64", target_endian = "little")
))]
use verified_garbage::hashes::sha256::Sha256;