section \<open>Example: Program Dependence Graphs\<close>
text_raw \<open>\label{sec:pdg}\<close>

text \<open>Program dependence graph (PDG) based slicing provides a very crude but direct approximation of
our characterisation. As such we can easily derive a corresponding correctness result.\<close>

theory PDG imports IFC
begin

context IFC
begin

text \<open>We utilise our established dependencies on program paths to define the PDG. Note that PDGs
usually only contain immediate control dependencies instead of the transitive ones we use here.
However as slicing is considering reachability questions this does not affect the result.\<close>
inductive_set pdg where
\<open>\<lbrakk>i cd\<^bsup>\<pi>\<^esup>\<rightarrow> k\<rbrakk> \<Longrightarrow> (\<pi> k, \<pi> i) \<in> pdg\<close> |
\<open>\<lbrakk>i dd\<^bsup>\<pi>,v\<^esup>\<rightarrow> k\<rbrakk> \<Longrightarrow> (\<pi> k, \<pi> i) \<in> pdg\<close>

text \<open>The set of sources is the set of nodes reading high variables.\<close>
inductive_set sources where
\<open>n \<in> nodes \<Longrightarrow> h \<in> hvars \<Longrightarrow> h \<in> reads n \<Longrightarrow> n\<in> sources\<close>

text \<open>The forward slice is the set of nodes reachable in the PDG from the set of sources.  To ensure
security slicing aims to prove that no observable node is contained in the \<close>
inductive_set slice where
\<open>n\<in> sources \<Longrightarrow> n \<in> slice\<close> |
\<open>m \<in> slice \<Longrightarrow> (m,n)\<in>pdg \<Longrightarrow> n \<in> slice\<close>

text \<open>As the PDG does not contain data control dependencies themselves we have to decompose these.\<close>
lemma dcd_pdg: assumes \<open>n dcd\<^bsup>\<pi>,v\<^esup>\<rightarrow> m via \<pi>' m'\<close> obtains l where \<open>(\<pi> m,l)\<in> pdg\<close> and \<open>(l,\<pi> n)\<in>pdg\<close>
proof -
  assume r: \<open>(\<And>l. (\<pi> m, l) \<in> pdg \<Longrightarrow> (l, \<pi> n) \<in> pdg \<Longrightarrow> thesis)\<close>
  obtain l' n' where ln: \<open>cs\<^bsup>\<pi>\<^esup> m = cs\<^bsup>\<pi>'\<^esup> m' \<and> cs\<^bsup>\<pi>\<^esup> n = cs\<^bsup>\<pi>'\<^esup> n' \<and> n' dd\<^bsup>\<pi>',v\<^esup>\<rightarrow> l' \<and> l' cd\<^bsup>\<pi>'\<^esup>\<rightarrow> m'\<close> using assms unfolding is_dcdi_via_def by metis
  hence  mn: \<open>\<pi>' m' = \<pi> m \<and> \<pi>' n' = \<pi> n\<close> by (metis last_cs ln)
  have 1: \<open>(\<pi> m, \<pi>' l') \<in> pdg\<close> by (metis ln mn pdg.intros(1))
  have 2: \<open>(\<pi>' l', \<pi> n) \<in> pdg\<close> by (metis ln mn pdg.intros(2))
  show thesis using 1 2 r by auto
qed

text \<open>By induction it directly follows that the slice is an approximation of the single critical paths.\<close>
lemma scp_slice: \<open>(\<pi>, i)\<in> scp \<Longrightarrow> \<pi> i \<in> slice\<close>
  apply (induction rule: scp.induct)
     apply (simp add: path_in_nodes slice.intros(1) sources.intros)
    using pdg.intros(1) slice.intros(2) apply blast
  using pdg.intros(2) slice.intros(2) apply blast
  by (metis dcd_pdg slice.intros(2))

lemma scop_slice: \<open>(\<pi>, i) \<in> scop \<Longrightarrow> \<pi> i \<in> slice \<inter> dom(att)\<close>  by (metis IntI scop.cases scp_slice)

text \<open>The requirement targeted by slicing, that no observable node is contained in the slice,
is thereby a sound criteria for security.\<close>
lemma pdg_correct: assumes \<open>slice \<inter> dom(att) = {}\<close> shows \<open>secure\<close>
proof (rule ccontr)
  assume \<open>\<not> secure\<close>
  then obtain \<pi> i where \<open>(\<pi>, i) \<in> scop\<close> using scop_correct by force
  thus \<open>False\<close> using scop_slice assms by auto
qed

end

end


section \<open>Noninterference\<close>

theory Noninterference
imports Automata
begin

