Files
inductive-verification-lean/InductiveVerification/NS_Public.lean
T
Your Name b5328b520b
Lean Action CI / build (push) Has been cancelled
Replaced some cases instances with grind
2026-03-05 12:55:43 +01:00

292 lines
10 KiB
Lean4

import InductiveVerification.Public
-- The Needham-Schroeder Public-Key Protocol
namespace NS_Public
variable [InvKey]
variable [Bad]
open Msg
open Event
open Bad
open HasInitState
open InvKey
-- Define the inductive set `ns_public`
inductive ns_public : List Event Prop
| Nil : ns_public []
| Fake : ns_public evsf
X synth (analz (spies evsf))
ns_public (Says Agent.Spy B X :: evsf)
| NS1 : ns_public evs1
Nonce NA used evs1
ns_public (Says A B (Crypt (pubEK B) Nonce NA, Agent A) :: evs1)
| NS2 : ns_public evs2
Nonce NB used evs2
Says A' B (Crypt (pubEK B) Nonce NA, Agent A) evs2
ns_public (Says B A (Crypt (pubEK A) Nonce NA, Nonce NB, Agent B) :: evs2)
| NS3 : ns_public evs3
Says A B (Crypt (pubEK B) Nonce NA, Agent A) evs3
Says B' A (Crypt (pubEK A) Nonce NA, Nonce NB, Agent B) evs3
ns_public (Says A B (Crypt (pubEK B) (Nonce NB)) :: evs3)
-- A "possibility property": there are traces that reach the end
theorem possibility_property :
NB, evs, ns_public evs Says A B (Crypt (pubEK B) (Nonce NB)) evs := by
exists 1
exists [ Says A B (Crypt (pubEK B) (Nonce 1)),
Says B A (Crypt (pubEK A) Nonce 0, Nonce 1, Agent B),
Says A B (Crypt (pubEK B) Nonce 0, Agent A),
]
constructor
· apply ns_public.NS3
· apply ns_public.NS2
· apply_rules[ns_public.NS1, ns_public.Nil, Nonce_notin_used_empty]
· simp
· tauto
all_goals tauto
· simp
-- Spy never sees another agent's private key unless it's bad at the start
@[simp, grind =]
theorem Spy_see_priEK {h : ns_public evs} :
(Key (priEK A) parts (spies evs)) A bad := by
constructor
· induction h with
| Nil => simp[priEK]
| Fake _ h =>
apply Fake_parts_sing at h
intro h₁; simp at h₁; apply Fake_parts_sing_helper (h := h) at h₁
simp_all
| NS1 => simp_all
| NS2 => simp_all
| NS3 => simp_all
· intro _; apply_rules [ parts_increasing, Spy_spies_bad_privateKey ]
@[simp]
theorem Spy_analz_priEK {h : ns_public evs} :
Key (priEK A) analz (spies evs) A bad
:= by grind
-- It is impossible to re-use a nonce in both NS1 and NS2, provided the nonce is
-- secret
@[grind! .]
theorem no_nonce_NS1_NS2 { evs: List Event} { h : ns_public evs } :
(Crypt (pubEK C) NA', Nonce NA, Agent D parts (spies evs)
(Crypt (pubEK B) Nonce NA, Agent A parts (spies evs)
Nonce NA analz (spies evs))) := by
intro h₁ h₂
induction h with
| Nil => simp at h₂
| Fake _ h =>
simp [*] at *
apply Fake_parts_sing at h
apply Fake_parts_sing_helper (h := h) at h₁
apply Fake_parts_sing_helper (h := h) at h₂
simp_all; grind
| NS1 =>
simp [*] at *
expand_parts_element at h₁; expand_parts_element at h₂;
grind
| NS2 =>
simp [*] at *
expand_parts_element at h₂;
grind
| NS3 => apply analz_spies_mono; simp_all
-- Unicity for NS1: nonce NA identifies agents A and B
@[grind! .]
theorem unique_NA { h : ns_public evs } :
(Crypt (pubEK B) Nonce NA, Agent A parts (spies evs)
(Crypt (pubEK B') Nonce NA, Agent A' parts (spies evs)
(Nonce NA analz (spies evs)
A = A' B = B'))) := by
induction h with
| Nil => simp
| Fake _ a a_ih =>
apply Fake_parts_sing at a; intro h₁ h₂ h₃;
apply analz_spies_mono_neg at h₃;
simp [*] at *
apply Fake_parts_sing_helper (h := a) at h₁
apply Fake_parts_sing_helper (h := a) at h₂
simp_all
| NS1 =>
intro h₁ h₂ _; simp [*] at *
expand_parts_element at h₁; expand_parts_element at h₂; grind
| NS2 => intro _ _ _; simp_all; grind
| NS3 => intro _ _ _; simp_all; grind
-- Spy does not see the nonce sent in NS1 if A and B are secure
@[grind! .]
theorem Spy_not_see_NA { h : ns_public evs }
{ not_bad_A : A bad }
{ not_bad_B : B bad } :
Says A B (Crypt (pubEK B) Nonce NA, Agent A) evs
Nonce NA 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
| NS1 _ a =>
simp_all; rcases h₁ with (_ | h)
· simp_all; apply a; aapply analz_knows_Spy_subset_used
· apply analz_insert_Crypt_subset at h₄; simp at h₄; cases h₄ <;> simp_all
apply Says_imp_used at h; apply used_parts_subset_parts at h;
simp_all[Set.subset_def]
| NS2 _ _ a a_ih =>
simp [*] at *; have _ := h₄; have c := h₁
apply Says_imp_parts_knows_Spy at h₁
have d := h₁
expand_parts_element at d
apply analz_insert_Crypt_subset at h₄; simp at h₄; rcases h₄ with (h |h |h)
<;> simp [*] at *;
· apply a_ih at c; have _ := c; apply Says_imp_parts_knows_Spy at a
apply unique_NA at h₁; apply h₁ at a; apply a at c; all_goals simp_all
· grind
| NS3 => apply analz_insert_Crypt_subset at h₄; simp[*] at h₄; grind
-- Authentication for `A`: if she receives message 2 and has used `NA` to start
-- a run, then `B` has sent message 2.
theorem A_trusts_NS2 {h : ns_public evs }
{ not_bad_A : A bad }
{ not_bad_B : B bad } :
Says A B (Crypt (pubEK B) Nonce NA, Agent A) evs
Says B' A (Crypt (pubEK B) Nonce NA, Nonce NB, Agent B) evs
Says B A (Crypt (pubEK B) Nonce NA, Nonce NB, Agent B) 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 snsNA := h₁; apply Spy_not_see_NA at snsNA <;> try assumption
apply analz_spies_mono_neg at snsNA
simp [*] at *
cases h₁
· simp_all
· apply Fake_parts_sing at a;
apply Fake_parts_sing_helper (h := a) at h₂; simp at h₂
grind
· aapply ns_public.Fake
| NS1 => simp [*] at *; expand_parts_element at h₂; grind
| NS2 => simp [*] at *; grind
| NS3 => simp_all;
-- If the encrypted message appears then it originated with Alice in `NS1`
lemma B_trusts_NS1 { h : ns_public evs} :
Crypt (pubEK B) Nonce NA, Agent A parts (spies evs)
Nonce NA analz (spies evs)
Says A B (Crypt (pubEK B) Nonce NA, Agent A) evs
:= by
intro h₁ h₂
induction h with
| Nil => simp at h₁
| Fake _ a =>
apply analz_spies_mono_neg at h₂
simp at h₁; apply Fake_parts_sing at a;
apply Fake_parts_sing_helper (h := a) at h₁; simp_all
| NS1 => apply analz_spies_mono_neg at h₂; simp_all; cases h₁ <;> simp_all
| NS2 => apply analz_spies_mono_neg at h₂; simp_all;
| NS3 => apply analz_spies_mono_neg at h₂; simp_all;
-- Authenticity Properties obtained from `NS2`
-- Unicity for `NS2`: nonce `NB` identifies nonce `NA` and agent `A`
@[grind! .]
theorem unique_NB { h : ns_public evs } :
(Crypt (pubEK A) Nonce NA, Nonce NB, Agent B parts (spies evs)
(Crypt (pubEK A') Nonce NA', Nonce NB, Agent B' parts (spies evs)
(Nonce NB analz (spies evs)
A = A' NA = NA' B = B'))) := by
-- Proof closely follows that of unique_NA
induction h with
| Nil => aesop (add safe analz_insertI)
| Fake _ a =>
apply Fake_parts_sing at a; intro h₁ h₂ h₃; simp [*] at *
apply Fake_parts_sing_helper (h := a) at h₁;
apply Fake_parts_sing_helper (h := a) at h₂; simp [*] at *
apply analz_insert_mono_neg at h₃
grind
| NS1 => intro _ _ h₃; apply analz_spies_mono_neg at h₃; simp_all
| NS2 =>
intro h₁ h₂ _; simp [*] at *
expand_parts_element at h₁; expand_parts_element at h₂
grind
| NS3 => intro _ _ _; simp_all; grind
-- `NB` remains secret
@[grind! .]
theorem Spy_not_see_NB { h : ns_public evs }
{ not_bad_A : A bad }
{ not_bad_B : B bad } :
Says B A (Crypt (pubEK A) Nonce NA, Nonce NB, Agent B) evs
Nonce NB 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;
| NS1 =>
simp [*] at *
apply analz_insert_Crypt_subset at h₄; simp at h₄
have h₂ := h₁; apply Says_imp_parts_knows_Spy at h₂
expand_parts_element at h₂
grind
| NS2 =>
simp [*] at *
have _ := h₄
apply analz_insert_Crypt_subset at h₄; simp at h₄
rcases h₁ with (_ | h₁)
· simp_all; grind
· have _ := h₁; apply Says_imp_parts_knows_Spy at h₁
expand_parts_element at h₁; grind
| NS3 =>
simp at h₁; simp[analz_insert_Crypt_element] at h₄;
rcases h₄ with (_, _ | _, _) <;> simp_all; grind
-- Authentication for `B`: if he receives message 3 and has used `NB` in message
-- 2, then `A` has sent message 3.
theorem B_trusts_NS3 { h : ns_public evs }
{ not_bad_A : A bad }
{ not_bad_B : B bad } :
Says B A (Crypt (pubEK A) Nonce NA, Nonce NB, Agent B) evs
Says A' B (Crypt (pubEK B) (Nonce NB)) evs
Says A B (Crypt (pubEK B) (Nonce NB)) evs
:= by
intro h₁ h₂
apply Says_imp_parts_knows_Spy at h₂
induction h with
| Nil => simp_all
| Fake _ a =>
simp [*] at *
apply Fake_parts_sing at a
apply Fake_parts_sing_helper (h := a) at h₂; simp at h₂
grind
| NS1 => simp_all
| NS2 => simp [*] at *; expand_parts_element at h₂; grind
| NS3 => simp [*] at *; grind
-- Overall guarantee for `B`
-- If NS3 has been sent and the nonce NB agrees with the nonce B joined with NA,
-- then A initiated the run using NA
theorem B_trusts_protocol { h : ns_public evs }
{ not_bad_A : A bad }
{ not_bad_B : B bad } :
Crypt (pubEK B) (Nonce NB) parts (spies evs)
Says B A (Crypt (pubEK A) Nonce NA, Nonce NB, Agent B) evs
Says A B (Crypt (pubEK B) Nonce NA, Agent A) evs := by
intro h₁ h₂
induction h with
| Nil => simp_all
| Fake _ a =>
simp [*] at *
apply Fake_parts_sing at a
apply Fake_parts_sing_helper (h := a) at h₁; expand_parts_element at h₁
grind
| NS1 => simp_all
| NS2 => simp [*] at *; expand_parts_element at h₁; grind
| NS3 => simp [*] at *; grind
end NS_Public