Theory Security

(*
Title: Value-Dependent SIFUM-Type-Systems
Authors: Toby Murray, Robert Sison, Edward Pierzchalski, Christine Rizkallah
(Based on the SIFUM-Type-Systems AFP entry, whose authors
 are: Sylvia Grewe, Heiko Mantel, Daniel Schoepe)
*)

section ‹Definition of the SIFUM-Security Property›

theory Security
imports Preliminaries
begin

type_synonym ('var, 'val) adaptation = "'var  ('val × 'val)"

definition apply_adaptation ::
  "bool  ('Var, 'Val) Mem  ('Var, 'Val) adaptation  ('Var, 'Val) Mem"
  where "apply_adaptation first mem A =
         (λ x. case (A x) of
               Some (v1, v2)  if first then v1 else v2
             | None  mem x)"

abbreviation apply_adaptation1 ::
  "('Var, 'Val) Mem  ('Var, 'Val) adaptation  ('Var, 'Val) Mem"
  ("_ [∥1 _]" [900, 0] 1000)
  where "mem [∥1 A]  apply_adaptation True mem A"

abbreviation apply_adaptation2 ::
  "('Var, 'Val) Mem  ('Var, 'Val) adaptation  ('Var, 'Val) Mem"
  ("_ [∥2 _]" [900, 0] 1000)
  where "mem [∥2 A]  apply_adaptation False mem A"

definition
  var_asm_not_written :: "'Var Mds  'Var  bool"
where
  "var_asm_not_written mds x  x  mds AsmNoWrite  x  mds AsmNoReadOrWrite"

context sifum_security_init begin

subsection ‹Evaluation of Concurrent Programs›

abbreviation eval_abv :: "('Com, 'Var, 'Val) LocalConf  (_, _, _) LocalConf  bool"
  (infixl "" 70)
where
  "x  y  (x, y)  eval"

abbreviation conf_abv :: "'Com  'Var Mds  ('Var, 'Val) Mem  (_,_,_) LocalConf"
  ("_, _, _" [0, 0, 0] 1000)
where
  " c, mds, mem   ((c, mds), mem)"

(* Labelled evaluation global configurations: *)
inductive_set meval :: "((_,_,_) GlobalConf × nat × (_,_,_) GlobalConf) set"
  and meval_abv :: "_  _  _  bool" ("_ ↝⇘_ _" 70)
