src/HOL/Auth/OtwayRees.ML
author paulson
Thu, 02 Jul 1998 17:48:11 +0200
changeset 5114 c729d4c299c1
parent 5076 fbc9d95b62ba
child 5223 4cb05273f764
permissions -rw-r--r--
Deleted leading parameters thanks to new Goal command

(*  Title:      HOL/Auth/OtwayRees
    ID:         $Id$
    Author:     Lawrence C Paulson, Cambridge University Computer Laboratory
    Copyright   1996  University of Cambridge

Inductive relation "otway" for the Otway-Rees protocol.

Version that encrypts Nonce NB

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

open OtwayRees;

set proof_timing;
HOL_quantifiers := false;

AddEs spies_partsEs;
AddDs [impOfSubs analz_subset_parts];
AddDs [impOfSubs Fake_parts_insert];


(*A "possibility property": there are traces that reach the end*)
Goal 
 "[| A ~= B; A ~= Server; B ~= Server |]   \
\     ==> EX K. EX NA. EX evs: otway.          \
\            Says B A {|Nonce NA, Crypt (shrK A) {|Nonce NA, Key K|}|} \
\              : set evs";
by (REPEAT (resolve_tac [exI,bexI] 1));
by (rtac (otway.Nil RS otway.OR1 RS otway.OR2 RS otway.OR3 RS otway.OR4) 2);
by possibility_tac;
result();


(**** Inductive proofs about otway ****)

(*Nobody sends themselves messages*)
Goal "evs : otway ==> ALL A X. Says A A X ~: set evs";
by (etac otway.induct 1);
by Auto_tac;
qed_spec_mp "not_Says_to_self";
Addsimps [not_Says_to_self];
AddSEs   [not_Says_to_self RSN (2, rev_notE)];


(** For reasoning about the encrypted portion of messages **)

Goal "Says A' B {|N, Agent A, Agent B, X|} : set evs \
\      ==> X : analz (spies evs)";
by (dtac (Says_imp_spies RS analz.Inj) 1);
by (Blast_tac 1);
qed "OR2_analz_spies";

Goal "Says S' B {|N, X, Crypt (shrK B) X'|} : set evs \
\      ==> X : analz (spies evs)";
by (dtac (Says_imp_spies RS analz.Inj) 1);
by (Blast_tac 1);
qed "OR4_analz_spies";

Goal "Says Server B {|NA, X, Crypt K' {|NB,K|}|} : set evs \
\      ==> K : parts (spies evs)";
by (Blast_tac 1);
qed "Oops_parts_spies";

bind_thm ("OR2_parts_spies",
          OR2_analz_spies RS (impOfSubs analz_subset_parts));
bind_thm ("OR4_parts_spies",
          OR4_analz_spies RS (impOfSubs analz_subset_parts));

(*For proving the easier theorems about X ~: parts (spies evs).*)
fun parts_induct_tac i = 
    etac otway.induct i			THEN 
    forward_tac [Oops_parts_spies] (i+6) THEN
    forward_tac [OR4_parts_spies]  (i+5) THEN
    forward_tac [OR2_parts_spies]  (i+3) THEN 
    prove_simple_subgoals_tac  i;


(** Theorems of the form X ~: parts (spies evs) imply that NOBODY
    sends messages containing X! **)

(*Spy never sees a good agent's shared key!*)
Goal 
 "evs : otway ==> (Key (shrK A) : parts (spies evs)) = (A : bad)";
by (parts_induct_tac 1);
by (ALLGOALS Blast_tac);
qed "Spy_see_shrK";
Addsimps [Spy_see_shrK];

Goal 
 "evs : otway ==> (Key (shrK A) : analz (spies evs)) = (A : bad)";
by (auto_tac(claset() addDs [impOfSubs analz_subset_parts], simpset()));
qed "Spy_analz_shrK";
Addsimps [Spy_analz_shrK];

AddSDs [Spy_see_shrK RSN (2, rev_iffD1), 
	Spy_analz_shrK RSN (2, rev_iffD1)];


(*Nobody can have used non-existent keys!*)
Goal "evs : otway ==>          \
\  Key K ~: used evs --> K ~: keysFor (parts (spies evs))";
by (parts_induct_tac 1);
(*Fake*)
by (blast_tac (claset() addSDs [keysFor_parts_insert]) 1);
(*OR2, OR3*)
by (ALLGOALS Blast_tac);
qed_spec_mp "new_keys_not_used";

bind_thm ("new_keys_not_analzd",
          [analz_subset_parts RS keysFor_mono,
           new_keys_not_used] MRS contra_subsetD);

Addsimps [new_keys_not_used, new_keys_not_analzd];



(*** Proofs involving analz ***)

(*Describes the form of K and NA when the Server sends this message.  Also
  for Oops case.*)
Goal 
 "[| Says Server B {|NA, X, Crypt (shrK B) {|NB, Key K|}|} : set evs; \
\    evs : otway |]                                           \
\     ==> K ~: range shrK & (EX i. NA = Nonce i) & (EX j. NB = Nonce j)";
by (etac rev_mp 1);
by (etac otway.induct 1);
by (ALLGOALS Simp_tac);
by (ALLGOALS Blast_tac);
qed "Says_Server_message_form";


(*For proofs involving analz.*)
val analz_spies_tac = 
    dtac OR2_analz_spies 4 THEN 
    dtac OR4_analz_spies 6 THEN
    forward_tac [Says_Server_message_form] 7 THEN assume_tac 7 THEN
    REPEAT ((eresolve_tac [exE, conjE] ORELSE' hyp_subst_tac) 7);


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

  Key K : analz (insert (Key KAB) (spies evs)) ==>
  Key K : analz (spies evs)

 A more general formula must be proved inductively.
****)


(** Session keys are not used to encrypt other session keys **)

(*The equality makes the induction hypothesis easier to apply*)
Goal  
 "evs : otway ==>                                    \
\  ALL K KK. KK <= Compl (range shrK) -->                   \
\     (Key K : analz (Key``KK Un (spies evs))) =  \
\     (K : KK | Key K : analz (spies evs))";
by (etac otway.induct 1);
by analz_spies_tac;
by (REPEAT_FIRST (resolve_tac [allI, impI]));
by (REPEAT_FIRST (rtac analz_image_freshK_lemma ));
by (ALLGOALS (asm_simp_tac analz_image_freshK_ss));
(*Fake*) 
by (spy_analz_tac 1);
qed_spec_mp "analz_image_freshK";


Goal
 "[| evs : otway;  KAB ~: range shrK |] ==>          \
\ Key K : analz (insert (Key KAB) (spies evs)) =  \
\ (K = KAB | Key K : analz (spies evs))";
by (asm_simp_tac (analz_image_freshK_ss addsimps [analz_image_freshK]) 1);
qed "analz_insert_freshK";


(*** The Key K uniquely identifies the Server's  message. **)

Goal 
 "evs : otway ==>                                                  \
\   EX B' NA' NB' X'. ALL B NA NB X.                                      \
\     Says Server B {|NA, X, Crypt (shrK B) {|NB, K|}|} : set evs -->     \
\     B=B' & NA=NA' & NB=NB' & X=X'";
by (etac otway.induct 1);
by (ALLGOALS (asm_simp_tac (simpset() addsimps [all_conj_distrib])));
by (ALLGOALS Clarify_tac);
(*Remaining cases: OR3 and OR4*)
by (ex_strip_tac 2);
by (Best_tac 2);	(*Blast_tac's too slow (in reconstruction)*)
by (expand_case_tac "K = ?y" 1);
by (REPEAT (ares_tac [refl,exI,impI,conjI] 2));
(*...we assume X is a recent message, and handle this case by contradiction*)
by (blast_tac (claset() addSEs spies_partsEs) 1);
val lemma = result();

Goal 
 "[| Says Server B {|NA, X, Crypt (shrK B) {|NB, K|}|}   : set evs; \ 
\    Says Server B' {|NA',X',Crypt (shrK B') {|NB',K|}|} : set evs; \
\    evs : otway |] ==> X=X' & B=B' & NA=NA' & NB=NB'";
by (prove_unique_tac lemma 1);
qed "unique_session_keys";



(**** Authenticity properties relating to NA ****)

(*Only OR1 can have caused such a part of a message to appear.*)
Goal 
 "[| A ~: bad;  evs : otway |]                             \
\ ==> Crypt (shrK A) {|NA, Agent A, Agent B|} : parts (spies evs) --> \
\     Says A B {|NA, Agent A, Agent B,                      \
\                Crypt (shrK A) {|NA, Agent A, Agent B|}|}  \
\      : set evs";
by (parts_induct_tac 1);
by (Blast_tac 1);
qed_spec_mp "Crypt_imp_OR1";


(** The Nonce NA uniquely identifies A's message. **)

Goal 
 "[| evs : otway; A ~: bad |]               \
\ ==> EX B'. ALL B.                                 \
\        Crypt (shrK A) {|NA, Agent A, Agent B|} : parts (spies evs) \
\        --> B = B'";
by (parts_induct_tac 1);
by (Blast_tac 1);
by (simp_tac (simpset() addsimps [all_conj_distrib]) 1); 
(*OR1: creation of new Nonce.  Move assertion into global context*)
by (expand_case_tac "NA = ?y" 1);
by (Blast_tac 1);
val lemma = result();

Goal 
 "[| Crypt (shrK A) {|NA, Agent A, Agent B|}: parts (spies evs); \
\          Crypt (shrK A) {|NA, Agent A, Agent C|}: parts (spies evs); \
\          evs : otway;  A ~: bad |]                                   \
\        ==> B = C";
by (prove_unique_tac lemma 1);
qed "unique_NA";


(*It is impossible to re-use a nonce in both OR1 and OR2.  This holds because
  OR2 encrypts Nonce NB.  It prevents the attack that can occur in the
  over-simplified version of this protocol: see OtwayRees_Bad.*)
Goal 
 "[| A ~: bad;  evs : otway |]                      \
\        ==> Crypt (shrK A) {|NA, Agent A, Agent B|} : parts (spies evs) --> \
\            Crypt (shrK A) {|NA', NA, Agent A', Agent A|}  \
\             ~: parts (spies evs)";
by (parts_induct_tac 1);
by (ALLGOALS Blast_tac);
qed_spec_mp"no_nonce_OR1_OR2";

val nonce_OR1_OR2_E = no_nonce_OR1_OR2 RSN (2, rev_notE);

(*Crucial property: If the encrypted message appears, and A has used NA
  to start a run, then it originated with the Server!*)
Goal 
 "[| A ~: bad;  evs : otway |]                                  \
\    ==> Crypt (shrK A) {|NA, Key K|} : parts (spies evs)              \
\        --> Says A B {|NA, Agent A, Agent B,                          \
\                       Crypt (shrK A) {|NA, Agent A, Agent B|}|}      \
\             : set evs -->                                            \
\            (EX NB. Says Server B                                     \
\                 {|NA,                                                \
\                   Crypt (shrK A) {|NA, Key K|},                      \
\                   Crypt (shrK B) {|NB, Key K|}|}                     \
\                   : set evs)";
by (parts_induct_tac 1);
by (Blast_tac 1);
(*OR1: it cannot be a new Nonce, contradiction.*)
by (Blast_tac 1);
(*OR3 and OR4*)
by (asm_simp_tac (simpset() addsimps [ex_disj_distrib]) 1);
by (rtac conjI 1);
by (ALLGOALS Clarify_tac);
(*OR4*)
by (blast_tac (claset() addSIs [Crypt_imp_OR1]) 3);
(*OR3, two cases*)  (** LEVEL 7 **)
by (blast_tac (claset() addSEs  [nonce_OR1_OR2_E]
                        delrules [conjI] (*stop split-up into 4 subgoals*)) 2);
by (blast_tac (claset() addIs  [unique_NA]) 1);
qed_spec_mp "NA_Crypt_imp_Server_msg";


(*Corollary: if A receives B's OR4 message and the nonce NA agrees
  then the key really did come from the Server!  CANNOT prove this of the
  bad form of this protocol, even though we can prove
  Spy_not_see_encrypted_key*)
Goal 
 "[| Says A  B {|NA, Agent A, Agent B,                       \
\                Crypt (shrK A) {|NA, Agent A, Agent B|}|} : set evs; \
\    Says B' A {|NA, Crypt (shrK A) {|NA, Key K|}|} : set evs; \
\    A ~: bad;  evs : otway |]                              \
\ ==> EX NB. Says Server B                                  \
\              {|NA,                                        \
\                Crypt (shrK A) {|NA, Key K|},              \
\                Crypt (shrK B) {|NB, Key K|}|}             \
\                : set evs";
by (blast_tac (claset() addSIs [NA_Crypt_imp_Server_msg]) 1);
qed "A_trusts_OR4";


(** Crucial secrecy property: Spy does not see the keys sent in msg OR3
    Does not in itself guarantee security: an attack could violate 
    the premises, e.g. by having A=Spy **)

Goal 
 "[| A ~: bad;  B ~: bad;  evs : otway |]                      \
\ ==> Says Server B                                            \
\       {|NA, Crypt (shrK A) {|NA, Key K|},                    \
\         Crypt (shrK B) {|NB, Key K|}|} : set evs -->         \
\     Notes Spy {|NA, NB, Key K|} ~: set evs -->               \
\     Key K ~: analz (spies evs)";
by (etac otway.induct 1);
by analz_spies_tac;
by (ALLGOALS
    (asm_simp_tac (simpset() addcongs [conj_cong] 
                             addsimps [analz_insert_eq, analz_insert_freshK]
                             addsimps (pushes@split_ifs))));
(*Oops*)
by (blast_tac (claset() addSDs [unique_session_keys]) 4);
(*OR4*) 
by (Blast_tac 3);
(*OR3*)
by (Blast_tac 2);
(*Fake*) 
by (spy_analz_tac 1);
val lemma = result() RS mp RS mp RSN(2,rev_notE);

Goal "[| Says Server B                                           \
\         {|NA, Crypt (shrK A) {|NA, Key K|},                    \
\               Crypt (shrK B) {|NB, Key K|}|} : set evs;        \
\        Notes Spy {|NA, NB, Key K|} ~: set evs;                 \
\        A ~: bad;  B ~: bad;  evs : otway |]                    \
\     ==> Key K ~: analz (spies evs)";
by (forward_tac [Says_Server_message_form] 1 THEN assume_tac 1);
by (blast_tac (claset() addSEs [lemma]) 1);
qed "Spy_not_see_encrypted_key";


(*A's guarantee.  The Oops premise quantifies over NB because A cannot know
  what it is.*)
Goal "[| Says A  B {|NA, Agent A, Agent B,                       \
\                    Crypt (shrK A) {|NA, Agent A, Agent B|}|} : set evs; \
\        Says B' A {|NA, Crypt (shrK A) {|NA, Key K|}|} : set evs; \
\        ALL NB. Notes Spy {|NA, NB, Key K|} ~: set evs;         \
\        A ~: bad;  B ~: bad;  evs : otway |]                    \
\     ==> Key K ~: analz (spies evs)";
by (blast_tac (claset() addSDs [A_trusts_OR4, Spy_not_see_encrypted_key]) 1);
qed "A_gets_good_key";


(**** Authenticity properties relating to NB ****)

(*Only OR2 can have caused such a part of a message to appear.  We do not
  know anything about X: it does NOT have to have the right form.*)
Goal 
 "[| B ~: bad;  evs : otway |]                         \
\     ==> Crypt (shrK B) {|NA, NB, Agent A, Agent B|}       \
\          : parts (spies evs) -->                       \
\         (EX X. Says B Server                              \
\          {|NA, Agent A, Agent B, X,                       \
\            Crypt (shrK B) {|NA, NB, Agent A, Agent B|}|}  \
\          : set evs)";
by (parts_induct_tac 1);
by (ALLGOALS Blast_tac);
bind_thm ("Crypt_imp_OR2", result() RSN (2,rev_mp) RS exE);


(** The Nonce NB uniquely identifies B's  message. **)

Goal "[| evs : otway; B ~: bad |]  \
\ ==> EX NA' A'. ALL NA A.            \
\      Crypt (shrK B) {|NA, NB, Agent A, Agent B|} : parts(spies evs) \
\      --> NA = NA' & A = A'";
by (parts_induct_tac 1);
by (Blast_tac 1);
by (simp_tac (simpset() addsimps [all_conj_distrib]) 1); 
(*OR2: creation of new Nonce.  Move assertion into global context*)
by (expand_case_tac "NB = ?y" 1);
by (Blast_tac 1);
val lemma = result();

Goal 
 "[| Crypt (shrK B) {|NA, NB, Agent A, Agent B|} : parts(spies evs); \
\          Crypt (shrK B) {|NC, NB, Agent C, Agent B|} : parts(spies evs); \
\          evs : otway;  B ~: bad |]             \
\        ==> NC = NA & C = A";
by (prove_unique_tac lemma 1);
qed "unique_NB";


(*If the encrypted message appears, and B has used Nonce NB,
  then it originated with the Server!*)
Goal "[| B ~: bad;  evs : otway |]                                    \
\ ==> Crypt (shrK B) {|NB, Key K|} : parts (spies evs)                \
\     --> (ALL X'. Says B Server                                      \
\                    {|NA, Agent A, Agent B, X',                      \
\                      Crypt (shrK B) {|NA, NB, Agent A, Agent B|}|}  \
\          : set evs                                                  \
\          --> Says Server B                                          \
\               {|NA, Crypt (shrK A) {|NA, Key K|},                   \
\                     Crypt (shrK B) {|NB, Key K|}|}                  \
\                   : set evs)";
by (parts_induct_tac 1);
by (Blast_tac 1);
(*OR1: it cannot be a new Nonce, contradiction.*)
by (Blast_tac 1);
(*OR4*)
by (blast_tac (claset() addSEs [Crypt_imp_OR2]) 2);
(*OR3*)
(*Provable in 38s by the single command
    by (blast_tac (claset() addDs  [unique_NB] addEs[nonce_OR1_OR2_E]) 1);
*)
by (safe_tac (claset() delrules [disjCI, impCE]));
by (Blast_tac 3); 
by (blast_tac (claset() addDs  [unique_NB]) 2);
by (blast_tac (claset() addSEs [nonce_OR1_OR2_E]
                        delrules [conjI] (*stop split-up*)) 1);
qed_spec_mp "NB_Crypt_imp_Server_msg";


(*Guarantee for B: if it gets a message with matching NB then the Server
  has sent the correct message.*)
Goal "[| Says B Server {|NA, Agent A, Agent B, X',              \
\                        Crypt (shrK B) {|NA, NB, Agent A, Agent B|} |} \
\         : set evs;                                            \
\        Says S' B {|NA, X, Crypt (shrK B) {|NB, Key K|}|} : set evs;   \
\        B ~: bad;  evs : otway |]                              \
\     ==> Says Server B                                         \
\              {|NA,                                            \
\                Crypt (shrK A) {|NA, Key K|},                  \
\                Crypt (shrK B) {|NB, Key K|}|}                 \
\                : set evs";
by (blast_tac (claset() addSIs [NB_Crypt_imp_Server_msg]) 1);
qed "B_trusts_OR3";


(*The obvious combination of B_trusts_OR3 with Spy_not_see_encrypted_key*)
Goal "[| Says B Server {|NA, Agent A, Agent B, X',              \
\                        Crypt (shrK B) {|NA, NB, Agent A, Agent B|} |} \
\          : set evs;                                           \
\        Says S' B {|NA, X, Crypt (shrK B) {|NB, Key K|}|} : set evs;   \
\        Notes Spy {|NA, NB, Key K|} ~: set evs;                \
\        A ~: bad;  B ~: bad;  evs : otway |]                   \
\     ==> Key K ~: analz (spies evs)";
by (blast_tac (claset() addSDs [B_trusts_OR3, Spy_not_see_encrypted_key]) 1);
qed "B_gets_good_key";


Goal "[| B ~: bad;  evs : otway |]                            \
\     ==> Says Server B                                       \
\           {|NA, Crypt (shrK A) {|NA, Key K|},               \
\             Crypt (shrK B) {|NB, Key K|}|} : set evs -->    \
\         (EX X. Says B Server {|NA, Agent A, Agent B, X,     \
\                         Crypt (shrK B) {|NA, NB, Agent A, Agent B|} |} \
\         : set evs)";
by (etac otway.induct 1);
by (ALLGOALS Asm_simp_tac);
by (blast_tac (claset() addSEs [Crypt_imp_OR2]) 3);
by (ALLGOALS Blast_tac);
bind_thm ("OR3_imp_OR2", result() RSN (2,rev_mp) RS exE);


(*After getting and checking OR4, agent A can trust that B has been active.
  We could probably prove that X has the expected form, but that is not
  strictly necessary for authentication.*)
Goal "[| Says B' A {|NA, Crypt (shrK A) {|NA, Key K|}|} : set evs;        \
\        Says A  B {|NA, Agent A, Agent B,                                \
\                    Crypt (shrK A) {|NA, Agent A, Agent B|}|} : set evs; \
\        A ~: bad;  B ~: bad;  evs : otway |]                             \
\     ==> EX NB X. Says B Server {|NA, Agent A, Agent B, X,               \
\                           Crypt (shrK B)  {|NA, NB, Agent A, Agent B|} |}\
\         : set evs";
by (blast_tac (claset() addSDs [A_trusts_OR4]
                        addSEs [OR3_imp_OR2]) 1);
qed "A_auths_B";