Theory m1_keydist
chapter ‹Key Establishment Protocols›
text ‹In this chapter, we develop several key establishment protocols:
\begin{itemize}
\item Needham-Schroeder Shared Key (NSSK)
\item core Kerberos IV and V, and
\item Denning-Sacco.
\end{itemize}
›
section ‹Basic abstract key distribution (L1)›
theory m1_keydist imports "../Refinement/Runs" "../Refinement/s0g_secrecy"
begin
text ‹The first refinement introduces the protocol roles, local memory of the
agents and the communication structure of the protocol. For actual
communication, the "receiver" directly reads the memory of the "sender".
It captures the core of essentials of server-based key distribution protocols:
The server generates a key that the clients read from his memory. At this
stage we are only interested in secrecy preservation, not in authentication.
›
declare option.split_asm [split]
declare domIff [simp, iff del]
consts
sk :: "nat"
subsection ‹State›
text ‹Runs record the protocol participants (initiator, responder) and the
keys learned during the execution. In later refinements, we will also add
nonces and timestamps to the run record.
The variables ‹kn› and ‹az› from ‹s0g_secrecy_leak›
are replaced by runs using a data refinement. Variable ‹lk› is
concretized into variable ‹leak›.
We define the state in two separate record definitions. The first one has
just a runs field and the second extends this with a leak field. Later
refinements may define different state for leaks (e.g. to record more context).
›
record m1r_state =
runs :: runs_t
record m1x_state = m1r_state +
leak :: "key set"
type_synonym m1x_obs = "m1x_state"
text ‹Predicate types for invariants and transition relation types. Use the
r-version for invariants and transitions if there is no reference to the leak
variable. This improves reusability in later refinements.
›
type_synonym 'x m1r_pred = "'x m1r_state_scheme set"
type_synonym 'x m1x_pred = "'x m1x_state_scheme set"
type_synonym 'x m1r_trans = "('x m1r_state_scheme × 'x m1r_state_scheme) set"
type_synonym 'x m1x_trans = "('x m1x_state_scheme × 'x m1x_state_scheme) set"
subsubsection ‹Key knowledge and authorization (reconstruction)›
text ‹Key knowledge and authorization relations, reconstructed from the runs
and an unspecified initial key setup. These auxiliary definitions are used in
some event guards and in the simulation relation (see below).›
text ‹Knowledge relation (reconstructed)›
inductive_set
knC :: "runs_t ⇒ (key × agent) set" for runz :: "runs_t"
where
knC_init:
"runz Ra = Some (Init, [A, B], aKey K # al) ⟹ (K, A) ∈ knC runz"
| knC_resp:
"runz Rb = Some (Resp, [A, B], aKey K # al) ⟹ (K, B) ∈ knC runz"
| knC_serv:
"⟦ Rs ∈ dom runz; fst (the (runz Rs)) = Serv ⟧ ⟹ (sesK (Rs$sk), Sv) ∈ knC runz"
| knC_0:
"(K, A) ∈ keySetup ⟹ (K, A) ∈ knC runz"
text ‹Authorization relation (reconstructed)›
inductive_set
azC :: "runs_t ⇒ (key × agent) set" for runz :: "runs_t"
where
azC_good:
"⟦ runz Rs = Some (Serv, [A, B], al); C ∈ {A, B, Sv} ⟧
⟹ (sesK (Rs$sk), C) ∈ azC runz"
| azC_bad:
"⟦ runz Rs = Some (Serv, [A, B], al); A ∈ bad ∨ B ∈ bad ⟧
⟹ (sesK (Rs$sk), C) ∈ azC runz"
| azC_0:
"⟦ (K, C) ∈ keySetup ⟧ ⟹ (K, C) ∈ azC runz"
declare knC.intros [intro]
declare azC.intros [intro]
text ‹Misc lemmas: empty state, projections, ...›
lemma knC_empty [simp]: "knC Map.empty = keySetup"
by (auto elim: knC.cases)
lemma azC_empty [simp]: "azC Map.empty = keySetup"
by (auto elim: azC.cases)
text ‹‹azC› and run abstraction›
lemma azC_map_runs [simp]: "azC (map_runs h runz) = azC runz"
by (auto simp add: map_runs_def elim!: azC.cases)
text ‹Update lemmas for @{term "knC"}›
lemma knC_upd_Init_Resp_None:
"⟦ R ∉ dom runz; rol ∈ {Init, Resp} ⟧
⟹ knC (runz(R ↦ (rol, [A, B], []))) = knC runz"
by (fastforce simp add: domIff elim!: knC.cases)
lemma knC_upd_Init_Some:
"⟦ runz Ra = Some (Init, [A, B], []) ⟧
⟹ knC (runz(Ra ↦ (Init, [A, B], [aKey Kab]))) = insert (Kab, A) (knC runz)"
apply (auto elim!: knC.cases)
apply (rename_tac Raa Aa Ba K al, rule_tac A=Aa and B=Ba and al=al in knC_init, auto)
apply (rename_tac Rb Aa Ba K al, rule_tac A=Aa and B=Ba and al=al in knC_resp, auto)
apply (rule_tac knC_serv, auto)
done
lemma knC_upd_Resp_Some:
"⟦ runz Ra = Some (Resp, [A, B], []) ⟧
⟹ knC (runz(Ra ↦ (Resp, [A, B], [aKey Kab]))) = insert (Kab, B) (knC runz)"
apply (auto elim!: knC.cases)
apply (rename_tac Raa Aa Ba K al, rule_tac A=Aa and B=Ba and al=al in knC_init, auto)
apply (rename_tac Raa Aa Ba K al, rule_tac A=Aa and B=Ba and al=al in knC_resp, auto)
apply (rule_tac knC_serv, auto)
done
lemma knC_upd_Server:
"⟦ Rs ∉ dom runz ⟧
⟹ knC (runz(Rs ↦ (Serv, [A, B], []))) = insert (sesK (Rs$sk), Sv) (knC runz)"
apply (auto elim!: knC.cases)
apply (rename_tac Raa Aa Ba K al, rule_tac A=Aa and B=Ba in knC_init, auto dest: dom_lemmas)
apply (rename_tac Raa Aa Ba K al, rule_tac A=Aa and B=Ba in knC_resp, auto dest: dom_lemmas)
done
lemmas knC_upd_lemmas [simp] =
knC_upd_Init_Resp_None knC_upd_Init_Some knC_upd_Resp_Some
knC_upd_Server
text ‹Update lemmas for @{term "azC"}›
lemma azC_upd_Init_None:
"⟦ Ra ∉ dom runz ⟧
⟹ azC (runz(Ra ↦ (Init, [A, B], []))) = azC runz"
by (auto simp add: azC.simps elim!: azC.cases dest: dom_lemmas)
lemma azC_upd_Resp_None:
"⟦ Rb ∉ dom runz ⟧
⟹ azC (runz(Rb ↦ (Resp, [A, B], []))) = azC runz"
by (auto simp add: azC.simps elim!: azC.cases dest: dom_lemmas)
lemma azC_upd_Init_Some:
"⟦ runz Ra = Some (Init, [A, B], []) ⟧
⟹ azC (runz(Ra ↦ (Init, [A, B], al))) = azC runz"
apply (auto elim!: azC.cases)
apply (rule_tac azC_good, auto)
apply (rule_tac azC_good, auto)
apply (rule_tac azC_good, auto)
apply (rule_tac azC_bad, auto)+
done
lemma azC_upd_Resp_Some:
"⟦ runz Rb = Some (Resp, [A, B], []) ⟧
⟹ azC (runz(Rb ↦ (Resp, [A, B], al))) = azC runz"
apply (auto elim!: azC.cases)
apply (rule_tac azC_good, auto)
apply (rule_tac azC_good, auto)
apply (rule_tac azC_good, auto)
apply (rule_tac azC_bad, auto)+
done
lemma azC_upd_Serv_bad:
"⟦ Rs ∉ dom runz; A ∈ bad ∨ B ∈ bad ⟧
⟹ azC (runz(Rs ↦ (Serv, [A, B], al))) = azC runz ∪ {sesK (Rs$sk)} × UNIV"
apply (auto elim!: azC.cases)
apply (
rename_tac Rsa Aa Ba ala, rule_tac A=Aa and B=Ba and al=ala in azC_good, auto dest: dom_lemmas,
rename_tac Rsa Aa Ba ala, rule_tac A=Aa and B=Ba and al=ala in azC_good, auto dest: dom_lemmas,
rename_tac Rsa Aa Ba ala, rule_tac A=Aa and B=Ba and al=ala in azC_good, auto dest: dom_lemmas,
rename_tac Rsa Aa Ba ala C, rule_tac A=Aa and B=Ba and al=ala in azC_bad, auto dest: dom_lemmas,
rename_tac Rsa Aa Ba ala C, rule_tac A=Aa and B=Ba and al=ala in azC_bad, auto dest: dom_lemmas
)+
done
lemma azC_upd_Serv_good:
"⟦ Rs ∉ dom runz; K = sesK (Rs$sk); A ∉ bad; B ∉ bad ⟧
⟹ azC (runz(Rs ↦ (Serv, [A, B], al)))
= azC runz ∪ {(K, A), (K, B), (K, Sv)}"
apply (auto elim!: azC.cases)
apply (
rename_tac Rsa Aa Ba ala, rule_tac A=Aa and B=Ba and al=ala in azC_good, auto dest: dom_lemmas,
rename_tac Rsa Aa Ba ala, rule_tac A=Aa and B=Ba and al=ala in azC_good, auto dest: dom_lemmas,
rename_tac Rsa Aa Ba ala, rule_tac A=Aa and B=Ba and al=ala in azC_good, auto dest: dom_lemmas,
rename_tac Rsa Aa Ba ala C, rule_tac A=Aa and B=Ba and al=ala in azC_bad, auto dest: dom_lemmas,
rename_tac Rsa Aa Ba ala C, rule_tac A=Aa and B=Ba and al=ala in azC_bad, auto dest: dom_lemmas
)+
done
lemma azC_upd_Serv:
"⟦ Rs ∉ dom runz; K = sesK (Rs$sk) ⟧
⟹ azC (runz(Rs ↦ (Serv, [A, B], al))) =
azC runz ∪ {K} × (if A ∉ bad ∧ B ∉ bad then {A, B, Sv} else UNIV)"
by (simp add: azC_upd_Serv_bad azC_upd_Serv_good)
lemmas azC_upd_lemmas [simp] =
azC_upd_Init_None azC_upd_Resp_None
azC_upd_Init_Some azC_upd_Resp_Some azC_upd_Serv
subsection ‹Events›
definition
m1x_step1 :: "[rid_t, agent, agent] ⇒ 'x m1r_trans"
where
"m1x_step1 Ra A B ≡ {(s, s1).
Ra ∉ dom (runs s) ∧
s1 = s⦇ runs := (runs s)(Ra ↦ (Init, [A, B], [])) ⦈
}"
definition
m1x_step2 :: "[rid_t, agent, agent] ⇒ 'x m1r_trans"
where
"m1x_step2 Rb A B ≡ {(s, s1).
Rb ∉ dom (runs s) ∧
s1 = s⦇ runs := (runs s)(Rb ↦ (Resp, [A, B], [])) ⦈
}"
definition
m1x_step3 :: "[rid_t, agent, agent, key] ⇒ 'x m1r_trans"
where
"m1x_step3 Rs A B Kab ≡ {(s, s1).
Rs ∉ dom (runs s) ∧
Kab = sesK (Rs$sk) ∧
s1 = s⦇ runs := (runs s)(Rs ↦ (Serv, [A, B], [])) ⦈
}"
definition
m1x_step4 :: "[rid_t, agent, agent, key] ⇒ 'x m1x_trans"
where
"m1x_step4 Ra A B Kab ≡ {(s, s1).
runs s Ra = Some (Init, [A, B], []) ∧
(Kab ∉ leak s ⟶ (Kab, A) ∈ azC (runs s)) ∧
s1 = s⦇ runs := (runs s)(Ra ↦ (Init, [A, B], [aKey Kab])) ⦈
}"
definition
m1x_step5 :: "[rid_t, agent, agent, key] ⇒ 'x m1x_trans"
where
"m1x_step5 Rb A B Kab ≡ {(s, s1).
runs s Rb = Some (Resp, [A, B], []) ∧
(Kab ∉ leak s ⟶ (Kab, B) ∈ azC (runs s)) ∧
s1 = s⦇ runs := (runs s)(Rb ↦ (Resp, [A, B], [aKey Kab])) ⦈
}"
definition
m1x_leak :: "rid_t ⇒ 'x m1x_trans"
where
"m1x_leak Rs ≡ {(s, s1).
Rs ∈ dom (runs s) ∧
fst (the (runs s Rs)) = Serv ∧
s1 = s⦇ leak := insert (sesK (Rs$sk)) (leak s) ⦈
}"
subsection ‹Specification›
definition
m1x_init :: "m1x_state set"
where
"m1x_init ≡ { ⦇
runs = Map.empty,
leak = corrKey
⦈ }"
definition
m1x_trans :: "'x m1x_trans" where
"m1x_trans ≡ (⋃A B Ra Rb Rs Kab.
m1x_step1 Ra A B ∪
m1x_step2 Rb A B ∪
m1x_step3 Rs A B Kab ∪
m1x_step4 Ra A B Kab ∪
m1x_step5 Rb A B Kab ∪
m1x_leak Rs ∪
Id
)"
definition
m1x :: "(m1x_state, m1x_obs) spec" where
"m1x ≡ ⦇
init = m1x_init,
trans = m1x_trans,
obs = id
⦈"
lemmas m1x_defs =
m1x_def m1x_init_def m1x_trans_def
m1x_step1_def m1x_step2_def m1x_step3_def m1x_step4_def m1x_step5_def
m1x_leak_def
lemma m1x_obs_id [simp]: "obs m1x = id"
by (simp add: m1x_def)
subsection ‹Invariants›
subsubsection ‹inv1: Key definedness›
text ‹Only run identifiers or static keys can be (concretely) known or
authorized keys. (This reading corresponds to the contraposition of the
property expressed below.)›
definition
m1x_inv1_key :: "m1x_state set"
where
"m1x_inv1_key ≡ {s. ∀Rs A.
Rs ∉ dom (runs s) ⟶
(sesK (Rs$sk), A) ∉ knC (runs s) ∧
(sesK (Rs$sk), A) ∉ azC (runs s) ∧
sesK (Rs$sk) ∉ leak s
}"
lemmas m1x_inv1_keyI = m1x_inv1_key_def [THEN setc_def_to_intro, rule_format]
lemmas m1x_inv1_keyE [elim] =
m1x_inv1_key_def [THEN setc_def_to_elim, rule_format]
lemmas m1x_inv1_keyD [dest] =
m1x_inv1_key_def [THEN setc_def_to_dest, rule_format, rotated 1]
text ‹Invariance proof.›
lemma PO_m1x_inv1_key_init [iff]:
"init m1x ⊆ m1x_inv1_key"
by (auto simp add: m1x_defs m1x_inv1_key_def)
lemma PO_m1x_inv1_key_trans [iff]:
"{m1x_inv1_key} trans m1x {> m1x_inv1_key}"
by (auto simp add: PO_hoare_defs m1x_defs intro!: m1x_inv1_keyI)
lemma PO_m1x_inv1_key [iff]: "reach m1x ⊆ m1x_inv1_key"
by (rule inv_rule_basic) (auto)
subsection ‹Refinement of s0g›
text ‹med10: The mediator function maps a concrete observation to an
abstract one.›
definition
med01x :: "m1x_obs ⇒ key s0g_obs"
where
"med01x t ≡ ⦇ kn = knC (runs t), az = azC (runs t), lk = leak t ⦈"
text ‹R01: The simulation relation expreses key knowledge and authorization
in terms of the client and server run information.›
definition
R01x :: "(key s0g_state × m1x_state) set" where
"R01x ≡ {(s, t). s = med01x t}"
lemmas R01x_defs = R01x_def med01x_def
text ‹Refinement proof.›
lemma PO_m1x_step1_refines_skip:
"{R01x}
Id, (m1x_step1 Ra A B)
{> R01x}"
by (auto simp add: PO_rhoare_defs R01x_defs s0g_defs m1x_defs)
lemma PO_m1x_step2_refines_skip:
"{R01x}
Id, (m1x_step2 Rb A B)
{> R01x}"
by (auto simp add: PO_rhoare_defs R01x_defs s0g_defs m1x_defs)
lemma PO_m1x_step3_refines_s0g_gen:
"{R01x ∩ UNIV × m1x_inv1_key}
(s0g_gen Kab Sv {Sv, A, B}), (m1x_step3 Rs A B Kab)
{> R01x}"
by (auto simp add: PO_rhoare_defs R01x_defs s0g_defs m1x_defs)
lemma PO_m1x_step4_refines_s0g_learn:
"{R01x}
(s0g_learn Kab A), (m1x_step4 Ra A B Kab)
{> R01x}"
by (auto simp add: PO_rhoare_defs R01x_defs s0g_defs m1x_defs)
lemma PO_m1x_step5_refines_s0g_learn:
"{R01x}
(s0g_learn Kab B), (m1x_step5 Rb A B Kab)
{> R01x}"
by (auto simp add: PO_rhoare_defs R01x_defs s0g_defs m1x_defs)
lemma PO_m1x_leak_refines_s0g_leak:
"{R01x}
(s0g_leak (sesK (Rs$sk))), (m1x_leak Rs)
{> R01x}"
by (fastforce simp add: PO_rhoare_defs R01x_defs s0g_defs m1x_defs)
text ‹All together now...›
lemmas PO_m1x_trans_refines_s0g_trans =
PO_m1x_step1_refines_skip PO_m1x_step2_refines_skip
PO_m1x_step3_refines_s0g_gen PO_m1x_step4_refines_s0g_learn
PO_m1x_step5_refines_s0g_learn PO_m1x_leak_refines_s0g_leak
lemma PO_m1x_refines_init_s0g [iff]:
"init m1x ⊆ R01x``(init s0g)"
by (auto simp add: R01x_defs s0g_defs m1x_defs intro!: s0g_secrecyI s0g_domI)
lemma PO_m1x_refines_trans_s0g [iff]:
"{R01x ∩ UNIV × m1x_inv1_key}
(trans s0g), (trans m1x)
{> R01x}"
by (auto simp add: m1x_def m1x_trans_def s0g_def s0g_trans_def
intro!: PO_m1x_trans_refines_s0g_trans)
text ‹Observation consistency.›
lemma obs_consistent_med01x [iff]:
"obs_consistent R01x med01x s0g m1x"
by (auto simp add: obs_consistent_def R01x_defs s0g_def m1x_def)
text ‹Refinement result.›
lemma PO_m1x_refines_s0g [iff]:
"refines
(R01x ∩ UNIV × m1x_inv1_key)
med01x s0g m1x"
by (rule Refinement_using_invariants) (auto del: subsetI)
lemma m1x_implements_s0g [iff]: "implements med01x s0g m1x"
by (rule refinement_soundness) (fast)
subsection ‹Derived invariants›
subsubsection ‹inv2: Secrecy›
text ‹Secrecy, expressed in terms of runs.›
definition
m1x_secrecy :: "'x m1x_pred"
where
"m1x_secrecy ≡ {s. knC (runs s) ⊆ azC (runs s) ∪ leak s × UNIV}"
lemmas m1x_secrecyI = m1x_secrecy_def [THEN setc_def_to_intro, rule_format]
lemmas m1x_secrecyE [elim] = m1x_secrecy_def [THEN setc_def_to_elim, rule_format]
text ‹Invariance proof.›
lemma PO_m1x_obs_secrecy [iff]: "oreach m1x ⊆ m1x_secrecy"
apply (rule external_invariant_translation [OF PO_s0g_obs_secrecy _ m1x_implements_s0g])
apply (auto simp add: med01x_def m1x_secrecy_def s0g_secrecy_def)
done
lemma PO_m1x_secrecy [iff]: "reach m1x ⊆ m1x_secrecy"
by (rule external_to_internal_invariant [OF PO_m1x_obs_secrecy], auto)
end