20 open TLS; |
20 open TLS; |
21 |
21 |
22 proof_timing:=true; |
22 proof_timing:=true; |
23 HOL_quantifiers := false; |
23 HOL_quantifiers := false; |
24 |
24 |
25 AddIffs [Spy_in_lost, Server_not_lost]; |
25 (** We mostly DO NOT unfold the definition of "certificate". The attached |
26 Addsimps [certificate_def]; |
26 lemmas unfold it lazily, when "certificate B KB" occurs in appropriate |
27 |
27 contexts. |
28 goal thy "!!A. A ~: lost ==> A ~= Spy"; |
28 **) |
29 by (Blast_tac 1); |
29 |
30 qed "not_lost_not_eq_Spy"; |
30 goalw thy [certificate_def] |
31 Addsimps [not_lost_not_eq_Spy]; |
31 "parts (insert (certificate B KB) H) = \ |
|
32 \ parts (insert (Crypt (priK Server) {|Agent B, Key KB|}) H)"; |
|
33 by (rtac refl 1); |
|
34 qed "parts_insert_certificate"; |
|
35 |
|
36 goalw thy [certificate_def] |
|
37 "analz (insert (certificate B KB) H) = \ |
|
38 \ analz (insert (Crypt (priK Server) {|Agent B, Key KB|}) H)"; |
|
39 by (rtac refl 1); |
|
40 qed "analz_insert_certificate"; |
|
41 Addsimps [parts_insert_certificate, analz_insert_certificate]; |
|
42 |
|
43 goalw thy [certificate_def] |
|
44 "(X = certificate B KB) = (Crypt (priK Server) {|Agent B, Key KB|} = X)"; |
|
45 by (Blast_tac 1); |
|
46 qed "eq_certificate_iff"; |
|
47 AddIffs [eq_certificate_iff]; |
|
48 |
32 |
49 |
33 (*Injectiveness of key-generating functions*) |
50 (*Injectiveness of key-generating functions*) |
34 AddIffs [inj_clientK RS inj_eq, inj_serverK RS inj_eq]; |
51 AddIffs [inj_clientK RS inj_eq, inj_serverK RS inj_eq]; |
35 |
52 |
36 (* invKey(clientK x) = clientK x and similarly for serverK*) |
53 (* invKey(clientK x) = clientK x and similarly for serverK*) |
37 Addsimps [isSym_clientK, rewrite_rule [isSymKey_def] isSym_clientK, |
54 Addsimps [isSym_clientK, rewrite_rule [isSymKey_def] isSym_clientK, |
38 isSym_serverK, rewrite_rule [isSymKey_def] isSym_serverK]; |
55 isSym_serverK, rewrite_rule [isSymKey_def] isSym_serverK]; |
39 |
|
40 |
|
41 (*Replacing the variable by a constant improves search speed by 50%!*) |
|
42 val Says_imp_sees_Spy' = |
|
43 read_instantiate_sg (sign_of thy) [("lost","lost")] Says_imp_sees_Spy; |
|
44 |
56 |
45 |
57 |
46 (*** clientK and serverK make symmetric keys; no clashes with pubK or priK ***) |
58 (*** clientK and serverK make symmetric keys; no clashes with pubK or priK ***) |
47 |
59 |
48 goal thy "pubK A ~= clientK arg"; |
60 goal thy "pubK A ~= clientK arg"; |
100 by possibility_tac; |
112 by possibility_tac; |
101 result(); |
113 result(); |
102 |
114 |
103 (*And one for ClientFinished. Either FINISHED message may come first.*) |
115 (*And one for ClientFinished. Either FINISHED message may come first.*) |
104 goal thy |
116 goal thy |
105 "!!A B. A ~= B ==> EX NA XA NB XB M. EX evs: tls. \ |
117 "!!A B. A ~= B ==> EX NA XA NB XB M. EX evs: tls. \ |
106 \ Says A B (Crypt (clientK(NA,NB,M)) \ |
118 \ Says A B (Crypt (clientK(NA,NB,M)) \ |
107 \ (Hash{|Hash{|Nonce NA, Nonce NB, Nonce M|}, \ |
119 \ (Hash{|Hash{|Nonce NA, Nonce NB, Nonce M|}, \ |
108 \ Nonce NA, Agent XA, \ |
120 \ Nonce NA, Agent XA, certificate A (pubK A), \ |
109 \ certificate A (pubK A), \ |
|
110 \ Nonce NB, Agent XB, Agent B|})) : set evs"; |
121 \ Nonce NB, Agent XB, Agent B|})) : set evs"; |
111 by (REPEAT (resolve_tac [exI,bexI] 1)); |
122 by (REPEAT (resolve_tac [exI,bexI] 1)); |
112 by (rtac (tls.Nil RS tls.ClientHello RS tls.ServerHello RS tls.ClientCertKeyEx |
123 by (rtac (tls.Nil RS tls.ClientHello RS tls.ServerHello RS tls.ClientCertKeyEx |
113 RS tls.ClientFinished) 2); |
124 RS tls.ClientFinished) 2); |
114 by possibility_tac; |
125 by possibility_tac; |
115 result(); |
126 result(); |
116 |
127 |
117 (*Another one, for CertVerify (which is optional)*) |
128 (*Another one, for CertVerify (which is optional)*) |
118 goal thy |
129 goal thy |
119 "!!A B. A ~= B ==> EX NB M. EX evs: tls. \ |
130 "!!A B. A ~= B ==> EX NB M. EX evs: tls. \ |
120 \ Says A B (Crypt (priK A) \ |
131 \ Says A B (Crypt (priK A) \ |
121 \ (Hash{|Nonce NB, certificate B (pubK B), Nonce M|})) : set evs"; |
132 \ (Hash{|Nonce NB, certificate B (pubK B), Nonce M|})) : set evs"; |
122 by (REPEAT (resolve_tac [exI,bexI] 1)); |
133 by (REPEAT (resolve_tac [exI,bexI] 1)); |
123 by (rtac (tls.Nil RS tls.ClientHello RS tls.ServerHello RS tls.ClientCertKeyEx |
134 by (rtac (tls.Nil RS tls.ClientHello RS tls.ServerHello RS tls.ClientCertKeyEx |
124 RS tls.CertVerify) 2); |
135 RS tls.CertVerify) 2); |
135 qed_spec_mp "not_Says_to_self"; |
146 qed_spec_mp "not_Says_to_self"; |
136 Addsimps [not_Says_to_self]; |
147 Addsimps [not_Says_to_self]; |
137 AddSEs [not_Says_to_self RSN (2, rev_notE)]; |
148 AddSEs [not_Says_to_self RSN (2, rev_notE)]; |
138 |
149 |
139 |
150 |
140 (** Theorems of the form X ~: parts (sees lost Spy evs) imply that NOBODY |
151 (*Induction for regularity theorems. If induction formula has the form |
|
152 X ~: analz (sees Spy evs) --> ... then it shortens the proof by discarding |
|
153 needless information about analz (insert X (sees Spy evs)) *) |
|
154 fun parts_induct_tac i = |
|
155 etac tls.induct i |
|
156 THEN |
|
157 REPEAT (FIRSTGOAL analz_mono_contra_tac) |
|
158 THEN |
|
159 fast_tac (!claset addss (!simpset)) i THEN |
|
160 ALLGOALS (asm_full_simp_tac (!simpset setloop split_tac [expand_if])); |
|
161 |
|
162 |
|
163 (** Theorems of the form X ~: parts (sees Spy evs) imply that NOBODY |
141 sends messages containing X! **) |
164 sends messages containing X! **) |
142 |
165 |
143 (*Spy never sees another agent's private key! (unless it's lost at start)*) |
166 (*Spy never sees another agent's private key! (unless it's lost at start)*) |
144 goal thy |
167 goal thy |
145 "!!evs. evs : tls \ |
168 "!!evs. evs : tls ==> (Key (priK A) : parts (sees Spy evs)) = (A : lost)"; |
146 \ ==> (Key (priK A) : parts (sees lost Spy evs)) = (A : lost)"; |
169 by (parts_induct_tac 1); |
147 by (etac tls.induct 1); |
|
148 by (prove_simple_subgoals_tac 1); |
|
149 by (asm_simp_tac (!simpset setloop split_tac [expand_if]) 2); |
|
150 by (Fake_parts_insert_tac 1); |
170 by (Fake_parts_insert_tac 1); |
151 qed "Spy_see_priK"; |
171 qed "Spy_see_priK"; |
152 Addsimps [Spy_see_priK]; |
172 Addsimps [Spy_see_priK]; |
153 |
173 |
154 goal thy |
174 goal thy |
155 "!!evs. evs : tls \ |
175 "!!evs. evs : tls ==> (Key (priK A) : analz (sees Spy evs)) = (A : lost)"; |
156 \ ==> (Key (priK A) : analz (sees lost Spy evs)) = (A : lost)"; |
|
157 by (auto_tac(!claset addDs [impOfSubs analz_subset_parts], !simpset)); |
176 by (auto_tac(!claset addDs [impOfSubs analz_subset_parts], !simpset)); |
158 qed "Spy_analz_priK"; |
177 qed "Spy_analz_priK"; |
159 Addsimps [Spy_analz_priK]; |
178 Addsimps [Spy_analz_priK]; |
160 |
179 |
161 goal thy "!!A. [| Key (priK A) : parts (sees lost Spy evs); \ |
180 goal thy "!!A. [| Key (priK A) : parts (sees Spy evs); \ |
162 \ evs : tls |] ==> A:lost"; |
181 \ evs : tls |] ==> A:lost"; |
163 by (blast_tac (!claset addDs [Spy_see_priK]) 1); |
182 by (blast_tac (!claset addDs [Spy_see_priK]) 1); |
164 qed "Spy_see_priK_D"; |
183 qed "Spy_see_priK_D"; |
165 |
184 |
166 bind_thm ("Spy_analz_priK_D", analz_subset_parts RS subsetD RS Spy_see_priK_D); |
185 bind_thm ("Spy_analz_priK_D", analz_subset_parts RS subsetD RS Spy_see_priK_D); |
167 AddSDs [Spy_see_priK_D, Spy_analz_priK_D]; |
186 AddSDs [Spy_see_priK_D, Spy_analz_priK_D]; |
168 |
187 |
169 |
188 |
170 (*This lemma says that no false certificates exist. One might extend the |
189 (*This lemma says that no false certificates exist. One might extend the |
171 model to include bogus certificates for the lost agents, but there seems |
190 model to include bogus certificates for the agents, but there seems |
172 little point in doing so: the loss of their private keys is a worse |
191 little point in doing so: the loss of their private keys is a worse |
173 breach of security.*) |
192 breach of security.*) |
174 goalw thy [certificate_def] |
193 goalw thy [certificate_def] |
175 "!!evs. evs : tls \ |
194 "!!evs. evs : tls \ |
176 \ ==> certificate B KB : parts (sees lost Spy evs) --> KB = pubK B"; |
195 \ ==> certificate B KB : parts (sees Spy evs) --> KB = pubK B"; |
177 by (etac tls.induct 1); |
196 by (parts_induct_tac 1); |
178 by (ALLGOALS (asm_full_simp_tac (!simpset setloop split_tac [expand_if]))); |
197 by (Fake_parts_insert_tac 1); |
179 by (Fake_parts_insert_tac 2); |
|
180 by (Blast_tac 1); |
|
181 bind_thm ("Server_cert_pubB", result() RSN (2, rev_mp)); |
198 bind_thm ("Server_cert_pubB", result() RSN (2, rev_mp)); |
182 |
199 |
183 |
200 |
184 (*Replace key KB in ClientCertKeyEx by (pubK B) *) |
201 (*Replace key KB in ClientCertKeyEx by (pubK B) *) |
185 val ClientCertKeyEx_tac = |
202 val ClientCertKeyEx_tac = |
186 forward_tac [Says_imp_sees_Spy' RS parts.Inj RS |
203 forward_tac [Says_imp_sees_Spy RS parts.Inj RS |
187 parts.Snd RS parts.Snd RS parts.Snd RS Server_cert_pubB] |
204 parts.Snd RS parts.Snd RS parts.Snd RS Server_cert_pubB] |
188 THEN' assume_tac |
205 THEN' assume_tac |
189 THEN' hyp_subst_tac; |
206 THEN' hyp_subst_tac; |
190 |
207 |
191 fun analz_induct_tac i = |
208 fun analz_induct_tac i = |
192 etac tls.induct i THEN |
209 etac tls.induct i THEN |
193 ClientCertKeyEx_tac (i+7) THEN (*ClientFinished*) |
210 ClientCertKeyEx_tac (i+7) THEN (*ClientFinished*) |
194 ClientCertKeyEx_tac (i+6) THEN (*CertVerify*) |
211 ClientCertKeyEx_tac (i+6) THEN (*CertVerify*) |
195 ClientCertKeyEx_tac (i+5) THEN (*ClientCertKeyEx*) |
212 ClientCertKeyEx_tac (i+5) THEN (*ClientCertKeyEx*) |
196 rewrite_goals_tac [certificate_def] THEN |
|
197 ALLGOALS (asm_simp_tac |
213 ALLGOALS (asm_simp_tac |
198 (!simpset addsimps [not_parts_not_analz] |
214 (!simpset addsimps [not_parts_not_analz] |
199 setloop split_tac [expand_if])) THEN |
215 setloop split_tac [expand_if])) THEN |
200 (*Remove instances of pubK B: the Spy already knows all public keys. |
216 (*Remove instances of pubK B: the Spy already knows all public keys. |
201 Combining the two simplifier calls makes them run extremely slowly.*) |
217 Combining the two simplifier calls makes them run extremely slowly.*) |
205 |
221 |
206 |
222 |
207 (*** Hashing of nonces ***) |
223 (*** Hashing of nonces ***) |
208 |
224 |
209 (*Every Nonce that's hashed is already in past traffic. *) |
225 (*Every Nonce that's hashed is already in past traffic. *) |
210 goal thy "!!evs. [| Hash {|Nonce N, X|} : parts (sees lost Spy evs); \ |
226 goal thy "!!evs. [| Hash {|Nonce N, X|} : parts (sees Spy evs); \ |
211 \ evs : tls |] \ |
227 \ evs : tls |] \ |
212 \ ==> Nonce N : parts (sees lost Spy evs)"; |
228 \ ==> Nonce N : parts (sees Spy evs)"; |
213 by (etac rev_mp 1); |
229 by (etac rev_mp 1); |
214 by (etac tls.induct 1); |
230 by (parts_induct_tac 1); |
215 by (ALLGOALS (asm_simp_tac (!simpset addsimps [parts_insert_sees] |
231 by (ALLGOALS (asm_simp_tac (!simpset addsimps [parts_insert_sees]))); |
216 setloop split_tac [expand_if]))); |
232 by (Fake_parts_insert_tac 1); |
217 by (Fake_parts_insert_tac 2); |
233 by (blast_tac (!claset addSDs [Says_imp_sees_Spy RS parts.Inj] |
218 by (REPEAT (blast_tac (!claset addSDs [Says_imp_sees_Spy' RS parts.Inj] |
234 addSEs partsEs) 1); |
219 addSEs partsEs) 1)); |
|
220 qed "Hash_imp_Nonce1"; |
235 qed "Hash_imp_Nonce1"; |
221 |
236 |
222 (*Lemma needed to prove Hash_Hash_imp_Nonce*) |
237 (*Lemma needed to prove Hash_Hash_imp_Nonce*) |
223 goal thy "!!evs. [| Hash{|Nonce NA, Nonce NB, Nonce M|} \ |
238 goal thy "!!evs. [| Hash{|Nonce NA, Nonce NB, Nonce M|} \ |
224 \ : parts (sees lost Spy evs); \ |
239 \ : parts (sees Spy evs); \ |
225 \ evs : tls |] \ |
240 \ evs : tls |] \ |
226 \ ==> Nonce M : parts (sees lost Spy evs)"; |
241 \ ==> Nonce M : parts (sees Spy evs)"; |
227 by (etac rev_mp 1); |
242 by (etac rev_mp 1); |
228 by (etac tls.induct 1); |
243 by (parts_induct_tac 1); |
229 by (ALLGOALS (asm_simp_tac (!simpset addsimps [parts_insert_sees] |
244 by (asm_simp_tac (!simpset addsimps [parts_insert_sees]) 1); |
230 setloop split_tac [expand_if]))); |
245 by (Fake_parts_insert_tac 1); |
231 by (Fake_parts_insert_tac 2); |
|
232 by (blast_tac (!claset addSDs [Says_imp_sees_Spy' RS parts.Inj] |
|
233 addSEs partsEs) 1); |
|
234 qed "Hash_imp_Nonce2"; |
246 qed "Hash_imp_Nonce2"; |
235 AddSDs [Hash_imp_Nonce2]; |
247 AddSDs [Hash_imp_Nonce2]; |
236 |
248 |
237 |
249 |
238 goal thy "!!evs. [| Notes A {|Agent B, X|} : set evs; evs : tls |] \ |
250 goal thy "!!evs. [| Notes A {|Agent B, X|} : set evs; evs : tls |] \ |
239 \ ==> Crypt (pubK B) X : parts (sees lost Spy evs)"; |
251 \ ==> Crypt (pubK B) X : parts (sees Spy evs)"; |
240 by (etac rev_mp 1); |
252 by (etac rev_mp 1); |
241 by (analz_induct_tac 1); |
253 by (analz_induct_tac 1); |
242 by (blast_tac (!claset addIs [parts_insertI]) 1); |
254 by (blast_tac (!claset addIs [parts_insertI]) 1); |
243 qed "Notes_Crypt_parts_sees"; |
255 qed "Notes_Crypt_parts_sees"; |
244 |
256 |
245 |
257 |
246 (*NEEDED??*) |
258 (*NEEDED??*) |
247 goal thy "!!evs. [| Hash {| Hash{|Nonce NA, Nonce NB, Nonce M|}, X |} \ |
259 goal thy "!!evs. [| Hash {| Hash{|Nonce NA, Nonce NB, Nonce M|}, X |} \ |
248 \ : parts (sees lost Spy evs); \ |
260 \ : parts (sees Spy evs); \ |
249 \ evs : tls |] \ |
261 \ evs : tls |] \ |
250 \ ==> Nonce M : parts (sees lost Spy evs)"; |
262 \ ==> Nonce M : parts (sees Spy evs)"; |
251 by (etac rev_mp 1); |
263 by (etac rev_mp 1); |
252 by (etac tls.induct 1); |
264 by (parts_induct_tac 1); |
253 by (ALLGOALS (asm_simp_tac (!simpset addsimps [parts_insert_sees] |
265 by (ALLGOALS (asm_simp_tac (!simpset addsimps [parts_insert_sees]))); |
254 setloop split_tac [expand_if]))); |
266 by (Fake_parts_insert_tac 1); |
255 by (Fake_parts_insert_tac 2); |
267 by (REPEAT (blast_tac (!claset addSDs [Notes_Crypt_parts_sees, |
256 by (step_tac (!claset addSDs [Notes_Crypt_parts_sees, |
268 Says_imp_sees_Spy RS parts.Inj] |
257 Says_imp_sees_Spy' RS parts.Inj] |
269 addSEs partsEs) 1)); |
258 addSEs partsEs) 1); |
|
259 qed "Hash_Hash_imp_Nonce"; |
270 qed "Hash_Hash_imp_Nonce"; |
260 |
271 |
261 |
272 |
262 (*NEEDED?? |
273 (*NEEDED?? |
263 Every Nonce that's hashed is already in past traffic. |
274 Every Nonce that's hashed is already in past traffic. |
264 This general formulation is tricky to prove and hard to use, since the |
275 This general formulation is tricky to prove and hard to use, since the |
265 2nd premise is typically proved by simplification.*) |
276 2nd premise is typically proved by simplification.*) |
266 goal thy "!!evs. [| Hash X : parts (sees lost Spy evs); \ |
277 goal thy "!!evs. [| Hash X : parts (sees Spy evs); \ |
267 \ Nonce N : parts {X}; evs : tls |] \ |
278 \ Nonce N : parts {X}; evs : tls |] \ |
268 \ ==> Nonce N : parts (sees lost Spy evs)"; |
279 \ ==> Nonce N : parts (sees Spy evs)"; |
269 by (etac rev_mp 1); |
280 by (etac rev_mp 1); |
270 by (etac tls.induct 1); |
281 by (parts_induct_tac 1); |
271 by (ALLGOALS (asm_simp_tac (!simpset setloop split_tac [expand_if]))); |
|
272 by (step_tac (!claset addSDs [Notes_Crypt_parts_sees, |
282 by (step_tac (!claset addSDs [Notes_Crypt_parts_sees, |
273 Says_imp_sees_Spy' RS parts.Inj] |
283 Says_imp_sees_Spy RS parts.Inj] |
274 addSEs partsEs) 1); |
284 addSEs partsEs) 1); |
275 by (ALLGOALS (asm_full_simp_tac (!simpset addsimps [parts_insert_sees]))); |
285 by (ALLGOALS (asm_full_simp_tac (!simpset addsimps [parts_insert_sees]))); |
276 by (Fake_parts_insert_tac 1); |
286 by (Fake_parts_insert_tac 1); |
277 (*CertVerify, ClientFinished, ServerFinished (?)*) |
287 (*CertVerify, ClientFinished, ServerFinished (?)*) |
278 by (REPEAT (Blast_tac 1)); |
288 by (REPEAT (Blast_tac 1)); |
283 |
293 |
284 (*B can check A's signature if he has received A's certificate. |
294 (*B can check A's signature if he has received A's certificate. |
285 Perhaps B~=Spy is unnecessary, but there's no obvious proof if the first |
295 Perhaps B~=Spy is unnecessary, but there's no obvious proof if the first |
286 message is Fake. We don't need guarantees for the Spy anyway. We must |
296 message is Fake. We don't need guarantees for the Spy anyway. We must |
287 assume A~:lost; otherwise, the Spy can forge A's signature.*) |
297 assume A~:lost; otherwise, the Spy can forge A's signature.*) |
288 goalw thy [certificate_def] |
298 goal thy |
289 "!!evs. [| X = Crypt (priK A) \ |
299 "!!evs. [| X = Crypt (priK A) \ |
290 \ (Hash{|Nonce NB, certificate B KB, Nonce M|}); \ |
300 \ (Hash{|Nonce NB, certificate B KB, Nonce M|}); \ |
291 \ evs : tls; A ~: lost; B ~= Spy |] \ |
301 \ evs : tls; A ~: lost; B ~= Spy |] \ |
292 \ ==> Says B A {|Nonce NA, Nonce NB, Agent XB, certificate B KB|} \ |
302 \ ==> Says B A {|Nonce NA, Nonce NB, Agent XB, certificate B KB|} \ |
293 \ : set evs --> \ |
303 \ : set evs --> \ |
294 \ X : parts (sees lost Spy evs) --> Says A B X : set evs"; |
304 \ X : parts (sees Spy evs) --> Says A B X : set evs"; |
295 by (hyp_subst_tac 1); |
305 by (hyp_subst_tac 1); |
296 by (etac tls.induct 1); |
306 by (parts_induct_tac 1); |
297 by (ALLGOALS (asm_simp_tac (!simpset setloop split_tac [expand_if]))); |
|
298 by (Fake_parts_insert_tac 1); |
307 by (Fake_parts_insert_tac 1); |
299 (*ServerHello: nonce NB cannot be in X because it's fresh!*) |
308 (*ServerHello: nonce NB cannot be in X because it's fresh!*) |
300 by (blast_tac (!claset addSDs [Hash_imp_Nonce1] |
309 by (blast_tac (!claset addSDs [Hash_imp_Nonce1] |
301 addSEs sees_Spy_partsEs) 1); |
310 addSEs sees_Spy_partsEs) 1); |
302 qed_spec_mp "TrustCertVerify"; |
311 qed_spec_mp "TrustCertVerify"; |
303 |
312 |
304 |
313 |
305 (*If CERTIFICATE VERIFY is present then A has chosen M.*) |
314 (*If CERTIFICATE VERIFY is present then A has chosen M.*) |
306 goal thy |
315 goal thy |
307 "!!evs. [| Crypt (priK A) (Hash{|Nonce NB, certificate B KB, Nonce M|}) \ |
316 "!!evs. [| Crypt (priK A) (Hash{|Nonce NB, certificate B KB, Nonce M|}) \ |
308 \ : parts (sees lost Spy evs); \ |
317 \ : parts (sees Spy evs); \ |
309 \ evs : tls; A ~: lost |] \ |
318 \ evs : tls; A ~: lost |] \ |
310 \ ==> Notes A {|Agent B, Nonce M|} : set evs"; |
319 \ ==> Notes A {|Agent B, Nonce M|} : set evs"; |
311 be rev_mp 1; |
320 be rev_mp 1; |
312 by (etac tls.induct 1); |
321 by (parts_induct_tac 1); |
313 by (ALLGOALS (asm_full_simp_tac (!simpset setloop split_tac [expand_if]))); |
322 by (Fake_parts_insert_tac 1); |
314 by (Fake_parts_insert_tac 2); |
|
315 by (Blast_tac 1); |
|
316 qed "UseCertVerify"; |
323 qed "UseCertVerify"; |
317 |
324 |
318 |
325 |
319 (*No collection of keys can help the spy get new private keys*) |
326 (*No collection of keys can help the spy get new private keys*) |
320 goal thy |
327 goal thy |
321 "!!evs. evs : tls ==> \ |
328 "!!evs. evs : tls ==> \ |
322 \ ALL KK. (Key(priK B) : analz (Key``KK Un (sees lost Spy evs))) = \ |
329 \ ALL KK. (Key(priK B) : analz (Key``KK Un (sees Spy evs))) = \ |
323 \ (priK B : KK | B : lost)"; |
330 \ (priK B : KK | B : lost)"; |
324 by (etac tls.induct 1); |
331 by (etac tls.induct 1); |
325 by (ALLGOALS |
332 by (ALLGOALS |
326 (asm_simp_tac (analz_image_keys_ss |
333 (asm_simp_tac (analz_image_keys_ss |
327 addsimps (certificate_def::keys_distinct)))); |
334 addsimps (certificate_def::keys_distinct)))); |
328 (*Fake*) |
335 (*Fake*) |
329 by (spy_analz_tac 2); |
336 by (spy_analz_tac 2); |
330 (*Base*) |
337 (*Base*) |
331 by (Blast_tac 1); |
338 by (Blast_tac 1); |
341 |
348 |
342 (*Knowing some clientKs and serverKs is no help in getting new nonces*) |
349 (*Knowing some clientKs and serverKs is no help in getting new nonces*) |
343 goal thy |
350 goal thy |
344 "!!evs. evs : tls ==> \ |
351 "!!evs. evs : tls ==> \ |
345 \ ALL KK. KK <= (range clientK Un range serverK) --> \ |
352 \ ALL KK. KK <= (range clientK Un range serverK) --> \ |
346 \ (Nonce N : analz (Key``KK Un (sees lost Spy evs))) = \ |
353 \ (Nonce N : analz (Key``KK Un (sees Spy evs))) = \ |
347 \ (Nonce N : analz (sees lost Spy evs))"; |
354 \ (Nonce N : analz (sees Spy evs))"; |
348 by (etac tls.induct 1); |
355 by (etac tls.induct 1); |
349 by (ClientCertKeyEx_tac 6); |
356 by (ClientCertKeyEx_tac 6); |
350 by (REPEAT_FIRST (resolve_tac [allI, impI])); |
357 by (REPEAT_FIRST (resolve_tac [allI, impI])); |
351 by (REPEAT_FIRST (rtac lemma)); |
358 by (REPEAT_FIRST (rtac lemma)); |
352 writeln"SLOW simplification: 50 secs!??"; |
359 writeln"SLOW simplification: 50 secs!??"; |
353 by (ALLGOALS |
360 by (ALLGOALS |
354 (asm_simp_tac (analz_image_keys_ss |
361 (asm_simp_tac (analz_image_keys_ss |
355 addsimps (analz_image_priK::certificate_def:: |
362 addsimps (analz_image_priK::certificate_def:: |
356 keys_distinct)))); |
363 keys_distinct)))); |
357 by (ALLGOALS (asm_simp_tac (analz_image_keys_ss addsimps [analz_image_priK]))); |
364 by (ALLGOALS (asm_simp_tac (analz_image_keys_ss addsimps [analz_image_priK]))); |
358 by (ALLGOALS (asm_simp_tac (!simpset addsimps [insert_absorb]))); |
365 by (ALLGOALS (asm_simp_tac (!simpset addsimps [insert_absorb]))); |
359 (*ClientCertKeyEx: a nonce is sent, but one needs a priK to read it.*) |
366 (*ClientCertKeyEx: a nonce is sent, but one needs a priK to read it.*) |
360 by (Blast_tac 3); |
367 by (Blast_tac 3); |
361 (*Fake*) |
368 (*Fake*) |
380 by (spy_analz_tac 1); |
387 by (spy_analz_tac 1); |
381 (*ServerHello and ClientCertKeyEx: mostly freshness reasoning*) |
388 (*ServerHello and ClientCertKeyEx: mostly freshness reasoning*) |
382 by (REPEAT (blast_tac (!claset addSEs partsEs |
389 by (REPEAT (blast_tac (!claset addSEs partsEs |
383 addDs [Notes_Crypt_parts_sees, |
390 addDs [Notes_Crypt_parts_sees, |
384 impOfSubs analz_subset_parts, |
391 impOfSubs analz_subset_parts, |
385 Says_imp_sees_Spy' RS analz.Inj]) 1)); |
392 Says_imp_sees_Spy RS analz.Inj]) 1)); |
386 bind_thm ("Spy_not_see_premaster_secret", result() RSN (2, rev_mp)); |
393 bind_thm ("Spy_not_see_premaster_secret", result() RSN (2, rev_mp)); |
387 |
394 |
388 |
395 |
389 (*** Protocol goal: serverK(NA,NB,M) and clientK(NA,NB,M) remain secure ***) |
396 (*** Protocol goal: serverK(NA,NB,M) and clientK(NA,NB,M) remain secure ***) |
390 |
397 |
393 |
400 |
394 (*Lemma: those write keys are never sent if M is secure. |
401 (*Lemma: those write keys are never sent if M is secure. |
395 Converse doesn't hold; betraying M doesn't force the keys to be sent!*) |
402 Converse doesn't hold; betraying M doesn't force the keys to be sent!*) |
396 |
403 |
397 goal thy |
404 goal thy |
398 "!!evs. [| Nonce M ~: analz (sees lost Spy evs); evs : tls |] \ |
405 "!!evs. [| Nonce M ~: analz (sees Spy evs); evs : tls |] \ |
399 \ ==> Key (clientK(NA,NB,M)) ~: parts (sees lost Spy evs)"; |
406 \ ==> Key (clientK(NA,NB,M)) ~: parts (sees Spy evs)"; |
400 by (etac rev_mp 1); |
407 by (etac rev_mp 1); |
401 by (analz_induct_tac 1); |
408 by (analz_induct_tac 1); |
402 (*SpyKeys*) |
409 (*SpyKeys*) |
403 by (asm_simp_tac (analz_image_keys_ss addsimps [analz_image_keys]) 3); |
410 by (asm_simp_tac (analz_image_keys_ss addsimps [analz_image_keys]) 3); |
404 by (blast_tac (!claset addDs [Says_imp_sees_Spy' RS analz.Inj]) 3); |
411 by (blast_tac (!claset addDs [Says_imp_sees_Spy RS analz.Inj]) 3); |
405 (*Fake*) |
412 (*Fake*) |
406 by (spy_analz_tac 2); |
413 by (spy_analz_tac 2); |
407 (*Base*) |
414 (*Base*) |
408 by (Blast_tac 1); |
415 by (Blast_tac 1); |
409 qed "clientK_notin_parts"; |
416 qed "clientK_notin_parts"; |
410 |
417 |
411 Addsimps [clientK_notin_parts]; |
418 Addsimps [clientK_notin_parts]; |
412 AddSEs [clientK_notin_parts RSN (2, rev_notE)]; |
419 AddSEs [clientK_notin_parts RSN (2, rev_notE)]; |
413 |
420 |
414 goal thy |
421 goal thy |
415 "!!evs. [| Nonce M ~: analz (sees lost Spy evs); evs : tls |] \ |
422 "!!evs. [| Nonce M ~: analz (sees Spy evs); evs : tls |] \ |
416 \ ==> Key (serverK(NA,NB,M)) ~: parts (sees lost Spy evs)"; |
423 \ ==> Key (serverK(NA,NB,M)) ~: parts (sees Spy evs)"; |
417 by (etac rev_mp 1); |
424 by (etac rev_mp 1); |
418 by (analz_induct_tac 1); |
425 by (analz_induct_tac 1); |
419 (*SpyKeys*) |
426 (*SpyKeys*) |
420 by (asm_simp_tac (analz_image_keys_ss addsimps [analz_image_keys]) 3); |
427 by (asm_simp_tac (analz_image_keys_ss addsimps [analz_image_keys]) 3); |
421 by (blast_tac (!claset addDs [Says_imp_sees_Spy' RS analz.Inj]) 3); |
428 by (blast_tac (!claset addDs [Says_imp_sees_Spy RS analz.Inj]) 3); |
422 (*Fake*) |
429 (*Fake*) |
423 by (spy_analz_tac 2); |
430 by (spy_analz_tac 2); |
424 (*Base*) |
431 (*Base*) |
425 by (Blast_tac 1); |
432 by (Blast_tac 1); |
426 qed "serverK_notin_parts"; |
433 qed "serverK_notin_parts"; |
463 |
470 |
464 Addsimps [Crypt_serverK_notin_parts]; |
471 Addsimps [Crypt_serverK_notin_parts]; |
465 AddEs [Crypt_serverK_notin_parts RSN (2, rev_notE)]; |
472 AddEs [Crypt_serverK_notin_parts RSN (2, rev_notE)]; |
466 |
473 |
467 |
474 |
468 (*Weakening A~:lost to A~=Spy would complicate later uses of the rule*) |
475 (*NEEDED??*) |
469 goal thy |
476 goal thy |
470 "!!evs. [| Says A B {|certA, Crypt KB (Nonce M)|} : set evs; \ |
477 "!!evs. [| Says A B {|certA, Crypt KB (Nonce M)|} : set evs; \ |
471 \ A ~: lost; evs : tls |] ==> KB = pubK B"; |
478 \ A ~= Spy; evs : tls |] ==> KB = pubK B"; |
472 be rev_mp 1; |
479 be rev_mp 1; |
473 by (analz_induct_tac 1); |
480 by (analz_induct_tac 1); |
474 qed "A_Crypt_pubB"; |
481 qed "A_Crypt_pubB"; |
475 |
482 |
476 |
483 |
477 (*** Unicity results for M, the pre-master-secret ***) |
484 (*** Unicity results for M, the pre-master-secret ***) |
478 |
485 |
479 (*Induction for theorems of the form X ~: analz (sees lost Spy evs) --> ... |
|
480 It simplifies the proof by discarding needless information about |
|
481 analz (insert X (sees lost Spy evs)) |
|
482 *) |
|
483 fun analz_mono_parts_induct_tac i = |
|
484 etac tls.induct i THEN |
|
485 ClientCertKeyEx_tac (i+5) THEN (*ClientCertKeyEx*) |
|
486 REPEAT_FIRST analz_mono_contra_tac; |
|
487 |
|
488 |
|
489 (*M determines B. Proof borrowed from NS_Public/unique_NA and from Yahalom*) |
486 (*M determines B. Proof borrowed from NS_Public/unique_NA and from Yahalom*) |
490 goal thy |
487 goal thy |
491 "!!evs. [| Nonce M ~: analz (sees lost Spy evs); evs : tls |] \ |
488 "!!evs. [| Nonce M ~: analz (sees Spy evs); evs : tls |] \ |
492 \ ==> EX B'. ALL B. \ |
489 \ ==> EX B'. ALL B. \ |
493 \ Crypt (pubK B) (Nonce M) : parts (sees lost Spy evs) --> B=B'"; |
490 \ Crypt (pubK B) (Nonce M) : parts (sees Spy evs) --> B=B'"; |
494 by (etac rev_mp 1); |
491 by (etac rev_mp 1); |
495 by (analz_mono_parts_induct_tac 1); |
492 by (parts_induct_tac 1); |
496 by (prove_simple_subgoals_tac 1); |
493 by (Fake_parts_insert_tac 1); |
497 by (asm_simp_tac (!simpset addsimps [all_conj_distrib] |
|
498 setloop split_tac [expand_if]) 2); |
|
499 (*ClientCertKeyEx*) |
494 (*ClientCertKeyEx*) |
500 by (expand_case_tac "M = ?y" 2 THEN |
495 by (ClientCertKeyEx_tac 1); |
501 REPEAT (blast_tac (!claset addSEs partsEs) 2)); |
496 by (asm_simp_tac (!simpset addsimps [all_conj_distrib]) 1); |
502 by (Fake_parts_insert_tac 1); |
497 by (expand_case_tac "M = ?y" 1 THEN |
|
498 blast_tac (!claset addSEs partsEs) 1); |
503 val lemma = result(); |
499 val lemma = result(); |
504 |
500 |
505 goal thy |
501 goal thy |
506 "!!evs. [| Crypt(pubK B) (Nonce M) : parts (sees lost Spy evs); \ |
502 "!!evs. [| Crypt(pubK B) (Nonce M) : parts (sees Spy evs); \ |
507 \ Crypt(pubK B') (Nonce M) : parts (sees lost Spy evs); \ |
503 \ Crypt(pubK B') (Nonce M) : parts (sees Spy evs); \ |
508 \ Nonce M ~: analz (sees lost Spy evs); \ |
504 \ Nonce M ~: analz (sees Spy evs); \ |
509 \ evs : tls |] \ |
505 \ evs : tls |] \ |
510 \ ==> B=B'"; |
506 \ ==> B=B'"; |
511 by (prove_unique_tac lemma 1); |
507 by (prove_unique_tac lemma 1); |
512 qed "unique_M"; |
508 qed "unique_M"; |
513 |
509 |
514 |
510 |
515 (*In A's note to herself, M determines A and B.*) |
511 (*In A's note to herself, M determines A and B.*) |
516 goal thy |
512 goal thy |
517 "!!evs. [| Nonce M ~: analz (sees lost Spy evs); evs : tls |] \ |
513 "!!evs. [| Nonce M ~: analz (sees Spy evs); evs : tls |] \ |
518 \ ==> EX A' B'. ALL A B. \ |
514 \ ==> EX A' B'. ALL A B. \ |
519 \ Notes A {|Agent B, Nonce M|} : set evs --> A=A' & B=B'"; |
515 \ Notes A {|Agent B, Nonce M|} : set evs --> A=A' & B=B'"; |
520 by (etac rev_mp 1); |
516 by (etac rev_mp 1); |
521 by (analz_mono_parts_induct_tac 1); |
517 by (parts_induct_tac 1); |
522 by (prove_simple_subgoals_tac 1); |
|
523 by (asm_simp_tac (!simpset addsimps [all_conj_distrib]) 1); |
518 by (asm_simp_tac (!simpset addsimps [all_conj_distrib]) 1); |
524 (*ClientCertKeyEx: if M is fresh, then it can't appear in Notes A X.*) |
519 (*ClientCertKeyEx: if M is fresh, then it can't appear in Notes A X.*) |
525 by (expand_case_tac "M = ?y" 1 THEN |
520 by (expand_case_tac "M = ?y" 1 THEN |
526 blast_tac (!claset addSDs [Notes_Crypt_parts_sees] addSEs partsEs) 1); |
521 blast_tac (!claset addSDs [Notes_Crypt_parts_sees] addSEs partsEs) 1); |
527 val lemma = result(); |
522 val lemma = result(); |
528 |
523 |
529 goal thy |
524 goal thy |
530 "!!evs. [| Notes A {|Agent B, Nonce M|} : set evs; \ |
525 "!!evs. [| Notes A {|Agent B, Nonce M|} : set evs; \ |
531 \ Notes A' {|Agent B', Nonce M|} : set evs; \ |
526 \ Notes A' {|Agent B', Nonce M|} : set evs; \ |
532 \ Nonce M ~: analz (sees lost Spy evs); \ |
527 \ Nonce M ~: analz (sees Spy evs); \ |
533 \ evs : tls |] \ |
528 \ evs : tls |] \ |
534 \ ==> A=A' & B=B'"; |
529 \ ==> A=A' & B=B'"; |
535 by (prove_unique_tac lemma 1); |
530 by (prove_unique_tac lemma 1); |
536 qed "Notes_unique_M"; |
531 qed "Notes_unique_M"; |
537 |
532 |
548 \ (Hash{|Hash{|Nonce NA, Nonce NB, Nonce M|}, \ |
543 \ (Hash{|Hash{|Nonce NA, Nonce NB, Nonce M|}, \ |
549 \ Nonce NA, Agent XA, Agent A, \ |
544 \ Nonce NA, Agent XA, Agent A, \ |
550 \ Nonce NB, Agent XB, certificate B (pubK B)|}); \ |
545 \ Nonce NB, Agent XB, certificate B (pubK B)|}); \ |
551 \ evs : tls; A ~: lost; B ~: lost |] \ |
546 \ evs : tls; A ~: lost; B ~: lost |] \ |
552 \ ==> Notes A {|Agent B, Nonce M|} : set evs --> \ |
547 \ ==> Notes A {|Agent B, Nonce M|} : set evs --> \ |
553 \ X : parts (sees lost Spy evs) --> Says B A X : set evs"; |
548 \ X : parts (sees Spy evs) --> Says B A X : set evs"; |
554 by (hyp_subst_tac 1); |
549 by (hyp_subst_tac 1); |
555 by (analz_induct_tac 1); |
550 by (analz_induct_tac 1); |
556 by (REPEAT_FIRST (rtac impI)); |
551 by (REPEAT_FIRST (rtac impI)); |
557 (*Fake: the Spy doesn't have the critical session key!*) |
552 (*Fake: the Spy doesn't have the critical session key!*) |
558 by (REPEAT (rtac impI 1)); |
553 by (REPEAT (rtac impI 1)); |
559 by (subgoal_tac "Key (serverK(NA,NB,M)) ~: analz (sees lost Spy evsa)" 1); |
554 by (subgoal_tac "Key (serverK(NA,NB,M)) ~: analz (sees Spy evsa)" 1); |
560 by (asm_simp_tac (!simpset addsimps [Spy_not_see_premaster_secret, |
555 by (asm_simp_tac (!simpset addsimps [Spy_not_see_premaster_secret, |
561 not_parts_not_analz]) 2); |
556 not_parts_not_analz]) 2); |
562 by (Fake_parts_insert_tac 1); |
557 by (Fake_parts_insert_tac 1); |
563 qed_spec_mp "TrustServerFinished"; |
558 qed_spec_mp "TrustServerFinished"; |
564 |
559 |
565 |
560 |
566 (*This version refers not to SERVER FINISHED but to any message from B. |
561 (*This version refers not to SERVER FINISHED but to any message from B. |
567 We don't assume B has received CERTIFICATE VERIFY, and an intruder could |
562 We don't assume B has received CERTIFICATE VERIFY, and an intruder could |
568 have changed A's identity in all other messages, so we can't be sure |
563 have changed A's identity in all other messages, so we can't be sure |
569 that B sends his message to A.*) |
564 that B sends his message to A. If CLIENT KEY EXCHANGE were augmented |
570 goal thy |
565 to bind A's identify with M, then we could replace A' by A below.*) |
571 "!!evs. [| evs : tls; A ~: lost; B ~: lost |] \ |
566 goal thy |
572 \ ==> Notes A {|Agent B, Nonce M|} : set evs --> \ |
567 "!!evs. [| evs : tls; A ~: lost; B ~: lost |] \ |
573 \ Crypt (serverK(NA,NB,M)) Y : parts (sees lost Spy evs) --> \ |
568 \ ==> Notes A {|Agent B, Nonce M|} : set evs --> \ |
|
569 \ Crypt (serverK(NA,NB,M)) Y : parts (sees Spy evs) --> \ |
574 \ (EX A'. Says B A' (Crypt (serverK(NA,NB,M)) Y) : set evs)"; |
570 \ (EX A'. Says B A' (Crypt (serverK(NA,NB,M)) Y) : set evs)"; |
575 by (analz_induct_tac 1); |
571 by (analz_induct_tac 1); |
576 by (REPEAT_FIRST (rtac impI)); |
572 by (REPEAT_FIRST (rtac impI)); |
577 (*Fake: the Spy doesn't have the critical session key!*) |
573 (*Fake: the Spy doesn't have the critical session key!*) |
578 by (subgoal_tac "Key (serverK(NA,NB,M)) ~: analz (sees lost Spy evsa)" 1); |
574 by (subgoal_tac "Key (serverK(NA,NB,M)) ~: analz (sees Spy evsa)" 1); |
579 by (asm_simp_tac (!simpset addsimps [Spy_not_see_premaster_secret, |
575 by (asm_simp_tac (!simpset addsimps [Spy_not_see_premaster_secret, |
580 not_parts_not_analz]) 2); |
576 not_parts_not_analz]) 2); |
581 by (Fake_parts_insert_tac 1); |
577 by (Fake_parts_insert_tac 1); |
582 (*ServerFinished. If the message is old then apply induction hypothesis...*) |
578 (*ServerFinished. If the message is old then apply induction hypothesis...*) |
583 by (rtac conjI 1 THEN Blast_tac 2); |
579 by (rtac conjI 1 THEN Blast_tac 2); |
584 (*...otherwise delete induction hyp and use unicity of M.*) |
580 (*...otherwise delete induction hyp and use unicity of M.*) |
585 by (thin_tac "?PP-->?QQ" 1); |
581 by (thin_tac "?PP-->?QQ" 1); |
586 by (Step_tac 1); |
582 by (Step_tac 1); |
587 by (subgoal_tac "Nonce M ~: analz (sees lost Spy evsa)" 1); |
583 by (subgoal_tac "Nonce M ~: analz (sees Spy evsa)" 1); |
588 by (asm_simp_tac (!simpset addsimps [Spy_not_see_premaster_secret]) 2); |
584 by (asm_simp_tac (!simpset addsimps [Spy_not_see_premaster_secret]) 2); |
589 by (blast_tac (!claset addSEs [MPair_parts] |
585 by (blast_tac (!claset addSEs [MPair_parts] |
590 addDs [Notes_Crypt_parts_sees, |
586 addDs [Notes_Crypt_parts_sees, |
591 Says_imp_sees_Spy' RS parts.Inj, |
587 Says_imp_sees_Spy RS parts.Inj, |
592 unique_M]) 1); |
588 unique_M]) 1); |
593 qed_spec_mp "TrustServerMsg"; |
589 qed_spec_mp "TrustServerMsg"; |
594 |
590 |
595 |
591 |
596 (*** Protocol goal: if B receives any message encrypted with clientK |
592 (*** Protocol goal: if B receives any message encrypted with clientK |
599 CLIENT FINISHED, then B can then check the quoted values XA, XB, etc. |
595 CLIENT FINISHED, then B can then check the quoted values XA, XB, etc. |
600 ***) |
596 ***) |
601 goal thy |
597 goal thy |
602 "!!evs. [| evs : tls; A ~: lost; B ~: lost |] \ |
598 "!!evs. [| evs : tls; A ~: lost; B ~: lost |] \ |
603 \ ==> Notes A {|Agent B, Nonce M|} : set evs --> \ |
599 \ ==> Notes A {|Agent B, Nonce M|} : set evs --> \ |
604 \ Crypt (clientK(NA,NB,M)) Y : parts (sees lost Spy evs) --> \ |
600 \ Crypt (clientK(NA,NB,M)) Y : parts (sees Spy evs) --> \ |
605 \ Says A B (Crypt (clientK(NA,NB,M)) Y) : set evs"; |
601 \ Says A B (Crypt (clientK(NA,NB,M)) Y) : set evs"; |
606 by (analz_induct_tac 1); |
602 by (analz_induct_tac 1); |
607 by (REPEAT_FIRST (rtac impI)); |
603 by (REPEAT_FIRST (rtac impI)); |
608 (*Fake: the Spy doesn't have the critical session key!*) |
604 (*Fake: the Spy doesn't have the critical session key!*) |
609 by (subgoal_tac "Key (clientK(NA,NB,M)) ~: analz (sees lost Spy evsa)" 1); |
605 by (subgoal_tac "Key (clientK(NA,NB,M)) ~: analz (sees Spy evsa)" 1); |
610 by (asm_simp_tac (!simpset addsimps [Spy_not_see_premaster_secret, |
606 by (asm_simp_tac (!simpset addsimps [Spy_not_see_premaster_secret, |
611 not_parts_not_analz]) 2); |
607 not_parts_not_analz]) 2); |
612 by (Fake_parts_insert_tac 1); |
608 by (Fake_parts_insert_tac 1); |
613 (*ClientFinished. If the message is old then apply induction hypothesis...*) |
609 (*ClientFinished. If the message is old then apply induction hypothesis...*) |
614 by (step_tac (!claset delrules [conjI]) 1); |
610 by (step_tac (!claset delrules [conjI]) 1); |
615 by (subgoal_tac "Nonce M ~: analz (sees lost Spy evsa)" 1); |
611 by (subgoal_tac "Nonce M ~: analz (sees Spy evsa)" 1); |
616 by (asm_simp_tac (!simpset addsimps [Spy_not_see_premaster_secret]) 2); |
612 by (asm_simp_tac (!simpset addsimps [Spy_not_see_premaster_secret]) 2); |
617 by (blast_tac (!claset addSEs [MPair_parts] |
613 by (blast_tac (!claset addSEs [MPair_parts] |
618 addDs [Notes_unique_M]) 1); |
614 addDs [Notes_unique_M]) 1); |
619 qed_spec_mp "TrustClientMsg"; |
615 qed_spec_mp "TrustClientMsg"; |
620 |
616 |