theory v2_isar_new_K
  imports Main "../QML"
begin

text \<open>
@{text "Compossibility\<^sub>7'"}: Necessarily, given any two non-overlapping hunks, @{term y} and @{term z},
and a table, @{term x}, made from @{term y},
for any table, @{term x'}, that might be made from @{term z}, it
is also possible that both @{term x} is a table made from @{term y} and @{term x'} is a table made
from @{term z}.

@{text "Origin Uniqueness\<^sub>6'"}:
Necessarily, if  @{term x} is a table made from  @{term y} and  @{term x'} is a table made
from  @{term z} and @{text "y\<noteq>z"}, then @{text "x\<noteq>x'"}.
\<close>

text \<open>
@{text "Origin Essentialism\<^sub>6'"}: Necessarily, given a table, @{term x}, made from a hunk, @{term y}, any table, @{term x'},
which might be made from a hunk, @{term z}, distinct from @{term y}, is distinct from @{term x}.
\<close>

(* (T x y) \<equiv> x made from y *)
consts makeTable ::  "\<mu> \<Rightarrow> \<mu> \<Rightarrow> \<sigma>"  ("T")
(* lemma necessity_of_distinctness: "\<lfloor>(\<^bold>\<forall>x. \<^bold>\<forall>y.(\<^bold>\<not>(x\<^bold>=\<^sup>Ly) \<^bold>\<rightarrow> \<^bold>\<box>(\<^bold>\<not>(x\<^bold>=\<^sup>Ly))))\<rfloor>" by auto *)

lemma
  assumes compossibilty1: "\<lfloor>(\<^bold>\<forall>x. \<^bold>\<forall>y. \<^bold>\<forall>x'. \<^bold>\<forall>z. (T x y \<^bold>\<and> \<^bold>\<not>(y\<^bold>=\<^sup>Lz)) \<^bold>\<rightarrow> \<^bold>\<box>(T x' z \<^bold>\<rightarrow> \<^bold>\<diamond>(T x y \<^bold>\<and> T x' z)))\<rfloor>"
  assumes origin_uniqueness1: "\<lfloor>(\<^bold>\<forall>x. \<^bold>\<forall>y. \<^bold>\<forall>x'. \<^bold>\<forall>z. ((\<^bold>\<not>(y\<^bold>=\<^sup>Lz) \<^bold>\<and> (T x y) \<^bold>\<and> (T x' z)) \<^bold>\<rightarrow> (\<^bold>\<not>(x\<^bold>=\<^sup>Lx'))))\<rfloor>"
  shows origin_essentialism1: "\<lfloor>(\<^bold>\<forall>x. \<^bold>\<forall>y. \<^bold>\<forall>x'. \<^bold>\<forall>z. T x y \<^bold>\<rightarrow> \<^bold>\<box>((\<^bold>\<not>(y\<^bold>=\<^sup>Lz) \<^bold>\<and> (T x' z)) \<^bold>\<rightarrow> \<^bold>\<not>(x\<^bold>=\<^sup>Lx')))\<rfloor>"
proof(rule allI)
  fix w
  show "(\<^bold>\<forall>x. \<^bold>\<forall>y. \<^bold>\<forall>x'. \<^bold>\<forall>z. T x y \<^bold>\<rightarrow> \<^bold>\<box>((\<^bold>\<not>(y\<^bold>=\<^sup>Lz) \<^bold>\<and> (T x' z)) \<^bold>\<rightarrow> \<^bold>\<not>(x\<^bold>=\<^sup>Lx'))) w"
  proof(rule allI)
    fix x
    show "(\<^bold>\<forall>y. \<^bold>\<forall>x'. \<^bold>\<forall>z. T x y \<^bold>\<rightarrow> \<^bold>\<box>((\<^bold>\<not>(y\<^bold>=\<^sup>Lz) \<^bold>\<and> (T x' z)) \<^bold>\<rightarrow> \<^bold>\<not>(x\<^bold>=\<^sup>Lx'))) w"
    proof(rule allI)
      fix y
      show  "(\<^bold>\<forall>x'. \<^bold>\<forall>z. T x y \<^bold>\<rightarrow> \<^bold>\<box>((\<^bold>\<not>(y\<^bold>=\<^sup>Lz) \<^bold>\<and> (T x' z)) \<^bold>\<rightarrow> \<^bold>\<not>(x\<^bold>=\<^sup>Lx'))) w"
      proof(rule allI)
        fix x'
        show  "(\<^bold>\<forall>z. T x y \<^bold>\<rightarrow> \<^bold>\<box>((\<^bold>\<not>(y\<^bold>=\<^sup>Lz) \<^bold>\<and> (T x' z)) \<^bold>\<rightarrow> \<^bold>\<not>(x\<^bold>=\<^sup>Lx'))) w"
        proof(rule allI)
          fix z
          show  "(T x y \<^bold>\<rightarrow> \<^bold>\<box>((\<^bold>\<not>(y\<^bold>=\<^sup>Lz) \<^bold>\<and> (T x' z)) \<^bold>\<rightarrow> \<^bold>\<not>(x\<^bold>=\<^sup>Lx'))) w"
          proof(rule impI)
            assume table_x1_from_y1: "T x y w"
            show  "(\<^bold>\<box>((\<^bold>\<not>(y\<^bold>=\<^sup>Lz) \<^bold>\<and> (T x' z)) \<^bold>\<rightarrow> \<^bold>\<not>(x\<^bold>=\<^sup>Lx'))) w"
            proof(rule allI)
              fix v
              show  "(w r v) \<longrightarrow> ((((\<^bold>\<not>(y\<^bold>=\<^sup>Lz) \<^bold>\<and> (T x' z)) \<^bold>\<rightarrow> \<^bold>\<not>(x\<^bold>=\<^sup>Lx'))) v)"
              proof (rule impI)
                assume "(w r v)"
                show "((((\<^bold>\<not>(y\<^bold>=\<^sup>Lz) \<^bold>\<and> (T x' z)) \<^bold>\<rightarrow> \<^bold>\<not>(x\<^bold>=\<^sup>Lx'))) v)"
                proof(rule impI)
                  assume antecedent: "((\<^bold>\<not>(y\<^bold>=\<^sup>Lz)) \<^bold>\<and> T x' z) v"
                  show "(\<^bold>\<not>(x\<^bold>=\<^sup>Lx')) v"
                  proof(rule notI)
                    assume identity: "(x\<^bold>=\<^sup>Lx') v"

                    from antecedent have table_x2_from_y2: "T x' z v" by (rule conjE)
                    from antecedent have non_overlapping: "(\<^bold>\<not>(y\<^bold>=\<^sup>Lz)) v" by (rule conjE)

                    from compossibilty1 have "(\<^bold>\<forall>x. \<^bold>\<forall>y. \<^bold>\<forall>x'. \<^bold>\<forall>z. (T x y \<^bold>\<and> \<^bold>\<not>(y\<^bold>=\<^sup>Lz)) \<^bold>\<rightarrow> \<^bold>\<box>(T x' z \<^bold>\<rightarrow> \<^bold>\<diamond>(T x y \<^bold>\<and> T x' z))) w"..
                    then have "(\<^bold>\<forall>y. \<^bold>\<forall>x'. \<^bold>\<forall>z. (T x y \<^bold>\<and> \<^bold>\<not>(y\<^bold>=\<^sup>Lz)) \<^bold>\<rightarrow> \<^bold>\<box>(T x' z \<^bold>\<rightarrow> \<^bold>\<diamond>(T x y \<^bold>\<and> T x' z))) w" by (rule allE)
                    then have "(\<^bold>\<forall>x'. \<^bold>\<forall>z. (T x y \<^bold>\<and> \<^bold>\<not>(y\<^bold>=\<^sup>Lz)) \<^bold>\<rightarrow> \<^bold>\<box>(T x' z \<^bold>\<rightarrow> \<^bold>\<diamond>(T x y \<^bold>\<and> T x' z))) w" by (rule allE)
                    then have "(\<^bold>\<forall>z. (T x y \<^bold>\<and> \<^bold>\<not>(y\<^bold>=\<^sup>Lz)) \<^bold>\<rightarrow> \<^bold>\<box>(T x' z \<^bold>\<rightarrow> \<^bold>\<diamond>(T x y \<^bold>\<and> T x' z))) w" by (rule allE)
                    then have "((T x y \<^bold>\<and> \<^bold>\<not>(y\<^bold>=\<^sup>Lz)) \<^bold>\<rightarrow> \<^bold>\<box>(T x' z \<^bold>\<rightarrow> \<^bold>\<diamond>(T x y \<^bold>\<and> T x' z))) w" by (rule allE)
                    then have "(\<^bold>\<box>(T x' z \<^bold>\<rightarrow> \<^bold>\<diamond>(T x y \<^bold>\<and> T x' z))) w"
                      using table_x1_from_y1 and non_overlapping by auto
                    then have "(T x' z \<^bold>\<rightarrow> \<^bold>\<diamond>(T x y \<^bold>\<and> T x' z)) v" using `w r v` by auto
                    then have "(\<^bold>\<diamond>(T x y \<^bold>\<and> T x' z)) v" using table_x2_from_y2 by (rule mp)
                    then obtain u where u: "v r u \<and> ((T x y \<^bold>\<and> T x' z) u)" by auto
                    then have origin_uniqueness1_ante: "((\<^bold>\<not>(y\<^bold>=\<^sup>Lz) \<^bold>\<and> (T x y) \<^bold>\<and> (T x' z))) u"
                      using  non_overlapping by auto
                    from u have "v r u" by (rule conjE)

                    from origin_uniqueness1 have "(\<^bold>\<forall>x. \<^bold>\<forall>y. \<^bold>\<forall>x'. \<^bold>\<forall>z. ((\<^bold>\<not>(y\<^bold>=\<^sup>Lz) \<^bold>\<and> (T x y) \<^bold>\<and> (T x' z)) \<^bold>\<rightarrow> (\<^bold>\<not>(x\<^bold>=\<^sup>Lx')))) u"..
                    then have "(\<^bold>\<forall>y. \<^bold>\<forall>x'. \<^bold>\<forall>z. ((\<^bold>\<not>(y\<^bold>=\<^sup>Lz) \<^bold>\<and> (T x y) \<^bold>\<and> (T x' z)) \<^bold>\<rightarrow> (\<^bold>\<not>(x\<^bold>=\<^sup>Lx')))) u" by (rule allE)
                    then have "(\<^bold>\<forall>x'. \<^bold>\<forall>z. ((\<^bold>\<not>(y\<^bold>=\<^sup>Lz) \<^bold>\<and> (T x y) \<^bold>\<and> (T x' z)) \<^bold>\<rightarrow> (\<^bold>\<not>(x\<^bold>=\<^sup>Lx')))) u" by (rule allE)
                    then have "(\<^bold>\<forall>z. ((\<^bold>\<not>(y\<^bold>=\<^sup>Lz) \<^bold>\<and> (T x y) \<^bold>\<and> (T x' z)) \<^bold>\<rightarrow> (\<^bold>\<not>(x\<^bold>=\<^sup>Lx')))) u" by (rule allE)
                    then have "(((\<^bold>\<not>(y\<^bold>=\<^sup>Lz) \<^bold>\<and> (T x y) \<^bold>\<and> (T x' z)) \<^bold>\<rightarrow> (\<^bold>\<not>(x\<^bold>=\<^sup>Lx')))) u" by (rule allE)
                    then have "(\<^bold>\<not>(x\<^bold>=\<^sup>Lx')) u" using origin_uniqueness1_ante by (rule mp)
                    then show "False" using identity and `v r u` by auto
                  qed
                qed
              qed
            qed
          qed
        qed
      qed
    qed
  qed
qed

end


section \<open>Functor Class\<close>

theory Functor
imports TypeApp Coerce
keywords "tycondef" :: thy_defn and "\<cdot>"
begin

subsection \<open>Class definition\<close>

text \<open>Here we define the \<open>functor\<close> class, which models the
Haskell class \texttt{Functor}. For technical reasons, we split the
definition of \<open>functor\<close> into two separate classes: First, we
introduce \<open>prefunctor\<close>, which only requires \<open>fmap\<close> to
preserve the identity function, and not function composition.\<close>

text \<open>The Haskell class \texttt{Functor f} fixes a polymorphic
function \texttt{fmap :: (a -> b) -> f a -> f b}. Since functions in
Isabelle type classes can only mention one type variable, we have the
\<open>prefunctor\<close> class fix a function \<open>fmapU\<close> that fixes both
of the polymorphic types to be the universal domain. We will use the
coercion operator to recover a polymorphic \<open>fmap\<close>.\<close>

text \<open>The single axiom of the \<open>prefunctor\<close> class is stated in
terms of the HOLCF constant \<open>isodefl\<close>, which relates a function
\<open>f :: 'a \<rightarrow> 'a\<close> with a deflation \<open>t :: udom defl\<close>:
@{thm isodefl_def [of f t, no_vars]}.\<close>

class prefunctor = "tycon" +
  fixes fmapU :: "(udom \<rightarrow> udom) \<rightarrow> udom\<cdot>'a \<rightarrow> udom\<cdot>'a::tycon"
  assumes isodefl_fmapU:
    "isodefl (fmapU\<cdot>(cast\<cdot>t)) (TC('a::tycon)\<cdot>t)"

text \<open>The \<open>functor\<close> class extends \<open>prefunctor\<close> with an
axiom stating that \<open>fmapU\<close> preserves composition.\<close>

class "functor" = prefunctor +
  assumes fmapU_fmapU [coerce_simp]:
    "\<And>f g (xs::udom\<cdot>'a::tycon).
      fmapU\<cdot>f\<cdot>(fmapU\<cdot>g\<cdot>xs) = fmapU\<cdot>(\<Lambda> x. f\<cdot>(g\<cdot>x))\<cdot>xs"

text \<open>We define the polymorphic \<open>fmap\<close> by coercion from \<open>fmapU\<close>, then we proceed to derive the polymorphic versions of the
functor laws.\<close>

definition fmap :: "('a \<rightarrow> 'b) \<rightarrow> 'a\<cdot>'f \<rightarrow> 'b\<cdot>'f::functor"
  where "fmap = coerce\<cdot>(fmapU :: _ \<rightarrow> udom\<cdot>'f \<rightarrow> udom\<cdot>'f)"

subsection \<open>Polymorphic functor laws\<close>

lemma fmapU_eq_fmap: "fmapU = fmap"
by (simp add: fmap_def eta_cfun)

lemma fmap_eq_fmapU: "fmap = fmapU"
  by (simp only: fmapU_eq_fmap)

lemma cast_TC:
  "cast\<cdot>(TC('f)\<cdot>t) = emb oo fmapU\<cdot>(cast\<cdot>t) oo PRJ(udom\<cdot>'f::prefunctor)"
by (rule isodefl_fmapU [unfolded isodefl_def])

lemma isodefl_cast: "isodefl (cast\<cdot>t) t"
by (simp add: isodefl_def)

lemma cast_cast_below1: "A \<sqsubseteq> B \<Longrightarrow> cast\<cdot>A\<cdot>(cast\<cdot>B\<cdot>x) = cast\<cdot>A\<cdot>x"
by (intro deflation_below_comp1 deflation_cast monofun_cfun_arg)

lemma cast_cast_below2: "A \<sqsubseteq> B \<Longrightarrow> cast\<cdot>B\<cdot>(cast\<cdot>A\<cdot>x) = cast\<cdot>A\<cdot>x"
by (intro deflation_below_comp2 deflation_cast monofun_cfun_arg)

lemma isodefl_fmap:
  assumes "isodefl d t"
  shows "isodefl (fmap\<cdot>d :: 'a\<cdot>'f \<rightarrow> _) (TC('f::functor)\<cdot>t)"
proof -
  have deflation_d: "deflation d"
    using assms by (rule isodefl_imp_deflation)
  have cast_t: "cast\<cdot>t = emb oo d oo prj"
    using assms unfolding isodefl_def .
  have t_below: "t \<sqsubseteq> DEFL('a)"
    apply (rule cast_below_imp_below)
    apply (simp only: cast_t cast_DEFL)
    apply (simp add: cfun_below_iff deflation.below [OF deflation_d])
    done
  have fmap_eq: "fmap\<cdot>d = PRJ('a\<cdot>'f) oo cast\<cdot>(TC('f)\<cdot>t) oo emb"
    by (simp add: fmap_def coerce_cfun cast_TC cast_t prj_emb cfcomp1)
  show ?thesis
    apply (simp add: fmap_eq isodefl_def cfun_eq_iff emb_prj)
    apply (simp add: DEFL_app)
    apply (simp add: cast_cast_below1 monofun_cfun t_below)
    apply (simp add: cast_cast_below2 monofun_cfun t_below)
    done
qed

lemma fmap_ID [simp]: "fmap\<cdot>ID = ID"
apply (rule isodefl_DEFL_imp_ID)
apply (subst DEFL_app)
apply (rule isodefl_fmap)
apply (rule isodefl_ID_DEFL)
done

lemma fmap_ident [simp]: "fmap\<cdot>(\<Lambda> x. x) = ID"
by (simp add: ID_def [symmetric])

lemma coerce_coerce_eq_fmapU_cast [coerce_simp]:
  fixes xs :: "udom\<cdot>'f::functor"
  shows "COERCE('a\<cdot>'f, udom\<cdot>'f)\<cdot>(COERCE(udom\<cdot>'f, 'a\<cdot>'f)\<cdot>xs) =
    fmapU\<cdot>(cast\<cdot>DEFL('a))\<cdot>xs"
by (simp add: coerce_def emb_prj DEFL_app cast_TC)

lemma fmap_fmap:
  fixes xs :: "'a\<cdot>'f::functor" and g :: "'a \<rightarrow> 'b" and f :: "'b \<rightarrow> 'c"
  shows "fmap\<cdot>f\<cdot>(fmap\<cdot>g\<cdot>xs) = fmap\<cdot>(\<Lambda> x. f\<cdot>(g\<cdot>x))\<cdot>xs"
unfolding fmap_def
by (simp add: coerce_simp)

lemma fmap_cfcomp: "fmap\<cdot>(f oo g) = fmap\<cdot>f oo fmap\<cdot>g"
by (simp add: cfcomp1 fmap_fmap eta_cfun)

subsection \<open>Derived properties of \<open>fmap\<close>\<close>

text \<open>Other theorems about \<open>fmap\<close> can be derived using only
the abstract functor laws.\<close>

lemma deflation_fmap:
  "deflation d \<Longrightarrow> deflation (fmap\<cdot>d)"
 apply (rule deflation.intro)
  apply (simp add: fmap_fmap deflation.idem eta_cfun)
 apply (subgoal_tac "fmap\<cdot>d\<cdot>x \<sqsubseteq> fmap\<cdot>ID\<cdot>x", simp)
 apply (rule monofun_cfun_fun, rule monofun_cfun_arg)
 apply (erule deflation.below_ID)
done

lemma ep_pair_fmap:
  "ep_pair e p \<Longrightarrow> ep_pair (fmap\<cdot>e) (fmap\<cdot>p)"
 apply (rule ep_pair.intro)
  apply (simp add: fmap_fmap ep_pair.e_inverse)
 apply (simp add: fmap_fmap)
 apply (rule_tac y="fmap\<cdot>ID\<cdot>y" in below_trans)
  apply (rule monofun_cfun_fun)
  apply (rule monofun_cfun_arg)
  apply (rule cfun_belowI, simp)
  apply (erule ep_pair.e_p_below)
 apply simp
done

lemma fmap_strict:
  fixes f :: "'a \<rightarrow> 'b"
  assumes "f\<cdot>\<bottom> = \<bottom>" shows "fmap\<cdot>f\<cdot>\<bottom> = (\<bottom>::'b\<cdot>'f::functor)"
proof (rule bottomI)
  have "fmap\<cdot>f\<cdot>(\<bottom>::'a\<cdot>'f) \<sqsubseteq> fmap\<cdot>f\<cdot>(fmap\<cdot>\<bottom>\<cdot>(\<bottom>::'b\<cdot>'f))"
    by (simp add: monofun_cfun)
  also have "... = fmap\<cdot>(\<Lambda> x. f\<cdot>(\<bottom>\<cdot>x))\<cdot>(\<bottom>::'b\<cdot>'f)"
    by (simp add: fmap_fmap)
  also have "... \<sqsubseteq> fmap\<cdot>ID\<cdot>\<bottom>"
    by (simp add: monofun_cfun assms del: fmap_ID)
  also have "... = \<bottom>"
    by simp
  finally show "fmap\<cdot>f\<cdot>\<bottom> \<sqsubseteq> (\<bottom>::'b\<cdot>'f::functor)" .
qed

subsection \<open>Proving that \<open>fmap\<cdot>coerce = coerce\<close>\<close>

lemma fmapU_cast_eq:
  "fmapU\<cdot>(cast\<cdot>A) =
    PRJ(udom\<cdot>'f) oo cast\<cdot>(TC('f::functor)\<cdot>A) oo emb"
by (subst cast_TC, rule cfun_eqI, simp)

lemma fmapU_cast_DEFL:
  "fmapU\<cdot>(cast\<cdot>DEFL('a)) =
    PRJ(udom\<cdot>'f) oo cast\<cdot>DEFL('a\<cdot>'f::functor) oo emb"
by (simp add: fmapU_cast_eq DEFL_app)

lemma coerce_functor: "COERCE('a\<cdot>'f, 'b\<cdot>'f::functor) = fmap\<cdot>coerce"
apply (rule cfun_eqI, rename_tac xs)
apply (simp add: fmap_def coerce_cfun)
apply (simp add: coerce_def)
apply (simp add: cfcomp1)
apply (simp only: emb_prj)
apply (subst fmapU_fmapU [symmetric])
apply (simp add: fmapU_cast_DEFL)
apply (simp add: emb_prj)
apply (simp add: cast_cast_below1 cast_cast_below2)
done

subsection \<open>Lemmas for reasoning about coercion\<close>

lemma fmapU_cast_coerce [coerce_simp]:
  fixes m :: "'a\<cdot>'f::functor"
  shows "fmapU\<cdot>(cast\<cdot>DEFL('a))\<cdot>(COERCE('a\<cdot>'f, udom\<cdot>'f)\<cdot>m) =
    COERCE('a\<cdot>'f, udom\<cdot>'f)\<cdot>m"
by (simp add: coerce_functor cast_DEFL fmapU_eq_fmap fmap_fmap eta_cfun)

lemma coerce_fmap [coerce_simp]:
  fixes xs :: "'a\<cdot>'f::functor" and f :: "'a \<rightarrow> 'b"
  shows "COERCE('b\<cdot>'f, 'c\<cdot>'f)\<cdot>(fmap\<cdot>f\<cdot>xs) = fmap\<cdot>(\<Lambda> x. COERCE('b,'c)\<cdot>(f\<cdot>x))\<cdot>xs"
by (simp add: coerce_functor fmap_fmap)

lemma fmap_coerce [coerce_simp]:
  fixes xs :: "'a\<cdot>'f::functor" and f :: "'b \<rightarrow> 'c"
  shows "fmap\<cdot>f\<cdot>(COERCE('a\<cdot>'f, 'b\<cdot>'f)\<cdot>xs) = fmap\<cdot>(\<Lambda> x. f\<cdot>(COERCE('a,'b)\<cdot>x))\<cdot>xs"
by (simp add: coerce_functor fmap_fmap)

subsection \<open>Configuration of Domain package\<close>

text \<open>We make various theorem declarations to enable Domain
  package definitions that involve \<open>tycon\<close> application.\<close>

setup \<open>Domain_Take_Proofs.add_rec_type (@{type_name app}, [true, false])\<close>

declare DEFL_app [domain_defl_simps]
declare fmap_ID [domain_map_ID]
declare deflation_fmap [domain_deflation]
declare isodefl_fmap [domain_isodefl]

subsection \<open>Configuration of the Tycon package\<close>

text \<open>We now set up a new type definition command, which is used for
  defining new \<open>tycon\<close> instances. The \<open>tycondef\<close> command
  is implemented using much of the same code as the Domain package,
  and supports a similar input syntax. It automatically generates a
  \<open>prefunctor\<close> instance for each new type. (The user must
  provide a proof of the composition law to obtain a \<open>functor\<close>
  class instance.)\<close>

ML_file \<open>tycondef.ML\<close>

end


(*  Title:      HOL/Auth/Kerberos_BAN.thy
    Author:     Giampaolo Bella, Cambridge University Computer Laboratory
    Copyright   1998  University of Cambridge
*)

section{*The Kerberos Protocol, BAN Version*}

theory Kerberos_BAN imports Public begin

text{*From page 251 of
  Burrows, Abadi and Needham (1989).  A Logic of Authentication.
  Proc. Royal Soc. 426

  Confidentiality (secrecy) and authentication properties are also
  given in a termporal version: strong guarantees in a little abstracted
  - but very realistic - model.
*}

(* Temporal model of accidents: session keys can be leaked
                                ONLY when they have expired *)

consts

    (*Duration of the session key*)
    sesKlife   :: nat

    (*Duration of the authenticator*)
    authlife :: nat

text{*The ticket should remain fresh for two journeys on the network at least*}
specification (sesKlife)
  sesKlife_LB [iff]: "2 \<le> sesKlife"
    by blast

text{*The authenticator only for one journey*}
specification (authlife)
  authlife_LB [iff]:    "authlife \<noteq> 0"
    by blast

abbreviation
  CT :: "event list=>nat" where
  "CT == length "

abbreviation
  expiredK :: "[nat, event list] => bool" where
  "expiredK T evs == sesKlife + T < CT evs"

abbreviation
  expiredA :: "[nat, event list] => bool" where
  "expiredA T evs == authlife + T < CT evs"

definition
 (* A is the true creator of X if she has sent X and X never appeared on
    the trace before this event. Recall that traces grow from head. *)
  Issues :: "[agent, agent, msg, event list] => bool"
             ("_ Issues _ with _ on _") where
   "A Issues B with X on evs =
      (\<exists>Y. Says A B Y \<in> set evs & X \<in> parts {Y} &
        X \<notin> parts (spies (takeWhile (% z. z  \<noteq> Says A B Y) (rev evs))))"

definition
 (* Yields the subtrace of a given trace from its beginning to a given event *)
  before :: "[event, event list] => event list" ("before _ on _")
  where "before ev on evs = takeWhile (% z. z ~= ev) (rev evs)"

definition
 (* States than an event really appears only once on a trace *)
  Unique :: "[event, event list] => bool" ("Unique _ on _")
  where "Unique ev on evs = (ev \<notin> set (tl (dropWhile (% z. z \<noteq> ev) evs)))"

inductive_set bankerberos :: "event list set"
 where

   Nil:  "[] \<in> bankerberos"

 | Fake: "\<lbrakk> evsf \<in> bankerberos;  X \<in> synth (analz (spies evsf)) \<rbrakk>
          \<Longrightarrow> Says Spy B X # evsf \<in> bankerberos"

 | BK1:  "\<lbrakk> evs1 \<in> bankerberos \<rbrakk>
          \<Longrightarrow> Says A Server \<lbrace>Agent A, Agent B\<rbrace> # evs1
                \<in>  bankerberos"

 | BK2:  "\<lbrakk> evs2 \<in> bankerberos;  Key K \<notin> used evs2; K \<in> symKeys;
             Says A' Server \<lbrace>Agent A, Agent B\<rbrace> \<in> set evs2 \<rbrakk>
          \<Longrightarrow> Says Server A
                (Crypt (shrK A)
                   \<lbrace>Number (CT evs2), Agent B, Key K,
                    (Crypt (shrK B) \<lbrace>Number (CT evs2), Agent A, Key K\<rbrace>)\<rbrace>)
                # evs2 \<in> bankerberos"

 | BK3:  "\<lbrakk> evs3 \<in> bankerberos;
             Says S A (Crypt (shrK A) \<lbrace>Number Tk, Agent B, Key K, Ticket\<rbrace>)
               \<in> set evs3;
             Says A Server \<lbrace>Agent A, Agent B\<rbrace> \<in> set evs3;
             \<not> expiredK Tk evs3 \<rbrakk>
          \<Longrightarrow> Says A B \<lbrace>Ticket, Crypt K \<lbrace>Agent A, Number (CT evs3)\<rbrace> \<rbrace>
               # evs3 \<in> bankerberos"

 | BK4:  "\<lbrakk> evs4 \<in> bankerberos;
             Says A' B \<lbrace>(Crypt (shrK B) \<lbrace>Number Tk, Agent A, Key K\<rbrace>),
                         (Crypt K \<lbrace>Agent A, Number Ta\<rbrace>) \<rbrace>: set evs4;
             \<not> expiredK Tk evs4;  \<not> expiredA Ta evs4 \<rbrakk>
          \<Longrightarrow> Says B A (Crypt K (Number Ta)) # evs4
                \<in> bankerberos"

        (*Old session keys may become compromised*)
 | Oops: "\<lbrakk> evso \<in> bankerberos;
         Says Server A (Crypt (shrK A) \<lbrace>Number Tk, Agent B, Key K, Ticket\<rbrace>)
               \<in> set evso;
             expiredK Tk evso \<rbrakk>
          \<Longrightarrow> Notes Spy \<lbrace>Number Tk, Key K\<rbrace> # evso \<in> bankerberos"

declare Says_imp_knows_Spy [THEN parts.Inj, dest]
declare parts.Body [dest]
declare analz_into_parts [dest]
declare Fake_parts_insert_in_Un [dest]

text{*A "possibility property": there are traces that reach the end.*}
lemma "\<lbrakk>Key K \<notin> used []; K \<in> symKeys\<rbrakk>
       \<Longrightarrow> \<exists>Timestamp. \<exists>evs \<in> bankerberos.
             Says B A (Crypt K (Number Timestamp))
                  \<in> set evs"
apply (cut_tac sesKlife_LB)
apply (intro exI bexI)
apply (rule_tac [2]
           bankerberos.Nil [THEN bankerberos.BK1, THEN bankerberos.BK2,
                             THEN bankerberos.BK3, THEN bankerberos.BK4])
apply (possibility, simp_all (no_asm_simp) add: used_Cons)
done

subsection{*Lemmas for reasoning about predicate "Issues"*}

lemma spies_Says_rev: "spies (evs @ [Says A B X]) = insert X (spies evs)"
apply (induct_tac "evs")
apply (rename_tac [2] a b)
apply (induct_tac [2] "a", auto)
done

lemma spies_Gets_rev: "spies (evs @ [Gets A X]) = spies evs"
apply (induct_tac "evs")
apply (rename_tac [2] a b)
apply (induct_tac [2] "a", auto)
done

lemma spies_Notes_rev: "spies (evs @ [Notes A X]) =
          (if A:bad then insert X (spies evs) else spies evs)"
apply (induct_tac "evs")
apply (rename_tac [2] a b)
apply (induct_tac [2] "a", auto)
done

lemma spies_evs_rev: "spies evs = spies (rev evs)"
apply (induct_tac "evs")
apply (rename_tac [2] a b)
apply (induct_tac [2] "a")
apply (simp_all (no_asm_simp) add: spies_Says_rev spies_Gets_rev spies_Notes_rev)
done

lemmas parts_spies_evs_revD2 = spies_evs_rev [THEN equalityD2, THEN parts_mono]

lemma spies_takeWhile: "spies (takeWhile P evs) <=  spies evs"
apply (induct_tac "evs")
apply (rename_tac [2] a b)
apply (induct_tac [2] "a", auto)
txt{* Resembles @{text"used_subset_append"} in theory Event.*}
done

lemmas parts_spies_takeWhile_mono = spies_takeWhile [THEN parts_mono]

text{*Lemmas for reasoning about predicate "before"*}
lemma used_Says_rev: "used (evs @ [Says A B X]) = parts {X} \<union> (used evs)"
apply (induct_tac "evs")
apply simp
apply (rename_tac a b)
apply (induct_tac "a")
apply auto
done

lemma used_Notes_rev: "used (evs @ [Notes A X]) = parts {X} \<union> (used evs)"
apply (induct_tac "evs")
apply simp
apply (rename_tac a b)
apply (induct_tac "a")
apply auto
done

lemma used_Gets_rev: "used (evs @ [Gets B X]) = used evs"
apply (induct_tac "evs")
apply simp
apply (rename_tac a b)
apply (induct_tac "a")
apply auto
done

lemma used_evs_rev: "used evs = used (rev evs)"
apply (induct_tac "evs")
apply simp
apply (rename_tac a b)
apply (induct_tac "a")
apply (simp add: used_Says_rev)
apply (simp add: used_Gets_rev)
apply (simp add: used_Notes_rev)
done

lemma used_takeWhile_used [rule_format]:
      "x : used (takeWhile P X) --> x : used X"
apply (induct_tac "X")
apply simp
apply (rename_tac a b)
apply (induct_tac "a")
apply (simp_all add: used_Nil)
apply (blast dest!: initState_into_used)+
done

lemma set_evs_rev: "set evs = set (rev evs)"
apply auto
done

lemma takeWhile_void [rule_format]:
      "x \<notin> set evs \<longrightarrow> takeWhile (\<lambda>z. z \<noteq> x) evs = evs"
apply auto
done

(**** Inductive proofs about bankerberos ****)

text{*Forwarding Lemma for reasoning about the encrypted portion of message BK3*}
lemma BK3_msg_in_parts_spies:
     "Says S A (Crypt KA \<lbrace>Timestamp, B, K, X\<rbrace>) \<in> set evs
      \<Longrightarrow> X \<in> parts (spies evs)"
apply blast
done

lemma Oops_parts_spies:
     "Says Server A (Crypt (shrK A) \<lbrace>Timestamp, B, K, X\<rbrace>) \<in> set evs
      \<Longrightarrow> K \<in> parts (spies evs)"
apply blast
done

text{*Spy never sees another agent's shared key! (unless it's bad at start)*}
lemma Spy_see_shrK [simp]:
     "evs \<in> bankerberos \<Longrightarrow> (Key (shrK A) \<in> parts (spies evs)) = (A \<in> bad)"
apply (erule bankerberos.induct)
apply (frule_tac [7] Oops_parts_spies)
apply (frule_tac [5] BK3_msg_in_parts_spies, simp_all, blast+)
done

lemma Spy_analz_shrK [simp]:
     "evs \<in> bankerberos \<Longrightarrow> (Key (shrK A) \<in> analz (spies evs)) = (A \<in> bad)"
apply auto
done

lemma Spy_see_shrK_D [dest!]:
     "\<lbrakk> Key (shrK A) \<in> parts (spies evs);
                evs \<in> bankerberos \<rbrakk> \<Longrightarrow> A:bad"
apply (blast dest: Spy_see_shrK)
done

lemmas Spy_analz_shrK_D = analz_subset_parts [THEN subsetD, THEN Spy_see_shrK_D,  dest!]

text{*Nobody can have used non-existent keys!*}
lemma new_keys_not_used [simp]:
    "\<lbrakk>Key K \<notin> used evs; K \<in> symKeys; evs \<in> bankerberos\<rbrakk>
     \<Longrightarrow> K \<notin> keysFor (parts (spies evs))"
apply (erule rev_mp)
apply (erule bankerberos.induct)
apply (frule_tac [7] Oops_parts_spies)
apply (frule_tac [5] BK3_msg_in_parts_spies, simp_all)
txt{*Fake*}
apply (force dest!: keysFor_parts_insert)
txt{*BK2, BK3, BK4*}
apply (force dest!: analz_shrK_Decrypt)+
done

subsection{* Lemmas concerning the form of items passed in messages *}

text{*Describes the form of K, X and K' when the Server sends this message.*}
lemma Says_Server_message_form:
     "\<lbrakk> Says Server A (Crypt K' \<lbrace>Number Tk, Agent B, Key K, Ticket\<rbrace>)
         \<in> set evs; evs \<in> bankerberos \<rbrakk>
      \<Longrightarrow> K' = shrK A & K \<notin> range shrK &
          Ticket = (Crypt (shrK B) \<lbrace>Number Tk, Agent A, Key K\<rbrace>) &
          Key K \<notin> used(before
                  Says Server A (Crypt K' \<lbrace>Number Tk, Agent B, Key K, Ticket\<rbrace>)
                  on evs) &
          Tk = CT(before
                  Says Server A (Crypt K' \<lbrace>Number Tk, Agent B, Key K, Ticket\<rbrace>)
                  on evs)"
apply (unfold before_def)
apply (erule rev_mp)
apply (erule bankerberos.induct, simp_all add: takeWhile_tail)
apply auto
 apply (metis length_rev set_rev takeWhile_void used_evs_rev)+
done

text{*If the encrypted message appears then it originated with the Server
  PROVIDED that A is NOT compromised!
  This allows A to verify freshness of the session key.
*}
lemma Kab_authentic:
     "\<lbrakk> Crypt (shrK A) \<lbrace>Number Tk, Agent B, Key K, X\<rbrace>
           \<in> parts (spies evs);
         A \<notin> bad;  evs \<in> bankerberos \<rbrakk>
       \<Longrightarrow> Says Server A (Crypt (shrK A) \<lbrace>Number Tk, Agent B, Key K, X\<rbrace>)
             \<in> set evs"
apply (erule rev_mp)
apply (erule bankerberos.induct)
apply (frule_tac [7] Oops_parts_spies)
apply (frule_tac [5] BK3_msg_in_parts_spies, simp_all, blast)
done

text{*If the TICKET appears then it originated with the Server*}
text{*FRESHNESS OF THE SESSION KEY to B*}
lemma ticket_authentic:
     "\<lbrakk> Crypt (shrK B) \<lbrace>Number Tk, Agent A, Key K\<rbrace> \<in> parts (spies evs);
         B \<notin> bad;  evs \<in> bankerberos \<rbrakk>
       \<Longrightarrow> Says Server A
            (Crypt (shrK A) \<lbrace>Number Tk, Agent B, Key K,
                          Crypt (shrK B) \<lbrace>Number Tk, Agent A, Key K\<rbrace>\<rbrace>)
           \<in> set evs"
apply (erule rev_mp)
apply (erule bankerberos.induct)
apply (frule_tac [7] Oops_parts_spies)
apply (frule_tac [5] BK3_msg_in_parts_spies, simp_all, blast)
done

text{*EITHER describes the form of X when the following message is sent,
  OR     reduces it to the Fake case.
  Use @{text Says_Server_message_form} if applicable.*}
lemma Says_S_message_form:
     "\<lbrakk> Says S A (Crypt (shrK A) \<lbrace>Number Tk, Agent B, Key K, X\<rbrace>)
            \<in> set evs;
         evs \<in> bankerberos \<rbrakk>
 \<Longrightarrow> (K \<notin> range shrK & X = (Crypt (shrK B) \<lbrace>Number Tk, Agent A, Key K\<rbrace>))
          | X \<in> analz (spies evs)"
apply (case_tac "A \<in> bad")
apply (force dest!: Says_imp_spies [THEN analz.Inj])
apply (frule Says_imp_spies [THEN parts.Inj])
apply (blast dest!: Kab_authentic Says_Server_message_form)
done

(****
 The following is to prove theorems of the form

  Key K \<in> analz (insert (Key KAB) (spies evs)) \<Longrightarrow>
  Key K \<in> analz (spies evs)

 A more general formula must be proved inductively.

****)

text{* Session keys are not used to encrypt other session keys *}
lemma analz_image_freshK [rule_format (no_asm)]:
     "evs \<in> bankerberos \<Longrightarrow>
   \<forall>K KK. KK \<subseteq> - (range shrK) \<longrightarrow>
          (Key K \<in> analz (Key`KK Un (spies evs))) =
          (K \<in> KK | Key K \<in> analz (spies evs))"
apply (erule bankerberos.induct)
apply (drule_tac [7] Says_Server_message_form)
apply (erule_tac [5] Says_S_message_form [THEN disjE], analz_freshK, spy_analz, auto)
done

lemma analz_insert_freshK:
     "\<lbrakk> evs \<in> bankerberos;  KAB \<notin> range shrK \<rbrakk> \<Longrightarrow>
      (Key K \<in> analz (insert (Key KAB) (spies evs))) =
      (K = KAB | Key K \<in> analz (spies evs))"
apply (simp only: analz_image_freshK analz_image_freshK_simps)
done

text{* The session key K uniquely identifies the message *}
lemma unique_session_keys:
     "\<lbrakk> Says Server A
           (Crypt (shrK A) \<lbrace>Number Tk, Agent B, Key K, X\<rbrace>) \<in> set evs;
         Says Server A'
          (Crypt (shrK A') \<lbrace>Number Tk', Agent B', Key K, X'\<rbrace>) \<in> set evs;
         evs \<in> bankerberos \<rbrakk> \<Longrightarrow> A=A' & Tk=Tk' & B=B' & X = X'"
apply (erule rev_mp)
apply (erule rev_mp)
apply (erule bankerberos.induct)
apply (frule_tac [7] Oops_parts_spies)
apply (frule_tac [5] BK3_msg_in_parts_spies, simp_all)
txt{*BK2: it can't be a new key*}
apply blast
done

lemma Server_Unique:
     "\<lbrakk> Says Server A
            (Crypt (shrK A) \<lbrace>Number Tk, Agent B, Key K, Ticket\<rbrace>) \<in> set evs;
        evs \<in> bankerberos \<rbrakk> \<Longrightarrow>
   Unique Says Server A (Crypt (shrK A) \<lbrace>Number Tk, Agent B, Key K, Ticket\<rbrace>)
   on evs"
apply (erule rev_mp, erule bankerberos.induct, simp_all add: Unique_def)
apply blast
done

subsection{*Non-temporal guarantees, explicitly relying on non-occurrence of
oops events - refined below by temporal guarantees*}

text{*Non temporal treatment of confidentiality*}

text{* Lemma: the session key sent in msg BK2 would be lost by oops
    if the spy could see it! *}
lemma lemma_conf [rule_format (no_asm)]:
     "\<lbrakk> A \<notin> bad;  B \<notin> bad;  evs \<in> bankerberos \<rbrakk>
  \<Longrightarrow> Says Server A
          (Crypt (shrK A) \<lbrace>Number Tk, Agent B, Key K,
                            Crypt (shrK B) \<lbrace>Number Tk, Agent A, Key K\<rbrace>\<rbrace>)
         \<in> set evs \<longrightarrow>
      Key K \<in> analz (spies evs) \<longrightarrow> Notes Spy \<lbrace>Number Tk, Key K\<rbrace> \<in> set evs"
apply (erule bankerberos.induct)
apply (frule_tac [7] Says_Server_message_form)
apply (frule_tac [5] Says_S_message_form [THEN disjE])
apply (simp_all (no_asm_simp) add: analz_insert_eq analz_insert_freshK pushes)
txt{*Fake*}
apply spy_analz
txt{*BK2*}
apply (blast intro: parts_insertI)
txt{*BK3*}
apply (case_tac "Aa \<in> bad")
 prefer 2 apply (blast dest: Kab_authentic unique_session_keys)
apply (blast dest: Says_imp_spies [THEN analz.Inj] Crypt_Spy_analz_bad elim!: MPair_analz)
txt{*Oops*}
apply (blast dest: unique_session_keys)
done

text{*Confidentiality for the Server: Spy does not see the keys sent in msg BK2
as long as they have not expired.*}
lemma Confidentiality_S:
     "\<lbrakk> Says Server A
          (Crypt K' \<lbrace>Number Tk, Agent B, Key K, Ticket\<rbrace>) \<in> set evs;
        Notes Spy \<lbrace>Number Tk, Key K\<rbrace> \<notin> set evs;
         A \<notin> bad;  B \<notin> bad;  evs \<in> bankerberos
      \<rbrakk> \<Longrightarrow> Key K \<notin> analz (spies evs)"
apply (frule Says_Server_message_form, assumption)
apply (blast intro: lemma_conf)
done

text{*Confidentiality for Alice*}
lemma Confidentiality_A:
     "\<lbrakk> Crypt (shrK A) \<lbrace>Number Tk, Agent B, Key K, X\<rbrace> \<in> parts (spies evs);
        Notes Spy \<lbrace>Number Tk, Key K\<rbrace> \<notin> set evs;
        A \<notin> bad;  B \<notin> bad;  evs \<in> bankerberos
      \<rbrakk> \<Longrightarrow> Key K \<notin> analz (spies evs)"
apply (blast dest!: Kab_authentic Confidentiality_S)
done

text{*Confidentiality for Bob*}
lemma Confidentiality_B:
     "\<lbrakk> Crypt (shrK B) \<lbrace>Number Tk, Agent A, Key K\<rbrace>
          \<in> parts (spies evs);
        Notes Spy \<lbrace>Number Tk, Key K\<rbrace> \<notin> set evs;
        A \<notin> bad;  B \<notin> bad;  evs \<in> bankerberos
      \<rbrakk> \<Longrightarrow> Key K \<notin> analz (spies evs)"
apply (blast dest!: ticket_authentic Confidentiality_S)
done

text{*Non temporal treatment of authentication*}

text{*Lemmas @{text lemma_A} and @{text lemma_B} in fact are common to both temporal and non-temporal treatments*}
lemma lemma_A [rule_format]:
     "\<lbrakk> A \<notin> bad; B \<notin> bad; evs \<in> bankerberos \<rbrakk>
      \<Longrightarrow>
         Key K \<notin> analz (spies evs) \<longrightarrow>
         Says Server A (Crypt (shrK A) \<lbrace>Number Tk, Agent B, Key K, X\<rbrace>)
         \<in> set evs \<longrightarrow>
          Crypt K \<lbrace>Agent A, Number Ta\<rbrace> \<in> parts (spies evs) \<longrightarrow>
         Says A B \<lbrace>X, Crypt K \<lbrace>Agent A, Number Ta\<rbrace>\<rbrace>
             \<in> set evs"
apply (erule bankerberos.induct)
apply (frule_tac [7] Oops_parts_spies)
apply (frule_tac [5] Says_S_message_form)
apply (frule_tac [6] BK3_msg_in_parts_spies, analz_mono_contra)
apply (simp_all (no_asm_simp) add: all_conj_distrib)
txt{*Fake*}
apply blast
txt{*BK2*}
apply (force dest: Crypt_imp_invKey_keysFor)
txt{*BK3*}
apply (blast dest: Kab_authentic unique_session_keys)
done

lemma lemma_B [rule_format]:
     "\<lbrakk> B \<notin> bad;  evs \<in> bankerberos \<rbrakk>
      \<Longrightarrow> Key K \<notin> analz (spies evs) \<longrightarrow>
          Says Server A (Crypt (shrK A) \<lbrace>Number Tk, Agent B, Key K, X\<rbrace>)
          \<in> set evs \<longrightarrow>
          Crypt K (Number Ta) \<in> parts (spies evs) \<longrightarrow>
          Says B A (Crypt K (Number Ta)) \<in> set evs"
apply (erule bankerberos.induct)
apply (frule_tac [7] Oops_parts_spies)
apply (frule_tac [5] Says_S_message_form)
apply (drule_tac [6] BK3_msg_in_parts_spies, analz_mono_contra)
apply (simp_all (no_asm_simp) add: all_conj_distrib)
txt{*Fake*}
apply blast
txt{*BK2*}
apply (force dest: Crypt_imp_invKey_keysFor)
txt{*BK4*}
apply (blast dest: ticket_authentic unique_session_keys
                   Says_imp_spies [THEN analz.Inj] Crypt_Spy_analz_bad)
done

text{*The "r" suffix indicates theorems where the confidentiality assumptions are relaxed by the corresponding arguments.*}

text{*Authentication of A to B*}
lemma B_authenticates_A_r:
     "\<lbrakk> Crypt K \<lbrace>Agent A, Number Ta\<rbrace> \<in> parts (spies evs);
         Crypt (shrK B) \<lbrace>Number Tk, Agent A, Key K\<rbrace>  \<in> parts (spies evs);
        Notes Spy \<lbrace>Number Tk, Key K\<rbrace> \<notin> set evs;
         A \<notin> bad;  B \<notin> bad;  evs \<in> bankerberos \<rbrakk>
      \<Longrightarrow> Says A B \<lbrace>Crypt (shrK B) \<lbrace>Number Tk, Agent A, Key K\<rbrace>,
                     Crypt K \<lbrace>Agent A, Number Ta\<rbrace>\<rbrace> \<in> set evs"
apply (blast dest!: ticket_authentic
          intro!: lemma_A
          elim!: Confidentiality_S [THEN [2] rev_notE])
done

text{*Authentication of B to A*}
lemma A_authenticates_B_r:
     "\<lbrakk> Crypt K (Number Ta) \<in> parts (spies evs);
        Crypt (shrK A) \<lbrace>Number Tk, Agent B, Key K, X\<rbrace> \<in> parts (spies evs);
        Notes Spy \<lbrace>Number Tk, Key K\<rbrace> \<notin> set evs;
        A \<notin> bad;  B \<notin> bad;  evs \<in> bankerberos \<rbrakk>
      \<Longrightarrow> Says B A (Crypt K (Number Ta)) \<in> set evs"
apply (blast dest!: Kab_authentic
          intro!: lemma_B elim!: Confidentiality_S [THEN [2] rev_notE])
done

lemma B_authenticates_A:
     "\<lbrakk> Crypt K \<lbrace>Agent A, Number Ta\<rbrace> \<in> parts (spies evs);
         Crypt (shrK B) \<lbrace>Number Tk, Agent A, Key K\<rbrace>  \<in> parts (spies evs);
        Key K \<notin> analz (spies evs);
         A \<notin> bad;  B \<notin> bad;  evs \<in> bankerberos \<rbrakk>
      \<Longrightarrow> Says A B \<lbrace>Crypt (shrK B) \<lbrace>Number Tk, Agent A, Key K\<rbrace>,
                     Crypt K \<lbrace>Agent A, Number Ta\<rbrace>\<rbrace> \<in> set evs"
apply (blast dest!: ticket_authentic intro!: lemma_A)
done

lemma A_authenticates_B:
     "\<lbrakk> Crypt K (Number Ta) \<in> parts (spies evs);
        Crypt (shrK A) \<lbrace>Number Tk, Agent B, Key K, X\<rbrace> \<in> parts (spies evs);
        Key K \<notin> analz (spies evs);
        A \<notin> bad;  B \<notin> bad;  evs \<in> bankerberos \<rbrakk>
      \<Longrightarrow> Says B A (Crypt K (Number Ta)) \<in> set evs"
apply (blast dest!: Kab_authentic intro!: lemma_B)
done

subsection{*Temporal guarantees, relying on a temporal check that insures that
no oops event occurred. These are available in the sense of goal availability*}

text{*Temporal treatment of confidentiality*}

text{* Lemma: the session key sent in msg BK2 would be EXPIRED
    if the spy could see it! *}
lemma lemma_conf_temporal [rule_format (no_asm)]:
     "\<lbrakk> A \<notin> bad;  B \<notin> bad;  evs \<in> bankerberos \<rbrakk>
  \<Longrightarrow> Says Server A
          (Crypt (shrK A) \<lbrace>Number Tk, Agent B, Key K,
                            Crypt (shrK B) \<lbrace>Number Tk, Agent A, Key K\<rbrace>\<rbrace>)
         \<in> set evs \<longrightarrow>
      Key K \<in> analz (spies evs) \<longrightarrow> expiredK Tk evs"
apply (erule bankerberos.induct)
apply (frule_tac [7] Says_Server_message_form)
apply (frule_tac [5] Says_S_message_form [THEN disjE])
apply (simp_all (no_asm_simp) add: less_SucI analz_insert_eq analz_insert_freshK pushes)
txt{*Fake*}
apply spy_analz
txt{*BK2*}
apply (blast intro: parts_insertI less_SucI)
txt{*BK3*}
apply (metis Crypt_Spy_analz_bad Kab_authentic Says_imp_analz_Spy
          Says_imp_parts_knows_Spy analz.Snd less_SucI unique_session_keys)
txt{*Oops: PROOF FAILS if unsafe intro below*}
apply (blast dest: unique_session_keys intro!: less_SucI)
done

text{*Confidentiality for the Server: Spy does not see the keys sent in msg BK2
as long as they have not expired.*}
lemma Confidentiality_S_temporal:
     "\<lbrakk> Says Server A
          (Crypt K' \<lbrace>Number T, Agent B, Key K, X\<rbrace>) \<in> set evs;
         \<not> expiredK T evs;
         A \<notin> bad;  B \<notin> bad;  evs \<in> bankerberos
      \<rbrakk> \<Longrightarrow> Key K \<notin> analz (spies evs)"
apply (frule Says_Server_message_form, assumption)
apply (blast intro: lemma_conf_temporal)
done

text{*Confidentiality for Alice*}
lemma Confidentiality_A_temporal:
     "\<lbrakk> Crypt (shrK A) \<lbrace>Number T, Agent B, Key K, X\<rbrace> \<in> parts (spies evs);
         \<not> expiredK T evs;
         A \<notin> bad;  B \<notin> bad;  evs \<in> bankerberos
      \<rbrakk> \<Longrightarrow> Key K \<notin> analz (spies evs)"
apply (blast dest!: Kab_authentic Confidentiality_S_temporal)
done

text{*Confidentiality for Bob*}
lemma Confidentiality_B_temporal:
     "\<lbrakk> Crypt (shrK B) \<lbrace>Number Tk, Agent A, Key K\<rbrace>
          \<in> parts (spies evs);
        \<not> expiredK Tk evs;
        A \<notin> bad;  B \<notin> bad;  evs \<in> bankerberos
      \<rbrakk> \<Longrightarrow> Key K \<notin> analz (spies evs)"
apply (blast dest!: ticket_authentic Confidentiality_S_temporal)
done

text{*Temporal treatment of authentication*}

text{*Authentication of A to B*}
lemma B_authenticates_A_temporal:
     "\<lbrakk> Crypt K \<lbrace>Agent A, Number Ta\<rbrace> \<in> parts (spies evs);
         Crypt (shrK B) \<lbrace>Number Tk, Agent A, Key K\<rbrace>
         \<in> parts (spies evs);
         \<not> expiredK Tk evs;
         A \<notin> bad;  B \<notin> bad;  evs \<in> bankerberos \<rbrakk>
      \<Longrightarrow> Says A B \<lbrace>Crypt (shrK B) \<lbrace>Number Tk, Agent A, Key K\<rbrace>,
                     Crypt K \<lbrace>Agent A, Number Ta\<rbrace>\<rbrace> \<in> set evs"
apply (blast dest!: ticket_authentic
          intro!: lemma_A
          elim!: Confidentiality_S_temporal [THEN [2] rev_notE])
done

text{*Authentication of B to A*}
lemma A_authenticates_B_temporal:
     "\<lbrakk> Crypt K (Number Ta) \<in> parts (spies evs);
         Crypt (shrK A) \<lbrace>Number Tk, Agent B, Key K, X\<rbrace>
         \<in> parts (spies evs);
         \<not> expiredK Tk evs;
         A \<notin> bad;  B \<notin> bad;  evs \<in> bankerberos \<rbrakk>
      \<Longrightarrow> Says B A (Crypt K (Number Ta)) \<in> set evs"
apply (blast dest!: Kab_authentic
          intro!: lemma_B elim!: Confidentiality_S_temporal [THEN [2] rev_notE])
done

subsection{*Treatment of the key distribution goal using trace inspection. All
guarantees are in non-temporal form, hence non available, though their temporal
form is trivial to derive. These guarantees also convey a stronger form of
authentication - non-injective agreement on the session key*}

lemma B_Issues_A:
     "\<lbrakk> Says B A (Crypt K (Number Ta)) \<in> set evs;
         Key K \<notin> analz (spies evs);
         A \<notin> bad;  B \<notin> bad; evs \<in> bankerberos \<rbrakk>
      \<Longrightarrow> B Issues A with (Crypt K (Number Ta)) on evs"
apply (simp (no_asm) add: Issues_def)
apply (rule exI)
apply (rule conjI, assumption)
apply (simp (no_asm))
apply (erule rev_mp)
apply (erule rev_mp)
apply (erule bankerberos.induct, analz_mono_contra)
apply (simp_all (no_asm_simp))
txt{*fake*}
apply blast
txt{*K4 obviously is the non-trivial case*}
apply (simp add: takeWhile_tail)
apply (blast dest: ticket_authentic parts_spies_takeWhile_mono [THEN subsetD] parts_spies_evs_revD2 [THEN subsetD] intro: A_authenticates_B_temporal)
done

lemma A_authenticates_and_keydist_to_B:
     "\<lbrakk> Crypt K (Number Ta) \<in> parts (spies evs);
        Crypt (shrK A) \<lbrace>Number Tk, Agent B, Key K, X\<rbrace> \<in> parts (spies evs);
         Key K \<notin> analz (spies evs);
         A \<notin> bad;  B \<notin> bad; evs \<in> bankerberos \<rbrakk>
      \<Longrightarrow> B Issues A with (Crypt K (Number Ta)) on evs"
apply (blast dest!: A_authenticates_B B_Issues_A)
done

lemma A_Issues_B:
     "\<lbrakk> Says A B \<lbrace>Ticket, Crypt K \<lbrace>Agent A, Number Ta\<rbrace>\<rbrace>
           \<in> set evs;
         Key K \<notin> analz (spies evs);
         A \<notin> bad;  B \<notin> bad;  evs \<in> bankerberos \<rbrakk>
   \<Longrightarrow> A Issues B with (Crypt K \<lbrace>Agent A, Number Ta\<rbrace>) on evs"
apply (simp (no_asm) add: Issues_def)
apply (rule exI)
apply (rule conjI, assumption)
apply (simp (no_asm))
apply (erule rev_mp)
apply (erule rev_mp)
apply (erule bankerberos.induct, analz_mono_contra)
apply (simp_all (no_asm_simp))
txt{*fake*}
apply blast
txt{*K3 is the non trivial case*}
apply (simp add: takeWhile_tail)
apply auto (*Technically unnecessary, merely clarifies the subgoal as it is presemted in the book*)
apply (blast dest: Kab_authentic Says_Server_message_form parts_spies_takeWhile_mono [THEN subsetD] parts_spies_evs_revD2 [THEN subsetD]
             intro!: B_authenticates_A)
done

lemma B_authenticates_and_keydist_to_A:
     "\<lbrakk> Crypt K \<lbrace>Agent A, Number Ta\<rbrace> \<in> parts (spies evs);
        Crypt (shrK B) \<lbrace>Number Tk, Agent A, Key K\<rbrace>  \<in> parts (spies evs);
        Key K \<notin> analz (spies evs);
        A \<notin> bad;  B \<notin> bad;  evs \<in> bankerberos \<rbrakk>
   \<Longrightarrow> A Issues B with (Crypt K \<lbrace>Agent A, Number Ta\<rbrace>) on evs"
apply (blast dest: B_authenticates_A A_Issues_B)
done

end


theory Subtypes
imports MainHC
begin

consts

X_gn_inj :: "'a => 'b" ("gn'_inj/'(_')" [3] 999)
X_gn_proj :: "'a => 'b partial" ("gn'_proj/'(_')" [3] 999)
X_gn_subt :: "'a => 'b => bool" ("gn'_subt/'(_,/ _')" [3,3] 999)

axioms
ga_subt_reflexive [rule_format] :
"ALL (x :: 'a). ALL (y :: 'a). gn_subt(x, y)"

ga_subt_transitive [rule_format] :
"ALL (x :: 'a).
 ALL (y :: 'b).
 ALL (z :: 'c). gn_subt(x, y) & gn_subt(y, z) --> gn_subt(x, z)"

ga_subt_inj_proj [rule_format] :
"ALL (x :: 'a).
 ALL (y :: 'b).
 gn_subt(x, y) -->
 y = (X_gn_inj :: 'a => 'b) x =
 (makePartial x = (X_gn_proj :: 'b => 'a partial) y)"

ga_inj_transitive [rule_format] :
"ALL (x :: 'a).
 ALL (y :: 'b).
 ALL (z :: 'c).
 gn_subt(x, y) & gn_subt(y, z) & y = (X_gn_inj :: 'a => 'b) x -->
 z = (X_gn_inj :: 'a => 'c) x = (z = (X_gn_inj :: 'b => 'c) y)"

lemma subtype_reflexive:
"X_gn_subt (x:: 'a) (y:: 'a)" by (rule ga_subt_reflexive)

lemma subtype_transitive:
"[| X_gn_subt (x:: 'a) (y:: 'b); X_gn_subt (z:: 'b) (t:: 'c) |]  ==> X_gn_subt (u:: 'a) (v:: 'c)"
proof-
  assume hypAB: "X_gn_subt (x:: 'a) (y:: 'b)"
  assume hypBC: "X_gn_subt (z:: 'b) (t:: 'c)"
  have A: "X_gn_subt u y" by (rule ga_subt_transitive [of u x y], simp add: ga_subt_reflexive hypAB)
  have B: "X_gn_subt y t" by (rule ga_subt_transitive [of y z t], simp add: ga_subt_reflexive hypBC)
  have C: "X_gn_subt u t" by (rule ga_subt_transitive [of u y t], simp add: A B)
  show "X_gn_subt u v" by (rule ga_subt_transitive [of u t v], simp add: ga_subt_reflexive C)
qed

-- "necessary when omitting the quantifiers in the axioms below"
lemma subtype_constant:
"X_gn_subt (x:: 'a) (y:: 'b) ==> (!!(u:: 'a) v:: 'b. X_gn_subt u v)"
proof-
  assume hyp: "X_gn_subt x y"
  fix u:: 'a
  fix v:: 'b
  show "X_gn_subt u v"

 by (rule subtype_transitive [of x x x y u v], (simp only: ga_subt_reflexive hyp)+)
qed

-- "INJECTION PROJECTION RULES"

lemma gn_proj_inj:
"X_gn_subt (x:: 'a) (y:: 'b) ==> (!!(z:: 'a). makePartial(z) = gn_proj(gn_inj(z):: 'b))"
proof-
  assume hyp: "X_gn_subt x y"
  fix z :: 'a

  have "(gn_inj(z) :: 'b) = gn_inj(z) = (makePartial z = gn_proj(gn_inj(z) :: 'b))"
    by (rule ga_subt_inj_proj, rule subtype_constant [of x y], simp only: hyp)
  thus "makePartial z = gn_proj(gn_inj(z) :: 'b)" by simp
qed

lemma gn_makeTotal_proj_inj:
"X_gn_subt (x:: 'a) (y:: 'b) ==> (!!(z:: 'a). makeTotal(gn_proj(gn_inj(z):: 'b)) = z)"
 by (simp only: partial_identity gn_proj_inj [symmetric])

-- "is used to derive defining predicate P for a subtype A, P(gn_inj(x::A))"
lemma gn_proj_def:
"X_gn_subt (x:: 'a) (y:: 'b) ==> defOp(gn_proj(gn_inj(x):: 'b):: 'a partial)"
proof-
  assume hyp: "gn_subt(x, y)"
  hence A: "makePartial(x) = gn_proj(gn_inj(x):: 'b)" by (rule gn_proj_inj [of x y x])
  show "defOp(gn_proj(gn_inj(x):: 'b):: 'a partial)"
    by (simp add: A [symmetric] makePartial_def)
qed

(*
lemma gn_inj_proj:
"X_gn_subt (x:: 'a) (y:: 'b) ==> (!!(z:: 'b). defOp(gn_proj(z):: 'a partial) ==>
  gn_inj(makeTotal(gn_proj(z):: 'a partial)) = z)"
proof-
  assume hyp: "gn_subt(x, y)"
  fix z :: 'b
  assume hyp': "defOp(gn_proj(z):: 'a partial)"
  have "EX (t :: 'a). gn_proj(z) = makePartial t"
    by (rule defOp_implies_makePartial [of "gn_proj(z)"], simp only: hyp')
  then obtain t :: 'a where eq1: "makePartial t = gn_proj(z)" by auto
  from hyp have A: "gn_subt(t, z)" by (simp only: subtype_constant)
  have B: "t=makeTotal(gn_proj(gn_inj(t):: 'b)) "
    by (rule gn_makeTotal_proj_inj [symmetric,of x y], simp only: hyp)
  have C: "z = gn_inj(t)"
    by (simp only: ga_subt_inj_proj subtype_reflexive A eq1)
  also have "\<dots> = gn_inj(makeTotal(gn_proj(gn_inj(t):: 'b)):: 'a)"
    by (subst B, simp)
  also have "\<dots> = gn_inj(makeTotal(gn_proj(z)):: 'a)" by (simp only: C)
  finally show "gn_inj(makeTotal(gn_proj(z)):: 'a) = z" ..
qed
*)

lemma gn_inj_diagram:
"[| X_gn_subt (x:: 'a) (y:: 'b); X_gn_subt (z:: 'b) (t:: 'c) |]
  ==> (!!(x':: 'a). (gn_inj(x'):: 'c) = gn_inj(gn_inj(x'):: 'b))"
proof-
  assume hypAB: "X_gn_subt (x:: 'a) (y:: 'b)"
  assume hypBC: "X_gn_subt (z:: 'b) (t:: 'c)"
  fix x' :: 'a

  def y_def: y' == "gn_inj(x'):: 'b"
  def z_def: z' == "gn_inj(x'):: 'c"

  from hypAB hypBC have A: "gn_subt(x',y') \<and> gn_subt(y',z')"
    by (simp add: subtype_constant)

  have B: "(z' = gn_inj(x')) = (z' = gn_inj(y'))"
    by (rule ga_inj_transitive [of x' y' z'], simp only: A, simp add: y_def)

  show "(gn_inj(x'):: 'c) = gn_inj(gn_inj(x'):: 'b)"
    by (simp only:  y_def [symmetric] z_def [symmetric], subst B [symmetric]
      , simp only: z_def)
qed

lemma gn_inj_injective :
  "X_gn_subt (u :: 'a) (v:: 'b) ==> inj (X_gn_inj :: 'a => 'b)"
  proof (rule injI)
    fix x:: 'a
    fix y:: 'a
    assume hyp1: "X_gn_subt (u:: 'a) (v:: 'b)"
      and hyp2: "(X_gn_inj x :: 'b) = X_gn_inj y"

    from hyp1 subtype_constant
    have fact: "!!(z:: 'a). makeTotal(gn_proj(gn_inj(z):: 'b)) = z"
      by (rule_tac gn_makeTotal_proj_inj, blast)

    hence "x = makeTotal(gn_proj(gn_inj(x):: 'b))" ..
    also from hyp2 have "... = makeTotal(gn_proj(gn_inj(y):: 'b))" by simp
    also from fact have "... = y" by simp
    finally show "x = y" by simp
  qed

lemma gn_inj_identity :
"!!x:: 'a. (gn_inj(x):: 'a) = x"
proof-
  fix x:: 'a
  have fact: "(gn_inj(x):: 'a) = gn_inj(gn_inj(x):: 'a)"
    by (simp_all add: gn_inj_diagram subtype_reflexive)
  thus "(gn_inj(x):: 'a) = x"
    by (subst injD [of "(X_gn_inj:: 'a=>'a)" "(gn_inj(x):: 'a)" x],
      simp_all only: gn_inj_injective subtype_reflexive fact [symmetric])
qed

end