text \<open>We now consider security of such automata in the sense of Goguen and Meseguer's notion
of @{emph \<open>noninterference\<close>}.\<close>

subsection \<open>Definition\<close>

text \<open>For a given automaton, we add a function @{text dom}, which assigns a security domain to each
action, and a flow policy, which is a reflexive and transitive relation between domains, specifying
which flows of information are allowed.\<close>

locale NI =
  Automaton s0 step out
  for s0 :: "'state"
  and step :: "'state \<Rightarrow> 'act \<Rightarrow> 'state"
  and out :: "'state \<Rightarrow> 'act \<Rightarrow> 'out"
+
  fixes FP :: "'dom rel"
    and dom :: "'act \<Rightarrow> 'dom"
  assumes "refl FP"
      and "trans FP"
begin

text \<open>We write @{text "D \<leadsto> D'"} if the flow policy allows a flow from @{text D} to @{text D'}.\<close>

abbreviation flow :: "'dom \<Rightarrow> 'dom \<Rightarrow> bool" ("_ \<leadsto> _")
where "(D \<leadsto> D') \<equiv> (D, D') \<in> FP"

text \<open>We define the @{text purge} function, which removes all actions from a trace @{text \<alpha>} that
belong to a domain from which no flow is allowed to a given domain @{text D}. In other words,
we remove actions that should be secret for @{text D}.\<close>

fun purge :: "'act list \<Rightarrow> 'dom \<Rightarrow> 'act list" where
  "purge [] D = []"
| "purge (a # \<alpha>) D = (if (dom a) \<leadsto> D
                      then a # purge \<alpha> D
                      else purge \<alpha> D)"

text \<open>An automaton is considered secure wrt.\ a noninterference policy if the output of an action
@{text a} is independent of the execution of secret actions: Purging secret actions from the trace
that has been executed before does not change the output of @{text a}.\<close>

definition "NI_secure
\<equiv> \<forall>a \<alpha>. out (run s0 \<alpha>) a = out (run s0 (purge \<alpha> (dom a))) a"

text \<open>This is the @{emph \<open>definition\<close>} of (noninterference) security for automata.
In the following sections, we focus on techniques for the @{emph \<open>verification\<close>} of security.\<close>

end

subsection \<open>Verification\<close>

text \<open>In order to use the unwinding verification technique, we first have to choose a
@{emph \<open>view partitioning\<close>}, i.e.\ a family of equivalence relations on states, indexed by domain.
Hence, we have to choose an equivalence relation for each domain. When choosing these relations,
the intuition is to consider states as equivalent for a domain if they are supposed to be
indistinguishable for that domain.\<close>

locale View_Partitioning =
  NI s0 step out FP dom
  for s0 :: "'state"
  and step :: "'state \<Rightarrow> 'act \<Rightarrow> 'state"
  and out :: "'state \<Rightarrow> 'act \<Rightarrow> 'out"
  and FP :: "'dom rel"
  and dom :: "'act \<Rightarrow> 'dom"
+
  fixes view :: "'dom \<Rightarrow> 'state rel"
  assumes view_equiv: "\<forall>u. equiv UNIV (view u)"
begin

text \<open>We write @{text "s \<sim>\<^bsub>u\<^esub> t"} if the states @{text s} and @{text t} are equivalent for the
domain @{text u} in our view partitioning.\<close>

abbreviation view' :: "'state \<Rightarrow> 'dom \<Rightarrow> 'state \<Rightarrow> bool" ("_ \<sim>\<^bsub>_\<^esub> _")
where "(s \<sim>\<^bsub>u\<^esub> t) \<equiv> (s, t) \<in> view u"

text \<open>Equivalence relations are reflexive, symmetric and transitive.\<close>

lemma view_refl: "s \<sim>\<^bsub>u\<^esub> s"
  and view_sym: "(s \<sim>\<^bsub>u\<^esub> s') \<Longrightarrow> (s' \<sim>\<^bsub>u\<^esub> s)"
  and view_trans: "(s \<sim>\<^bsub>u\<^esub> s') \<Longrightarrow> (s' \<sim>\<^bsub>u\<^esub> t) \<Longrightarrow> (s \<sim>\<^bsub>u\<^esub> t)"
using view_equiv
unfolding equiv_def refl_on_def sym_def trans_def
by blast+

text \<open>The unwinding theorem states that an automaton is secure wrt.\ a noninterference policy
if the following three properties are satisfied for the given view partitioning.\<close>

text \<open>@{emph \<open>Output consistency\<close>} requires that two states give the same output for an action
@{text a}, if they are equivalent for the domain of that action. In other words, observers in
a given domain cannot distinguish equivalent states via the outputs of their actions.\<close>

