-
Notifications
You must be signed in to change notification settings - Fork 0
/
SecretGuessing.eca
295 lines (233 loc) · 7.37 KB
/
SecretGuessing.eca
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
(* SecretGuessing.eca *)
(* Secret Guessing Game *)
(* This abstract theory provides a guessing game, in which the
adversary gets a limited number of attempts to guess a secret. *)
prover quorum=2 ["Alt-Ergo" "Z3"]. (* both Alt-Ergo and Z3 must succeed *)
(***************************** Standard Theories ******************************)
require import AllCore FSet Distr Mu_mem.
require import StdOrder. import RealOrder.
(***************************** Theory Parameters ******************************)
(* a secret is a bitstring of length sec_len *)
op sec_len : int. (* length of a secret *)
axiom sec_len_ge0 : 0 <= sec_len.
type sec. (* type of secrets *)
op sec_default : sec. (* default secret *)
op sec_distr : sec distr. (* uniform, full and lossless distribution on
secrets *)
axiom mu1_sec_distr (sec : sec) :
mu1 sec_distr sec = 1%r / (2 ^ sec_len)%r.
axiom sec_distr_ll : is_lossless sec_distr.
(* guessing limit *)
op limit : int.
axiom limit_ge0 : 0 <= limit.
(************************** End of Theory Parameters **************************)
(* guessing oracle type *)
module type SG_OR = {
proc init(x : sec) : unit (* initialize oracle, including secret *)
proc guess(x : sec) : unit (* try to guess secret *)
}.
(* guessing oracle *)
module SGOr : SG_OR = {
var secret : sec (* secret *)
var ctr : int (* counter *)
var guessed : bool (* true iff secret already guessed *)
proc init(sec : sec) : unit = {
secret <- sec;
ctr <- 0;
guessed <- false;
}
(* only the first limit calls to guess are taken into account *)
proc guess(x : sec) : unit = {
if (ctr < limit) { (* invariant: ctr <= limit *)
ctr <- ctr + 1;
if (x = secret) {
guessed <- true;
}
}
}
}.
(* adversary type *)
module type SG_ADV(O : SG_OR) = {
proc main() : unit {O.guess}
}.
(* guessing game *)
module Guessing(SGAdv : SG_ADV) = {
module A = SGAdv(SGOr)
proc main() : unit = {
var sec : sec;
sec <$ sec_distr; (* randomly choose secret *)
SGOr.init(sec); (* initialize oracle *)
A.main(); (* let adversary try to guess secret *)
}
}.
(* see end of section for main lemma, GuessingProb *)
(******************************** Body of Proof *******************************)
(* the probability of a randomly chosen secret being in a set of
secrets with cardinality n is n%r / (2 ^ sec_len)%r *)
lemma mu_sec_distr_mem (xs : sec fset) :
mu sec_distr (mem xs) = (card xs)%r / (2 ^ sec_len)%r.
proof.
apply (mu_mem _ _ (1%r / (2 ^ sec_len)%r)) => x mem_xs_x.
apply mu1_sec_distr.
qed.
(* version of above where cardinality of set is upper bounded
by limit *)
lemma mu_sec_distr_mem_limit (xs : sec fset) :
card xs <= limit =>
mu sec_distr (mem xs) <= limit%r / (2 ^ sec_len)%r.
proof.
move => lim.
rewrite mu_sec_distr_mem ler_wpmul2r 1:invr_ge0 le_fromint //.
case (sec_len = 0) => [-> | ne0_sec_len];
[by rewrite expr0 lez01 | by rewrite IntOrder.expr_ge0].
qed.
section.
declare module SGAdv <: SG_ADV{-SGOr}.
(* version of oracle that simply accumulates guesses *)
local module SGOrAccum : SG_OR = {
var secret : sec
var ctr : int
var guesses : sec fset (* guesses made so far *)
proc init(sec : sec) : unit = {
secret <- sec;
ctr <- 0;
guesses <- fset0;
}
proc guess(x : sec) : unit = {
if (ctr < limit) {
ctr <- ctr + 1;
guesses <- guesses `|` fset1 x;
}
}
}.
local lemma SGOrAccum_guess_Card :
hoare
[SGOrAccum.guess :
SGOrAccum.ctr <= limit /\
card SGOrAccum.guesses <= SGOrAccum.ctr ==>
SGOrAccum.ctr <= limit /\
card SGOrAccum.guesses <= SGOrAccum.ctr].
proof.
proc; auto; progress.
smt().
rewrite fcardU fcard1; smt(fcard_ge0).
qed.
local lemma SGOr_SGOrAccum_guess :
equiv
[SGOr.guess ~ SGOrAccum.guess :
={x} /\ ={secret, ctr}(SGOr, SGOrAccum) /\
(SGOr.guessed{1} <=> mem SGOrAccum.guesses{2} SGOrAccum.secret{2}) ==>
={secret, ctr}(SGOr, SGOrAccum) /\
(SGOr.guessed{1} <=> mem SGOrAccum.guesses{2} SGOrAccum.secret{2})].
proof.
proc; if; auto; progress.
by rewrite in_fsetU1.
rewrite in_fsetU; by left.
smt(in_fsetU1).
qed.
(* G1 is like Guessing, except it uses SGOrAccum not SGOr, and it has
a global variable, guessed, whose value it calculates using
SGOrAccum.guesses *)
local module G1 = {
var guessed : bool
module A = SGAdv(SGOrAccum)
proc main() : unit = {
var sec : sec;
sec <$ sec_distr;
SGOrAccum.init(sec);
A.main();
guessed <- mem SGOrAccum.guesses sec;
}
}.
local lemma Guessing_G1_main :
equiv
[Guessing(SGAdv).main ~ G1.main :
={glob SGAdv} ==>
={secret, ctr}(SGOr, SGOrAccum) /\
(SGOr.guessed{1} <=> G1.guessed{2})].
proof.
proc.
seq 2 2 :
(={glob SGAdv, sec} /\ sec{1} = SGOr.secret{1} /\ ={secret, ctr}(SGOr, SGOrAccum) /\
(SGOr.guessed{1} <=> mem SGOrAccum.guesses{2} SGOrAccum.secret{2})).
inline *; auto; progress; by rewrite in_fset0.
wp.
call
(_ :
={glob SGAdv} /\ ={secret, ctr}(SGOr, SGOrAccum) /\
(SGOr.guessed{1} <=> mem SGOrAccum.guesses{2} SGOrAccum.secret{2}) ==>
={res} /\ ={secret, ctr}(SGOr, SGOrAccum) /\
(SGOr.guessed{1} <=> mem SGOrAccum.guesses{2} SGOrAccum.secret{2})).
proc
(={secret, ctr}(SGOr, SGOrAccum) /\
(SGOr.guessed{1} <=> mem SGOrAccum.guesses{2} SGOrAccum.secret{2})) => //.
conseq SGOr_SGOrAccum_guess => //.
auto.
qed.
local lemma Guessing_G1 &m :
Pr[Guessing(SGAdv).main() @ &m : SGOr.guessed] =
Pr[G1.main() @ &m : G1.guessed].
proof. by byequiv Guessing_G1_main. qed.
(* in G2, we don't tell SGOrAccum about the chosen secret, and
secret is chosen just before membership test *)
local module G2 = {
var guessed : bool
module A = SGAdv(SGOrAccum)
proc main() : unit = {
var sec : sec;
SGOrAccum.init(sec_default);
A.main();
sec <$ sec_distr;
guessed <- mem SGOrAccum.guesses sec;
}
}.
local lemma G1_G2_main :
equiv[G1.main ~ G2.main : ={glob SGAdv} ==> ={guessed}(G1, G2)].
proof.
proc.
swap {2} 3 -2.
seq 2 2 : (={glob SGAdv, sec, SGOrAccum.ctr, SGOrAccum.guesses}).
inline *; auto.
sim.
qed.
local lemma G1_G2 &m :
Pr[G1.main() @ &m : G1.guessed] = Pr[G2.main() @ &m : G2.guessed].
proof. by byequiv G1_G2_main. qed.
local lemma Guessing_G2 &m :
Pr[Guessing(SGAdv).main() @ &m : SGOr.guessed] =
Pr[G2.main() @ &m : G2.guessed].
proof. by rewrite (Guessing_G1 &m) (G1_G2 &m). qed.
(* upper bounding of probabiliy of secret being guessed *)
local lemma G2_Prob &m :
Pr[G2.main() @ &m : G2.guessed] <=
limit%r / (2 ^ sec_len)%r.
proof.
byphoare (_ : true ==> G2.guessed) => //.
proc.
seq 2 :
(SGOrAccum.ctr <= limit /\ card SGOrAccum.guesses <= SGOrAccum.ctr)
1%r
(limit%r / (2 ^ sec_len)%r)
0%r
1%r => //.
wp; rnd; skip.
progress; rewrite (mu_sec_distr_mem_limit SGOrAccum.guesses{hr}) /#.
hoare; simplify.
call
(_ :
SGOrAccum.ctr <= limit /\ card SGOrAccum.guesses <= SGOrAccum.ctr ==>
SGOrAccum.ctr <= limit /\ card SGOrAccum.guesses <= SGOrAccum.ctr).
proc
(SGOrAccum.ctr <= limit /\ card SGOrAccum.guesses <= SGOrAccum.ctr) => //.
apply SGOrAccum_guess_Card.
inline *; auto; smt(limit_ge0 fcards0).
qed.
lemma GuessingProb' &m :
Pr[Guessing(SGAdv).main() @ &m : SGOr.guessed] <=
limit%r / (2 ^ sec_len)%r.
proof. by rewrite (Guessing_G2 &m) (G2_Prob &m). qed.
end section.
lemma GuessingProb (SGAdv <: SG_ADV{-SGOr}) &m :
Pr[Guessing(SGAdv).main() @ &m : SGOr.guessed] <=
limit%r / (2 ^ sec_len)%r.
proof. apply (GuessingProb' SGAdv &m). qed.