(***************************************************************************** * 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. ******************************************************************************) theory Unify imports HOL_ext InferenceRules WeakTyping (* referenced because we use espl_definitions.ML and espl_methods.ML. With a uses clause it would be loaded twice, which may result in strange effects. *) Automation keywords "note_prefix_closed" "note_unified" "note_cyclic" :: prf_decl begin section{* Explicit unification of sets of equality theorems *} text{* Specialized to the use cases of our ESPL *} text{* Additional required lemmas *} lemma eq_imp_size_eq: "x = y \ size x = size y" by (rule arg_cong) ML{* (* Strategy: Per theorem 1. check that it is an unconditional equality 2. rewrite using current simpset 3. if false then stop and return theorem false otherwise split conjunctions and add to current simpset NOTE: We have to maintain the invariant that the simpset is non-looping. Hence, we may have to rebuild it several times. Probably whenever a thread identifier equality occurs. *) (* Utilities ************) (* apply a function 'f' to a list and return the partition of all elements left unchanged by 'f' w.r.t. the given order and the changed elements. *) fun partition_changed ord f xs = let fun go unchanged changed [] = (rev unchanged, rev changed) | go unchanged changed (y::ys) = let val y' = f y in case ord (y, y') of EQUAL => go (y::unchanged) changed ys | _ => go unchanged (y'::changed) ys end in go [] [] xs end (* Remove an outer Trueprop if there is any *) fun remove_Trueprop t = the_default t (try HOLogic.dest_Trueprop t) (* Destruct a symbolic representation of the contents of a protocol variable. *) fun dest_MVar_store_lookup t = case ESPL_Definitions.dest_variable_store_lookup t of (s, (@{const MVar} $ v, tid)) => (s, (v, tid)) | _ => raise TERM ("dest_MVar_store_lookup: no MVar",[t]) val is_variable_store_lookup = can ESPL_Definitions.dest_variable_store_lookup val is_MVar_store_lookup = can dest_MVar_store_lookup (* Check if a term is a roleMap application *) fun is_roleMap (@{const roleMap} $ _ $ _) = true | is_roleMap _ = false (* Check if a term is a key inversion *) fun is_key_inversion (@{term "ExecMessage.inv :: execmsg \ execmsg"} $ _) = true | is_key_inversion _ = false (* If the given theorem is a HOL equality it gets reoriented such that the lhs is guaranteed to be smaller than the right-hand side with respect to the given order. Note that reflexive equalities are dropped! *) fun reorient_HOL_eq ord th = (case th |> Thm.concl_of |> HOLogic.dest_Trueprop |> HOLogic.dest_eq |> ord of GREATER => SOME (th RS @{thm sym}) | LESS => SOME th | EQUAL => NONE ) handle TERM _ => SOME th | THM (name,k,info) => raise THM ("reorient_HOL_eq:" ^ name, k,info) *} ML{* (* The term order we use for orienting the unification equalities: 1. s x is smaller than any other non-store term 2. roleMap x y is smaller than any other non-roleMap term 2. otherwise the standard term order is used. TODO: This could possibly be implemented using the lexicographic path ordering. Moreover, think about situations involving 'inv'. (We probably don't want to have 'inv' on the lhs.) *) fun unify_term_ord (lhs,rhs) = let fun proj t = (not (is_variable_store_lookup t) , (not (is_MVar_store_lookup t) , (not (is_roleMap t), t) ) ) in (prod_ord bool_ord (prod_ord bool_ord (prod_ord bool_ord Term_Ord.term_ord))) (proj lhs, proj rhs) end fun old_unify_term_ord (arg as (lhs,rhs)) = if (is_variable_store_lookup lhs) then if (is_variable_store_lookup rhs) then Term_Ord.term_ord arg else LESS else if (is_variable_store_lookup rhs) then GREATER else if (is_roleMap lhs) then if (is_roleMap rhs) then Term_Ord.term_ord arg else LESS else if (is_roleMap rhs) then GREATER else Term_Ord.term_ord arg; *} ML{* structure UnifyDest = Named_Thms (val name = @{binding "unify_dest"} val description = "Destruction rules to be used in note_unified") *} setup{* UnifyDest.setup *} declare (in reachable_state) split_before[unify_dest] declare (in reachable_state) split_knows[unify_dest] ML{* (* Tries to derive 'False' from equality of sizes of equated terms. *) fun inequal_sizes ss eq_th = let val opt_size_eq = SOME (simplify ss (eq_th RS @{thm eq_imp_size_eq})) handle THM _ => NONE in case opt_size_eq of SOME size_eq => (case size_eq |> Thm.concl_of |> remove_Trueprop of @{term False} => SOME size_eq | _ => NONE) | NONE => NONE end (* Unify the equalities in the given list of theorems using rewriting with the simpset of the given context. *) fun unify do_occurs_check ctxt ths = let (* TODO: Remove hack by using Named_Thms data functor. *) val thm_by_name = Proof_Context.get_thm ctxt; val ss = simpset_of ctxt delsimps map thm_by_name ["tid_eq_commute", "reorient_store_eq_store"]; (* substitute an equality theorem in the given list of 'done' and 'todo' theorems. The changed theorems from 'done' are readded to the output 'todo' list (the second component of the result). *) fun subst_eq (done, todo) eq_th = let val apply = simplify (ss addsimps [eq_th]); val (doneUc, doneC) = partition_changed Thm.thm_ord apply done; in (eq_th::doneUc, doneC @ map apply todo) end; (* occurence check: tries to prove 'False' from size equality *) fun occurs_check eq_th = if do_occurs_check then inequal_sizes ss eq_th else NONE (* solve a single unification equation *) fun solve1 (done, [] ) = done | solve1 (done, th::todo) = if (member Thm.eq_thm done th) then solve1 (done, todo) else if not (Thm.no_prems th) then solve1 (th::done, todo) else (case remove_Trueprop (Thm.concl_of th) of (@{const True}) => solve1 (done, todo) | (@{const False}) => [th] | (@{const "HOL.conj"} $ _ $ _) => solve1 ( done , (th RS @{thm conjunct1}) :: (th RS @{thm conjunct2}) :: todo ) | (Const (@{const_name "HOL.eq"},_) $ _ $ _) => (case reorient_HOL_eq unify_term_ord th of NONE => solve1 (done, todo) | SOME th' => (case occurs_check th' of SOME th_false => [th_false] | NONE => solve1 (subst_eq (done,todo) th') ) ) | _ => (case map_filter (try (curry (op RS) th)) (UnifyDest.get ctxt) of [] => solve1 (th::done, todo) | ths => solve1 (done, ths @ todo) ) ) in solve1 ([], map (simplify ss) ths) end *} ML{* (* Note a set of theorems modified by a function 'f:: Proof.Context -> thm list -> thm list'. This should be supported by attributes. However, currently we resort to the following hack. We do a normal note_theoerms and the retrieve the bound theorems, modify them, and store them again WITHOUT any attributes being applied. This is fragile. However, I couldn't see a better way with the current framework. *) fun note_modified_thmss f args = let (* copied from Pure/Isar/proof.ML *) fun map_context_result f state = f (Proof.context_of state) ||> (fn ctxt => Proof.map_context (K ctxt) state); val bindings = map (fst o fst) args; fun modify_bound_thms ctxt b = let val ths = Proof_Context.get_thms ctxt (Proof_Context.full_name ctxt b); in ((b,[]), [(f ctxt ths,[])]) end; fun modify_and_renote state = state |> map_context_result (fn ctxt => ctxt |> (Proof_Context.note_thmss "" (map (modify_bound_thms ctxt) bindings)) ) |> (fn (named_thss, state') => state' |> Proof.set_facts (maps snd named_thss) ) in modify_and_renote o Proof.note_thmss_cmd args end *} ML{* local fun define_cmd name info f = Outer_Syntax.command name info (Parse_Spec.name_facts >> (Toplevel.print oo (Toplevel.proof o (note_modified_thmss f)))); in val _ = define_cmd @{command_spec "note_prefix_closed"} "prefix close facts and store them under the given name" ESPL_Methods.prefix_close_thms; val _ = define_cmd @{command_spec "note_unified"} "unify equality facts and store them under the given name" (unify true); (* TODO: Implement this command - currently it is just notes. *) val _ = define_cmd @{command_spec "note_cyclic"} "try to derive a cyclicity violation from the given facts and store them under the given name" (K I); end *} end