datatype constructors are injective, we have the theorem
{thm [display,indent=0] msg.inject(5) [THEN iffD1, of K X K' X']}
ciphertext can be decrypted using only one key and
yield only one plaintext. In the real world, decryption with the
key succeeds but yields garbage. Our model of encryption is
if encryption adds some redundancy to the plaintext, such as a
, so that garbage can be detected. ›
(*<*) text‹Concrete syntax: messages appear as ‹{A,B,NA}›, etc...› syntax "_MTuple" :: "['a, args] ==> 'a * 'b" ("(‹indent=2 notation=‹mixfix message tuple››\_,/ _})")
syntax_consts "_MTuple"⇌ MPair translations "{x, y, z}"⇌"{x, {y, z}}" "{x, y}"⇌"CONST MPair x y"
definition keysFor :: "msg set ==> key set"where
― ‹Keys useful to decrypt elements of a message set› "keysFor H == invKey ` {K. ∃X. Crypt K X ∈ H}"
subsubsection‹Inductive Definition of All Parts" of a Message›
inductive_set
parts :: "msg set ==> msg set" for H :: "msg set" where
Inj [intro]: "X ∈ H ==> X ∈ parts H"
| Fst: "{X,Y}∈ parts H ==> X ∈ parts H"
| Snd: "{X,Y}∈ parts H ==> Y ∈ parts H"
| Body: "Crypt K X ∈ parts H ==> X ∈ parts H"
lemma Crypt_imp_invKey_keysFor: "Crypt K X ∈ H ==> invKey K ∈ keysFor H" by (unfold keysFor_def, blast)
subsection‹Inductive relation "parts"›
lemma MPair_parts: "[| {X,Y}∈ parts H; [| X ∈ parts H; Y ∈ parts H |] ==> P |] ==> P" by (blast dest: parts.Fst parts.Snd)
declare MPair_parts [elim!] parts.Body [dest!] text‹NB These two rules are UNSAFE in the formal sense, as they discard the
compound message. They work well on THIS FILE. ‹MPair_parts› is left as SAFE because it speeds up proofs.
The Crypt rule is normally kept UNSAFE to avoid breaking up certificates.›
lemma parts_increasing: "H ⊆ parts(H)" by blast
lemmas parts_insertI = subset_insertI [THEN parts_mono, THEN subsetD]
lemma parts_emptyE [elim!]: "X∈ parts{} ==> P" by simp
text‹WARNING: loops if H = {Y}, therefore must not be repeated!› lemma parts_singleton: "X∈ parts H ==>∃Y∈H. X∈ parts {Y}" by (erule parts.induct, fast+)
lemma parts_insert: "parts (insert X H) = parts {X} ∪ parts H" apply (subst insert_is_Un [of _ H]) apply (simp only: parts_Un) done
text‹TWO inserts to avoid looping. This rewrite is better than nothing.
Not suitable for Addsimps: its behaviour can be strange.› lemma parts_insert2: "parts (insert X (insert Y H)) = parts {X} ∪ parts {Y} ∪ parts H" apply (simp add: Un_assoc) apply (simp add: parts_insert [symmetric]) done
lemma parts_UN_subset1: "(∪x∈A. parts(H x)) ⊆ parts(∪x∈A. H x)" by (intro UN_least parts_mono UN_upper)
lemma parts_UN [simp]: "parts(∪x∈A. H x) = (∪x∈A. parts(H x))" by (intro equalityI parts_UN_subset1 parts_UN_subset2)
text‹Added to simplify arguments to parts, analz and synth.
NOTE: the UN versions are no longer used!›
text‹This allows ‹blast› to simplify occurrences of term‹parts(G∪H)› in the assumption.› lemmas in_parts_UnE = parts_Un [THEN equalityD1, THEN subsetD, THEN UnE] declare in_parts_UnE [elim!]
lemma parts_insert_subset: "insert X (parts H) ⊆ parts(insert X H)" by (blast intro: parts_mono [THEN [2] rev_subsetD])
subsubsection‹Idempotence and transitivity›
lemma parts_partsD [dest!]: "X ∈ parts (parts H) ==> X∈ parts H" by (erule parts.induct, blast+)
lemma parts_idem [simp]: "parts (parts H) = parts H" by blast
lemma parts_subset_iff [simp]: "(parts G ⊆ parts H) = (G ⊆ parts H)" apply (rule iffI) apply (iprover intro: subset_trans parts_increasing) apply (frule parts_mono, simp) done
lemma parts_trans: "[| X∈ parts G; G ⊆ parts H |] ==> X∈ parts H" by (drule parts_mono, blast)
text‹Cut› lemma parts_cut: "[| Y∈ parts (insert X G); X∈ parts H |] ==> Y∈ parts (G ∪ H)" by (blast intro: parts_trans)
lemma parts_cut_eq [simp]: "X∈ parts H ==> parts (insert X H) = parts H" by (force dest!: parts_cut intro: parts_insertI)
subsubsection‹Rewrite rules for pulling out atomic messages›
text‹In any message, there is an upper bound N on its greatest nonce.› lemma msg_Nonce_supply: "∃N. ∀n. N≤n ⟶ Nonce n ∉ parts {msg}" apply (induct_tac "msg") apply (simp_all (no_asm_simp) add: exI parts_insert2) txt‹MPair case: blast works out the necessary sum itself!› prefer2apply auto apply (blast elim!: add_leE) txt‹Nonce case› apply (rename_tac nat) apply (rule_tac x = "N + Suc nat"in exI, auto) done (*>*)
section‹Modelling the Adversary›
text‹
spy is part of the system and must be built into the model. He is
malicious user who does not have to follow the protocol. He
the network and uses any keys he knows to decrypt messages.
he accumulates additional keys and nonces. These he can use to
new messages, which he may send to anybody.
functions enable us to formalize this behaviour: ‹analz› and ‹synth›. Each function maps a sets of messages to another set of
. The set ‹analz H› formalizes what the adversary can learn
the set of messages~$H$. The closure properties of this set are
inductively. ›
inductive_set
analz :: "msg set ==> msg set" for H :: "msg set" where
Inj [intro,simp] : "X ∈ H ==> X ∈ analz H"
| Fst: "{X,Y}∈ analz H ==> X ∈ analz H"
| Snd: "{X,Y}∈ analz H ==> Y ∈ analz H"
| Decrypt [dest]: "[Crypt K X ∈ analz H; Key(invKey K) ∈ analz H] ==> X ∈ analz H" (*<*) text‹Monotonicity; Lemma 1 of Lowe's paper› lemma analz_mono: "G⊆H ==> analz(G) ⊆ analz(H)" apply auto apply (erule analz.induct) apply (auto dest: analz.Fst analz.Snd) done
text‹Making it safe speeds up proofs› lemma MPair_analz [elim!]: "[| {X,Y}∈ analz H; [| X ∈ analz H; Y ∈ analz H |] ==> P |] ==> P" by (blast dest: analz.Fst analz.Snd)
lemma analz_increasing: "H ⊆ analz(H)" by blast
lemma analz_subset_parts: "analz H ⊆ parts H" apply (rule subsetI) apply (erule analz.induct, blast+) done
text‹Converse fails: we can analz more from the union than from the
separate parts, as a key in one might decrypt a message in the other› lemma analz_Un: "analz(G) ∪ analz(H) ⊆ analz(G ∪ H)" by (intro Un_least analz_mono Un_upper1 Un_upper2)
lemma analz_insert: "insert X (analz H) ⊆ analz(insert X H)" by (blast intro: analz_mono [THEN [2] rev_subsetD])
subsubsection‹Rewrite rules for pulling out atomic messages›
text‹Can pull out enCrypted message if the Key is not known› lemma analz_insert_Crypt: "Key (invKey K) ∉ analz H ==> analz (insert (Crypt K X) H) = insert (Crypt K X) (analz H)" apply (rule analz_insert_eq_I) apply (erule analz.induct, auto)
done
lemma lemma1: "Key (invKey K) ∈ analz H ==> analz (insert (Crypt K X) H) ⊆ insert (Crypt K X) (analz (insert X H))" apply (rule subsetI) apply (erule_tac x = x in analz.induct, auto) done
lemma lemma2: "Key (invKey K) ∈ analz H ==> insert (Crypt K X) (analz (insert X H)) ⊆ analz (insert (Crypt K X) H)" apply auto apply (erule_tac x = x in analz.induct, auto) apply (blast intro: analz_insertI analz.Decrypt) done
lemma analz_insert_Decrypt: "Key (invKey K) ∈ analz H ==> analz (insert (Crypt K X) H) = insert (Crypt K X) (analz (insert X H))" by (intro equalityI lemma1 lemma2)
text‹Case analysis: either the message is secure, or it is not! Effective,
can cause subgoals to blow up! Use with ‹if_split›; apparently ‹split_tac› does not cope with patterns such as term‹analz (insert
Crypt K X) H)›› lemma analz_Crypt_if [simp]: "analz (insert (Crypt K X) H) = (if (Key (invKey K) ∈ analz H) then insert (Crypt K X) (analz (insert X H)) else insert (Crypt K X) (analz H))" by (simp add: analz_insert_Crypt analz_insert_Decrypt)
text‹This rule supposes "for the sake of argument" that we have the key.› lemma analz_insert_Crypt_subset: "analz (insert (Crypt K X) H) ⊆ insert (Crypt K X) (analz (insert X H))" apply (rule subsetI) apply (erule analz.induct, auto) done
lemma analz_trans: "[| X∈ analz G; G ⊆ analz H |] ==> X∈ analz H" by (drule analz_mono, blast)
text‹Cut; Lemma 2 of Lowe› lemma analz_cut: "[| Y∈ analz (insert X H); X∈ analz H |] ==> Y∈ analz H" by (erule analz_trans, blast)
(*Cut can be proved easily by induction on "Y\<in>analz(insertXH)\<Longrightarrow>X\<in>analzH\<longrightarrow>Y\<in>analzH"
*)
text‹This rewrite rule helps in the simplification of messages that involve
the forwarding of unknown components (X). Without it, removing occurrences
of X can be very complicated.› lemma analz_insert_eq: "X∈ analz H ==> analz (insert X H) = analz H" by (blast intro: analz_cut analz_insertI)
lemma analz_cong: "[| analz G = analz G'; analz H = analz H' |] ==> analz (G ∪ H) = analz (G' ∪ H')" by (intro equalityI analz_subset_cong, simp_all)
lemma analz_insert_cong: "analz H = analz H' ==> analz(insert X H) = analz(insert X H')" by (force simp only: insert_def intro!: analz_cong)
text‹If there are no pairs or encryptions then analz does nothing› lemma analz_trivial: "[| ∀X Y. {X,Y}∉ H; ∀X K. Crypt K X ∉ H |] ==> analz H = H" apply safe apply (erule analz.induct, blast+) done
text‹These two are obsolete (with a single Spy) but cost little to prove...› lemma analz_UN_analz_lemma: "X∈ analz (∪i∈A. analz (H i)) ==> X∈ analz (∪i∈A. H i)" apply (erule analz.induct) apply (blast intro: analz_mono [THEN [2] rev_subsetD])+ done
lemma analz_UN_analz [simp]: "analz (∪i∈A. analz (H i)) = analz (∪i∈A. H i)" by (blast intro: analz_UN_analz_lemma analz_mono [THEN [2] rev_subsetD]) (*>*) text‹
the ‹Decrypt› rule: the spy can decrypt a
encrypted with key~$K$ if he has the matching key,~$K^{-1}$.
proved by rule induction include the following:
{named_thms [display,indent=0] analz_mono [no_vars] (analz_mono) analz_idem [no_vars] (analz_idem)}
set of fake messages that an intruder could invent
from~‹H› is ‹synth(analz H)›, where ‹synth H›
what the adversary can build from the set of messages~$H$. ›
inductive_set
synth :: "msg set ==> msg set" for H :: "msg set" where
Inj [intro]: "X ∈ H ==> X ∈ synth H"
| Agent [intro]: "Agent agt ∈ synth H"
| MPair [intro]: "[X ∈ synth H; Y ∈ synth H]==>{X,Y}∈ synth H"
| Crypt [intro]: "[X ∈ synth H; Key K ∈ H]==> Crypt K X ∈ synth H" (*<*) lemma synth_mono: "G⊆H ==> synth(G) ⊆ synth(H)" by (auto, erule synth.induct, auto)
inductive_cases Key_synth [elim!]: "Key K ∈ synth H" inductive_cases MPair_synth [elim!]: "{X,Y}∈ synth H" inductive_cases Crypt_synth [elim!]: "Crypt K X ∈ synth H"
lemma analz_synth [simp]: "analz (synth H) = analz H ∪ synth H" apply (cut_tac H = "{}"in analz_synth_Un) apply (simp (no_asm_use)) done (*>*) text‹
set includes all agent names. Nonces and keys are assumed to be
, so none are included beyond those already in~$H$. Two
of term‹synth H› can be combined, and an element can be encrypted
a key present in~$H$.
‹analz›, this set operator is monotone and idempotent. It also
an interesting equation involving ‹analz›:
{named_thms [display,indent=0] analz_synth [no_vars] (analz_synth)}
inversion plays a major role in reasoning about ‹synth›, through
such as this one: ›
inductive_cases Nonce_synth [elim!]: "Nonce n ∈ synth H"
text‹
noindent
resulting elimination rule replaces every assumption of the form term‹Nonce n ∈ synth H› by term‹Nonce n ∈ H›,
that a nonce cannot be guessed.
third operator, ‹parts›, is useful for stating correctness
. The set term‹parts H› consists of the components of elements of~$H$. This set
~‹H› and is closed under the projections from a compound
to its immediate parts.
definition resembles that of ‹analz› except in the rule
to the constructor ‹Crypt›:
{thm [display,indent=5] parts.Body [no_vars]}
body of an encrypted message is always regarded as part of it. We can ‹parts› to express general well-formedness properties of a protocol,
example, that an uncompromised agent's private key will never be
as a component of any message. › (*<*) lemma synth_increasing: "H ⊆ synth(H)" by blast
subsubsection‹Unions›
text‹Converse fails: we can synth more from the union than from the
separate parts, building a compound message using elements of each.› lemma synth_Un: "synth(G) ∪ synth(H) ⊆ synth(G ∪ H)" by (intro Un_least synth_mono Un_upper1 Un_upper2)
lemma synth_insert: "insert X (synth H) ⊆ synth(insert X H)" by (blast intro: synth_mono [THEN [2] rev_subsetD])
subsubsection‹For reasoning about the Fake rule in traces›
lemma parts_insert_subset_Un: "X ∈ G ==> parts(insert X H) ⊆ parts G ∪ parts H" by (rule subset_trans [OF parts_mono parts_Un_subset2], blast)
text‹More specifically for Fake. Very occasionally we could do with a version
of the form term‹parts{X} ⊆ synth (analz H) ∪ parts H›› lemma Fake_parts_insert: "X ∈ synth (analz H) ==> parts (insert X H) ⊆ synth (analz H) ∪ parts H" apply (drule parts_insert_subset_Un) apply (simp (no_asm_use)) apply blast done
lemma Fake_parts_insert_in_Un: "[|Z ∈ parts (insert X H); X ∈ synth (analz H)|] ==> Z ∈ synth (analz H) ∪ parts H" by (blast dest: Fake_parts_insert [THEN subsetD, dest])
text‹term‹H› is sometimes term‹Key ` KK ∪ spies evs›, so can't put term‹G=H›.› lemma Fake_analz_insert: "X ∈ synth (analz G) ==> analz (insert X H) ⊆ synth (analz G) ∪ analz (G ∪ H)" apply (rule subsetI) apply (subgoal_tac "x ∈ analz (synth (analz G) ∪ H) ") prefer2apply (blast intro: analz_mono [THEN [2] rev_subsetD] analz_mono [THEN synth_mono, THEN [2] rev_subsetD]) apply (simp (no_asm_use)) apply blast done
lemma analz_conj_parts [simp]: "(X ∈ analz H & X ∈ parts H) = (X ∈ analz H)" by (blast intro: analz_subset_parts [THEN subsetD])
lemma analz_disj_parts [simp]: "(X ∈ analz H | X ∈ parts H) = (X ∈ parts H)" by (blast intro: analz_subset_parts [THEN subsetD])
text‹Without this equation, other rules for synth and analz would yield
redundant cases› lemma MPair_synth_analz [iff]: "({X,Y}∈ synth (analz H)) = (X ∈ synth (analz H) & Y ∈ synth (analz H))" by blast
lemma Crypt_synth_analz: "[| Key K ∈ analz H; Key (invKey K) ∈ analz H |] ==> (Crypt K X ∈ synth (analz H)) = (X ∈ synth (analz H))" by blast
text‹We do NOT want Crypt... messages broken up in protocols!!› declare parts.Body [rule del]
text‹Rewrites to push in Key and Crypt messages, so that other messages can
be pulled out using the ‹analz_insert› rules›
lemmas pushKeys =
insert_commute [of "Key K""Agent C"]
insert_commute [of "Key K""Nonce N"]
insert_commute [of "Key K""MPair X Y"]
insert_commute [of "Key K""Crypt X K'"] for K C N X Y K'
lemmas pushCrypts =
insert_commute [of "Crypt X K""Agent C"]
insert_commute [of "Crypt X K""Agent C"]
insert_commute [of "Crypt X K""Nonce N"]
insert_commute [of "Crypt X K""MPair X' Y"] for X K C N X' Y
text‹Cannot be added with ‹[simp]› -- messages should not always be
re-ordered.› lemmas pushes = pushKeys pushCrypts
but this application is no longer necessary if analz_insert_eq is used.
Abstraction over i is ESSENTIAL: it delays the dereferencing of claset
DEPENDS UPON "X" REFERRING TO THE FRADULENT MESSAGE *)
(*Apply rules to break down assumptions of the form Y\<in>parts(insertXH)andY\<in>analz(insertXH)
*) fun impOfSubs th = th RSN (2, @{thm rev_subsetD})
fun spy_analz_tac ctxt i =
DETERM
(SELECT_GOAL
(EVERY
[ (*push in occurrences of X...*)
(REPEAT o CHANGED)
(Rule_Insts.res_inst_tac ctxt [((("x", 1), Position.none), "X")] []
(@{thm insert_commute} RS ssubst) 1), (*...allowing further simplifications*)
simp_tac ctxt 1,
REPEAT (FIRSTGOAL (resolve_tac ctxt [allI,impI,notI,conjI,iffI])),
DEPTH_SOLVE (atomic_spy_analz_tac ctxt 1)]) i); ›
text‹By default only ‹o_apply› is built-in. But in the presence of
eta-expansion this means that some terms displayed as term‹f o g› will be
rewritten, and others will not!› declare o_def [simp]
lemma Crypt_notin_image_Key [simp]: "Crypt K X ∉ Key ` A" by auto
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.