definition "output_consistent
\<equiv> \<forall>a s t. (s \<sim>\<^bsub>dom a\<^esub> t) \<longrightarrow> out s a = out t a"

text \<open>@{emph \<open>Step consistency\<close>} requires that performing a visible action from two equivalent
states again results in equivalent states.\<close>

definition "step_consistent
\<equiv> \<forall>s t u a. (((dom a) \<leadsto> u) \<and> (s \<sim>\<^bsub>u\<^esub> t)) \<longrightarrow> ((step s a) \<sim>\<^bsub>u\<^esub> (step t a))"

text \<open>@{emph \<open>Local respect of @{text \<leadsto>}\<close>} requires that performing a secret action preserves the
equivalence class of the current state.\<close>

definition "locally_respects_FP
\<equiv> \<forall>s u a. \<not>((dom a) \<leadsto> u) \<longrightarrow> (s \<sim>\<^bsub>u\<^esub> (step s a))"

text \<open>In order to prove noninterference using these conditions, we use structural induction over
the list of actions @{text \<alpha>}.

The induction schema for lists looks as follows:
@{thm [display] list.induct [no_vars]}

We prove noninterference using a helper lemma: Instead of letting the lists of actions (unpurged
and purged) run from the starting state, we let them run from two equivalent, but otherwise
arbitrary states @{text s} and @{text t}, respectively. This makes the induction hypothesis more
useful. Moreover, we focus first on showing that the states after the two runs are equivalent for
an observer in the domain @{text u} (when purging for @{text u}).
\<close>

lemma unwinding_lemma:
  assumes lr: "locally_respects_FP"
      and sc: "step_consistent"
      and "s \<sim>\<^bsub>u\<^esub> t"
  shows "(run s \<alpha>) \<sim>\<^bsub>u\<^esub> (run t (purge \<alpha> u))"
using `s \<sim>\<^bsub>u\<^esub> t`
proof (induction \<alpha> arbitrary: s t)
  case Nil
    \<comment> \<open>Base case: empty list []\<close>
    have "run s [] = s"
     and "run t (purge [] u) = t"
      by auto
    with `s \<sim>\<^bsub>u\<^esub> t` show "(run s []) \<sim>\<^bsub>u\<^esub> (run t (purge [] u))" by simp
next
  case (Cons a' \<alpha>')
    \<comment> \<open>Inductive step:\<close>
    \<comment> \<open>List starts with action @{text a'} and continues with list @{text \<alpha>'} (possibly empty).\<close>
    \<comment> \<open>The following induction hypothesis about @{text \<alpha>'} is available
        (for any @{text s'} and @{text t'}):\<close>
    \<comment> \<open>@{thm Cons.IH[of "s'" "t'"]}\<close>
    \<comment> \<open>We proceed by a case distinction whether @{text a'} is visible for @{text u} or not.\<close>
    show "(run s (a' # \<alpha>')) \<sim>\<^bsub>u\<^esub> (run t (purge (a' # \<alpha>') u))"
    proof (cases)
      assume flow: "(dom a') \<leadsto> u"
      with `s \<sim>\<^bsub>u\<^esub> t` have "(step s a') \<sim>\<^bsub>u\<^esub> (step t a')"
        using sc              \<comment> \<open>Step consistency\<close>
        unfolding step_consistent_def
        by auto
      then have "(run (step s a') \<alpha>') \<sim>\<^bsub>u\<^esub> (run (step t a') (purge \<alpha>' u))"
        using Cons.IH         \<comment> \<open>Induction hypothesis!\<close>
        by simp
      then show ?case using flow by auto
    next
      assume noflow: "(dom a', u) \<notin> FP"
      then have "s \<sim>\<^bsub>u\<^esub> (step s a')"
        using lr              \<comment> \<open>Locally respects @{text \<leadsto>}\<close>
        unfolding locally_respects_FP_def
        by auto
      with `s \<sim>\<^bsub>u\<^esub> t` have "(step s a') \<sim>\<^bsub>u\<^esub> t"
        using view_sym view_trans
        by blast
      then have "(run (step s a') \<alpha>') \<sim>\<^bsub>u\<^esub> (run t (purge \<alpha>' u))"
        using Cons.IH         \<comment> \<open>Induction hypothesis!\<close>
        by simp
      then show ?case using noflow by auto
    qed
qed

text \<open>With this lemma, noninterference follows directly by setting @{text "s = t = s0"} and
using output consistency.\<close>

theorem unwinding_theorem:
  assumes "output_consistent"
      and "locally_respects_FP"
      and "step_consistent"
  shows "NI_secure"
using assms unwinding_lemma[where s = s0 and t = s0] view_refl[of s0]
unfolding NI_secure_def output_consistent_def
by auto

end

end