diff --git a/InductiveVerification/Event.lean b/InductiveVerification/Event.lean index a4ba4fd..10b29ca 100644 --- a/InductiveVerification/Event.lean +++ b/InductiveVerification/Event.lean @@ -185,10 +185,10 @@ lemma knows_Spy_partsEs [Bad] : Event.Says A B X ∈ evs → X ∈ parts (knows Agent.Spy evs) := by exact Says_imp_parts_knows_Spy -lemma Says_imp_analz_Spy [InvKey] [Bad] : - ∀ {A B : Agent} {X : Msg} {evs : List Event}, +lemma Says_imp_analz_Spy [InvKey] [Bad] + {A B : Agent} {X : Msg} {evs : List Event} : Event.Says A B X ∈ evs → X ∈ analz (knows Agent.Spy evs) := by - intro A B X evs h + intro h apply analz.inj apply Says_imp_knows_Spy exact h @@ -209,6 +209,18 @@ lemma parts_insert_spies [Bad] : by apply parts_insert +lemma analz_spies_mono [InvKey] [Bad] + { h : M ∈ analz (knows Agent.Spy evs) } : + M ∈ analz (knows Agent.Spy (ev :: evs)) +:= by + aapply analz_mono; exact knows_subset_knows_Cons + +lemma analz_spies_mono_neg [InvKey] [Bad] + { h : M ∉ analz (knows Agent.Spy (ev :: evs)) } : + M ∉ analz (knows Agent.Spy evs) +:= by + intro h₁; apply h; aapply analz_spies_mono + -- Knowledge of Agents lemma knows_subset_knows_Says [Bad] : ∀ {A A' B : Agent} {X : Msg} {evs : List Event}, diff --git a/InductiveVerification/Message.lean b/InductiveVerification/Message.lean index 736edbb..f1bb933 100644 --- a/InductiveVerification/Message.lean +++ b/InductiveVerification/Message.lean @@ -911,7 +911,6 @@ by lemma analz_subset_iff {G H : Set Msg} [InvKey] : (G ⊆ analz H) ↔ (analz G ⊆ analz H) := by apply analzClosureOperator.le_closure_iff -@[simp] lemma analz_trans {G H : Set Msg} {X : Msg} [InvKey] : X ∈ analz G → G ⊆ analz H → X ∈ analz H := by diff --git a/InductiveVerification/NS_Public.lean b/InductiveVerification/NS_Public.lean index c395bfc..67fb5ae 100644 --- a/InductiveVerification/NS_Public.lean +++ b/InductiveVerification/NS_Public.lean @@ -108,7 +108,7 @@ lemma no_nonce_NS1_NS2_helper2 | inr => aapply h₁ -- It is impossible to re-use a nonce in both NS1 and NS2, provided the nonce is secret -theorem no_nonce_NS1_NS2 { h : ns_public evs } : +theorem no_nonce_NS1_NS2 { evs: List Event} { h : ns_public evs } {A B C D : Agent} : (Crypt (pubEK C) ⦃NA', Nonce NA, Agent D⦄ ∈ parts (spies evs) → (Crypt (pubEK B) ⦃Nonce NA, Agent A⦄ ∈ parts (spies evs) → Nonce NA ∈ analz (spies evs))) := by @@ -143,6 +143,18 @@ theorem no_nonce_NS1_NS2 { h : ns_public evs } : | inr => rw[spies, knows]; apply analz_mono; apply Set.subset_insert; simp_all | NS3 => rw[spies, knows]; apply analz_mono; apply Set.subset_insert; simp_all +lemma unique_Nonce_apply_ih {P : Prop} +{h₃ : Nonce NA ∉ analz (spies (Says Agent.Spy C X :: evsf))} +{h₁ : M₁ ∈ parts (spies evsf)} +{h₂ : M₂ ∈ parts (spies evsf)} +{a_ih : M₁ ∈ parts (spies evsf) + → M₂ ∈ parts (spies evsf) + → Nonce NA ∉ analz (spies evsf) → P} +: P := by + simp[spies, knows] at h₃; apply Set.notMem_subset at h₃ + · aapply a_ih; + · apply analz_mono; apply Set.subset_insert + lemma unique_NA_apply_ih {P : Prop} {a_ih : Crypt (pubEK B) ⦃Nonce NA, Msg.Agent A⦄ ∈ parts (spies evsf) → Crypt (pubEK B') ⦃Nonce NA, Msg.Agent A'⦄ ∈ parts (spies evsf) @@ -151,9 +163,7 @@ lemma unique_NA_apply_ih {P : Prop} {h₁ : Crypt (pubEK B) ⦃Nonce NA, Msg.Agent A⦄ ∈ parts (spies evsf)} {h₂ : Crypt (pubEK B') ⦃Nonce NA, Msg.Agent A'⦄ ∈ parts (spies evsf)} : P := by - simp[spies, knows] at h₃; apply Set.notMem_subset at h₃ - · aapply a_ih; - · apply analz_mono; apply Set.subset_insert + aapply unique_Nonce_apply_ih (h₁ := h₁) (h₂ := h₂) lemma unique_NA_contradict {h₃ : Nonce NA ∉ analz (spies (Says Agent.Spy B X :: evsf))} @@ -207,31 +217,368 @@ theorem unique_NA { h : ns_public evs } : | inr => aapply unique_NA_apply_ih | inr => aapply unique_NA_apply_ih | NS1 _ _ a_ih => intro h₁ h₂ h₃; simp at h₁; cases h₁ with - | inl => sorry - | inr => simp at h₂; cases h₂ with - | inl h₂ => simp at h₃; rw[analz_Crypt] at h₃ - rcases h₂ with ⟨_, ⟨nonce_eq, _⟩⟩; rw[nonce_eq] at h₃; simp at h₃; + | inl h₁ => simp at h₂; cases h₂ with + | inl h₂ => rcases h₁ with ⟨h₁, _⟩; rcases h₂ with ⟨h₂, _⟩; + apply injective_publicKey at h₁; + apply injective_publicKey at h₂; + simp_all; + | inr h₂ => apply parts.body at h₂; apply parts.fst at h₂; + apply parts_knows_Spy_subset_used at h₂; simp_all; + | inr h₁ => simp at h₂; cases h₂ with + | inl h₂ => apply parts.body at h₁; apply parts.fst at h₁; + apply parts_knows_Spy_subset_used at h₁; simp_all; | inr => aapply unique_NA_apply_ih; - | NS2 => sorry - | NS3 => sorry + | NS2 _ _ _ a_ih => intro h₁ h₂ h₃; simp_all; apply a_ih + apply Set.notMem_subset at h₃ + · apply h₃; + · apply_rules [analz_mono, Set.subset_insert] + | NS3 _ _ _ a_ih => intro h₁ h₂ h₃; simp_all; apply a_ih; + apply Set.notMem_subset at h₃ + · apply h₃; + · apply_rules [analz_mono, Set.subset_insert] -- Spy does not see the nonce sent in NS1 if A and B are secure -theorem Spy_not_see_NA { h : ns_public evs }: +theorem Spy_not_see_NA { h : ns_public evs } + { not_bad_A : A ∉ bad } + { not_bad_B : B ∉ bad } : Says A B (Crypt (pubEK B) ⦃Nonce NA, Agent A⦄) ∈ evs → - A ∉ bad → - B ∉ bad → Nonce NA ∉ analz (spies evs) := by - intro h - induction h <;> simp_all [analz_insertI, no_nonce_NS1_NS2] + intro h₁ h₄ + induction h with + | Nil => simp_all + | Fake _ a a_ih => + apply Fake_analz_insert at a; apply a at h₄; simp_all; cases h₁ with + | inl h => rcases h with ⟨l, _⟩; simp_all [Spy_in_bad]; + | inr h => cases h₄ with + | inl h₄ => cases h₄; apply a_ih at h; contradiction; + | inr => apply a_ih at h; contradiction; + | NS1 _ a a_ih => simp_all; cases h₁ with + | inl h => rcases h with ⟨_, ⟨_, ⟨_, ⟨h, _⟩⟩⟩⟩; simp_all; apply a + apply parts_knows_Spy_subset_used + aapply analz_subset_parts + | inr h => apply analz_insert_Crypt_subset at h₄; cases h₄ with + | inl h₄ => contradiction; + | inr h₄ => simp at h₄; cases h₄ + · simp_all; apply a; apply Says_imp_used at h; + apply used_parts_subset_parts at h; simp at h; apply h; + tauto; + · simp_all; + | NS2 _ not_used_NB a a_ih => + cases h₁ with | tail _ b => + have _ := h₄ + simp at h₄; apply analz_insert_Crypt_subset at h₄ + simp at h₄; rcases h₄ with ( h | ( h | h )) + · simp at a_ih; have c := b; apply a_ih at c; rw[h] at b; + have _ := c; rw[h] at c; + apply Says_imp_parts_knows_Spy at b + apply Says_imp_parts_knows_Spy at a + apply unique_NA at b; apply b at a; apply a at c; simp_all + assumption + · rw [h] at b + apply not_used_NB; apply parts_knows_Spy_subset_used; apply parts.fst; + apply parts.body; apply Says_imp_parts_knows_Spy; assumption + · aapply a_ih + | NS3 _ a₁ a₂ a_ih => + cases h₁ with | tail _ b => + have _ := h₄ + simp at h₄; apply analz_insert_Crypt_subset at h₄ + simp at h₄; rcases h₄ with ( h | ( h | h )) + · have c := b; have d := a₁; have e := a₂ + rw[h] at b; apply Says_imp_parts_knows_Spy at b + apply Says_imp_parts_knows_Spy at a₂ + aapply a_ih; apply no_nonce_NS1_NS2 + · assumption + · rw[h]; exact a₂ + · rw[h]; exact b + · aapply a_ih; aapply analz.inj + · aapply a_ih; aapply analz.fst + · aapply a_ih; aapply analz.snd + · aapply a_ih; aapply analz.decrypt + +-- Authentication for `A`: if she receives message 2 and has used `NA` to start a run, then `B` has sent message 2. +theorem A_trusts_NS2 {h : ns_public evs } + { not_bad_A : A ∉ bad } + { not_bad_B : B ∉ bad } : + Says A B (Crypt (pubEK B) ⦃Nonce NA, Agent A⦄) ∈ evs → + Says B' A (Crypt (pubEK B) ⦃Nonce NA, Nonce NB, Agent B⦄) ∈ evs → + Says B A (Crypt (pubEK B) ⦃Nonce NA, Nonce NB, Agent B⦄) ∈ evs +:= by + intro h₁ h₂; + -- have snsNA := h₁; apply Spy_not_see_NA at snsNA <;> try assumption + apply Says_imp_parts_knows_Spy at h₂ + -- use unique_NA to show that B' = B + induction h with + | Nil => simp_all + | Fake _ a a_ih => + have snsNA := h₁; apply Spy_not_see_NA at snsNA <;> try assumption + cases h₁ + · have _ := Spy_in_bad; contradiction + · right; simp at h₂; cases h₂ with + | inl h₂ => apply Fake_parts_sing at a; apply a at h₂; cases h₂ with + | inl h₂ => aapply a_ih; cases h₂ with + | inj => aapply analz_subset_parts + | crypt h => apply False.elim; apply snsNA + apply MPair_synth_analz.mp at h; rcases h with ⟨⟨l⟩ , _⟩; + aapply analz_spies_mono + | inr h₂ => aapply a_ih; + | inr => aapply a_ih; + · aapply ns_public.Fake + | NS1 _ a a_ih => right; simp at h₂; cases h₁ + · apply False.elim; apply a + apply parts_knows_Spy_subset_used; apply parts.fst + aapply parts.body + · aapply a_ih; + | NS2 _ _ a a_ih => simp at h₁; have b := h₁; have snsNA := h₁ + apply Spy_not_see_NA at snsNA <;> try assumption + simp at h₂; cases h₂ with + | inl h => apply Says_imp_parts_knows_Spy at a + apply Says_imp_parts_knows_Spy at b + rcases h with ⟨e₁ , ⟨e₂ , ⟨e₃, e₄⟩⟩⟩ + apply unique_NA at a + rw[e₂] at b; rw[e₂] at snsNA + apply a at b + apply b at snsNA + simp_all[-e₄]; assumption + | inr => right; aapply a_ih + | NS3 _ _ a a_ih => simp at h₁; simp at h₂; right; aapply a_ih + +-- If the encrypted message appears then it originated with Alice in `NS1` +lemma B_trusts_NS1 { h : ns_public evs} : + Crypt (pubEK B) ⦃Nonce NA, Agent A⦄ ∈ parts (spies evs) → + Nonce NA ∉ analz (spies evs) → + Says A B (Crypt (pubEK B) ⦃Nonce NA, Agent A⦄) ∈ evs +:= by + intro h₁ h₂ + induction h with + | Nil => simp[spies] at h₁; rw[knows] at h₁; simp[initState] at h₁ + | Fake _ a a_ih => simp at h₁; cases h₁ with + | inl h₁ => apply Fake_parts_sing at a; apply a at h₁; cases h₁ with + | inl h₁ => cases h₁ with + | inj h₁ => right; apply analz_subset_parts at h₁; aapply a_ih + aapply analz_spies_mono_neg + | crypt h₁ => apply False.elim; + apply MPair_synth_analz.mp at h₁ + rcases h₁ with ⟨⟨h₁⟩, _⟩; aapply analz_spies_mono_neg + | inr => right; aapply a_ih; aapply analz_spies_mono_neg + | inr h₁ => right; aapply a_ih; aapply analz_spies_mono_neg + | NS1 _ _ a_ih => simp at h₁; cases h₁ with + | inl h₁ => rcases h₁ with ⟨e₁, ⟨e₂, e₃⟩⟩; apply injective_publicKey at e₁ + simp_all + | inr => right; aapply a_ih; aapply analz_spies_mono_neg + | NS2 _ _ _ a_ih => simp at h₁; right; aapply a_ih; aapply analz_spies_mono_neg + | NS3 _ _ _ a_ih => simp at h₁; right; aapply a_ih; aapply analz_spies_mono_neg + +-- Authenticity Properties obtained from `NS2` +-- Helper lemmas for unique_NB +lemma unique_NB_apply_ih {P : Prop} +{ a_ih : + Crypt (pubEK A) ⦃Nonce NA, ⦃Nonce NB, Msg.Agent B⦄⦄ ∈ parts (spies evsf) → + Crypt (pubEK A') ⦃Nonce NA', ⦃Nonce NB, Msg.Agent B'⦄⦄ ∈ parts (spies evsf) → + Nonce NB ∉ analz (spies evsf) → P } +{ h₁ : Crypt (pubEK A) ⦃Nonce NA, ⦃Nonce NB, Msg.Agent B⦄⦄ ∈ parts (spies evsf) } +{ h₂ : Crypt (pubEK A') ⦃Nonce NA', ⦃Nonce NB, Msg.Agent B'⦄⦄ ∈ parts (spies evsf) } +{ h₃ : Nonce NB ∉ analz (spies (Says Agent.Spy B X :: evsf)) } +: P := by + aapply unique_Nonce_apply_ih (h₁ := h₁) (h₂ := h₂) (h₃ := h₃) + +lemma unique_NB_contradict +{ h₃ : Nonce NB ∉ analz (spies (Says Agent.Spy B X :: evsf)) } +{ h₂ : synth (analz (spies evsf)) ⦃Nonce NA', ⦃Nonce NB, Msg.Agent B'⦄⦄ } +{P : Prop} +: P := by + apply MPair_synth_analz.mp at h₂; apply False.elim + rcases h₂ with ⟨_, r⟩; cases r with + | inj r => aapply analz_spies_mono_neg; aapply analz.fst + | mpair r₁ _ => cases r₁; aapply analz_spies_mono_neg + +-- Unicity for `NS2`: nonce `NB` identifies nonce `NA` and agent `A` +theorem unique_NB { h : ns_public evs } : + (Crypt (pubEK A) ⦃Nonce NA, Nonce NB, Agent B⦄ ∈ parts (spies evs) → + (Crypt (pubEK A') ⦃Nonce NA', Nonce NB, Agent B'⦄ ∈ parts (spies evs) → + (Nonce NB ∉ analz (spies evs) → + A = A' ∧ NA = NA' ∧ B = B'))) := by + -- Proof closely follows that of unique_NA + induction h with + | Nil => aesop (add norm spies, norm knows, safe analz_insertI) + | Fake _ a a_ih => + apply Fake_parts_sing at a; intro h₁ h₂ h₃; + simp[spies, knows] at h₁; cases h₁ with + | inl h₁ => apply a at h₁; cases h₁ with + | inl h₁ => cases h₁ with + | inj h₁ => simp[spies, knows] at h₂; cases h₂ with + | inl h₂ => apply a at h₂; cases h₂ with + | inl h₂ => cases h₂ with + | inj h₂ => apply analz_subset_parts at h₁ + apply analz_subset_parts at h₂ + aapply unique_NB_apply_ih + | crypt h₂ => aapply unique_NB_contradict + | inr h₂ => apply analz_subset_parts at h₁ + aapply unique_NB_apply_ih + | inr h₂ => apply analz_subset_parts at h₁ + aapply unique_NB_apply_ih + | crypt h₁ => aapply unique_NB_contradict + | inr h₁ => simp[spies] at h₁; simp[spies, knows] at h₂; cases h₂ with + | inl h₂ => apply a at h₂; cases h₂ with + | inl h₂ => cases h₂ with + | inj h₂ => apply analz_subset_parts at h₂ + aapply unique_NB_apply_ih + | crypt => aapply unique_NB_contradict + | inr => aapply unique_NB_apply_ih + | inr => aapply unique_NB_apply_ih + | inr => simp[spies, knows] at h₂; cases h₂ with + | inl h₂ => apply a at h₂; cases h₂ with + | inl h₂ => cases h₂ with + | inj h₂ => apply analz_subset_parts at h₂ + aapply unique_NB_apply_ih + | crypt => aapply unique_NB_contradict + | inr => aapply unique_NB_apply_ih + | inr => aapply unique_NB_apply_ih + | NS1 _ _ a_ih => intro h₁ h₂ h₃; simp at h₁; simp at h₂; aapply a_ih + aapply analz_spies_mono_neg + | NS2 _ _ _ a_ih => intro h₁ h₂ h₃; simp at h₁; simp at h₂; cases h₁ with + | inl h₁ => rcases h₁ with ⟨e₁, _⟩; apply injective_publicKey at e₁ + cases h₂ with + | inl h₂ => rcases h₂ with ⟨e₂, _⟩; apply injective_publicKey at e₂ + simp_all + | inr h₂ => apply parts.body at h₂; apply parts.snd at h₂ + apply parts.fst at h₂; apply parts_knows_Spy_subset_used at h₂; + simp_all; + | inr h₁ => cases h₂ with + | inl h₂ => apply parts.body at h₁; apply parts.snd at h₁ + apply parts.fst at h₁; apply parts_knows_Spy_subset_used at h₁; + simp_all + | inr => aapply a_ih; aapply analz_spies_mono_neg + | NS3 _ _ _ a_ih => intro h₁ h₂ h₃; simp_all; apply a_ih; + apply Set.notMem_subset at h₃ + · apply h₃; + · apply_rules [analz_mono, Set.subset_insert] + +-- `NB` remains secret +theorem Spy_not_see_NB { h : ns_public evs } + { not_bad_A : A ∉ bad } + { not_bad_B : B ∉ bad } : + Says B A (Crypt (pubEK A) ⦃Nonce NA, Nonce NB, Agent B⦄) ∈ evs → + Nonce NB ∉ analz (spies evs) +:= by + intro h₁ h₄ + induction h with + | Nil => simp_all + | Fake _ a a_ih => + apply Fake_analz_insert at a; apply a at h₄; simp_all; cases h₁ with + | inl h => rcases h with ⟨l, _⟩; simp_all [Spy_in_bad]; + | inr h => cases h₄ with + | inl h₄ => cases h₄; apply a_ih at h; contradiction; + | inr => apply a_ih at h; contradiction; + | NS1 _ a a_ih => simp at h₁; simp[spies, knows] at h₄ + apply analz_insert_Crypt_subset at h₄; simp at h₄ + cases h₄ with + | inl e => rw[e] at h₁; apply a; apply parts_knows_Spy_subset_used + apply parts.fst; apply parts.snd; apply parts.body + aapply Says_imp_parts_knows_Spy + | inr => aapply a_ih + | NS2 _ not_used_NB a a_ih => simp at h₁; simp[spies, knows] at h₄; + cases h₁ with + | inl h => rcases h with ⟨_, ⟨_, ⟨e₃, _⟩⟩⟩; apply injective_publicKey at e₃; + simp_all; apply not_used_NB; apply parts_knows_Spy_subset_used; + aapply analz_subset_parts + | inr h => apply analz_insert_Crypt_subset at h₄; simp at h₄; cases h₄ with + | inl e => aapply a_ih; rw[e]; apply Says_imp_parts_knows_Spy at a + apply Says_imp_parts_knows_Spy at h; rw[e] at h + aapply no_nonce_NS1_NS2 + | inr h => cases h + · simp_all; apply not_used_NB; apply parts_knows_Spy_subset_used + apply parts.fst; apply parts.snd; apply parts.body + aapply Says_imp_parts_knows_Spy + · aapply a_ih + | @NS3 evs3 _ B' _ _ _ _ a₁ a₂ a_ih => + cases h₁ with | tail _ b => + simp at h₄; by_cases bad_B' : Key (invKey (pubEK B')) ∈ analz (spies evs3) + · have aC := bad_B'; apply analz_subset_parts at bad_B' + apply Spy_see_priEK.mp at bad_B'; have c := b; apply a_ih at c; + apply analz_insert_Decrypt at aC; rw[aC] at h₄; simp at h₄; cases h₄ with + | inl h₄ => + apply Says_imp_parts_knows_Spy at a₂ + apply Says_imp_parts_knows_Spy at b; rw[h₄] at b + apply unique_NB at a₂; apply a₂ at b; + rw[h₄] at c; simp_all; assumption + | inr h₄ => aapply a_ih + · apply analz_Crypt at aC; rw[aC] at h₄; simp at h₄; aapply a_ih; + +-- Authentication for `B`: if he receives message 3 and has used `NB` in message 2, then `A` has sent message 3. +theorem B_trusts_NS3 { h : ns_public evs } + { not_bad_A : A ∉ bad } + { not_bad_B : B ∉ bad } : + Says B A (Crypt (pubEK A) ⦃Nonce NA, Nonce NB, Agent B⦄) ∈ evs → + Says A' B (Crypt (pubEK B) (Nonce NB)) ∈ evs → + Says A B (Crypt (pubEK B) (Nonce NB)) ∈ evs +:= by + intro h₁ h₂ + apply Says_imp_parts_knows_Spy at h₂ + induction h with + | Nil => simp_all + | Fake _ a a_ih => right; simp at h₁; simp at h₂; cases h₁ with + | inl => simp_all[Spy_in_bad] + | inr h₁ => cases h₂ with + | inl h₂ => apply Fake_parts_sing at a; apply a at h₂; cases h₂ with + | inl h₂ => simp at h₂; cases h₂ with + | inj => aapply a_ih; aapply analz_subset_parts; + | crypt h₂ => cases h₂; apply Spy_not_see_NB at h₁ <;> simp_all + | inr => aapply a_ih + | inr => aapply a_ih + | NS1 _ a a_ih => right; simp at h₂; simp at h₁; aapply a_ih; + | NS2 _ _ a a_ih => right; simp at h₁; simp at h₂; cases h₁ with + | inl => apply parts.body at h₂; apply parts_knows_Spy_subset_used at h₂ + simp_all + | inr => aapply a_ih + | NS3 _ a₁ a₂ a_ih => simp at h₁; simp at h₂; cases h₂ with + | inl h₂ => simp_all; left; rcases h₂ with ⟨e₁, _⟩ + apply injective_publicKey at e₁; simp_all + have h₁c := h₁ + apply Says_imp_parts_knows_Spy at h₁ + apply Says_imp_parts_knows_Spy at a₂ + apply unique_NB at h₁; apply h₁ at a₂ + apply Spy_not_see_NB at h₁c + apply a₂ at h₁c + all_goals simp_all + | inr => right; aapply a_ih + +-- Overall guarantee for `B` + -- If NS3 has been sent and the nonce NB agrees with the nonce B joined with NA, then A initiated the run using NA -theorem B_trusts_protocol { h : ns_public evs }: - A ∉ bad → - B ∉ bad → +theorem B_trusts_protocol { h : ns_public evs } + { not_bad_A : A ∉ bad } + { not_bad_B : B ∉ bad } : Crypt (pubEK B) (Nonce NB) ∈ parts (spies evs) → Says B A (Crypt (pubEK A) ⦃Nonce NA, Nonce NB, Agent B⦄) ∈ evs → Says A B (Crypt (pubEK B) ⦃Nonce NA, Agent A⦄) ∈ evs := by - intro h - induction h <;> simp_all [analz_insertI, no_nonce_NS1_NS2] + intro h₁ h₂ + induction h with + | Nil => simp_all + | Fake _ a a_ih => right; simp at h₁; simp at h₂; cases h₂ with + | inl => simp_all[Spy_in_bad] + | inr h₂ => cases h₁ with + | inl h₁ => apply Fake_parts_sing at a; apply a at h₁; cases h₁ with + | inl h₁ => simp at h₁; cases h₁ with + | inj => aapply a_ih; aapply analz_subset_parts + | crypt h₁ => cases h₁; apply Spy_not_see_NB at h₂ <;> simp_all + | inr => aapply a_ih + | inr => aapply a_ih + | NS1 _ a a_ih => right; simp at h₂; simp at h₁; aapply a_ih; + | NS2 _ _ a a_ih => right; simp at h₁; simp at h₂; cases h₂ with + | inl => apply parts.body at h₁; apply parts_knows_Spy_subset_used at h₁ + simp_all + | inr => aapply a_ih + | NS3 _ a₁ a₂ a_ih => simp at h₁; simp at h₂; cases h₁ with + | inl h₁ => simp_all; rcases h₁ with ⟨e₁, _⟩ + apply injective_publicKey at e₁; simp_all + have h₂c := h₂ + apply Says_imp_parts_knows_Spy at h₂ + apply Says_imp_parts_knows_Spy at a₂ + apply unique_NB at h₂; apply h₂ at a₂ + apply Spy_not_see_NB at h₂c + apply a₂ at h₂c + all_goals simp_all + | inr => right; aapply a_ih end NS_Public