packages feed

scyther-proof-0.3.0: data/isabelle/src/ESPLogic/espl_methods.ML

(*****************************************************************************
 * ESPL --- an embedded security protocol logic
 *          http://people.inf.ethz.ch/meiersi/espl/
 *
 *   Copyright (c) 2009-2011, Simon Meier, ETH Zurich, Switzerland
 *
 * Extension to compromising adversaries:
 *
 *   Copyright (c) 2010-2011, Martin Schaub, ETH Zurich, Switzerland
 *
 * All rights reserved. See file LICENCE for more information.
 ******************************************************************************)
(* Attributes and proof methods supporting decryption chain reasoning.
   Side effect: Registers the "prefix_close" and "note_prefix_closed" commands. *)

signature ESPL_METHODS =
sig
  (* "sources" proof method *)
  val sources_tac
     : Proof.context -> bool -> term ->
       thm option -> thm list -> int -> thm -> (cases * thm) Seq.seq
  val sourcesP
     : Context.generic * OuterLex.token list ->
       ((bool * term) * thm option) * (Context.generic * OuterLex.token list)

  (* "prefix_close" proof state transformation *)
  val prefix_close_thms: Proof.context -> thm list -> thm list
  val prefix_close_cmd: Proof.state -> Proof.state
  val note_prefix_closed_cmd: Proof.state -> Proof.state

  (* "completeness_cases_rule" attribute *)
  val completeness_cases_attrib: Context.generic -> thm -> thm
end;

structure ESPL_Methods: ESPL_METHODS =
struct

open ESPL_Utils;



(******************************************************************************
**  "sources" proof method
******************************************************************************)

(* Pretty print resolution failure *)
fun err_no_matching_rule ctxt raw_rules knows_thm =
  error (Pretty.string_of (Pretty.block (Pretty.fbreaks
            [ Pretty.str "none of the rules:"
            , Pretty.indent 2 (Display.pretty_thms ctxt raw_rules)
            , Pretty.str "resolved against:"
            , Pretty.indent 2 (Display.pretty_thm ctxt knows_thm)
            ])))