where
  "conf ↝⇘kconf'  (conf, k, conf')  meval" |
  meval_intro [iff]: " (cms ! n, mem)  (cm', mem'); n < length cms  
  ((cms, mem), n, (cms [n := cm'], mem'))  meval"

inductive_cases meval_elim [elim!]: "((cms, mem), k, (cms', mem'))  meval"

inductive neval :: "('Com, 'Var, 'Val) LocalConf  nat  (_, _, _) LocalConf  bool"
  (infixl "↝⇗_" 70)
where
  neval_0: "x = y  x ↝⇗0y" |
  neval_S_n: "x  y  y ↝⇗nz  x ↝⇗Suc nz"

inductive_cases neval_ZeroE: "neval x 0 y"
inductive_cases neval_SucE: "neval x (Suc n) y"

lemma neval_det:
  "x ↝⇗ny  x ↝⇗ny'  y = y'"
  apply(induct arbitrary: y' rule: neval.induct)
   apply(blast elim: neval_ZeroE)
  apply(blast elim: neval_SucE dest: deterministic)
  done

lemma neval_Suc_simp [simp]:
  "neval x (Suc 0) y = x  y"
proof
  assume a: "neval x (Suc 0) y"
  have "n. neval x n y  n = Suc 0  x  y"
  proof -
    fix n
    assume "neval x n y"
       and "n = Suc 0"
    thus "x  y"
    by(induct rule: neval.induct, auto elim: neval_ZeroE)
  qed
  with a show "x  y" by simp
  next
  assume "x  y"
  thus "neval x (Suc 0) y"
    by(force intro: neval.intros)
qed

fun 
  lc_set_var :: "(_, _, _) LocalConf  'Var  'Val  (_, _, _) LocalConf"
where
  "lc_set_var (c, mem) x v = (c, mem (x := v))"

fun 
  meval_sched :: "nat list  ('Com, 'Var, 'Val) GlobalConf  (_, _, _) GlobalConf  bool"
where
  "meval_sched [] c c' = (c = c')" |
  "meval_sched (n#ns) c c' = ( c''.  c ↝⇘nc''  meval_sched ns c'' c')"

abbreviation
  meval_sched_abv :: "(_,_,_) GlobalConf  nat list  (_,_,_) GlobalConf  bool" ("_ →⇘_ _" 70)
where
  "c →⇘nsc'  meval_sched ns c c'"

lemma meval_sched_det:
  "meval_sched ns c c'  meval_sched ns c c''  c' = c''"
  apply(induct ns arbitrary: c)
   apply(auto dest: deterministic)
  done

subsection ‹Low-equivalence and Strong Low Bisimulations›

(* Low-equality between memory states: *)
definition 
  low_eq :: "('Var, 'Val) Mem  (_, _) Mem  bool" (infixl "=l" 80)
where
  "mem1 =l mem2  ( x. dma mem1 x = Low  mem1 x = mem2 x)"


(* Low-equality modulo a given mode state: *)
definition 
  low_mds_eq :: "'Var Mds  ('Var, 'Val) Mem  (_, _) Mem  bool"
  ("_ =ıl _" [100, 100] 80)
where
  "(mem1 =mdsl mem2)  ( x. dma mem1 x = Low  (x  𝒞  x  mds AsmNoReadOrWrite)  mem1 x = mem2 x)"

lemma low_eq_low_mds_eq:
  "(mem1 =l mem2) = (mem1 =(λm. {})l mem2)"
  by(simp add: low_eq_def low_mds_eq_def)

lemma low_mds_eq_dma:
  "(mem1 =mdsl mem2)  dma mem1 = dma mem2"
  apply(rule dma_𝒞)
  apply(simp add: low_mds_eq_def 𝒞_Low)
  done

lemma low_mds_eq_sym:
  "(mem1 =mdsl mem2)  (mem2 =mdsl mem1)"
  apply(frule low_mds_eq_dma)
  apply(simp add: low_mds_eq_def)
  done

lemma low_eq_sym:
  "(mem1 =l mem2)  (mem2 =l mem1)"
  apply(simp add: low_eq_low_mds_eq low_mds_eq_sym)
  done

lemma [simp]: "mem =l mem'  mem =mdsl mem'"
  by (simp add: low_mds_eq_def low_eq_def)

lemma [simp]: "( mds. mem =mdsl mem')  mem =l mem'"
  by (auto simp: low_mds_eq_def low_eq_def)

lemma High_not_in_𝒞 [simp]:
  "dma mem1 x = High  x  𝒞"
  apply(case_tac "x  𝒞")
  by(simp add: 𝒞_Low)

(* Closedness under globally consistent changes: *)
definition 
  closed_glob_consistent :: "(('Com, 'Var, 'Val) LocalConf) rel  bool"
where
  "closed_glob_consistent  =
  ( c1 mds mem1 c2 mem2. ( c1, mds, mem1 ,  c2, mds, mem2 )   
   ( A. ((x. case A x of Some (v,v')  (mem1 x  v  mem2 x  v')  ¬ var_asm_not_written mds x | _  True) 
          (x. dma (mem1 [∥1 A]) x  dma mem1 x  ¬ var_asm_not_written mds x) 
          (x. dma (mem1 [∥1 A]) x = Low  (x  mds AsmNoReadOrWrite  x  𝒞)  (mem1 [∥1 A]) x = (mem2 [∥2 A]) x)) 
         ( c1, mds, mem1[∥1 A] ,  c2, mds, mem2[∥2 A] )  ))"

(* Strong low bisimulations modulo modes: *)
definition 
  strong_low_bisim_mm :: "(('Com, 'Var, 'Val) LocalConf) rel  bool"
where
  "strong_low_bisim_mm  
  sym  
  closed_glob_consistent  
  ( c1 mds mem1 c2 mem2. ( c1, mds, mem1 ,  c2, mds, mem2 )   
   (mem1 =mdsl mem2) 
   ( c1' mds' mem1'.  c1, mds, mem1    c1', mds', mem1'  
    ( c2' mem2'.  c2, mds, mem2    c2', mds', mem2'  
                  ( c1', mds', mem1' ,  c2', mds', mem2' )  )))"

inductive_set mm_equiv :: "(('Com, 'Var, 'Val) LocalConf) rel"
  and mm_equiv_abv :: "('Com, 'Var, 'Val) LocalConf  
  ('Com, 'Var, 'Val) LocalConf  bool" (infix "" 60)
where
  "mm_equiv_abv x y  (x, y)  mm_equiv" |
  mm_equiv_intro [iff]: " strong_low_bisim_mm  ; (lc1, lc2)     (lc1, lc2)  mm_equiv"

inductive_cases mm_equiv_elim [elim]: " c1, mds, mem1    c2, mds, mem2 "

definition low_indistinguishable :: "'Var Mds  'Com  'Com  bool"
  ("_ ı _" [100, 100] 80)
where 
  "c1 mdsc2 = ( mem1 mem2. mem1 =mdsl mem2   c1, mds, mem1    c2, mds, mem2 )"

subsection ‹SIFUM-Security›

(* SIFUM-security for commands: *)
definition 
  com_sifum_secure :: "'Com × 'Var Mds  bool"
where 
  "com_sifum_secure cmd  case cmd of (c,mdss)  c mdssc"

(* Continuous SIFUM-security 
   (where we don't care about whether the prog has any assumptions at termination *)
definition 
  prog_sifum_secure_cont :: "('Com × 'Var Mds) list  bool"
where "prog_sifum_secure_cont cmds =
   ( mem1 mem2. INIT mem1  INIT mem2  mem1 =l mem2 
    ( sched cms1' mem1'.
     (cmds, mem1) →⇘sched(cms1', mem1') 
      ( cms2' mem2'. (cmds, mem2) →⇘sched(cms2', mem2') 
                      map snd cms1' = map snd cms2' 
                      length cms2' = length cms1' 
                      ( x. dma mem1' x = Low  (x  𝒞  ( i < length cms1'.
                        x  snd (cms1' ! i) AsmNoReadOrWrite))  mem1' x = mem2' x))))"

(* Note that it is equivalent to the following because the 
   programming language is deterministic *)
lemma prog_sifum_secure_cont_def2:
  "prog_sifum_secure_cont cmds 
   ( mem1 mem2. INIT mem1  INIT mem2  mem1 =l mem2 
    ( sched cms1' mem1'.
     (cmds, mem1) →⇘sched(cms1', mem1') 
      ( cms2' mem2'. (cmds, mem2) →⇘sched(cms2', mem2')) 
      ( cms2' mem2'. (cmds, mem2) →⇘sched(cms2', mem2') 
                      map snd cms1' = map snd cms2' 
                      length cms2' = length cms1' 
                      ( x. dma mem1' x = Low  (x  𝒞  ( i < length cms1'.
                        x  snd (cms1' ! i) AsmNoReadOrWrite))  mem1' x = mem2' x))))"
  apply(rule eq_reflection)
  unfolding prog_sifum_secure_cont_def
  apply(rule iffI)
   apply(blast dest: meval_sched_det)
  by fastforce

subsection ‹Sound Mode Use›


(* Suggestive notation for substitution / function application *)
definition 
  subst :: "('a  'b)  ('a  'b)  ('a  'b)"
where
  "subst f mem = (λ x. case f x of
                         None  mem x |
                         Some v  v)"

abbreviation 
  subst_abv :: "('a  'b)  ('a  'b)  ('a  'b)" ("_ [↦_]" [900, 0] 1000)
where
  "f [↦ σ]  subst σ f"

lemma subst_not_in_dom : " x  dom σ   mem [↦ σ] x = mem x"
  by (simp add: domIff subst_def)

(* Toby: we restrict not reading to also imply not modifying a variable.
         This is done mostly to simplify part of the reasoning in the
         Compositionality theory, but also because reconciling doesnt_read_or_modify
         forcing also not reading the variable's classification in the case
         in the case where it would allow non-reading modifications proved
         to get too unwieldy *)
definition 
  doesnt_read_or_modify_vars :: "'Com  'Var set  bool"
where
  "doesnt_read_or_modify_vars c X = ( mds mem c' mds' mem'.
   c, mds, mem    c', mds', mem'  
  ((xX. ( v.  c, mds, mem (x := v)    c', mds', mem' (x := v) ))))"

definition
  vars_𝒞 :: "'Var set  'Var set"
where
  "vars_𝒞 X  xX. 𝒞_vars x"

lemma vars_𝒞_subset_𝒞:
  "vars_𝒞 X  𝒞"
  by(auto simp: 𝒞_def vars_𝒞_def)

(* Toby: doesnt_read_or_modify now implies that the classification is not read too *)
definition 
  doesnt_read_or_modify :: "'Com  'Var  bool"
where
  "doesnt_read_or_modify c x  doesnt_read_or_modify_vars c ({x}  𝒞_vars x)"

definition 
  doesnt_modify :: "'Com  'Var  bool"
where
  "doesnt_modify c x = ( mds mem c' mds' mem'. ( c, mds, mem    c', mds', mem' ) 
                        mem x = mem' x   dma mem x = dma mem' x)"

lemma noread_nowrite:
  assumes step: "c, mds, mem  c', mds', mem'"
  assumes noread: "(v. c, mds, mem(x := v)  c', mds', mem'(x := v))"
  shows "mem x = mem' x"
proof -
  from noread have "c, mds, mem(x := (mem x))  c', mds', mem'(x := (mem x))"
    by blast
  hence "c, mds, mem  c', mds', mem'(x := (mem x))" by simp
  from step this have "mem' = mem'(x := (mem x))" by (blast dest: deterministic)
  hence "mem' x = (mem'(x := (mem x))) x" by(rule arg_cong)
  thus ?thesis by simp
qed

(* Toby: not sure whether this implication should just be made explicit in the
         definition of doesnt_read_or_modify or not *)
lemma doesnt_read_or_modify_doesnt_modify:
  "doesnt_read_or_modify c x  doesnt_modify c x"
  by(fastforce simp: doesnt_modify_def doesnt_read_or_modify_def doesnt_read_or_modify_vars_def 
               intro: noread_nowrite dma_𝒞_vars)

(* Local reachability of local configurations: *)
inductive_set 
  loc_reach :: "('Com, 'Var, 'Val) LocalConf  ('Com, 'Var, 'Val) LocalConf set"
  for lc :: "(_, _, _) LocalConf"
where
  refl : "fst (fst lc), snd (fst lc), snd lc  loc_reach lc" |
  step : " c', mds', mem'  loc_reach lc;
            c', mds', mem'  c'', mds'', mem''  
          c'', mds'', mem''  loc_reach lc" |
  mem_diff : "  c', mds', mem'   loc_reach lc;
                ( x. var_asm_not_written mds' x  mem' x = mem'' x  dma mem' x = dma mem'' x)  
               c', mds', mem''   loc_reach lc"

lemma neval_loc_reach:
  "neval lc' n lc''  lc'  loc_reach lc  lc''  loc_reach lc"
proof(induct rule: neval.induct)
  case (neval_0 x y)
    thus ?case by simp
next
  case (neval_S_n x y n z)
  from x  loc_reach lc and x  y have "y  loc_reach lc" 
    (* TODO: can we get rid of this case_tac nonsense? *)
    apply(case_tac x, rename_tac a b, case_tac a, clarsimp)
    apply(case_tac y, rename_tac c d, case_tac c, clarsimp)
    by(blast intro: loc_reach.step)
  thus ?case
    using neval_S_n(3) by blast
qed
    

definition 
  locally_sound_mode_use :: "(_, _, _) LocalConf  bool"
where
  "locally_sound_mode_use lc =
  ( c' mds' mem'.  c', mds', mem'   loc_reach lc 
    ( x. (x  mds' GuarNoReadOrWrite  doesnt_read_or_modify c' x) 
          (x  mds' GuarNoWrite  doesnt_modify c' x)))"

(* RobS: The same property, but for an individual evaluation. Note it doesn't rely on mem! *)
definition 
  respects_own_guarantees :: "('Com × 'Var Mds)  bool"
where
  "respects_own_guarantees cm 
  ( x. (x  (snd cm) GuarNoReadOrWrite  doesnt_read_or_modify (fst cm) x) 
        (x  (snd cm) GuarNoWrite  doesnt_modify (fst cm) x))"

lemma locally_sound_mode_use_def2:
  "locally_sound_mode_use lc  lc'  loc_reach lc. respects_own_guarantees (fst lc')"
  apply(rule eq_reflection)
  apply(simp add: locally_sound_mode_use_def respects_own_guarantees_def)
  apply force
  done

lemma locally_sound_respects_guarantees:
  "locally_sound_mode_use (cm, mem)  respects_own_guarantees cm"
  unfolding locally_sound_mode_use_def respects_own_guarantees_def
  by (metis fst_conv loc_reach.refl)

definition 
  compatible_modes :: "('Var Mds) list  bool"
where
  "compatible_modes mdss = ( (i :: nat) x. i < length mdss 
    (x  (mdss ! i) AsmNoReadOrWrite 
     ( j < length mdss. j  i  x  (mdss ! j) GuarNoReadOrWrite)) 
    (x  (mdss ! i) AsmNoWrite 
     ( j < length mdss. j  i  x  (mdss ! j) GuarNoWrite)))"

definition 
  reachable_mode_states :: "('Com, 'Var, 'Val) GlobalConf  (('Var Mds) list) set"
where 
  "reachable_mode_states gc 
     {mdss. ( cms' mem' sched. gc →⇘sched(cms', mem')  map snd cms' = mdss)}"

definition 
  globally_sound_mode_use :: "('Com, 'Var, 'Val) GlobalConf  bool"
where 
  "globally_sound_mode_use gc 
     ( mdss. mdss  reachable_mode_states gc  compatible_modes mdss)"

primrec 
  sound_mode_use :: "(_, _, _) GlobalConf  bool"
where
  "sound_mode_use (cms, mem) =
     (list_all (λ cm. locally_sound_mode_use (cm, mem)) cms 
      globally_sound_mode_use (cms, mem))"

(* We now show that mm_equiv itself forms a strong low bisimulation modulo modes: *)
lemma mm_equiv_sym:
  assumes equivalent: "c1, mds1, mem1  c2, mds2, mem2"
  shows "c2, mds2, mem2  c1, mds1, mem1"
proof -
  from equivalent obtain 
    where ℛ_bisim: "strong_low_bisim_mm   (c1, mds1, mem1, c2, mds2, mem2)  "
    by (metis mm_equiv.simps)
  hence "sym "
    by (auto simp: strong_low_bisim_mm_def)
  hence "(c2, mds2, mem2, c1, mds1, mem1)  "
    by (metis ℛ_bisim symE)
  thus ?thesis
    by (metis ℛ_bisim mm_equiv.intros)
qed

lemma low_indistinguishable_sym: "lc mdslc'  lc' mdslc"
  apply(clarsimp simp: low_indistinguishable_def)
  apply(rule mm_equiv_sym)
  apply(blast dest: low_mds_eq_sym)
  done

lemma mm_equiv_glob_consistent: "closed_glob_consistent mm_equiv"
  unfolding closed_glob_consistent_def
  apply clarify
  apply (erule mm_equiv_elim)
  by (auto simp: strong_low_bisim_mm_def closed_glob_consistent_def)

lemma mm_equiv_strong_low_bisim: "strong_low_bisim_mm mm_equiv"
  unfolding strong_low_bisim_mm_def
proof (auto)
  show "closed_glob_consistent mm_equiv" by (rule mm_equiv_glob_consistent)
next
  fix c1 mds mem1 c2 mem2 x
  assume " c1, mds, mem1    c2, mds, mem2 "
  then obtain  where
    "strong_low_bisim_mm   ( c1, mds, mem1 ,  c2, mds, mem2 )  "
    by blast
  thus "mem1 =mdsl mem2" by (auto simp: strong_low_bisim_mm_def)
next
  fix c1 :: 'Com
  fix mds mem1 c2 mem2 c1' mds' mem1'
  let ?lc1 = " c1, mds, mem1 " and
      ?lc1' = " c1', mds', mem1' " and
      ?lc2 = " c2, mds, mem2 "
  assume "?lc1  ?lc2"
  then obtain  where "strong_low_bisim_mm   (?lc1, ?lc2)  "
    by (rule mm_equiv_elim, blast)
  moreover assume "?lc1  ?lc1'"
  ultimately show " c2' mem2'. ?lc2   c2', mds', mem2'   ?lc1'   c2', mds', mem2' "
    by (simp add: strong_low_bisim_mm_def mm_equiv_sym, blast)
next
  show "sym mm_equiv"
    by (auto simp: sym_def mm_equiv_sym)
qed

end

end