diff --git a/README.md b/README.md
index 25b339af4..97f86bd67 100644
--- a/README.md
+++ b/README.md
@@ -125,6 +125,22 @@ yours to keep:
+| SHA-224 |
+
+✅ |
+
+❌ |
+
+❌ |
+
+❌ |
+
+❌ |
+
+
+
+
+
| SHA-256 |
✅ |
diff --git a/docs/algorithms/sha224.toml b/docs/algorithms/sha224.toml
new file mode 100644
index 000000000..4b3c87af3
--- /dev/null
+++ b/docs/algorithms/sha224.toml
@@ -0,0 +1,5 @@
+name = "SHA-224"
+family = "Hashes"
+specs = ["Sha256"]
+modules = ["src/hashes/sha224.rs"]
+asm = ["sha256"]
diff --git a/lean/VerifiedGarbage/Proof/Hmac/Arm/Finalize.lean b/lean/VerifiedGarbage/Proof/Hmac/Arm/Finalize.lean
index 72b2e27aa..4fd8c8e7c 100644
--- a/lean/VerifiedGarbage/Proof/Hmac/Arm/Finalize.lean
+++ b/lean/VerifiedGarbage/Proof/Hmac/Arm/Finalize.lean
@@ -167,7 +167,7 @@ theorem fin_ok {s₀ : State} (hp : Pre s₀) {s : State} (hrd : s.rd = s₀.rd)
· exact ⟨scR s₀, by simp, 0, by simp, by simp⟩
· intro s' h₁ h₂ h₃ h₄ hg hpost
simp only [Proof.Sha256.finalizeArm, State.withRegions_gpr, State.withRegions_mem, h0, e0] at hpost
- exact hQ s' h₁ h₂ h₃ h₄ (by rw [hg _ r0_ok (by decide), h0]) hpost
+ exact hQ s' h₁ h₂ h₃ h₄ (by rw [hg _ r0_ok (by decide), h0]) fun m hr hc => hpost Spec.Sha256.H0 m hr hc
/-! ## Saving the outer hash value -/
diff --git a/lean/VerifiedGarbage/Proof/Pbkdf2/Md/AArch64/Hashes/Sha256.lean b/lean/VerifiedGarbage/Proof/Pbkdf2/Md/AArch64/Hashes/Sha256.lean
index 0f75c37f0..38e6e8007 100644
--- a/lean/VerifiedGarbage/Proof/Pbkdf2/Md/AArch64/Hashes/Sha256.lean
+++ b/lean/VerifiedGarbage/Proof/Pbkdf2/Md/AArch64/Hashes/Sha256.lean
@@ -98,13 +98,17 @@ def streamOK : Hmac.Generic.AArch64.HashOK (hash v).stream where
hW := by simp only [hash, Hash.stream] <;> decide
repr := sha256_repr
init := Proof.Sha256.AArch64.Stream.init_verified
- upd := v.update_verified
+ upd := v.update_verified.of_implies
+ { pre := fun _ h => h
+ post := fun _ _ _ h m hr hc => h Spec.Sha256.H0 m hr hc
+ pub := fun _ _ _ _ h => h
+ sat := v.update_verified.2.2 }
fin := v.finalize_verified.of_implies
{ pre := fun _ h => h
post := fun s s' _ h m hr _ hc => by
show List.take 32 (Spec.Sha256.bytesAt s'.mem _ 32) = _
rw [List.take_of_length_le (by simp [Spec.Sha256.bytesAt])]
- exact h m hr hc
+ exact h Spec.Sha256.H0 m hr hc
pub := fun _ _ _ _ h => h
sat := v.finalize_verified.2.2 }
initDepth := by simp only [hash, Hash.stream] <;> decide +kernel
diff --git a/lean/VerifiedGarbage/Proof/Sha256/AArch64/Contract.lean b/lean/VerifiedGarbage/Proof/Sha256/AArch64/Contract.lean
index 46279def1..8666c554a 100644
--- a/lean/VerifiedGarbage/Proof/Sha256/AArch64/Contract.lean
+++ b/lean/VerifiedGarbage/Proof/Sha256/AArch64/Contract.lean
@@ -56,8 +56,8 @@ open AArch64 in
/-- AArch64 contract for
`vg_sha256_update(state: *mut [u8; 96], count: u64, data: *const u8, len: usize, scratch: *mut [u64; 20])`:
if the streaming state at `state` represents a message `m` of `count` bytes
-(modulo 2⁶⁴), then afterwards it represents `m` followed by the `len` bytes at
-`data`.
+(modulo 2⁶⁴), hashed from any initial hash value `iv`, then afterwards it
+represents `m` followed by the `len` bytes at `data`, from `iv`.
The code may read `data` (`len` bytes) and read and write `state` (96
bytes) and `scratch` (160 bytes, whose contents on exit are unspecified).
@@ -73,8 +73,8 @@ def updateAArch64 : Contract AArch64.isa where
s.rd = [data] ∧ s.wr = [state, scratch] ∧
state.Disjoint scratch ∧ data.Disjoint state ∧ data.Disjoint scratch ∧
16 ≤ s.sp.toNat ∧ stack.Disjoint state ∧ stack.Disjoint data ∧ stack.Disjoint scratch
- post s s' := ∀ m, Repr s.mem (s.gpr .x0) m → s.gpr .x1 = BitVec.ofNat 64 m.length →
- Repr s'.mem (s.gpr .x0) (m ++ bytesAt s.mem (s.gpr .x2) (s.gpr .x3).toNat)
+ post s s' := ∀ iv m, ReprFrom iv s.mem (s.gpr .x0) m → s.gpr .x1 = BitVec.ofNat 64 m.length →
+ ReprFrom iv s'.mem (s.gpr .x0) (m ++ bytesAt s.mem (s.gpr .x2) (s.gpr .x3).toNat)
pub s₁ s₂ :=
s₁.gpr .x0 = s₂.gpr .x0 ∧ s₁.gpr .x1 = s₂.gpr .x1 ∧ s₁.gpr .x2 = s₂.gpr .x2 ∧
s₁.gpr .x3 = s₂.gpr .x3 ∧ s₁.gpr .x4 = s₂.gpr .x4 ∧ s₁.sp = s₂.sp
@@ -83,7 +83,8 @@ open AArch64 in
/-- AArch64 contract for
`vg_sha256_finalize(state: *mut [u8; 96], count: u64, out: *mut [u8; 32], scratch: *mut [u64; 20])`:
if the streaming state at `state` represents a message `m` of `count` bytes
-(modulo 2⁶⁴), writes the SHA-256 digest of `m` to `out`.
+(modulo 2⁶⁴), hashed from the initial hash value `iv`, writes the final hash
+value of `m` from `iv` to `out` (the SHA-256 digest if `iv` is `H0`).
The code may read and write `state` (96 bytes, whose contents on exit are
unspecified), `out` (32 bytes) and `scratch` (160 bytes, whose contents on
@@ -99,8 +100,8 @@ def finalizeAArch64 : Contract AArch64.isa where
s.rd = [] ∧ s.wr = [state, out, scratch] ∧
state.Disjoint out ∧ state.Disjoint scratch ∧ out.Disjoint scratch ∧
16 ≤ s.sp.toNat ∧ stack.Disjoint state ∧ stack.Disjoint out ∧ stack.Disjoint scratch
- post s s' := ∀ m, Repr s.mem (s.gpr .x0) m → s.gpr .x1 = BitVec.ofNat 64 m.length →
- bytesAt s'.mem (s.gpr .x2) 32 = Spec.Sha256.hash m
+ post s s' := ∀ iv m, ReprFrom iv s.mem (s.gpr .x0) m → s.gpr .x1 = BitVec.ofNat 64 m.length →
+ bytesAt s'.mem (s.gpr .x2) 32 = Spec.Sha256.finalHash iv m
pub s₁ s₂ :=
s₁.gpr .x0 = s₂.gpr .x0 ∧ s₁.gpr .x1 = s₂.gpr .x1 ∧ s₁.gpr .x2 = s₂.gpr .x2 ∧
s₁.gpr .x3 = s₂.gpr .x3 ∧ s₁.sp = s₂.sp
diff --git a/lean/VerifiedGarbage/Proof/Sha256/AArch64/Stream/Md.lean b/lean/VerifiedGarbage/Proof/Sha256/AArch64/Stream/Md.lean
index 1201fa1b0..71c27ce1e 100644
--- a/lean/VerifiedGarbage/Proof/Sha256/AArch64/Stream/Md.lean
+++ b/lean/VerifiedGarbage/Proof/Sha256/AArch64/Stream/Md.lean
@@ -46,7 +46,7 @@ theorem update_verified : Verified AArch64.target Impl.Sha256.AArch64.Stream.upd
(VG.Taint.constantTime (A := taint) (Taint.ofRegs [.x0, .x1, .x2, .x3, .x4])
(fun _ _ _ _ hp => MdStream.AArch64.Update.agree₀ hp) (by taint_decide))
(instrs_keeps (by lit_decide)) (by lit_decide)
- Verified.of_implies h ⟨fun _ h => h, fun _ _ _ h m hr hc => h Spec.Sha256.H0 m hr hc, fun _ _ _ _ h => h, h.2.2⟩
+ Verified.of_implies h ⟨fun _ h => h, fun _ _ _ h iv m hr hc => h iv m hr hc, fun _ _ _ _ h => h, h.2.2⟩
/-- A state satisfying `update`'s precondition. -/
abbrev sat : State := MdStream.AArch64.Update.sat params
@@ -61,7 +61,7 @@ theorem finalize_verified : Verified AArch64.target Impl.Sha256.AArch64.Stream.f
(fun _ _ _ _ hp => MdStream.AArch64.Finalize.agree₀ hp) (by taint_decide))
(instrs_keeps (by lit_decide)) (by lit_decide)
Verified.of_implies h
- ⟨fun _ h => h, fun _ _ _ h m hr hc => h Spec.Sha256.H0 m hr trivial hc, fun _ _ _ _ h => h, h.2.2⟩
+ ⟨fun _ h => h, fun _ _ _ h iv m hr hc => h iv m hr trivial hc, fun _ _ _ _ h => h, h.2.2⟩
/-- A state satisfying `finalize`'s precondition. -/
abbrev sat : State := MdStream.AArch64.Finalize.sat params
diff --git a/lean/VerifiedGarbage/Proof/Sha256/AArch64/Variant.lean b/lean/VerifiedGarbage/Proof/Sha256/AArch64/Variant.lean
index efbbb427b..21e2e4ac0 100644
--- a/lean/VerifiedGarbage/Proof/Sha256/AArch64/Variant.lean
+++ b/lean/VerifiedGarbage/Proof/Sha256/AArch64/Variant.lean
@@ -46,13 +46,13 @@ theorem callee : CalleeOk (P := Stream.params) md v.code := ⟨v.verified.1, v.n
theorem update_verified : Verified AArch64.target v.update Proof.Sha256.updateAArch64 := by
have h := MdStream.AArch64.Update.verified Stream.dims v.callee v.updateCT v.updateKeeps
(by rw [v.updateDepth]; decide)
- exact h.of_implies ⟨fun _ h => h, fun _ _ _ h m hr hc => h Spec.Sha256.H0 m hr hc,
+ exact h.of_implies ⟨fun _ h => h, fun _ _ _ h iv m hr hc => h iv m hr hc,
fun _ _ _ _ h => h, h.2.2⟩
theorem finalize_verified : Verified AArch64.target v.finalize Proof.Sha256.finalizeAArch64 := by
have h := MdStream.AArch64.Finalize.verified Stream.dims Stream.shape v.callee v.finalizeCT
v.finalizeKeeps (by rw [v.finalizeDepth]; decide)
- exact h.of_implies ⟨fun _ h => h, fun _ _ _ h m hr hc => h Spec.Sha256.H0 m hr trivial hc,
+ exact h.of_implies ⟨fun _ h => h, fun _ _ _ h iv m hr hc => h iv m hr trivial hc,
fun _ _ _ _ h => h, h.2.2⟩
end Compress
diff --git a/lean/VerifiedGarbage/Proof/Sha256/Arm/Contract.lean b/lean/VerifiedGarbage/Proof/Sha256/Arm/Contract.lean
index 0a255d856..883dc5d61 100644
--- a/lean/VerifiedGarbage/Proof/Sha256/Arm/Contract.lean
+++ b/lean/VerifiedGarbage/Proof/Sha256/Arm/Contract.lean
@@ -65,8 +65,8 @@ open Arm in
/-- 32-bit ARM contract for
`vg_sha256_update(state: *mut [u8; 96], count: u64, data: *const u8, len: usize, scratch: *mut [u64; 20])`:
if the streaming state at `state` represents a message `m` of `count` bytes
-(modulo 2⁶⁴), then afterwards it represents `m` followed by the `len` bytes at
-`data`.
+(modulo 2⁶⁴), hashed from any initial hash value `iv`, then afterwards it
+represents `m` followed by the `len` bytes at `data`, from `iv`.
Under AAPCS, `state` is in `r0`, `count` in `r2:r3`, and `data`, `len` and
`scratch` are the stack arguments 0, 1 and 2. The code may read those
@@ -87,8 +87,8 @@ def updateArm : Contract Arm.isa where
args.Disjoint state ∧ args.Disjoint scratch ∧
(s.gpr .r0).toNat + 96 ≤ 2 ^ 32 ∧ (stackArg s 0).toNat + (stackArg s 1).toNat ≤ 2 ^ 32 ∧
(stackArg s 2).toNat + 160 ≤ 2 ^ 32 ∧ s.sp.toNat + 12 ≤ 2 ^ 32
- post s s' := ∀ m, Repr s.mem (State.addr (s.gpr .r0)) m → countArm s = BitVec.ofNat 64 m.length →
- Repr s'.mem (State.addr (s.gpr .r0))
+ post s s' := ∀ iv m, ReprFrom iv s.mem (State.addr (s.gpr .r0)) m → countArm s = BitVec.ofNat 64 m.length →
+ ReprFrom iv s'.mem (State.addr (s.gpr .r0))
(m ++ bytesAt s.mem (State.addr (stackArg s 0)) (stackArg s 1).toNat)
pub s₁ s₂ :=
s₁.sp = s₂.sp ∧ s₁.gpr .r0 = s₂.gpr .r0 ∧ s₁.gpr .r2 = s₂.gpr .r2 ∧ s₁.gpr .r3 = s₂.gpr .r3 ∧
@@ -98,7 +98,8 @@ open Arm in
/-- 32-bit ARM contract for
`vg_sha256_finalize(state: *mut [u8; 96], count: u64, out: *mut [u8; 32], scratch: *mut [u64; 20])`:
if the streaming state at `state` represents a message `m` of `count` bytes
-(modulo 2⁶⁴), writes the SHA-256 digest of `m` to `out`.
+(modulo 2⁶⁴), hashed from the initial hash value `iv`, writes the final hash
+value of `m` from `iv` to `out` (the SHA-256 digest if `iv` is `H0`).
Under AAPCS, `state` is in `r0`, `count` in `r2:r3`, and `out` and `scratch`
are the stack arguments 0 and 1. The code may read those arguments (8 bytes
@@ -118,8 +119,8 @@ def finalizeArm : Contract Arm.isa where
args.Disjoint state ∧ args.Disjoint out ∧ args.Disjoint scratch ∧
(s.gpr .r0).toNat + 96 ≤ 2 ^ 32 ∧ (stackArg s 0).toNat + 32 ≤ 2 ^ 32 ∧
(stackArg s 1).toNat + 160 ≤ 2 ^ 32 ∧ s.sp.toNat + 8 ≤ 2 ^ 32
- post s s' := ∀ m, Repr s.mem (State.addr (s.gpr .r0)) m → countArm s = BitVec.ofNat 64 m.length →
- bytesAt s'.mem (State.addr (stackArg s 0)) 32 = Spec.Sha256.hash m
+ post s s' := ∀ iv m, ReprFrom iv s.mem (State.addr (s.gpr .r0)) m → countArm s = BitVec.ofNat 64 m.length →
+ bytesAt s'.mem (State.addr (stackArg s 0)) 32 = Spec.Sha256.finalHash iv m
pub s₁ s₂ :=
s₁.sp = s₂.sp ∧ s₁.gpr .r0 = s₂.gpr .r0 ∧ s₁.gpr .r2 = s₂.gpr .r2 ∧ s₁.gpr .r3 = s₂.gpr .r3 ∧
stackArg s₁ 0 = stackArg s₂ 0 ∧ stackArg s₁ 1 = stackArg s₂ 1
diff --git a/lean/VerifiedGarbage/Proof/Sha256/Arm/Stream/Md.lean b/lean/VerifiedGarbage/Proof/Sha256/Arm/Stream/Md.lean
index 068ffd2c3..5527c5e33 100644
--- a/lean/VerifiedGarbage/Proof/Sha256/Arm/Stream/Md.lean
+++ b/lean/VerifiedGarbage/Proof/Sha256/Arm/Stream/Md.lean
@@ -43,7 +43,7 @@ theorem update_verified : Verified Arm.target Impl.Sha256.Arm.Stream.update Proo
have h := MdStream.Arm.Update.verified (name := "vg_sha256_compress") dims callee
(VG.Taint.constantTime (A := taint) (MdStream.Arm.Update.τ₀ params)
(fun _ _ h₁ h₂ hp => MdStream.Arm.Update.agree₀ h₁ h₂ hp) (by taint_decide))
- Verified.of_implies h ⟨fun _ h => h, fun _ _ _ h m hr hc => h Spec.Sha256.H0 m hr hc, fun _ _ _ _ h => h, h.2.2⟩
+ Verified.of_implies h ⟨fun _ h => h, fun _ _ _ h iv m hr hc => h iv m hr hc, fun _ _ _ _ h => h, h.2.2⟩
/-- A state satisfying `update`'s precondition. -/
abbrev sat : State := MdStream.Arm.Update.sat params
@@ -57,7 +57,7 @@ theorem finalize_verified : Verified Arm.target Impl.Sha256.Arm.Stream.finalize
(VG.Taint.constantTime (A := taint) (MdStream.Arm.Finalize.τ₀ params)
(fun _ _ h₁ h₂ hp => MdStream.Arm.Finalize.agree₀ h₁ h₂ hp) (by taint_decide))
Verified.of_implies h
- ⟨fun _ h => h, fun _ _ _ h m hr hc => h Spec.Sha256.H0 m hr trivial hc, fun _ _ _ _ h => h, h.2.2⟩
+ ⟨fun _ h => h, fun _ _ _ h iv m hr hc => h iv m hr trivial hc, fun _ _ _ _ h => h, h.2.2⟩
/-- A state satisfying `finalize`'s precondition. -/
abbrev sat : State := MdStream.Arm.Finalize.sat params
diff --git a/lean/VerifiedGarbage/Proof/Sha256/Stream.lean b/lean/VerifiedGarbage/Proof/Sha256/Stream.lean
index b63de7fbe..b1fa38fe3 100644
--- a/lean/VerifiedGarbage/Proof/Sha256/Stream.lean
+++ b/lean/VerifiedGarbage/Proof/Sha256/Stream.lean
@@ -114,7 +114,7 @@ theorem bytesAt_writeBytes (m : Mem) (p : Addr) (r : Nat) (xs : List Byte) (h :
/-! ## `Repr` -/
theorem repr_nil {mem : Mem} {p : Addr} (h : stateAt mem p = H0) : Spec.Sha256.Repr mem p [] := by
- simp [Spec.Sha256.Repr, h, compressList_zero, bytesAt]
+ simp [Spec.Sha256.Repr, Spec.Sha256.ReprFrom, h, compressList_zero, bytesAt]
/-- Appending bytes that stay within the buffer. -/
theorem repr_append_buf {mem mem' : Mem} {p : Addr} {m xs : List Byte} (hr : Spec.Sha256.Repr mem p m)
@@ -219,7 +219,7 @@ theorem hash_eq (m : List Byte) (nt : Nat)
have hlen : (pad m).length / 64 = m.length / 64 + nt := by
rw [hp]; simp only [List.length_append, List.length_replicate, lenBytes_length, List.length_singleton]
omega
- simp only [Spec.Sha256.hash]
+ simp only [Spec.Sha256.hash, Spec.Sha256.finalHash]
rw [hlen, compressList_add, hp, compressList_append (by omega),
List.drop_append_of_le_length (by omega)]
simp only [List.append_assoc]
diff --git a/lean/VerifiedGarbage/Proof/Sha256/X86/Contract.lean b/lean/VerifiedGarbage/Proof/Sha256/X86/Contract.lean
index e2bb8ea28..9e9b7e509 100644
--- a/lean/VerifiedGarbage/Proof/Sha256/X86/Contract.lean
+++ b/lean/VerifiedGarbage/Proof/Sha256/X86/Contract.lean
@@ -6,7 +6,7 @@ import VerifiedGarbage.TCB.X86.Target
**Untrusted**: the contracts the proofs are written against; the artifacts are emitted with the shared contracts of `Spec/`, which imply these (`Contract.Implies`). The contracts of the x86 (32-bit)
implementations of the compression function and of streaming SHA-256
-(`init`/`update`/`finalize`, on the representation `Repr`), in terms of
+(`init`/`update`/`finalize`, on the representation `ReprFrom`), in terms of
`Spec/Sha256.lean`.
The shared contracts let the streaming functions write their own argument
@@ -83,8 +83,9 @@ open X86 in
`vg_sha256_update(state: *mut [u8; 96], count: u64, data: *const u8, len: usize, scratch: *mut [u64; 20])`,
whose arguments are on the stack (cdecl: `state`, the low and high words of
`count`, `data`, `len`, `scratch`): if the streaming state at `state`
-represents a message `m` of `count` bytes (modulo 2⁶⁴), then afterwards it
-represents `m` followed by the `len` bytes at `data`.
+represents a message `m` of `count` bytes (modulo 2⁶⁴), hashed from any
+initial hash value `iv`, then afterwards it represents `m` followed by the
+`len` bytes at `data`, from `iv`.
The code may read the arguments (24 bytes above the return address) and
`data` (`len` bytes), and read and write `state` (96 bytes) and `scratch`
@@ -108,8 +109,8 @@ def updateX86 : Contract X86.isa where
stack.Disjoint data ∧
(arg s 0).toNat + 96 ≤ 2 ^ 32 ∧ (arg s 3).toNat + (arg s 4).toNat ≤ 2 ^ 32 ∧
(arg s 5).toNat + 160 ≤ 2 ^ 32 ∧ 20 ≤ (s.gpr .esp).toNat ∧ (s.gpr .esp).toNat + 28 ≤ 2 ^ 32
- post s s' := ∀ m, Repr s.mem ((arg s 0).setWidth 64) m → countX86 s = BitVec.ofNat 64 m.length →
- Repr s'.mem ((arg s 0).setWidth 64)
+ post s s' := ∀ iv m, ReprFrom iv s.mem ((arg s 0).setWidth 64) m → countX86 s = BitVec.ofNat 64 m.length →
+ ReprFrom iv s'.mem ((arg s 0).setWidth 64)
(m ++ bytesAt s.mem ((arg s 3).setWidth 64) (arg s 4).toNat)
pub s₁ s₂ :=
s₁.gpr .esp = s₂.gpr .esp ∧ ∀ i < 6, arg s₁ i = arg s₂ i
@@ -119,8 +120,9 @@ open X86 in
`vg_sha256_finalize(state: *mut [u8; 96], count: u64, out: *mut [u8; 32], scratch: *mut [u64; 20])`,
whose arguments are on the stack (cdecl: `state`, the low and high words of
`count`, `out`, `scratch`): if the streaming state at `state` represents a
-message `m` of `count` bytes (modulo 2⁶⁴), writes the SHA-256 digest of `m`
-to `out`.
+message `m` of `count` bytes (modulo 2⁶⁴), hashed from the initial hash value
+`iv`, writes the final hash value of `m` from `iv` to `out` (the SHA-256
+digest if `iv` is `H0`).
The code may read and write the arguments (20 bytes above the return
address, whose contents on exit are unspecified), `state` (96 bytes, whose
@@ -145,8 +147,8 @@ def finalizeX86 : Contract X86.isa where
stack.Disjoint state ∧ stack.Disjoint out ∧ stack.Disjoint scratch ∧
(arg s 0).toNat + 96 ≤ 2 ^ 32 ∧ (arg s 3).toNat + 32 ≤ 2 ^ 32 ∧
(arg s 4).toNat + 160 ≤ 2 ^ 32 ∧ 20 ≤ (s.gpr .esp).toNat ∧ (s.gpr .esp).toNat + 24 ≤ 2 ^ 32
- post s s' := ∀ m, Repr s.mem ((arg s 0).setWidth 64) m → countX86 s = BitVec.ofNat 64 m.length →
- bytesAt s'.mem ((arg s 3).setWidth 64) 32 = Spec.Sha256.hash m
+ post s s' := ∀ iv m, ReprFrom iv s.mem ((arg s 0).setWidth 64) m → countX86 s = BitVec.ofNat 64 m.length →
+ bytesAt s'.mem ((arg s 3).setWidth 64) 32 = Spec.Sha256.finalHash iv m
pub s₁ s₂ :=
s₁.gpr .esp = s₂.gpr .esp ∧ ∀ i < 5, arg s₁ i = arg s₂ i
diff --git a/lean/VerifiedGarbage/Proof/Sha256/X86/Shared.lean b/lean/VerifiedGarbage/Proof/Sha256/X86/Shared.lean
index 98d5a4d79..d15d84ac9 100644
--- a/lean/VerifiedGarbage/Proof/Sha256/X86/Shared.lean
+++ b/lean/VerifiedGarbage/Proof/Sha256/X86/Shared.lean
@@ -210,7 +210,7 @@ theorem update_verified : Verified X86.target Impl.Sha256.X86.Stream.update Proo
rw [update_eq] at ct ⊢
have h := MdStream.X86.Update.verified (name := "vg_sha256_compress") dims callee ct
exact Verified.of_implies h
- ⟨fun _ h => h, fun _ _ _ h m hr hc => h Spec.Sha256.H0 m hr hc, fun _ _ _ _ h => h, h.2.2⟩
+ ⟨fun _ h => h, fun _ _ _ h iv m hr hc => h iv m hr hc, fun _ _ _ _ h => h, h.2.2⟩
/-- A state satisfying `update`'s precondition. -/
abbrev sat : State := MdStream.X86.Update.sat params 160
@@ -227,7 +227,7 @@ theorem finalize_verified : Verified X86.target Impl.Sha256.X86.Stream.finalize
rw [finalize_eq] at ct ⊢
have h := MdStream.X86.Finalize.verified (name := "vg_sha256_compress") dims shape callee ct
exact Verified.of_implies h
- ⟨fun _ h => h, fun _ _ _ h m hr hc => h Spec.Sha256.H0 m hr trivial hc, fun _ _ _ _ h => h, h.2.2⟩
+ ⟨fun _ h => h, fun _ _ _ h iv m hr hc => h iv m hr trivial hc, fun _ _ _ _ h => h, h.2.2⟩
/-- A state satisfying `finalize`'s precondition. -/
abbrev sat : State := MdStream.X86.Finalize.sat params 160
diff --git a/lean/VerifiedGarbage/Proof/Sha256/X86/Stream/Variant.lean b/lean/VerifiedGarbage/Proof/Sha256/X86/Stream/Variant.lean
index 824da9bd9..e1689f077 100644
--- a/lean/VerifiedGarbage/Proof/Sha256/X86/Stream/Variant.lean
+++ b/lean/VerifiedGarbage/Proof/Sha256/X86/Stream/Variant.lean
@@ -22,7 +22,7 @@ theorem update_of (ct : ConstantTime isa (updK (P := params) md 160).pre
Verified X86.target (Impl.MdStream.X86.update params name code) Proof.Sha256.updateX86 := by
have h := MdStream.X86.Update.verified (name := name) dims hcomp ct
exact Verified.of_implies h
- ⟨fun _ h => h, fun _ _ _ h m hr hc => h Spec.Sha256.H0 m hr hc,
+ ⟨fun _ h => h, fun _ _ _ h iv m hr hc => h iv m hr hc,
fun _ _ _ _ h => h, h.2.2⟩
/-- Any verified compressor gives the same SHA-256 finalization contract. -/
@@ -31,7 +31,7 @@ theorem finalize_of (ct : ConstantTime isa (finK (P := params) md 160).pre
Verified X86.target (Impl.MdStream.X86.finalize params name code) Proof.Sha256.finalizeX86 := by
have h := MdStream.X86.Finalize.verified (name := name) dims shape hcomp ct
exact Verified.of_implies h
- ⟨fun _ h => h, fun _ _ _ h m hr hc => h Spec.Sha256.H0 m hr trivial hc,
+ ⟨fun _ h => h, fun _ _ _ h iv m hr hc => h iv m hr trivial hc,
fun _ _ _ _ h => h, h.2.2⟩
end VG.Proof.Sha256.X86.Stream
diff --git a/lean/VerifiedGarbage/Proof/Sha256/X86_64/Contract.lean b/lean/VerifiedGarbage/Proof/Sha256/X86_64/Contract.lean
index 9fbdceb39..36584fdad 100644
--- a/lean/VerifiedGarbage/Proof/Sha256/X86_64/Contract.lean
+++ b/lean/VerifiedGarbage/Proof/Sha256/X86_64/Contract.lean
@@ -56,8 +56,8 @@ open X86_64 in
/-- x86-64 contract for
`vg_sha256_update(state: *mut [u8; 96], count: u64, data: *const u8, len: usize, scratch: *mut [u64; 76])`:
if the streaming state at `state` represents a message `m` of `count` bytes
-(modulo 2⁶⁴), then afterwards it represents `m` followed by the `len` bytes at
-`data`.
+(modulo 2⁶⁴), hashed from any initial hash value `iv`, then afterwards it
+represents `m` followed by the `len` bytes at `data`, from `iv`.
The code may read `data` (`len` bytes) and read and write `state` (96
bytes) and `scratch` (608 bytes, whose contents on exit are unspecified).
@@ -77,8 +77,8 @@ def updateX86_64 : Contract X86_64.isa where
state.Disjoint scratch ∧ data.Disjoint state ∧ data.Disjoint scratch ∧
ret.Disjoint state ∧ ret.Disjoint scratch ∧
stack.Disjoint state ∧ stack.Disjoint data ∧ stack.Disjoint scratch
- post s s' := ∀ m, Repr s.mem (s.gpr .rdi) m → s.gpr .rsi = BitVec.ofNat 64 m.length →
- Repr s'.mem (s.gpr .rdi) (m ++ bytesAt s.mem (s.gpr .rdx) (s.gpr .rcx).toNat)
+ post s s' := ∀ iv m, ReprFrom iv s.mem (s.gpr .rdi) m → s.gpr .rsi = BitVec.ofNat 64 m.length →
+ ReprFrom iv s'.mem (s.gpr .rdi) (m ++ bytesAt s.mem (s.gpr .rdx) (s.gpr .rcx).toNat)
pub s₁ s₂ :=
s₁.gpr .rdi = s₂.gpr .rdi ∧ s₁.gpr .rsi = s₂.gpr .rsi ∧ s₁.gpr .rdx = s₂.gpr .rdx ∧
s₁.gpr .rcx = s₂.gpr .rcx ∧ s₁.gpr .r8 = s₂.gpr .r8 ∧ s₁.gpr .rsp = s₂.gpr .rsp
@@ -87,7 +87,8 @@ open X86_64 in
/-- x86-64 contract for
`vg_sha256_finalize(state: *mut [u8; 96], count: u64, out: *mut [u8; 32], scratch: *mut [u64; 76])`:
if the streaming state at `state` represents a message `m` of `count` bytes
-(modulo 2⁶⁴), writes the SHA-256 digest of `m` to `out`.
+(modulo 2⁶⁴), hashed from the initial hash value `iv`, writes the final hash
+value of `m` from `iv` to `out` (the SHA-256 digest if `iv` is `H0`).
The code may read and write `state` (96 bytes, whose contents on exit are
unspecified), `out` (32 bytes) and `scratch` (608 bytes, whose contents on
@@ -106,8 +107,8 @@ def finalizeX86_64 : Contract X86_64.isa where
state.Disjoint out ∧ state.Disjoint scratch ∧ out.Disjoint scratch ∧
ret.Disjoint state ∧ ret.Disjoint out ∧ ret.Disjoint scratch ∧
stack.Disjoint state ∧ stack.Disjoint out ∧ stack.Disjoint scratch
- post s s' := ∀ m, Repr s.mem (s.gpr .rdi) m → s.gpr .rsi = BitVec.ofNat 64 m.length →
- bytesAt s'.mem (s.gpr .rdx) 32 = Spec.Sha256.hash m
+ post s s' := ∀ iv m, ReprFrom iv s.mem (s.gpr .rdi) m → s.gpr .rsi = BitVec.ofNat 64 m.length →
+ bytesAt s'.mem (s.gpr .rdx) 32 = Spec.Sha256.finalHash iv m
pub s₁ s₂ :=
s₁.gpr .rdi = s₂.gpr .rdi ∧ s₁.gpr .rsi = s₂.gpr .rsi ∧ s₁.gpr .rdx = s₂.gpr .rdx ∧
s₁.gpr .rcx = s₂.gpr .rcx ∧ s₁.gpr .rsp = s₂.gpr .rsp
diff --git a/lean/VerifiedGarbage/Proof/Sha256/X86_64/Stream/Md.lean b/lean/VerifiedGarbage/Proof/Sha256/X86_64/Stream/Md.lean
index 72db32e5b..b8ba26155 100644
--- a/lean/VerifiedGarbage/Proof/Sha256/X86_64/Stream/Md.lean
+++ b/lean/VerifiedGarbage/Proof/Sha256/X86_64/Stream/Md.lean
@@ -52,7 +52,7 @@ include hf
theorem verified_of (hm : (update f).allInstrs (fun i => !loadsMxcsr i) = true) :
Verified X86_64.target (update f) Proof.Sha256.updateX86_64 :=
have h := MdStream.X86_64.Update.verified dims taints (callee hf) hm
- Verified.of_implies h ⟨fun _ h => h, fun _ _ _ h m hr hc => h Spec.Sha256.H0 m hr hc, fun _ _ _ _ h => h, h.2.2⟩
+ Verified.of_implies h ⟨fun _ h => h, fun _ _ _ h iv m hr hc => h iv m hr hc, fun _ _ _ _ h => h, h.2.2⟩
omit hf
@@ -73,7 +73,7 @@ theorem correct {s₀ : State} (hp : MdStream.X86_64.Finalize.Pre params s₀) :
WP isa (finalize f) s₀ fun s' => gprPreserved s₀ s' ∧ Proof.Sha256.finalizeX86_64.post s₀ s' ∧
s'.gpr .rdi = s₀.gpr .rdi ∧ s'.gpr .rcx = s₀.gpr .rcx :=
(MdStream.X86_64.Finalize.correct dims shape (callee hf) hp).mono fun _ ⟨g, h, di, cx⟩ =>
- ⟨g, fun m hr hc => h Spec.Sha256.H0 m hr trivial hc, di, cx⟩
+ ⟨g, fun iv m hr hc => h iv m hr trivial hc, di, cx⟩
theorem constantTime :
ConstantTime isa Proof.Sha256.finalizeX86_64.pre Proof.Sha256.finalizeX86_64.pub (finalize f) :=
@@ -84,7 +84,7 @@ theorem verified_of (hm : (finalize f).allInstrs (fun i => !loadsMxcsr i) = true
Verified X86_64.target (finalize f) Proof.Sha256.finalizeX86_64 :=
have h := MdStream.X86_64.Finalize.verified dims shape taints (callee hf) hm
Verified.of_implies h
- ⟨fun _ h => h, fun _ _ _ h m hr hc => h Spec.Sha256.H0 m hr trivial hc, fun _ _ _ _ h => h, h.2.2⟩
+ ⟨fun _ h => h, fun _ _ _ h iv m hr hc => h iv m hr trivial hc, fun _ _ _ _ h => h, h.2.2⟩
omit hf
diff --git a/lean/VerifiedGarbage/Spec/Sha256.lean b/lean/VerifiedGarbage/Spec/Sha256.lean
index 0f48217c4..373cf36c2 100644
--- a/lean/VerifiedGarbage/Spec/Sha256.lean
+++ b/lean/VerifiedGarbage/Spec/Sha256.lean
@@ -1,19 +1,23 @@
import VerifiedGarbage.TCB.Mem
/-!
-# SHA-256 (FIPS 180-4)
-
-**Trusted** (as every file in `Spec/`). The hash function SHA-256, transcribed
-from FIPS 180-4, *Secure Hash Standard* (August 2015); section numbers below
-refer to it. Messages are sequences of bytes (the standard allows any number
-of bits); every multi-byte quantity is big-endian (§3.1).
-
-The primitives implemented in assembly are the compression function over a
-run of whole blocks (`compressBlocks`), and the streaming (incremental)
-interface: initialize, absorb message bytes, pad and output the digest, on a
-streaming state that `Repr` relates to the message absorbed so far. Their
-contracts on each target, which say how the code's arguments and memory
-relate to these definitions, are in `Spec/Sha256/.lean`.
+# SHA-224 and SHA-256 (FIPS 180-4)
+
+**Trusted** (as every file in `Spec/`). The hash functions SHA-224 and
+SHA-256, transcribed from FIPS 180-4, *Secure Hash Standard* (August 2015);
+section numbers below refer to it. Messages are sequences of bytes (the
+standard allows any number of bits); every multi-byte quantity is big-endian
+(§3.1).
+
+Both functions share the SHA-256 compression function and differ only in the
+initial hash value and in how much of the final hash value is output (§6.2,
+§6.3). The primitives implemented in assembly are the compression function
+over a run of whole blocks (`compressBlocks`), and the streaming
+(incremental) interface: initialize with either initial hash value, absorb
+message bytes, and pad and output the final hash value, on a streaming state
+that `ReprFrom` relates to the message absorbed so far. Their contracts,
+which say how the code's arguments and memory relate to these definitions,
+are in `Spec/Sha256/Contract.lean`.
-/
namespace VG.Spec.Sha256
@@ -48,7 +52,7 @@ def ssig0 (x : Word) : Word := x.rotateRight 7 ^^^ x.rotateRight 18 ^^^ x >>> 3
/-- `σ₁(x) = ROTR¹⁷(x) ⊕ ROTR¹⁹(x) ⊕ SHR¹⁰(x)` -/
def ssig1 (x : Word) : Word := x.rotateRight 17 ^^^ x.rotateRight 19 ^^^ x >>> 10
-/-! ## Constants (§4.2.2) and the initial hash value (§5.3.3) -/
+/-! ## Constants (§4.2.2) and initial hash values (§5.3.2, §5.3.3) -/
/-- The sixty-four constants `K₀ … K₆₃`. -/
def Ks : List Word := [
@@ -64,10 +68,14 @@ def Ks : List Word := [
/-- `Kₜ` -/
def K (t : Nat) : Word := Ks.getD t 0
-/-- `H⁽⁰⁾` -/
+/-- `H⁽⁰⁾` for SHA-256 (§5.3.3). -/
def H0 : HashValue :=
#v[0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19]
+/-- `H⁽⁰⁾` for SHA-224 (§5.3.2). -/
+def H0_224 : HashValue :=
+ #v[0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4]
+
/-! ## Preprocessing (§5.1.1, §5.2.1) -/
/-- The big-endian bytes of a word. -/
@@ -77,7 +85,8 @@ def wordBytes (x : Word) : List Byte :=
/-- §5.1.1: append the bit `1`, then the least number of `0` bits that makes
the length `≡ 448 (mod 512)`, then the message length `ℓ` in bits as a 64-bit
big-endian integer. For a message of bytes, the `1` bit and the first seven
-`0` bits are the byte `0x80`. (SHA-256 is only defined for `ℓ < 2⁶⁴`.) -/
+`0` bits are the byte `0x80`. (SHA-224 and SHA-256 are only defined for
+`ℓ < 2⁶⁴`.) -/
def pad (m : List Byte) : List Byte :=
let ℓ : BitVec 64 := BitVec.ofNat 64 (8 * m.length)
m ++ [0x80] ++ List.replicate ((119 - m.length % 64) % 64) 0 ++
@@ -126,10 +135,17 @@ def compress (H : HashValue) (M : Block) : HashValue :=
def compressList (H : HashValue) (p : List Byte) (n : Nat) : HashValue :=
(List.range n).foldl (fun H i => compress H (parseBlock fun k => p.getD (64 * i + k) 0)) H
-/-- The SHA-256 digest of a message: `H⁽ᴺ⁾` as 32 big-endian bytes. -/
-def hash (m : List Byte) : List Byte :=
+/-- §6.2: the final hash value `H⁽ᴺ⁾` of a message, starting from `iv`, as
+32 big-endian bytes. -/
+def finalHash (iv : HashValue) (m : List Byte) : List Byte :=
let p := pad m
- (compressList H0 p (p.length / 64)).toList.flatMap wordBytes
+ (compressList iv p (p.length / 64)).toList.flatMap wordBytes
+
+/-- The SHA-256 digest (§6.2): all of `H⁽ᴺ⁾`, 32 bytes. -/
+def hash (m : List Byte) : List Byte := finalHash H0 m
+
+/-- The SHA-224 digest (§6.3): the left-most 224 bits of `H⁽ᴺ⁾`, 28 bytes. -/
+def sha224 (m : List Byte) : List Byte := (finalHash H0_224 m).take 28
/-! ## The compression function on memory
@@ -157,18 +173,25 @@ def bytesAt (m : Mem) (p : Addr) (n : Nat) : List Byte :=
The streaming primitives hash a message given in pieces. Their state is 96
bytes: the hash value after the message's whole blocks (stored as by
`stateAt`), followed by a 64-byte buffer holding the bytes of the message
-after its last whole block. The length of the message is not part of the
-state: the caller keeps it (in bytes, modulo 2⁶⁴) and passes it to every
-call. (Lengths are public, so it may live anywhere; and `pad` only uses the
-length modulo 2⁶⁴ bits, so this suffices even beyond SHA-256's limit of
-`ℓ < 2⁶⁴` bits.) -/
-
-/-- The streaming state at `p` (96 bytes) represents the message `m`: its hash
-value is `H⁽⁰⁾` updated with the `⌊|m| / 64⌋` whole blocks of `m`, and its
-buffer starts with the remaining `|m| mod 64` bytes of `m`. The rest of the
-buffer is unspecified. -/
-def Repr (mem : Mem) (p : Addr) (m : List Byte) : Prop :=
- stateAt mem p = compressList H0 m (m.length / 64) ∧
+after its last whole block. SHA-224 and SHA-256 share the state and differ
+only in the initial hash value it starts from, and in how much of the final
+hash value is output.
+
+The length of the message is not part of the state: the caller keeps it (in
+bytes, modulo 2⁶⁴) and passes it to every call. (Lengths are public, so it
+may live anywhere; and `pad` only uses the length modulo 2⁶⁴ bits, so this
+suffices even beyond the limit of `ℓ < 2⁶⁴` bits.) -/
+
+/-- The streaming state at `p` (96 bytes) represents the message `m`, hashed
+from the initial hash value `iv`: its hash value is `iv` updated with the
+`⌊|m| / 64⌋` whole blocks of `m`, and its buffer starts with the remaining
+`|m| mod 64` bytes of `m`. The rest of the buffer is unspecified. -/
+def ReprFrom (iv : HashValue) (mem : Mem) (p : Addr) (m : List Byte) : Prop :=
+ stateAt mem p = compressList iv m (m.length / 64) ∧
bytesAt mem (p + 32) (m.length % 64) = m.drop (64 * (m.length / 64))
+/-- The streaming state at `p` represents the message `m`, hashed by SHA-256
+(from `H0`). -/
+def Repr (mem : Mem) (p : Addr) (m : List Byte) : Prop := ReprFrom H0 mem p m
+
end VG.Spec.Sha256
diff --git a/lean/VerifiedGarbage/Spec/Sha256/Contract.lean b/lean/VerifiedGarbage/Spec/Sha256/Contract.lean
index 870695793..deb98b3c4 100644
--- a/lean/VerifiedGarbage/Spec/Sha256/Contract.lean
+++ b/lean/VerifiedGarbage/Spec/Sha256/Contract.lean
@@ -5,8 +5,9 @@ import VerifiedGarbage.TCB.Artifact
# SHA-256: the contracts, on every target
**Trusted** (as every file in `Spec/`). The contracts of the compression
-function and of streaming SHA-256 (`init`/`update`/`finalize`, on the
-representation `Repr`), in terms of `Spec/Sha256.lean`, for any target:
+function and of streaming SHA-224 and SHA-256 (`init`/`update`/`finalize`,
+on the representation `ReprFrom`), in terms of `Spec/Sha256.lean`, for any
+target:
`A` is the target's calling convention. The signatures fix where the
arguments are, the memory each function may access, disjointness, and that
the pointers and lengths are public (see `TCB/Sig.lean`); the contracts add
@@ -15,6 +16,9 @@ the postconditions and which other arguments are public. `update` and
convention allows it (`writeArgs`), to pass arguments to the code they
inline.
+SHA-224 and SHA-256 share `update` and `finalize`, which hold for a state
+hashed from any initial hash value; each has its own `init`.
+
`update` and `finalize` take the number of bytes of stack below the stack pointer that
an implementation's calls use (`stack`, see `Sig.contract`), 0 for one that
makes no call: it depends on the target, and on which functions the
@@ -71,6 +75,26 @@ def initApi : Api where
buffered partial block (`VG.Spec.Sha256.Repr`)."
safety := []
+/-- `vg_sha224_init(state: *mut [u8; 96])`, with `vg_sha256_init`'s signature
+(`initSig`): makes the streaming state at `state` represent the empty message,
+hashed from SHA-224's initial hash value `H0_224`. -/
+def init224Contract {M : ISA} (A : Abi M) : Contract M :=
+ initSig.contract A (post := fun state _ m' _ => ReprFrom H0_224 m' state [])
+
+/-- `vg_sha224_init` on every target. -/
+def init224Api : Api where
+ module := "sha256"
+ name := "vg_sha224_init"
+ sig := initSig
+ contracts := some fun A _ => init224Contract A
+ summary := "Starts a SHA-224 computation: makes the SHA-256 streaming state `*state` represent \
+ the empty message, hashed from the initial hash value of SHA-224 \
+ (`VG.Spec.Sha256.H0_224`). Continue with `vg_sha256_update` and `vg_sha256_finalize`, and \
+ take the first 28 bytes of the final hash value as the digest.\n\n\
+ Contract: `VG.Spec.Sha256.init224Contract`. The streaming state is the hash value followed by \
+ a buffered partial block (`VG.Spec.Sha256.ReprFrom`)."
+ safety := []
+
/-- `vg_sha256_update(state: *mut [u8; 96], count: u64, data: *const u8, len: usize, scratch: *mut [u64; 76])`.
`count` is public; `scratch` is working space. -/
def updateSig : Sig where
@@ -78,12 +102,13 @@ def updateSig : Sig where
("data", .slice false .u8 "len"), ("scratch", .array true .u64 76)]
/-- If the streaming state at `state` represents a message `msg` of `count`
-bytes (modulo 2⁶⁴), then afterwards it represents `msg` followed by the `len`
-bytes at `data`. The state and the data are secret. -/
+bytes (modulo 2⁶⁴), hashed from any initial hash value, then afterwards it
+represents `msg` followed by the `len` bytes at `data`, from the same one.
+The state and the data are secret. -/
def updateContract {M : ISA} (A : Abi M) (stack : Nat := 0) : Contract M :=
updateSig.contract A (post := fun state count data len _scratch m m' _ =>
- ∀ msg, Repr m state msg → count = BitVec.ofNat 64 msg.length →
- Repr m' state (msg ++ bytesAt m data len.toNat))
+ ∀ iv msg, ReprFrom iv m state msg → count = BitVec.ofNat 64 msg.length →
+ ReprFrom iv m' state (msg ++ bytesAt m data len.toNat))
(writeArgs := true)
(stack := stack)
@@ -94,9 +119,9 @@ def updateApi : Api where
sig := updateSig
writeArgs := true
contracts := some fun A stack => updateContract A stack
- summary := "Absorbs data into a SHA-256 computation: if the streaming state `*state` represents \
- a message of `count` bytes (modulo 2⁶⁴), it then represents that message followed by the `len` \
- bytes at `data`.\n\n\
+ summary := "Absorbs data into a SHA-224 or SHA-256 computation: if the streaming state `*state` \
+ represents a message of `count` bytes (modulo 2⁶⁴), it then represents that message followed \
+ by the `len` bytes at `data`.\n\n\
Contract: `VG.Spec.Sha256.updateContract`. Constant time: only the pointers, `count` and `len` \
may affect timing, not the state or the data."
safety := ["The contents of `scratch` on return are unspecified."]
@@ -109,11 +134,14 @@ def finalizeSig : Sig where
("out", .array true .u8 32), ("scratch", .array true .u64 76)]
/-- If the streaming state at `state` represents a message `msg` of `count`
-bytes (modulo 2⁶⁴), writes the SHA-256 digest of `msg` to `out`. The state is
-secret. -/
+bytes (modulo 2⁶⁴), hashed from the initial hash value `iv`, writes the final
+hash value `H⁽ᴺ⁾` of `msg` from `iv` (32 bytes; `finalHash iv msg`) to `out`:
+the SHA-256 digest of `msg` if `iv` is `H0`, and the SHA-224 digest followed
+by four more bytes if `iv` is `H0_224`. The state is secret. -/
def finalizeContract {M : ISA} (A : Abi M) (stack : Nat := 0) : Contract M :=
finalizeSig.contract A (post := fun state count out _scratch m m' _ =>
- ∀ msg, Repr m state msg → count = BitVec.ofNat 64 msg.length → bytesAt m' out 32 = hash msg)
+ ∀ iv msg, ReprFrom iv m state msg → count = BitVec.ofNat 64 msg.length →
+ bytesAt m' out 32 = finalHash iv msg)
(writeArgs := true)
(stack := stack)
@@ -124,8 +152,10 @@ def finalizeApi : Api where
sig := finalizeSig
writeArgs := true
contracts := some fun A stack => finalizeContract A stack
- summary := "Finishes a SHA-256 computation: if the streaming state `*state` represents a message \
- of `count` bytes (modulo 2⁶⁴), writes the SHA-256 digest of that message to `*out`.\n\n\
+ summary := "Finishes a SHA-224 or SHA-256 computation: if the streaming state `*state` \
+ represents a message of `count` bytes (modulo 2⁶⁴), hashed from an initial hash value, writes \
+ the final hash value `H⁽ᴺ⁾` of that message (32 bytes) to `*out`. The SHA-256 digest is all of \
+ it; the SHA-224 digest is its first 28 bytes.\n\n\
Contract: `VG.Spec.Sha256.finalizeContract`. Constant time: only the pointers and `count` may \
affect timing, not the state."
safety := [
diff --git a/src/asm/aarch64/sha256.rs b/src/asm/aarch64/sha256.rs
index 3d9336e81..f632e6240 100644
--- a/src/asm/aarch64/sha256.rs
+++ b/src/asm/aarch64/sha256.rs
@@ -3007,7 +3007,7 @@ pub(crate) unsafe extern "C" fn vg_sha256_compress_sha2(state: *mut [u32; 8], bl
)
}
-/// Absorbs data into a SHA-256 computation: if the streaming state `*state` represents a message of `count` bytes (modulo 2⁶⁴), it then represents that message followed by the `len` bytes at `data`.
+/// Absorbs data into a SHA-224 or SHA-256 computation: if the streaming state `*state` represents a message of `count` bytes (modulo 2⁶⁴), it then represents that message followed by the `len` bytes at `data`.
///
/// Contract: `VG.Spec.Sha256.updateContract`. Constant time: only the pointers, `count` and `len` may affect timing, not the state or the data.
///
@@ -3138,7 +3138,7 @@ pub(crate) unsafe extern "C" fn vg_sha256_update(state: *mut [u8; 96], count: u6
)
}
-/// Finishes a SHA-256 computation: if the streaming state `*state` represents a message of `count` bytes (modulo 2⁶⁴), writes the SHA-256 digest of that message to `*out`.
+/// Finishes a SHA-224 or SHA-256 computation: if the streaming state `*state` represents a message of `count` bytes (modulo 2⁶⁴), hashed from an initial hash value, writes the final hash value `H⁽ᴺ⁾` of that message (32 bytes) to `*out`. The SHA-256 digest is all of it; the SHA-224 digest is its first 28 bytes.
///
/// Contract: `VG.Spec.Sha256.finalizeContract`. Constant time: only the pointers and `count` may affect timing, not the state.
///
@@ -3248,7 +3248,7 @@ pub(crate) unsafe extern "C" fn vg_sha256_finalize(state: *mut [u8; 96], count:
/// The CPU features `vg_sha256_update_sha2` requires (`Artifact.features`).
pub(crate) const VG_SHA256_UPDATE_SHA2_FEATURES: &[&str] = &["sha2"];
-/// Absorbs data into a SHA-256 computation: if the streaming state `*state` represents a message of `count` bytes (modulo 2⁶⁴), it then represents that message followed by the `len` bytes at `data`.
+/// Absorbs data into a SHA-224 or SHA-256 computation: if the streaming state `*state` represents a message of `count` bytes (modulo 2⁶⁴), it then represents that message followed by the `len` bytes at `data`.
///
/// Contract: `VG.Spec.Sha256.updateContract`. Constant time: only the pointers, `count` and `len` may affect timing, not the state or the data.
///
@@ -3385,7 +3385,7 @@ pub(crate) unsafe extern "C" fn vg_sha256_update_sha2(state: *mut [u8; 96], coun
/// The CPU features `vg_sha256_finalize_sha2` requires (`Artifact.features`).
pub(crate) const VG_SHA256_FINALIZE_SHA2_FEATURES: &[&str] = &["sha2"];
-/// Finishes a SHA-256 computation: if the streaming state `*state` represents a message of `count` bytes (modulo 2⁶⁴), writes the SHA-256 digest of that message to `*out`.
+/// Finishes a SHA-224 or SHA-256 computation: if the streaming state `*state` represents a message of `count` bytes (modulo 2⁶⁴), hashed from an initial hash value, writes the final hash value `H⁽ᴺ⁾` of that message (32 bytes) to `*out`. The SHA-256 digest is all of it; the SHA-224 digest is its first 28 bytes.
///
/// Contract: `VG.Spec.Sha256.finalizeContract`. Constant time: only the pointers and `count` may affect timing, not the state.
///
diff --git a/src/asm/arm/sha256.rs b/src/asm/arm/sha256.rs
index 674959dd0..ce598e523 100644
--- a/src/asm/arm/sha256.rs
+++ b/src/asm/arm/sha256.rs
@@ -2407,7 +2407,7 @@ pub(crate) unsafe extern "C" fn vg_sha256_init(state: *mut [u8; 96]) {
)
}
-/// Absorbs data into a SHA-256 computation: if the streaming state `*state` represents a message of `count` bytes (modulo 2⁶⁴), it then represents that message followed by the `len` bytes at `data`.
+/// Absorbs data into a SHA-224 or SHA-256 computation: if the streaming state `*state` represents a message of `count` bytes (modulo 2⁶⁴), it then represents that message followed by the `len` bytes at `data`.
///
/// Contract: `VG.Spec.Sha256.updateContract`. Constant time: only the pointers, `count` and `len` may affect timing, not the state or the data.
///
@@ -2548,7 +2548,7 @@ pub(crate) unsafe extern "C" fn vg_sha256_update(state: *mut [u8; 96], count: u6
)
}
-/// Finishes a SHA-256 computation: if the streaming state `*state` represents a message of `count` bytes (modulo 2⁶⁴), writes the SHA-256 digest of that message to `*out`.
+/// Finishes a SHA-224 or SHA-256 computation: if the streaming state `*state` represents a message of `count` bytes (modulo 2⁶⁴), hashed from an initial hash value, writes the final hash value `H⁽ᴺ⁾` of that message (32 bytes) to `*out`. The SHA-256 digest is all of it; the SHA-224 digest is its first 28 bytes.
///
/// Contract: `VG.Spec.Sha256.finalizeContract`. Constant time: only the pointers and `count` may affect timing, not the state.
///
diff --git a/src/asm/x86/sha256.rs b/src/asm/x86/sha256.rs
index c8a1f786c..73703777b 100644
--- a/src/asm/x86/sha256.rs
+++ b/src/asm/x86/sha256.rs
@@ -3397,7 +3397,7 @@ pub(crate) unsafe extern "C" fn vg_sha256_compress(state: *mut [u32; 8], blocks:
)
}
-/// Absorbs data into a SHA-256 computation: if the streaming state `*state` represents a message of `count` bytes (modulo 2⁶⁴), it then represents that message followed by the `len` bytes at `data`.
+/// Absorbs data into a SHA-224 or SHA-256 computation: if the streaming state `*state` represents a message of `count` bytes (modulo 2⁶⁴), it then represents that message followed by the `len` bytes at `data`.
///
/// Contract: `VG.Spec.Sha256.updateContract`. Constant time: only the pointers, `count` and `len` may affect timing, not the state or the data.
///
@@ -3538,7 +3538,7 @@ pub(crate) unsafe extern "C" fn vg_sha256_update(state: *mut [u8; 96], count: u6
)
}
-/// Finishes a SHA-256 computation: if the streaming state `*state` represents a message of `count` bytes (modulo 2⁶⁴), writes the SHA-256 digest of that message to `*out`.
+/// Finishes a SHA-224 or SHA-256 computation: if the streaming state `*state` represents a message of `count` bytes (modulo 2⁶⁴), hashed from an initial hash value, writes the final hash value `H⁽ᴺ⁾` of that message (32 bytes) to `*out`. The SHA-256 digest is all of it; the SHA-224 digest is its first 28 bytes.
///
/// Contract: `VG.Spec.Sha256.finalizeContract`. Constant time: only the pointers and `count` may affect timing, not the state.
///
diff --git a/src/asm/x86_64/sha256.rs b/src/asm/x86_64/sha256.rs
index 38e09d3ae..3a8af49b2 100644
--- a/src/asm/x86_64/sha256.rs
+++ b/src/asm/x86_64/sha256.rs
@@ -6966,7 +6966,7 @@ pub(crate) unsafe extern "sysv64" fn vg_sha256_compress_avx2(state: *mut [u32; 8
)
}
-/// Absorbs data into a SHA-256 computation: if the streaming state `*state` represents a message of `count` bytes (modulo 2⁶⁴), it then represents that message followed by the `len` bytes at `data`.
+/// Absorbs data into a SHA-224 or SHA-256 computation: if the streaming state `*state` represents a message of `count` bytes (modulo 2⁶⁴), it then represents that message followed by the `len` bytes at `data`.
///
/// Contract: `VG.Spec.Sha256.updateContract`. Constant time: only the pointers, `count` and `len` may affect timing, not the state or the data.
///
@@ -7098,7 +7098,7 @@ pub(crate) unsafe extern "sysv64" fn vg_sha256_update(state: *mut [u8; 96], coun
)
}
-/// Finishes a SHA-256 computation: if the streaming state `*state` represents a message of `count` bytes (modulo 2⁶⁴), writes the SHA-256 digest of that message to `*out`.
+/// Finishes a SHA-224 or SHA-256 computation: if the streaming state `*state` represents a message of `count` bytes (modulo 2⁶⁴), hashed from an initial hash value, writes the final hash value `H⁽ᴺ⁾` of that message (32 bytes) to `*out`. The SHA-256 digest is all of it; the SHA-224 digest is its first 28 bytes.
///
/// Contract: `VG.Spec.Sha256.finalizeContract`. Constant time: only the pointers and `count` may affect timing, not the state.
///
@@ -7215,7 +7215,7 @@ pub(crate) unsafe extern "sysv64" fn vg_sha256_finalize(state: *mut [u8; 96], co
/// The CPU features `vg_sha256_update_avx2` requires (`Artifact.features`).
pub(crate) const VG_SHA256_UPDATE_AVX2_FEATURES: &[&str] = &["avx", "avx2", "bmi1", "bmi2"];
-/// Absorbs data into a SHA-256 computation: if the streaming state `*state` represents a message of `count` bytes (modulo 2⁶⁴), it then represents that message followed by the `len` bytes at `data`.
+/// Absorbs data into a SHA-224 or SHA-256 computation: if the streaming state `*state` represents a message of `count` bytes (modulo 2⁶⁴), it then represents that message followed by the `len` bytes at `data`.
///
/// Contract: `VG.Spec.Sha256.updateContract`. Constant time: only the pointers, `count` and `len` may affect timing, not the state or the data.
///
@@ -7351,7 +7351,7 @@ pub(crate) unsafe extern "sysv64" fn vg_sha256_update_avx2(state: *mut [u8; 96],
/// The CPU features `vg_sha256_finalize_avx2` requires (`Artifact.features`).
pub(crate) const VG_SHA256_FINALIZE_AVX2_FEATURES: &[&str] = &["avx", "avx2", "bmi1", "bmi2"];
-/// Finishes a SHA-256 computation: if the streaming state `*state` represents a message of `count` bytes (modulo 2⁶⁴), writes the SHA-256 digest of that message to `*out`.
+/// Finishes a SHA-224 or SHA-256 computation: if the streaming state `*state` represents a message of `count` bytes (modulo 2⁶⁴), hashed from an initial hash value, writes the final hash value `H⁽ᴺ⁾` of that message (32 bytes) to `*out`. The SHA-256 digest is all of it; the SHA-224 digest is its first 28 bytes.
///
/// Contract: `VG.Spec.Sha256.finalizeContract`. Constant time: only the pointers and `count` may affect timing, not the state.
///
@@ -7469,7 +7469,7 @@ pub(crate) unsafe extern "sysv64" fn vg_sha256_finalize_avx2(state: *mut [u8; 96
/// The CPU features `vg_sha256_update_shani` requires (`Artifact.features`).
pub(crate) const VG_SHA256_UPDATE_SHANI_FEATURES: &[&str] = &["sha", "ssse3"];
-/// Absorbs data into a SHA-256 computation: if the streaming state `*state` represents a message of `count` bytes (modulo 2⁶⁴), it then represents that message followed by the `len` bytes at `data`.
+/// Absorbs data into a SHA-224 or SHA-256 computation: if the streaming state `*state` represents a message of `count` bytes (modulo 2⁶⁴), it then represents that message followed by the `len` bytes at `data`.
///
/// Contract: `VG.Spec.Sha256.updateContract`. Constant time: only the pointers, `count` and `len` may affect timing, not the state or the data.
///
@@ -7605,7 +7605,7 @@ pub(crate) unsafe extern "sysv64" fn vg_sha256_update_shani(state: *mut [u8; 96]
/// The CPU features `vg_sha256_finalize_shani` requires (`Artifact.features`).
pub(crate) const VG_SHA256_FINALIZE_SHANI_FEATURES: &[&str] = &["sha", "ssse3"];
-/// Finishes a SHA-256 computation: if the streaming state `*state` represents a message of `count` bytes (modulo 2⁶⁴), writes the SHA-256 digest of that message to `*out`.
+/// Finishes a SHA-224 or SHA-256 computation: if the streaming state `*state` represents a message of `count` bytes (modulo 2⁶⁴), hashed from an initial hash value, writes the final hash value `H⁽ᴺ⁾` of that message (32 bytes) to `*out`. The SHA-256 digest is all of it; the SHA-224 digest is its first 28 bytes.
///
/// Contract: `VG.Spec.Sha256.finalizeContract`. Constant time: only the pointers and `count` may affect timing, not the state.
///