(* Remove trivial cases,
   insert known facts (except case names), and
   attach case names using Rule_Cases
   to a specialized knows_cases_XXX rule.
*)
fun finalize_knows_cases ctxt minimal facts case_th =
  let
    val is_hint_thm = 
      can (Hints.dest_hint o HOLogic.dest_Trueprop o Thm.prop_of);

    val facts'    = filter (not o is_hint_thm) facts;
    val final_tac = ALLGOALS (
          (if minimal then 
             TRY o Simplifier.full_simp_tac (simpset_of ctxt)
           else
             TRY o Method.insert_tac facts' THEN'
             TRY o (
                Clasimp.clarsimp_tac (clasimpset_of ctxt) THEN'
                TRY o Orders.order_tac ctxt []
             )
          )
        );
    fun mk_Rule_Cases_cases th =
      Rule_Cases.get (Rule_Cases.name 
        (ESPL_Definitions.source_case_names th) th)
  in
    case_th
    |> HOL_Ext.refine_rule ctxt (K final_tac)
    |> ` mk_Rule_Cases_cases
  end;

(* The tactic underlying the "sources" proof method *)
fun sources_tac ctxt minimal msg_t opt_raw_rule facts =
  let
    val thy = ProofContext.theory_of ctxt;
    val cert = Thm.cterm_of thy;
    val css as (_, ss) = Clasimp.clasimpset_of ctxt;

    val raw_rules = case opt_raw_rule of
        SOME th => [th]
      | NONE    => ESPL_Definitions.KnowsCasesCache.get ctxt;

    val _ =
      if null raw_rules then error "no source elimination rules given" else ();

    (* we assume the first premise of every rule is the knows predicate *)
    val knows_ct = 
      hd raw_rules
      |> Thm.prop_of
      |> hd o Logic.strip_imp_prems 
      |> HOLogic.dest_mem o HOLogic.dest_Trueprop
      |> apfst (K msg_t)
      |> HOLogic.mk_Trueprop o HOLogic.mk_mem
      |> cert;

    val predOrd_elim_ths = @{thms in_knows_predOrd1 in_knows_predOrd2}
    val knows_thm = 
      Goal.prove_internal [] knows_ct 
        (fn facts' => 
        ALLGOALS (Method.insert_tac (facts' @ facts)) THEN
        (ALLGOALS (assume_tac ORELSE' eresolve_tac predOrd_elim_ths)
         ORELSE auto_tac (css addIs2 predOrd_elim_ths))
        )
      |> simplify ss

    val (cases, rule) = 
      case get_first (try (curry (op RS) knows_thm)) raw_rules of
        NONE    => err_no_matching_rule ctxt raw_rules knows_thm
      | SOME th => finalize_knows_cases ctxt minimal facts th
  in
    fn i => fn st =>
      CASES (Rule_Cases.make_common false (thy, Thm.prop_of rule) (fst cases)
            )
            (Tactic.rtac rule i) st
  end;

(* Parser for the sources proof method:

     BNF: sources["!"] term ["rule" thm]

   The ! means that a strict mode should be used, where the surrounding facts
   are not passed through automatically.

   The optional theorem can be used to state the rule for the case distinction
   explicitly.
*)
local
  structure P = OuterParse;
  val ruleN = "rule";
  val rule = Scan.lift (Args.$$$ ruleN -- Args.colon) |-- Attrib.thm;
  val opt_bang = Scan.lift (Scan.optional (P.$$$ "!" >> K true) false);
in
  val sourcesP = opt_bang -- Args.term -- Scan.option rule;
end;


(******************************************************************************
**  "prefix_close" proof state transformation
******************************************************************************)


fun dest_roleMap_eq t = 
  case HOLogic.dest_eq t of
    (Const (@{const_name roleMap},_) $ r $ i, 
       Const (@{const_name Some},_) $ R) => (r, (i, R))
  | _ => raise TERM ("dest_roleMap_eq", [t])
    

fun dest_in_steps t = 
  case HOLogic.dest_mem t of
    (estep, Const (@{const_name steps}, _) $ t) => 
      (case HOLogic.strip_tuple estep of
        i :: step :: _ => (i, step, t)
      | _ => raise TERM ("dest_MkStep_in_steps",[t]))
  | _ => raise TERM ("dest_MkStep_in_steps",[t])

fun split_conj_thm th =
  (split_conj_thm (th RS @{thm conjunct1}) @
  (split_conj_thm (th RS @{thm conjunct2}))) 
  handle THM _ => [th];

fun derive_step_thms th = map_filter (try (fn rule => th RS rule)) 
  @{thms in_steps_predOrd1 in_steps_predOrd2 steps_in_steps}


(* NOTE: this works only correctly in the context of a reachable_state locale
   as the theorem ext_prefixClose is dependent on this locale.
*)
fun prefix_close_thms ctxt facts = 
  let
    val prefixClose_th = ProofContext.get_thm ctxt "ext_prefixClose";
    fun close step roleMap = [step, roleMap] MRS prefixClose_th;
    val expand = Simplifier.simplify (Simplifier.simpset_of ctxt);
    val distinct_prop = distinct (Thm.eq_thm);
    (* fun new_fact th = 
        forall (not_equal (Thm.prop_of th) o Thm.prop_of) facts; *)
  in
    facts
    |> maps split_conj_thm
    |> maps derive_step_thms
    |> distinct_prop
    |> maps (fn step_th => map_filter (try (close step_th)) facts)
    |> maps (split_conj_thm o expand)
    |> curry (op @) facts
    |> distinct_prop
     (* |> filter new_fact *)
  end;

(* Note all facts derivable from the given theorems using all rules from the
   decryption chain calculus except the chain rule
*)
fun note_prefix_closed_cmd state =
  let val ctxt  = Proof.context_of state;
      val facts = Proof.the_facts state;
      val facts' = facts @ prefix_close_thms ctxt facts
  in Proof.put_facts (SOME facts') state end

(* A proof state transformation implementing prefix closing of
   the currently used facts.
*)
fun prefix_close_cmd state = 
  let
    val using = state 
      |> Proof.goal
      |> (fn {context = ctxt, facts=using, goal=_} => 
           prefix_close_thms ctxt using)

  in Proof.using_i [[(using,[])]] state end;

(* registering the prefix_close command *)
val _ =
  OuterSyntax.command "prefix_close" "prefix close facts being used"
    (OuterKeyword.tag_proof OuterKeyword.prf_decl)
    (Scan.succeed (Toplevel.print o (Toplevel.proof prefix_close_cmd)));


(******************************************************************************
**  "completeness_cases" attribute
******************************************************************************)

(* Extract the case name of the premise by checking for the corresponding hint *)
fun extract_completeness_case_name prems = 
  case Hints.gather_by_name "completenessCase" prems of
    (data :: _) => 
      (case HOLogic.strip_tuple data of
        (Const (step_name,_) :: v :: _) => 
          List.last (space_explode "." step_name) ^ "_" ^ HOLogic.dest_string v
       | _ => raise TERM ("extract_completeness_case_name", prems)
       )
  | [] => raise TERM ("extract_completeness_case_name", prems)


(* Return the case names according to the hints *)
fun completeness_case_names th =
  let
    val extract = 
      extract_completeness_case_name o 
      Logic.strip_imp_prems o Term.strip_all_body;
    fun name_of prem = 
      case try extract prem of
        SOME name => name
      | NONE => ESPL_Definitions.unnamed_caseN
  in
    th 
     |> Thm.prop_of
     |> Logic.strip_imp_prems
     |> map name_of
     |> blank_zero_numbers (op =)
     |> map (append_optional_number "_")
  end;

(* Convert the conclusion of a completeness induction rule into elimination
   form and name all the different cases according to the hints. 
*)
fun completeness_cases_attrib ctxt th =
  let
    val cs = Classical.get_cs ctxt;
    val ss = (Context.cases Simplifier.global_simpset_of Simplifier.simpset_of ctxt)
             addsimps @{thms finite_setdiff_compute};
    fun add_case_info th = th
      |> Rule_Cases.add_consumes 0
      |> Rule_Cases.name (completeness_case_names th)

  in 
    th
    |> SINGLE (REPEAT (safe_tac cs THEN (TRYALL (full_simp_tac ss))))    
    |> the
    |> add_case_info
  end

end