Files
inductive-verification-lean/InductiveVerification/Blanchet.lean
T
Your Name e41142896f
Lean Action CI / build (push) Has been cancelled
Proved basic properties of Blanchet protocol
2026-03-11 17:11:44 +01:00

227 lines
7.6 KiB
Lean4
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import InductiveVerification.Public
-- The Blanchet Protocol
namespace Blanchet
variable [InvKey]
variable [Bad]
variable [AgentKeys]
open Msg
open Event
open Bad
open HasInitState
open InvKey
-- Define the inductive set `blanchet`
inductive blanchet : List Event Prop
| Nil : blanchet []
| Fake : blanchet evsf
X synth (analz (spies evsf))
blanchet (Says Agent.Spy B X :: evsf)
| B1 : blanchet evs1
sk symKeys
-- sk is fresh
Key sk used evs1 Key '' keysFor (used evs1)
blanchet (Says A B (Crypt (pubEK B) (Sign (priSK A) Agent B, Key sk)) :: evs1)
| B2 : blanchet evs2
Nonce s used evs2
Says A B (Crypt (pubEK B) (Sign (priSK A) Agent B, Key k)) evs2
blanchet (Says B A (Crypt k (Nonce s)) :: evs2)
-- A "possibility property": there are traces that reach the end
theorem possibility_property :
sk symKeys, s, evs, blanchet evs Says B A (Crypt sk s) evs
:= by
obtain sk, _, _ := symK_supply (evs := [])
exists sk
simp_all
exists Nonce 0
exists [
Says B A (Crypt sk (Nonce 0)),
Says A B (Crypt (pubEK B) (Sign (priSK A) Agent B, Key sk)),
]
constructor
· apply blanchet.B2
· apply_rules [ blanchet.B1, blanchet.Nil ]; simp_all
· simp [used]
· tauto
· simp
-- Spy never sees another agent's private key unless it's bad at the start
@[simp, grind =]
theorem Spy_see_priEK {h : blanchet evs} :
(Key (priEK A) parts (spies evs)) A bad := by
constructor
· induction h with
| Nil => simp [ priEK, initState ]
| Fake _ h =>
intro h₁; simp at h₁
apply Or.imp_left (f := Fake_parts_sing (h := h)) at h₁
simp_all
| B1 => simp_all;
| B2 => simp_all;
· intro _; apply_rules [ parts_increasing, Spy_spies_bad_privateKey ]
@[simp]
theorem Spy_analz_priEK {h : blanchet evs} :
Key (priEK A) analz (spies evs) A bad
:= by grind
@[simp, grind =]
theorem Spy_see_priSK {h : blanchet evs} :
Key (priSK A) parts (spies evs) A bad
:= by
constructor
· induction h with
| Nil => simp [ priSK, initState ]
| Fake _ h =>
intro h₁; simp at h₁
apply Or.imp_left (f := Fake_parts_sing (h := h)) at h₁
simp_all
| B1 => simp_all;
| B2 => simp_all;
· intro _; apply_rules [ parts_increasing, Spy_spies_bad_privateKey ]
@[simp]
theorem Spy_analz_priSK {h : blanchet evs} :
Key (priSK A) analz (spies evs) A bad
:= by grind
-- Unicity for NS1: nonce NA identifies agents A and B
@[grind! .]
theorem unique_B1 { h : blanchet evs } :
(Crypt (pubEK B) (Sign (priSK A) Agent B, Key sk) parts (spies evs)
(Crypt (pubEK B') (Sign (priSK A') Agent B', Key sk) parts (spies evs)
Key sk analz (spies evs)
A = A' B = B')) := by
intro h₁ h₂ h₃
induction h with
| Nil => simp_all [ initState ]
| Fake _ a a_ih =>
apply mt (h₁ := analz_spies_mono) at h₃;
simp [*, priSK] at *
apply Or.imp_left (f := Fake_parts_sing (h := a)) at h₁
apply Or.imp_left (f := Fake_parts_sing (h := a)) at h₂
simp_all;
| B1 =>
simp [*] at *; expand_parts_element at h₁; expand_parts_element at h₂; grind
| B2 => simp_all; grind
lemma keysFor_used_knows_Spy_helper :
K keysFor (analz (spies evs)) K keysFor (used evs)
:= by
aapply keysFor_mono; apply analz_knows_Spy_subset_used
-- Spy does not see the key sent in B1 if A and B are secure
@[grind! .]
theorem Spy_not_see_sk { h : blanchet evs }
{ not_bad_A : A bad }
{ not_bad_B : B bad } :
Says A B (Crypt (pubEK B) (Sign (priSK A) Agent B, Key sk)) evs
Key sk analz (spies evs) := by
intro h₁ h₂
induction h with
| Nil => simp_all
| Fake _ a => apply Fake_analz_insert at a; apply a at h₂; simp_all
| B1 _ _ a =>
simp_all; obtain a, b := a; rcases h₁ with (_ | h)
· simp_all; apply a; aapply analz_knows_Spy_subset_used
· have _ := h; apply Says_imp_used at h; apply used_parts_subset_parts at h;
apply mt (h₁ := keysFor_used_knows_Spy_helper) at b
apply analz_insert_Crypt_subset at h₂; simp at h₂
apply analz_insert_Crypt_subset at h₂; simp at h₂
rw [analz_insert_Key] at h₂
cases h₂; all_goals simp_all[Set.subset_def]
| B2 => apply analz_insert_Crypt_subset at h₂; simp at h₂; grind
-- Authentication for `A`: if she receives message 2 and has used `sk` to start
-- a run, then `B` has sent message 2.
theorem A_trusts_B2 {h : blanchet evs }
{ not_bad_A : A bad }
{ not_bad_B : B bad } :
Says A B (Crypt (pubEK B) (Sign (priSK A) Agent B, Key sk)) evs
Says B' A (Crypt sk (Number s)) evs
Says B A (Crypt sk (Number s)) evs
:= by
intro h₁ h₂;
apply Says_imp_parts_knows_Spy at h₂
-- use unique_NA to show that B' = B
induction h with
| Nil => simp_all
| Fake _ a =>
have snssk := h₁; apply Spy_not_see_sk at snssk <;> try assumption
apply mt (h₁ := analz_spies_mono) at snssk
simp [*] at *
cases h₁
· simp_all
· apply Or.imp_left (f := Fake_parts_sing (h := a)) at h₂; simp_all
· aapply blanchet.Fake
| B1 => simp [*] at *; expand_parts_element at h₂; simp_all [keysFor]; grind
| B2 => simp [*] at *; grind
theorem sk_symmetric_B1 {h :blanchet evs }
{ not_bad_A : A bad } :
Says A B (Crypt (pubEK B) (Sign (priSK A) Agent B, Key sk)) evs
sk symKeys
:= by
intro h₁; induction h <;> simp_all
· grind
· cases h₁ <;> simp_all
-- If the encrypted message appears then it originated with Alice in `NS1`
lemma B_trusts_B1 { h : blanchet evs}
{ not_bad_A : A bad } :
Crypt (pubEK B) (Sign (priSK A) Agent B, Key sk) parts (spies evs)
Key sk analz (spies evs)
Says A B (Crypt (pubEK B) (Sign (priSK A) Agent B, Key sk)) evs
:= by
intro h₁ h₂
induction h with
| Nil => simp [initState] at h₁
| Fake _ a =>
apply mt (h₁ := analz_spies_mono) at h₂
simp at h₁; apply Or.imp_left (f := Fake_parts_sing (h := a)) at h₁; simp_all
| B1 => apply mt (h₁ := analz_spies_mono) at h₂; simp_all; grind
| B2 => apply mt (h₁ := analz_spies_mono) at h₂; simp_all;
-- Authenticity Properties obtained from `NS2`
-- B only respods to requests from A
theorem B_is_response { h : blanchet evs }
{ not_bad_B : B bad } :
Says B A (Crypt sk (Nonce s)) evs
Says A B (Crypt (pubEK B) (Sign (priSK A) Agent B, Key sk)) evs
:= by
intro h₁
induction h <;> simp_all <;> grind
-- `s` remains secret
theorem Spy_not_see_s { h : blanchet evs }
{ not_bad_A : A bad }
{ not_bad_B : B bad } :
Says B A (Crypt sk (Nonce s)) evs
Nonce s analz (spies evs)
:= by
intro h₁
induction h with
| Nil => simp_all
| Fake _ a => intro h₂; apply Fake_analz_insert at a; apply a at h₂; simp_all;
| B1 _ _ a =>
simp at a; obtain a, b := a;
simp [*, analz_insert_Crypt_element] at *
have _ := h₁; apply Says_imp_parts_knows_Spy at h₁
expand_parts_element at h₁; simp_all; intro h₁ h₂
rw[analz_insert_Key] at h₂; simp at h₂; grind;
aapply mt (h₁ := keysFor_used_knows_Spy_helper)
| B2 h not_used a a_ih =>
simp at h₁; rcases h₁ with (_ | h) <;> simp_all[analz_insert_Crypt_element]
. apply And.intro;
· have b := a; apply Spy_not_see_sk at a; apply sk_symmetric_B1 at b
all_goals simp_all
· grind
· apply Says_imp_used at h
apply used_parts_subset_parts at h; rw[Set.subset_def] at h
intro _ _; simp_all
end Blanchet