Theory NS_Public_Bad (Isabelle2005: October 2005)

# Theory NS_Public_Bad

Up to index of Isabelle/HOL/Auth

theory NS_Public_Bad
imports Public
begin

    (*  Title:      HOL/Auth/NS_Public_Bad
        ID:         $Id: NS_Public_Bad.thy,v 1.20 2005/06/17 14:13:06 haftmann Exp $
        Author:     Lawrence C Paulson, Cambridge University Computer Laboratory
        Copyright   1996  University of Cambridge

    Inductive relation "ns_public" for the Needham-Schroeder Public-Key protocol.
    Flawed version, vulnerable to Lowe's attack.

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

    header{*Verifying the Needham-Schroeder Public-Key Protocol*}

    theory NS_Public_Bad imports Public begin

    consts  ns_public  :: "event list set"

    inductive ns_public
      intros
             (*Initial trace is empty*)
       Nil:  "[] ∈ ns_public"

             (*The spy MAY say anything he CAN say.  We do not expect him to
               invent new nonces here, but he can also use NS1.  Common to
               all similar protocols.*)
       Fake: "[|evsf ∈ ns_public;  X ∈ synth (analz (spies evsf))|]
              ==> Says Spy B X  # evsf ∈ ns_public"

             (*Alice initiates a protocol run, sending a nonce to Bob*)
       NS1:  "[|evs1 ∈ ns_public;  Nonce NA ∉ used evs1|]
              ==> Says A B (Crypt (pubEK B) \<lbrace>Nonce NA, Agent A\<rbrace>)
                    # evs1  ∈  ns_public"

             (*Bob responds to Alice's message with a further nonce*)
       NS2:  "[|evs2 ∈ ns_public;  Nonce NB ∉ used evs2;
               Says A' B (Crypt (pubEK B) \<lbrace>Nonce NA, Agent A\<rbrace>) ∈ set evs2|]
              ==> Says B A (Crypt (pubEK A) \<lbrace>Nonce NA, Nonce NB\<rbrace>)
                    # evs2  ∈  ns_public"

             (*Alice proves her existence by sending NB back to Bob.*)
       NS3:  "[|evs3 ∈ ns_public;
               Says A  B (Crypt (pubEK B) \<lbrace>Nonce NA, Agent A\<rbrace>) ∈ set evs3;
               Says B' A (Crypt (pubEK A) \<lbrace>Nonce NA, Nonce NB\<rbrace>) ∈ set evs3|]
              ==> Says A B (Crypt (pubEK B) (Nonce NB)) # evs3 ∈ ns_public"

    declare knows_Spy_partsEs [elim]
    declare analz_into_parts [dest]
    declare Fake_parts_insert_in_Un  [dest]
    declare image_eq_UN [simp]  (*accelerates proofs involving nested images*)

    (*A "possibility property": there are traces that reach the end*)
    lemma "∃NB. ∃evs ∈ ns_public. Says A B (Crypt (pubEK B) (Nonce NB)) ∈ set evs"
    apply (intro exI bexI)
    apply (rule_tac [2] ns_public.Nil [THEN ns_public.NS1, THEN ns_public.NS2,
                                       THEN ns_public.NS3])
    by possibility

    (**** Inductive proofs about ns_public ****)

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

    (*Spy never sees another agent's private key! (unless it's bad at start)*)
    lemma Spy_see_priEK [simp]:
          "evs ∈ ns_public ==> (Key (priEK A) ∈ parts (spies evs)) = (A ∈ bad)"
    by (erule ns_public.induct, auto)

    lemma Spy_analz_priEK [simp]:
          "evs ∈ ns_public ==> (Key (priEK A) ∈ analz (spies evs)) = (A ∈ bad)"
    by auto

    (*** Authenticity properties obtained from NS2 ***)

    (*It is impossible to re-use a nonce in both NS1 and NS2, provided the nonce
      is secret.  (Honest users generate fresh nonces.)*)
    lemma no_nonce_NS1_NS2 [rule_format]:
          "evs ∈ ns_public
           ==> Crypt (pubEK C) \<lbrace>NA', Nonce NA\<rbrace> ∈ parts (spies evs) -->
               Crypt (pubEK B) \<lbrace>Nonce NA, Agent A\<rbrace> ∈ parts (spies evs) -->
               Nonce NA ∈ analz (spies evs)"
    apply (erule ns_public.induct, simp_all)
    apply (blast intro: analz_insertI)+
    done

    (*Unicity for NS1: nonce NA identifies agents A and B*)
    lemma unique_NA:
         "[|Crypt(pubEK B)  \<lbrace>Nonce NA, Agent A \<rbrace> ∈ parts(spies evs);
           Crypt(pubEK B') \<lbrace>Nonce NA, Agent A'\<rbrace> ∈ parts(spies evs);
           Nonce NA ∉ analz (spies evs); evs ∈ ns_public|]
          ==> A=A' ∧ B=B'"
    apply (erule rev_mp, erule rev_mp, erule rev_mp)
    apply (erule ns_public.induct, simp_all)
    (*Fake, NS1*)
    apply (blast intro!: analz_insertI)+
    done

    (*Secrecy: Spy does not see the nonce sent in msg NS1 if A and B are secure
      The major premise "Says A B ..." makes it a dest-rule, so we use
      (erule rev_mp) rather than rule_format. *)
    theorem Spy_not_see_NA:
          "[|Says A B (Crypt(pubEK B) \<lbrace>Nonce NA, Agent A\<rbrace>) ∈ set evs;
            A ∉ bad;  B ∉ bad;  evs ∈ ns_public|]
           ==> Nonce NA ∉ analz (spies evs)"
    apply (erule rev_mp)
    apply (erule ns_public.induct, simp_all, spy_analz)
    apply (blast dest: unique_NA intro: no_nonce_NS1_NS2)+
    done

    (*Authentication for A: if she receives message 2 and has used NA
      to start a run, then B has sent message 2.*)
    lemma A_trusts_NS2_lemma [rule_format]:
       "[|A ∉ bad;  B ∉ bad;  evs ∈ ns_public|]
        ==> Crypt (pubEK A) \<lbrace>Nonce NA, Nonce NB\<rbrace> ∈ parts (spies evs) -->
            Says A B (Crypt(pubEK B) \<lbrace>Nonce NA, Agent A\<rbrace>) ∈ set evs -->
            Says B A (Crypt(pubEK A) \<lbrace>Nonce NA, Nonce NB\<rbrace>) ∈ set evs"
    apply (erule ns_public.induct)
    apply (auto dest: Spy_not_see_NA unique_NA)
    done

    theorem A_trusts_NS2:
         "[|Says A  B (Crypt(pubEK B) \<lbrace>Nonce NA, Agent A\<rbrace>) ∈ set evs;
           Says B' A (Crypt(pubEK A) \<lbrace>Nonce NA, Nonce NB\<rbrace>) ∈ set evs;
           A ∉ bad;  B ∉ bad;  evs ∈ ns_public|]
          ==> Says B A (Crypt(pubEK A) \<lbrace>Nonce NA, Nonce NB\<rbrace>) ∈ set evs"
    by (blast intro: A_trusts_NS2_lemma)

    (*If the encrypted message appears then it originated with Alice in NS1*)
    lemma B_trusts_NS1 [rule_format]:
         "evs ∈ ns_public
          ==> Crypt (pubEK B) \<lbrace>Nonce NA, Agent A\<rbrace> ∈ parts (spies evs) -->
              Nonce NA ∉ analz (spies evs) -->
              Says A B (Crypt (pubEK B) \<lbrace>Nonce NA, Agent A\<rbrace>) ∈ set evs"
    apply (erule ns_public.induct, simp_all)
    (*Fake*)
    apply (blast intro!: analz_insertI)
    done

    (*** Authenticity properties obtained from NS2 ***)

    (*Unicity for NS2: nonce NB identifies nonce NA and agent A
      [proof closely follows that for unique_NA] *)
    lemma unique_NB [dest]:
         "[|Crypt(pubEK A)  \<lbrace>Nonce NA, Nonce NB\<rbrace> ∈ parts(spies evs);
           Crypt(pubEK A') \<lbrace>Nonce NA', Nonce NB\<rbrace> ∈ parts(spies evs);
           Nonce NB ∉ analz (spies evs); evs ∈ ns_public|]
         ==> A=A' ∧ NA=NA'"
    apply (erule rev_mp, erule rev_mp, erule rev_mp)
    apply (erule ns_public.induct, simp_all)
    (*Fake, NS2*)
    apply (blast intro!: analz_insertI)+
    done

    (*NB remains secret PROVIDED Alice never responds with round 3*)
    theorem Spy_not_see_NB [dest]:
         "[|Says B A (Crypt (pubEK A) \<lbrace>Nonce NA, Nonce NB\<rbrace>) ∈ set evs;
           ∀C. Says A C (Crypt (pubEK C) (Nonce NB)) ∉ set evs;
           A ∉ bad;  B ∉ bad;  evs ∈ ns_public|]
         ==> Nonce NB ∉ analz (spies evs)"
    apply (erule rev_mp, erule rev_mp)
    apply (erule ns_public.induct, simp_all, spy_analz)
    apply (simp_all add: all_conj_distrib) (*speeds up the next step*)
    apply (blast intro: no_nonce_NS1_NS2)+
    done

    (*Authentication for B: if he receives message 3 and has used NB
      in message 2, then A has sent message 3--to somebody....*)

    lemma B_trusts_NS3_lemma [rule_format]:
         "[|A ∉ bad;  B ∉ bad;  evs ∈ ns_public|]
          ==> Crypt (pubEK B) (Nonce NB) ∈ parts (spies evs) -->
              Says B A  (Crypt (pubEK A) \<lbrace>Nonce NA, Nonce NB\<rbrace>) ∈ set evs -->
              (∃C. Says A C (Crypt (pubEK C) (Nonce NB)) ∈ set evs)"
    apply (erule ns_public.induct, auto)
    by (blast intro: no_nonce_NS1_NS2)+

    theorem B_trusts_NS3:
         "[|Says B A  (Crypt (pubEK A) \<lbrace>Nonce NA, Nonce NB\<rbrace>) ∈ set evs;
           Says A' B (Crypt (pubEK B) (Nonce NB)) ∈ set evs;
           A ∉ bad;  B ∉ bad;  evs ∈ ns_public|]
          ==> ∃C. Says A C (Crypt (pubEK C) (Nonce NB)) ∈ set evs"
    by (blast intro: B_trusts_NS3_lemma)

    (*Can we strengthen the secrecy theorem Spy_not_see_NB?  NO*)
    lemma "[|A ∉ bad;  B ∉ bad;  evs ∈ ns_public|]
           ==> Says B A (Crypt (pubEK A) \<lbrace>Nonce NA, Nonce NB\<rbrace>) ∈ set evs
               --> Nonce NB ∉ analz (spies evs)"
    apply (erule ns_public.induct, simp_all, spy_analz)
    (*NS1: by freshness*)
    apply blast
    (*NS2: by freshness and unicity of NB*)
    apply (blast intro: no_nonce_NS1_NS2)
    (*NS3: unicity of NB identifies A and NA, but not B*)
    apply clarify
    apply (frule_tac A' = A in
           Says_imp_knows_Spy [THEN parts.Inj, THEN unique_NB], auto)
    apply (rename_tac C B' evs3)
    txt{*This is the attack!
    @{subgoals[display,indent=0,margin=65]}
    *}
    oops

    (*
    THIS IS THE ATTACK!
    Level 8
    !!evs. [|A ∉ bad; B ∉ bad; evs ∈ ns_public|]
           ==> Says B A (Crypt (pubEK A) \<lbrace>Nonce NA, Nonce NB\<rbrace>) ∈ set evs -->
               Nonce NB ∉ analz (spies evs)
     1. !!C B' evs3.
           [|A ∉ bad; B ∉ bad; evs3 ∈ ns_public
            Says A C (Crypt (pubEK C) \<lbrace>Nonce NA, Agent A\<rbrace>) ∈ set evs3;
            Says B' A (Crypt (pubEK A) \<lbrace>Nonce NA, Nonce NB\<rbrace>) ∈ set evs3;
            C ∈ bad;
            Says B A (Crypt (pubEK A) \<lbrace>Nonce NA, Nonce NB\<rbrace>) ∈ set evs3;
            Nonce NB ∉ analz (spies evs3)|]
           ==> False
    *)

    end

lemma

      ∃NB. ∃evs∈ns_public. Says A B (Crypt (pubK B) (Nonce NB)) ∈ set evs

lemma Spy_see_priEK:

      evs ∈ ns_public ==> (Key (priEK A) ∈ parts (knows Spy evs)) = (A ∈ bad)

lemma Spy_analz_priEK:

      evs ∈ ns_public ==> (Key (priEK A) ∈ analz (knows Spy evs)) = (A ∈ bad)

lemma no_nonce_NS1_NS2:

      [| evs ∈ ns_public; Crypt (pubK C) {|NA', Nonce NA|} ∈ parts (knows Spy evs);
         Crypt (pubK B) {|Nonce NA, Agent A|} ∈ parts (knows Spy evs) |]
      ==> Nonce NA ∈ analz (knows Spy evs)

lemma unique_NA:

      [| Crypt (pubK B) {|Nonce NA, Agent A|} ∈ parts (knows Spy evs);
         Crypt (pubK B') {|Nonce NA, Agent A'|} ∈ parts (knows Spy evs);
         Nonce NA ∉ analz (knows Spy evs); evs ∈ ns_public |]
      ==> A = A' ∧ B = B'

theorem Spy_not_see_NA:

      [| Says A B (Crypt (pubK B) {|Nonce NA, Agent A|}) ∈ set evs; A ∉ bad; B ∉ bad;
         evs ∈ ns_public |]
      ==> Nonce NA ∉ analz (knows Spy evs)

lemma A_trusts_NS2_lemma:

      [| A ∉ bad; B ∉ bad; evs ∈ ns_public;
         Crypt (pubK A) {|Nonce NA, Nonce NB|} ∈ parts (knows Spy evs);
         Says A B (Crypt (pubK B) {|Nonce NA, Agent A|}) ∈ set evs |]
      ==> Says B A (Crypt (pubK A) {|Nonce NA, Nonce NB|}) ∈ set evs

theorem A_trusts_NS2:

      [| Says A B (Crypt (pubK B) {|Nonce NA, Agent A|}) ∈ set evs;
         Says B' A (Crypt (pubK A) {|Nonce NA, Nonce NB|}) ∈ set evs; A ∉ bad;
         B ∉ bad; evs ∈ ns_public |]
      ==> Says B A (Crypt (pubK A) {|Nonce NA, Nonce NB|}) ∈ set evs

lemma B_trusts_NS1:

      [| evs ∈ ns_public;
         Crypt (pubK B) {|Nonce NA, Agent A|} ∈ parts (knows Spy evs);
         Nonce NA ∉ analz (knows Spy evs) |]
      ==> Says A B (Crypt (pubK B) {|Nonce NA, Agent A|}) ∈ set evs

lemma unique_NB:

      [| Crypt (pubK A) {|Nonce NA, Nonce NB|} ∈ parts (knows Spy evs);
         Crypt (pubK A') {|Nonce NA', Nonce NB|} ∈ parts (knows Spy evs);
         Nonce NB ∉ analz (knows Spy evs); evs ∈ ns_public |]
      ==> A = A' ∧ NA = NA'

theorem Spy_not_see_NB:

      [| Says B A (Crypt (pubK A) {|Nonce NA, Nonce NB|}) ∈ set evs;
         ∀C. Says A C (Crypt (pubK C) (Nonce NB)) ∉ set evs; A ∉ bad; B ∉ bad;
         evs ∈ ns_public |]
      ==> Nonce NB ∉ analz (knows Spy evs)

lemma B_trusts_NS3_lemma:

      [| A ∉ bad; B ∉ bad; evs ∈ ns_public;
         Crypt (pubK B) (Nonce NB) ∈ parts (knows Spy evs);
         Says B A (Crypt (pubK A) {|Nonce NA, Nonce NB|}) ∈ set evs |]
      ==> ∃C. Says A C (Crypt (pubK C) (Nonce NB)) ∈ set evs

theorem B_trusts_NS3:

      [| Says B A (Crypt (pubK A) {|Nonce NA, Nonce NB|}) ∈ set evs;
         Says A' B (Crypt (pubK B) (Nonce NB)) ∈ set evs; A ∉ bad; B ∉ bad;
         evs ∈ ns_public |]
      ==> ∃C. Says A C (Crypt (pubK C) (Nonce NB)) ∈ set evs