(***************************************************************************** * ESPL --- an embedded security protocol logic * http://people.inf.ethz.ch/meiersi/espl/ * * Copyright (c) 2009-2011, Simon Meier, ETH Zurich, Switzerland * * Extension to compromising adversaries: * * Copyright (c) 2010-2011, Martin Schaub, ETH Zurich, Switzerland * * All rights reserved. See file LICENCE for more information. ******************************************************************************) theory WeakTyping imports InferenceRules Syntax begin section{* A Shallow Embedding of a Message Type System *} subsection{* Introduction *} text{* The goal of this theory is to develop a simple way for specifying type invariants for the message variables of a protocol. A message type is a set of messages that depends on the thread and the system state that this message type is interpreted relative to. A typing is then a function assigning to each combination of a role and an identifier of a message variable in this role a message type. Each typing approximates the set of states where all instantiated message variables are instantiated with a message corresponding to their type. *} type_synonym msgtype = "tid \ state \ execmsg set" type_synonym typing = "(role \ id) \ msgtype" definition approx :: "typing \ state set" where "approx typing \ {q. case q of (t,r,s) \ \ (i,step) \ steps t. \ R. roleMap r i = Some R \ (\ n. MVar n \ FV (stepPat step) \ s (MVar n, i) \ typing (R, n) i (t,r,s) )}" locale typed_state = reachable_state+ fixes typing :: "typing" assumes approximates: "(t,r,s) \ approx typing" locale typed_thread = reachable_thread+ fixes typing :: "typing" assumes approximates: "(t,r,s) \ approx typing" sublocale typed_thread \ typed_state using approximates by unfold_locales lemma approx_unfold: "((t,r,s) \ approx typing) = (\ (i,step) \ steps t. \ R. roleMap r i = Some R \ (\ n. MVar n \ FV (stepPat step) \ s (MVar n, i) \ typing (R, n) i (t,r,s)))" by (auto simp: approx_def) lemma in_approxI: assumes all_vars: "\ i step R n. \ (i,step) \ steps t; roleMap r i = Some R; MVar n \ FV (stepPat step) \ \ s (MV n i) \ typing (R, n) i (t,r,s)" shows "(t,r,s) \ approx typing" by (auto simp: approx_unfold dest!: all_vars) lemma in_approxD: assumes approx: "(t,r,s) \ approx typing" and asms: "(i,step) \ steps t" "roleMap r i = Some R" "MVar n \ FV (stepPat step)" shows "s (MV n i) \ typing (R, n) i (t,r,s)" using approx asms by (auto simp: approx_unfold) subsection{* A Combinator Language for Message Types *} subsubsection{* Structural Types *} text{* Structural message types specify the structure of a message. Their interpretation is independent of the thread that they are interpreted in. All of them except the type for nonces of a specific role are also independent of the state that they are interpreted in. *} definition ConstT :: "id \ msgtype" where "ConstT c i q \ { LC c }" definition AgentT :: msgtype where "AgentT i q \ Agent" declare AgentT_def [simp] definition NonceT :: "role \ id \ msgtype" where "NonceT R n i q \ case q of (t,r,s) \ { LN n i' | i'. roleMap r i' = Some R }" definition HashT :: "msgtype \ msgtype" where "HashT ty i q \ { Hash m | m. m \ ty i q }" definition TupT :: "msgtype \ msgtype \ msgtype" where "TupT ty1 ty2 i q \ { \x, y\ | x y. x \ ty1 i q \ y \ ty2 i q }" definition EncT :: "msgtype \ msgtype \ msgtype" where "EncT tym tyk i q \ { Enc m k | m k. m \ tym i q \ k \ tyk i q }" definition PKT :: "msgtype \ msgtype" where "PKT ty i q \ { PK m | m. m \ ty i q }" definition SKT :: "msgtype \ msgtype" where "SKT ty i q \ { SK m | m. m \ ty i q }" definition KT :: "msgtype \ msgtype \ msgtype" where "KT ty1 ty2 i q \ { K m1 m2 | m1 m2. m1 \ ty1 i q \ m2 \ ty2 i q }" definition KShrT :: "msgtype" where "KShrT i q \ { KShr A | A. True }" text{* TODO: Make this syntax abbreviation work. *} syntax "@TTuple" :: "['a, args] => 'a * 'b" ("(2{$_,/ _$})") syntax (xsymbols) "@TTuple" :: "['a, args] => 'a * 'b" ("(2\$_,/ _$\)") translations "{$x, y, z$}" == "{$x, {$y, z$}$}" "{$x, y$}" == "WeakTyping.TupT x y" subsubsection{* Special Types *} text{* The empty type denotes that a variable is never instantiated. The sum type denotes the union of two message types, while the intersection type denotes the intersection of the two message types. *} definition EmptyT :: "msgtype" where "EmptyT i q \ {}" declare EmptyT_def [simp] definition SumT :: "msgtype \ msgtype \ msgtype" where "SumT ty1 ty2 i q \ ty1 i q \ ty2 i q" definition InterT :: "msgtype \ msgtype \ msgtype" where "InterT ty1 ty2 i q \ ty1 i q \ ty2 i q" text{* The known before a specific role step type captures the interaction of the intruder with the execution of a protocol. If the intruder fakes a message being received, then the variables are instantiated with messages known to the intruder \emph{before} the receive step was executed. *} definition KnownT :: "rolestep \ msgtype" where "KnownT step i q \ case q of (t,r,s) \ { m | m. predOrd t (Ln m) (St (i,step)) }" subsubsection{* Constructing Type Invariants *} text{* The following function converts a list of type assignments to a typing. It will be used to specify the type invariants of specific protocols. *} definition mk_typing :: "((role \ id) \ msgtype) list \ typing" where "mk_typing tyeqs \ foldr (\(x, ty) typing. typing(x:=ty)) tyeqs (\x. EmptyT)" subsection{* Proving Well-Typedness *} text{* A protocol is well-typed with respect to a typing iff all its reachable state are approximated by the typing. We specialize the induction scheme that we get from the definition of the set of reachable states to a ``type checking'' induction scheme. It works for message typings that are monotoneous with respect to state updates. *} definition monoTyp :: "typing \ bool" where "monoTyp typ \ (\ x i t r s t' r'. roleMap r \\<^sub>m roleMap r' \ typ x i (t,r,s) \ typ x i (t@t',r',s) )" lemma monoTypI: "\ \ x i t r s t' r' s' m. \ roleMap r \\<^sub>m roleMap r'; m \ typ x i (t,r,s) \ \ m \ typ x i (t@t',r',s) \ \ monoTyp typ" by(auto simp: monoTyp_def) lemma monoTypD: "\ monoTyp typ; m \ typ x i (t,r,s); roleMap r \\<^sub>m roleMap r' \ \ m \ typ x i (t@t',r',s)" unfolding monoTyp_def by blast lemma monoTyp_freeD: "\ monoTyp typ; m \ typ x i (t,r,s); roleMap r \\<^sub>m roleMap r'; t' = t@t'' \ \ m \ typ x i (t',r',s)" unfolding monoTyp_def by blast lemma monoTyp_appendD: "\ monoTyp typ; m \ typ x i (t,r,s) \ \ m \ typ x i (t@t',r,s)" by (rule monoTyp_freeD) auto lemma (in reachable_state) reachable_in_approxI: assumes monoTyp: "monoTyp typing" and recv_case: "\ t r s i done todo skipped m R step n. \ R \ P; roleMap r i = Some R; (done, step # todo) \ set (splits R); recvStep step; MVar n \ FV (stepPat step); \ step' \ set done. MVar n \ FV (stepPat step'); hint ''completenessCase'' (step, n); (t,r,s) \ reachable P; (t,r,s) \ approx typing; r i = Some (done, step # todo, skipped); Some m = inst s i (stepPat step); m \ knows t \ \ s (MV n i) \ typing (R, n) i (t @ [Step (i, step)], r(i \ (done @ [step], todo, skipped)), s)" shows "(t,r,s) \ approx typing" proof - { fix i step "done" todo skipped n have "\ r i = Some (done, todo, skipped); (i,step) \ steps t; MVar n \ FV (stepPat step) \ \ s (MV n i) \ typing (done@todo, n) i (t,r,s)" proof(induct arbitrary: i "done" todo step n skipped rule: reachable_induct) case (send t r s i "done" l pt todo skipped m i' done' todo' step n skipped') then interpret th1: reachable_thread P t r s i "done" "Send l pt # todo" skipped by unfold_locales auto from send have "(i', step) \ steps t \ (i' = i) \ step = Send l pt" (is "?old \ ?new") by auto moreover { assume ?old hence "s(MV n i') \ typing (done'@todo', n) i' (t,r,s)" using send by (auto split: if_splits) hence ?case proof(rule monoTyp_freeD[OF monoTyp]) qed (auto simp: th1.thread_exists) } moreover { let ?role = "done @ Send l pt # todo" note IH = send(2) assume "?new" hence "MVar n \ FV pt" using send by auto then obtain l' msg' where "(i, Recv l' msg') \ steps t" "MVar n \ FV msg'" by (fastforce dest!: th1.send_step_FV[OF th1.thread_exists]) hence typed: "s (MV n i) \ typing (?role, n) i (t, r, s)" by (auto dest: IH[OF th1.thread_exists]) have "done'@todo' = ?role" using send `?new` by auto hence ?case using `?new` apply - apply(rule monoTyp_freeD[OF monoTyp]) by (auto intro!: typed simp: th1.thread_exists) } ultimately show ?case by fast next case (recv t r s i "done" l pt todo skipped m i' done' todo' step n skipped') then interpret th1: reachable_thread P t r s i "done" "Recv l pt # todo" skipped by unfold_locales auto from recv have "(i', step) \ steps t \ (i' = i) \ step = Recv l pt" (is "?old \ ?new") by auto moreover { assume ?old hence "s(MV n i') \ typing (done'@todo', n) i' (t,r,s)" using recv by (auto split: if_splits) hence ?case using `?old` apply - apply(rule monoTyp_freeD[OF monoTyp]) apply(auto simp: th1.thread_exists) done } moreover { let ?role = "done @ Recv l pt # todo" note IH = recv(2) assume "?new" hence cur_thread: "?role = done' @ todo'" "i = i'" using recv by auto have ?case proof(cases "\ step' \ set done. MVar n \ FV (stepPat step')") case True with recv obtain step'' where FV: "MVar n \ FV (stepPat step'')" and step'': "step'' \ set done" by auto thus ?thesis proof(cases "step'' \ skipped") case False then obtain step' where FV: "MVar n \ FV (stepPat step')" and notinSkipped: "step' \ skipped" and step': "(i,step') \ steps t" using FV step'' by(auto simp add: th1.in_steps_conv_done_skipped) hence "s (MV n i) \ typing (?role, n) i (t, r, s)" by (auto intro!: IH th1.thread_exists) hence "s (MV n i') \ typing (done'@todo', n) i' (t, r, s)" using cur_thread by simp thus ?thesis apply - apply(rule monoTyp_freeD[OF monoTyp]) apply(auto simp: th1.thread_exists) done next case True then obtain step' where FV: "MVar n \ FV (stepPat step')" and inSkipped: "step' \ skipped" and inDone: "step' \ set done" using FV step'' by auto then obtain l' ty' pt' where noteEq: "(Note l' ty' pt') = step'" by(auto dest!: th1.note_in_skipped) hence "\ l'' pt''. listOrd ?role (Recv l'' pt'') (Note l' ty' pt') \ MVar n \ FV (stepPat (Recv l'' pt''))" using FV inSkipped inDone apply - apply(drule FV_FMV_conv[THEN iffD2]) apply(subgoal_tac "Note l' ty' pt' \ set ?role") apply(drule th1.Note_FV) by (auto dest: FV_FMV_conv[THEN iffD1]) then obtain l'' pt'' where roleBefore: "listOrd ?role (Recv l'' pt'') (Note l' ty' pt')" and varOfRecv: "MVar n \ FV (stepPat (Recv l'' pt''))" by auto hence "Recv l'' pt'' \ set done" using inDone noteEq apply - apply(drule listOrd_append[THEN iffD1]) apply(case_tac "listOrd done (Recv l'' pt'') (Note l' ty' pt')") apply(fastforce dest: in_set_listOrd1) apply(case_tac "listOrd (Recv l pt # todo) (Recv l'' pt'') (Note l' ty' pt')") apply(fastforce dest: in_set_listOrd2 th1.done_notin_todoD) apply(case_tac "Recv l'' pt'' \ set done \ Note l' ty' pt' \ set (Recv l pt # todo)") by(auto dest: th1.done_notin_todoD) hence "(i, Recv l'' pt'') \ steps t" by auto hence "s (MV n i) \ typing (?role, n) i (t, r, s)" using varOfRecv by (auto intro!: IH th1.thread_exists) hence "s (MV n i') \ typing (done'@todo', n) i' (t, r, s)" using cur_thread by simp thus ?thesis apply - apply(rule monoTyp_freeD[OF monoTyp]) apply(auto simp: th1.thread_exists) done qed next case False moreover have "(t, r, s) \ approx typing" by (auto simp: approx_unfold dest!: IH roleMap_SomeD) moreover have "MVar n \ FV pt" using `MVar n \ FV (stepPat step)` `?new` by auto moreover note `Some m = inst s i pt` moreover note `m \ knows t` moreover note cur_thread[symmetric] ultimately show ?thesis apply - apply(clarsimp simp del: fun_upd_apply) apply(rule recv_case) apply(simp_all add: th1.role_in_P th1.roleMap th1.thread_exists in_set_splits_conv remove_hints) done qed } ultimately show ?case by fastforce next case (compr t r s i "done" l ty pt todo skipped m i' done' todo' step n skipped') then interpret th1: reachable_thread P t r s i "done" "Note l ty pt # todo" skipped by unfold_locales auto from compr have "(i', step) \ steps t \ (i' = i) \ step = Note l ty pt" (is "?old \ ?new") by auto moreover { assume ?old hence "s(MV n i') \ typing (done'@todo', n) i' (t,r,s)" using compr by (auto split: if_splits) hence ?case proof(rule monoTyp_freeD[OF monoTyp]) qed (auto simp: th1.thread_exists) } moreover { let ?role = "done @ Note l ty pt # todo" note IH = compr(2) assume "?new" hence "MVar n \ FV pt" using compr by auto then obtain l' msg' where "(i, Recv l' msg') \ steps t" "MVar n \ FV msg'" by (fastforce dest!: th1.note_step_FV[OF th1.thread_exists]) hence typed: "s (MV n i) \ typing (?role, n) i (t, r, s)" by (auto dest: IH[OF th1.thread_exists]) have "done'@todo' = ?role" using compr `?new` by auto hence ?case using `?new` apply - apply(rule monoTyp_freeD[OF monoTyp]) by (auto intro!: typed simp: th1.thread_exists) } ultimately show ?case by fast next case (skip t r s i "done" l ty pt todo skipped i' done' todo' step n skipped') then interpret th1: reachable_thread P t r s i "done" "Note l ty pt # todo" skipped by unfold_locales auto from skip have "(i', step) \ steps t" (is "?old") by auto hence "s(MV n i') \ typing (done'@todo', n) i' (t,r,s)" using skip by (auto split: if_splits) thus ?case proof(rule monoTyp_freeD[OF monoTyp]) qed (auto simp: th1.thread_exists) qed (auto intro: monoTyp_appendD[OF monoTyp]) } thus ?thesis unfolding approx_unfold by(auto elim!: roleMap_SomeE) qed text{* We prove a variant of the above lemma, which is suitable for automation. The difference is the description of the variables that need to be checked. Here, it is done such that Isabelle's simplifier gets a finite set that it can rewrite into normal form. *} lemma (in reachable_state) reachable_in_approxI_ext: assumes monoTyp: "monoTyp typing" and recv_case: "\ t r s i done todo skipped m R step n. \ R \ P; roleMap r i = Some R; (done, step # todo) \ set (splits R); recvStep step; MVar n \ foldl (\ fv step'. fv - FV (stepPat step')) (FV (stepPat step)) done; hint ''completenessCase'' (step, n); (t,r,s) \ reachable P; (t,r,s) \ approx typing; r i = Some (done, step # todo ,skipped); Some m = inst s i (stepPat step); m \ knows t \ \ s (MV n i) \ typing (R, n) i ( t @ [Step (i, step)], r(i \ (done @ [step], todo, skipped)), s)" shows "(t,r,s) \ approx typing" proof(induct rule: reachable_in_approxI[OF monoTyp]) case (1 t r s i "done" todo skipped m R step n) { fix v V assume "v \ V" and "\step'\set done. v \ FV (stepPat step')" hence "v \ foldl (\ fv step'. fv - FV (stepPat step')) V done" by (induct "done" arbitrary: V) auto } hence "MVar n \ foldl (\ fv step'. fv - FV (stepPat step')) (FV (stepPat step)) done" using 1 by auto thus ?case using 1 apply(subgoal_tac "True") apply(clarsimp) apply(rule recv_case) by(assumption | simp add: remove_hints)+ qed text{* Proving typing monotonicity *} subsubsection{* Monotonicity Proofs *} definition monoMsgTyp :: "msgtype \ bool" where "monoMsgTyp ty \ (\i t r s t' r'. roleMap r \\<^sub>m roleMap r' \ ty i (t, r, s) \ ty i (t @ t', r', s))" lemma monoMsgTypD: "\ monoMsgTyp ty; m \ ty i (t,r,s); roleMap r \\<^sub>m roleMap r' \ \ m \ ty i (t@t',r',s)" unfolding monoMsgTyp_def by blast lemma monoMsgTyp_SumTI[intro!]: assumes ty1: "monoMsgTyp ty1" and ty2: "monoMsgTyp ty2" shows "monoMsgTyp (SumT ty1 ty2)" by(auto simp: monoMsgTyp_def SumT_def dest: monoMsgTypD[OF ty1] monoMsgTypD[OF ty2]) lemma monoMsgTyp_InterTI[intro!]: assumes ty1: "monoMsgTyp ty1" and ty2: "monoMsgTyp ty2" shows "monoMsgTyp (InterT ty1 ty2)" by(auto simp: monoMsgTyp_def InterT_def dest: monoMsgTypD[OF ty1] monoMsgTypD[OF ty2]) lemma monoMsgTyp_KnownTI[iff]: shows "monoMsgTyp (KnownT step)" by(auto simp: monoMsgTyp_def KnownT_def ) lemma monoMsgTyp_NonceTI[iff]: shows "monoMsgTyp (NonceT R n)" by(auto simp: monoMsgTyp_def NonceT_def dest: map_leD) lemma monoMsgTyp_ConstTI[iff]: shows "monoMsgTyp (ConstT c)" by(auto simp: monoMsgTyp_def ConstT_def dest: map_leD) lemma monoMsgTyp_AgentTI[iff]: shows "monoMsgTyp AgentT" by(auto simp: monoMsgTyp_def AgentT_def dest: map_leD) lemma monoMsgTyp_EncTI[intro!]: assumes ty1: "monoMsgTyp ty1" and ty2: "monoMsgTyp ty2" shows "monoMsgTyp (EncT ty1 ty2)" by(auto simp: monoMsgTyp_def EncT_def dest: monoMsgTypD[OF ty1] monoMsgTypD[OF ty2]) lemma monoMsgTyp_KTI[intro!]: assumes ty1: "monoMsgTyp ty1" and ty2: "monoMsgTyp ty2" shows "monoMsgTyp (KT ty1 ty2)" by(auto simp: monoMsgTyp_def KT_def dest: monoMsgTypD[OF ty1] monoMsgTypD[OF ty2]) lemma monoMsgTyp_KShrTI[intro!]: shows "monoMsgTyp KShrT" by(auto simp: monoMsgTyp_def KShrT_def) lemma monoMsgTyp_TupTI[intro!]: assumes ty1: "monoMsgTyp ty1" and ty2: "monoMsgTyp ty2" shows "monoMsgTyp (TupT ty1 ty2)" by(auto simp: monoMsgTyp_def TupT_def dest: monoMsgTypD[OF ty1] monoMsgTypD[OF ty2]) lemma monoMsgTyp_HashTI[intro!]: assumes ty: "monoMsgTyp ty" shows "monoMsgTyp (HashT ty)" by(auto simp: monoMsgTyp_def HashT_def dest: monoMsgTypD[OF ty]) lemma monoMsgTyp_PKTI[intro!]: assumes ty: "monoMsgTyp ty" shows "monoMsgTyp (PKT ty)" by(auto simp: monoMsgTyp_def PKT_def dest: monoMsgTypD[OF ty]) lemma monoMsgTyp_SKTI[intro!]: assumes ty: "monoMsgTyp ty" shows "monoMsgTyp (SKT ty)" by(auto simp: monoMsgTyp_def SKT_def dest: monoMsgTypD[OF ty]) lemma monoTyp_mk_typing[intro!]: assumes monoMsgTyp: "\ pos ty. (pos, ty) \ set tyeqs \ monoMsgTyp ty" shows "monoTyp (mk_typing tyeqs)" using monoMsgTyp proof(induct tyeqs) case Nil thus ?case by(auto intro!: monoTypI simp: mk_typing_def EmptyT_def) next case (Cons tyeq tyeqs) thus ?case proof(cases tyeq) case (Pair pos' ty') have mk_typing_Cons [simp]: "mk_typing ((pos',ty')#tyeqs) = (mk_typing tyeqs)(pos' := ty')" by (simp add: mk_typing_def) with Pair show ?thesis apply(auto intro!: monoTypI) apply(auto intro: monoTypD[OF Cons(1), OF Cons(2)] monoMsgTypD[OF Cons(2)] simp: Pair) done qed qed subsection{* Automation *} text{* The automation of the welltypedness proofs is rather fragile. As a general rule of operation we try to unfold type information as late as possible. This allows to guide the automation tools by matching on the structure of the types. However, it also implies that more rules are necessary. Moreover the current set of rules is not guaranteed to be complete. *} subsubsection{* ACI of Sum and Intersection Types *} lemma SumT_absorb [simp]: "SumT ty ty = ty" by (rule ext, rule ext) (auto simp: SumT_def) lemma SumT_left_absorb: "SumT ty1 (SumT ty1 ty2) = SumT ty1 ty2" by (rule ext, rule ext) (auto simp: SumT_def) lemma SumT_commute: "SumT ty1 ty2 = SumT ty1 ty2" by (rule ext, rule ext) (auto simp: SumT_def) lemma SumT_left_commute: "SumT ty1 (SumT ty2 ty3) = SumT ty2 (SumT ty1 ty3)" by (rule ext, rule ext) (auto simp: SumT_def) lemma SumT_assoc: "SumT (SumT ty1 ty2) ty3 = SumT ty1 (SumT ty2 ty3)" by (rule ext, rule ext) (auto simp: SumT_def) lemmas SumT_ac = SumT_assoc SumT_left_absorb SumT_commute SumT_left_commute -- {* Type sum is an AC-operator *} lemma InterT_absorb [simp]: "InterT ty ty = ty" by (rule ext, rule ext) (auto simp: InterT_def) lemma InterT_left_absorb: "InterT ty1 (InterT ty1 ty2) = InterT ty1 ty2" by (rule ext, rule ext) (auto simp: InterT_def) lemma InterT_commute: "InterT ty1 ty2 = InterT ty1 ty2" by (rule ext, rule ext) (auto simp: InterT_def) lemma InterT_left_commute: "InterT ty1 (InterT ty2 ty3) = InterT ty2 (InterT ty1 ty3)" by (rule ext, rule ext) (auto simp: InterT_def) lemma InterT_assoc: "InterT (InterT ty1 ty2) ty3 = InterT ty1 (InterT ty2 ty3)" by (rule ext, rule ext) (auto simp: InterT_def) lemmas InterT_ac = InterT_assoc InterT_left_absorb InterT_commute InterT_left_commute -- {* Type intersection is an AC-operator *} subsubsection{* Type Membership *} text{* Special types *} text{* The rules for SumT are special, as their usage currently depends on the combination of SumT and KnownT. *} lemma in_SumTE: "\ m \ SumT ty1 ty2 i q; m \ ty1 i q \ R; m \ ty2 i q \ R \ \ R" by(auto simp: SumT_def) lemma notin_SumTE [elim!]: "\ m \ SumT ty1 ty2 i q; \ m \ ty1 i q; m \ ty2 i q \ \ R \ \ R" by(auto simp: SumT_def) lemma notin_KnownT_append_StepE [dest!]: "m \ KnownT step i (t@ [Step (i, step)], r, s) \ m \ knows t" by(auto simp: KnownT_def) text{* Direct unfoldings *} lemma in_ConstT_simp [iff]: "(m \ ConstT c i q) = (m = LC c)" by(auto simp: ConstT_def) lemma in_KnownTD [dest!]: "(x \ KnownT step i (t,r,s)) \ (predOrd t (Ln x) (St (i, step)))" by(simp add: KnownT_def) text{* To be used together with if\_splits in a well-typedness proof *} lemma in_NonceT [simp]: "(LN n i \ NonceT R n i' (t,r,s)) = (roleMap r i = Some R)" by(simp add: NonceT_def) lemma in_NonceTE [elim!]: "\ m \ NonceT R n i (t,r,s); \ nTid. \ m = LN n nTid; roleMap r nTid = Some R \ \ Q \ \ Q" by(auto simp: NonceT_def) lemma notin_NonceT_thenonceE [elim!]: "\ LN n nTid \ NonceT nR n i (t, r(i \ (done,todo,skipped)), s); nTid = i \ Q; \ nTid \ i; roleMap r nTid \ Some nR \ \ Q \ \ Q" by(auto simp: NonceT_def split: if_splits) text{* These rules ensure that type information is exploited when a match impossible without intruder activity happening. *} lemma Tup_in_SumT_KnownT_NonceTD [dest!]: "Tup x y \ SumT (KnownT step) (NonceT R n) i (t, r, s) \ predOrd t (Ln (Tup x y)) (St (i, step))" by(auto simp: KnownT_def SumT_def) lemma Hash_in_SumT_KnownT_NonceTD [dest!]: "Hash x \ SumT (KnownT step) (NonceT R n) i (t, r, s) \ predOrd t (Ln (Hash x)) (St (i, step))" by(auto simp: KnownT_def SumT_def) lemma Enc_in_SumT_KnownT_NonceTD [dest!]: "Enc x y \ SumT (KnownT step) (NonceT R n) i (t, r, s) \ predOrd t (Ln (Enc x y)) (St (i, step))" by(auto simp: KnownT_def SumT_def) text{* TODO: Extend these lemmas for further combinations. This requires some more thinking. However, for a well-designed protocols the message structure is not required for disambiguation because the encryptions contain tags that identify them. *} text{* Structural types *} text{* The following rules are used in well-typedness proofs *} lemma Hash_in_HashT [simp]: "(Hash x \ HashT ty i q) = (x \ ty i q)" by (simp add: HashT_def) lemma Tup_in_TupT [simp]: "(Tup x y \ TupT ty1 ty2 i q) = (x \ ty1 i q \ y \ ty2 i q)" by (simp add: TupT_def) lemma Enc_in_EncT [simp]: "(Enc x y \ EncT ty1 ty2 i q) = (x \ ty1 i q \ y \ ty2 i q)" by (simp add: EncT_def) lemma PK_in_PKT [simp]: "(PK x \ PKT ty i q) = (x \ ty i q)" by (simp add: PKT_def) lemma SK_in_SKT [simp]: "(SK x \ SKT ty i q) = (x \ ty i q)" by (simp add: SKT_def) lemma K_in_KT [simp]: "(K x y \ KT ty1 ty2 i q) = (x \ ty1 i q \ y \ ty2 i q)" by (simp add: KT_def) text{* The following rules ensure that messages are expanded automatically in when applying the sources rule. *} lemma in_HashTE [elim!]: "\ m \ HashT ty i q; \ x. \ m = Hash x; x \ ty i q \ \ Q \ \ Q" by(auto simp: HashT_def) lemma in_EncTE [elim!]: "\ m \ EncT ty1 ty2 i q; \ x k. \ m = Enc x k; x \ ty1 i q; k \ ty2 i q \ \ Q \ \ Q" by(auto simp: EncT_def) lemma in_TupTE [elim!]: "\ m \ TupT ty1 ty2 i q; \ x y. \ m = \ x, y \; x \ ty1 i q; y \ ty2 i q \ \ Q \ \ Q" by(auto simp: TupT_def) lemma in_KTE [elim!]: "\ m \ KT ty1 ty2 i q; \ x y. \ m = K x y; x \ ty1 i q; y \ ty2 i q \ \ Q \ \ Q" by(auto simp: KT_def) lemma in_PKTE [elim!]: "\ m \ PKT ty i q; \ x. \ m = PK x; x \ ty i q \ \ Q \ \ Q" by(auto simp: PKT_def) lemma in_SKTE [elim!]: "\ m \ SKT ty i q; \ x. \ m = SK x; x \ ty i q \ \ Q \ \ Q" by(auto simp: SKT_def) subsection{* Specialization of the Chain Rule *} text{* We prove the case distinction on messages known to the intruder with respect to an typed state, because we need a sufficiently precise invariant when reasoning about the contents of a variable. *} lemma (in typed_state) knows_cases: assumes known: "m' \ knows t" shows " (m' \ IK0 \ hint ''case_name'' ''ik0'' ) \ (\ m. m' = Hash m \ Ln m \ Ln (Hash m) \ hint ''case_name'' ''fake'' ) \ (\ m k. m' = Enc m k \ Ln m \ Ln (Enc m k) \ Ln k \ Ln (Enc m k) \ hint ''case_name'' ''fake'' ) \ (\ x y. m' = Tup x y \ Ln x \ Ln (Tup x y) \ Ln y \ Ln (Tup x y) \ hint ''case_name'' ''fake'' ) \ (\ R \ P. \ i. roleMap r i = Some R \ (\ step \ set R. (sendStep step \ noteStep step) \ (\ m. Some m = inst s i (stepPat step) \ decrChain [] t {St (i, step)} m m') \ prefixClose s t R step i \ (\ v \ FV (stepPat step). \ n. v = MVar n \ s (MVar n, i) \ typing (R, n) i (t,r,s)) \ hint ''decrChainFrom'' (i, R, step) \ hint ''case_name'' ''decrypt'' ) ) \ (\ a. m' = SK a \ LKR a \ Ln m' \ hint ''case_name'' ''asym_lkr'') \ (\ a b. m' = K a b \ LKR a \ Ln m' \ hint ''case_name'' ''sym_lkr1'') \ (\ a b. m' = K a b \ LKR b \ Ln m' \ hint ''case_name'' ''sym_lkr2'') \ (\ A. \ a \ A. m' = KShr A \ LKR (LAg a) \ Ln m' \ hint ''case_name'' ''shr_lkr'') " (is "?ik0 \ ?hash \ ?encr \ ?tup \ ?decr \ ?keys") proof - from known have "(m' \ IK0) \ (\ m. m' = Hash m \ Ln m \ Ln (Hash m)) \ (\ m k. m' = Enc m k \ Ln m \ Ln (Enc m k) \ Ln k \ Ln (Enc m k)) \ (\ x y. m' = Tup x y \ Ln x \ Ln (Tup x y) \ Ln y \ Ln (Tup x y)) \ (\ i done todo skipped. r i = Some (done, todo, skipped) \ (\ l pt m. Send l pt \ set done \ Some m = inst s i pt \ decrChain [] t {St (i, Send l pt)} m m' ) ) \ (\ i done todo skipped. r i = Some (done, todo, skipped) \ (\ l ty pt m. Note l ty pt \ set done \ Note l ty pt \ skipped \ Some m = inst s i pt \ decrChain [] t {St (i, Note l ty pt)} m m' ) ) \ (\ a. m' = SK a \ LKR a \ Ln m') \ (\ a b. m' = K a b \ LKR a \ Ln m') \ (\ a b. m' = K a b \ LKR b \ Ln m') \ (\ A. \ a \ A. m' = KShr A \ LKR (LAg a) \ Ln m') " (is "?ik0_raw \ ?hash_raw \ ?encr_raw \ ?tup_raw \ ?decr_raw \ ?note_raw \ ?keys_raw") by (rule knows_cases_raw) moreover { assume "?ik0_raw" hence ?thesis by( simp add: remove_hints) } moreover { assume "?hash_raw" hence ?thesis by( simp add: remove_hints) } moreover { assume "?encr_raw" hence ?thesis by( simp add: remove_hints) } moreover { assume "?tup_raw" hence ?thesis by( simp add: remove_hints) } moreover { assume "?keys_raw" hence ?thesis by( simp add: remove_hints) } moreover { assume ?decr_raw then obtain i "done" todo l msg skipped m where thread_exists: "r i = Some (done, todo,skipped)" and send_done: "Send l msg \ set done" and msg: "Some m = inst s i msg" and decrChain: "decrChain [] t {St (i, Send l msg)} m m'" by fast then interpret th1: typed_thread P t r s i "done" todo skipped "typing" using approximates by unfold_locales from send_done have send_step: "(i, Send l msg) \ steps t" by (rule th1.in_steps_send[THEN iffD1]) moreover have "prefixClose s t (done@todo) (Send l msg) i" using send_step by(auto intro!: prefixCloseI th1.roleMap) moreover have send_in_role: "Send l msg \ set (done @ todo)" using send_done by simp moreover note in_approxD[OF th1.approximates send_step th1.roleMap, simplified] ultimately have ?decr using decrChain msg apply - apply(rule bexI[OF _ th1.role_in_P]) apply(rule exI) apply(rule conjI[OF th1.roleMap]) apply(rule bexI[OF _ send_in_role]) by(auto simp: remove_hints) hence ?thesis by blast } moreover { assume ?note_raw then obtain i "done" todo skipped l ty pt m where thread_exists: "r i = Some (done, todo,skipped)" and inDone: "Note l ty pt \ set done" and notinSkipped: "Note l ty pt \ skipped" and msg: "Some m = inst s i pt" and decrChain: "decrChain [] t {St (i, Note l ty pt)} m m'" by fast then interpret th1: typed_thread P t r s i "done" todo skipped "typing" using approximates by unfold_locales from inDone and notinSkipped have note_step: "(i, Note l ty pt) \ steps t" by (fastforce dest!: th1.in_steps_eq_in_done) moreover have "prefixClose s t (done@todo) (Note l ty pt) i" using note_step by(auto intro!: prefixCloseI th1.roleMap) moreover have note_in_role: "Note l ty pt \ set (done @ todo)" using inDone by simp moreover note in_approxD[OF th1.approximates note_step th1.roleMap, simplified] ultimately have ?decr using decrChain msg apply - apply(rule bexI[OF _ th1.role_in_P]) apply(rule exI)+ apply(rule conjI[OF th1.roleMap]) apply(rule bexI[OF _ note_in_role]) by(auto simp: remove_hints) hence ?thesis by blast } ultimately show ?thesis by fastforce qed lemma (in reachable_state) decrChain_AgentE: assumes decrChain: "decrChain path t from m m'" and Agent: "m \ Agent" and nonempty: "from = {} \ Q" shows "Q" using decrChain Agent nonempty by (auto simp: Agent_def) (* lemma (in reachable_state) decrChain_AgentTE: assumes decrChain: "decrChain path t from m m'" and AgentT: "m \ AgentT i (t,r,s)" and elim: "m = m' \ Q" shows "Q" using decrChain AgentT elim by (auto simp: AgentT_def) *) lemma (in reachable_state) decrChain_imp_predOrd: "\ decrChain path t from m m' \ \ \ im \ pairParts m. (\ f \ from. f \ Ln im) \ Ln im \ Ln m'" proof(induct m arbitrary: path "from") case (Tup x y) thus ?case apply(simp) apply(erule disjE) apply(clarsimp) apply(rule disjI2) apply(fastforce intro: less_le_trans) done qed fastforce+ lemma (in reachable_state) decrChain_KnownT: assumes decrChain: "decrChain path t from m m'" and KnownT: "predOrd t (Ln m) (St (i, step))" shows "\ im \ pairParts m. (\ f \ from. f \ Ln im) \ Ln im \ St (i, step)" proof - obtain im where "im \ pairParts m" and "\f \ from. f \ Ln im" using decrChain by (fast dest!: decrChain_imp_predOrd) with KnownT show ?thesis by (fastforce intro: pairParts_before) qed lemma (in reachable_state) decrChain_KnownTE: assumes decrChain: "decrChain path t from m m'" and KnownT: "predOrd t (Ln m) (St (i, step))" and elim: "\ im. \ im \ pairParts m; \ f \ from. f \ Ln im; Ln im \ St (i, step) \ \ Q" shows "Q" using decrChain KnownT by(auto dest!: decrChain_KnownT elim) lemma (in reachable_state) decrChain_SumT_KnownTE: assumes KnownT: "m \ SumT (KnownT step) ty i (t,r,s)" and decrChain: "decrChain path t from m m'" and elimK: "\ im. \ im \ pairParts m; \ f \ from. f \ Ln im; Ln im \ St (i, step) \ \ Q" and elimTy: "m \ ty i (t,r,s) \ Q" shows "Q" using decrChain KnownT elimK elimTy by(auto elim!: in_SumTE decrChain_KnownTE) lemma (in reachable_state) AV_in_Agent [iff]: "s (AV a aTid) \ Agent" using inst_AVar_cases by (auto simp: Agent_def) end (* TODO: - Remove agent variables from protocol model by replacing them with type annotations. [General version will require us to reintroduce create event. But composition becomes nicer, as we can now model protocols, which use parameters that are secret values and so on. ] - Put decryption chain case into recursion of chain predicate to make more information available locally. *)