Skip to content

Commit 2ca18f4

Browse files
committed
[cryptolib] generalize PRF definitions, PRP<->PRF
This takes a swipe at cleaning up the PRF and PRP theories, as well as the associated switching lemmas. This should bring them more in line with modern EasyCrypt practice while also making them better examples of modern EasyCrypt development themselves—perhaps serving as advanced tutorials.
1 parent 2077146 commit 2ca18f4

3 files changed

Lines changed: 629 additions & 476 deletions

File tree

theories/crypto/PRF.eca

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -57,30 +57,23 @@ end RF.
5757
abstract theory PseudoRF.
5858
type K.
5959

60-
op dK: { K distr | is_lossless dK } as dK_ll.
61-
62-
op F : K -> D -> R.
63-
6460
module type PseudoRF = {
6561
proc keygen(): K
66-
proc f(_ : K * D): R
62+
proc f(_: K * D): R
6763
}.
6864

69-
module PseudoRF = {
70-
proc keygen() = {
71-
var k;
65+
module PRF (P : PseudoRF) = {
66+
var k : K
7267

73-
k <$ dK;
74-
return k;
68+
proc init() = {
69+
k <@ P.keygen();
7570
}
7671

77-
proc f(k, x) = { return F k x; }
78-
}.
79-
80-
module PRF = {
81-
var k : K
72+
proc f(x: D) = {
73+
var r;
8274

83-
proc init() = { k <$ dK; }
84-
proc f(x: D) = { return F k x; }
75+
r <@ P.f(k, x);
76+
return r;
77+
}
8578
}.
8679
end PseudoRF.

theories/crypto/prp_prf/RP_RF.eca

Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
1+
require import AllCore Distr List FSet FMap.
2+
require import Dexcepted.
3+
require (*--*) PRP.
4+
(*---*) import RField StdOrder.RealOrder.
5+
6+
require (*--*) FelTactic.
7+
8+
(** We assume a finite domain D, equipped with a full and lossless
9+
distribution. (We need fullness and losslessness to know that
10+
resampling always works.) **)
11+
type D.
12+
13+
op [lossless full] dD: D distr.
14+
op hmin_dD: { real | forall x, mu1 dD x <= hmin_dD } as hmin_dDP.
15+
16+
lemma ge0_hmin_dD: 0%r <= hmin_dD.
17+
proof.
18+
apply: (ler_trans (mu1 dD witness)).
19+
+ exact: ge0_mu.
20+
exact: hmin_dDP.
21+
qed.
22+
23+
(** and a type K equipped with a lossless distribution **)
24+
type K.
25+
26+
clone import PRP as PRPt with
27+
type D <- D
28+
proof *.
29+
30+
clone import StrongPRP as PRPSec
31+
proof *.
32+
33+
clone import RP as PRPi with
34+
op dD <- dD
35+
proof *
36+
rename "RP" as "PRPi".
37+
realize dD_ll by exact: dD_ll.
38+
39+
clone import PRF as PRFt with
40+
type D <- D,
41+
type R <- D
42+
proof *.
43+
44+
clone import RF with
45+
op dR _ <- dD
46+
proof *
47+
rename "RF" as "PRFi".
48+
realize dR_ll by rewrite dD_ll.
49+
50+
module Count (P: PRF): PRF = {
51+
var count: int
52+
53+
proc init() = {
54+
P.init();
55+
count <- 0;
56+
}
57+
58+
proc f(x) = {
59+
var r;
60+
61+
r <@ P.f(x);
62+
count <- count + 1;
63+
return r;
64+
}
65+
}.
66+
67+
section.
68+
69+
(** The following is a proof that holds **)
70+
(** For all lossless distinguisher D ... **)
71+
declare module D <: Distinguisher { -PRPi, -PRFi, -Count }.
72+
declare axiom D_ll (O <: PRF_Oracles {-D}): islossless O.f => islossless D(O).distinguish.
73+
74+
(** ... that makes at most q queries for some non-negative q. **)
75+
declare op q : { int | 0 <= q } as ge0_q.
76+
declare axiom D_count (O <: PRF { -D, -Count }) c0:
77+
hoare [D(Count(O)).distinguish: Count.count = c0 ==> Count.count <= c0 + q].
78+
79+
(** We make use of a generic principle for sampling in a conditional
80+
distribution **)
81+
local clone import TwoStepSampling with
82+
type i <- unit,
83+
type t <- D,
84+
op dt _ <- dD
85+
proof *.
86+
87+
(** And we put some work into turning our random function (PRFi) and
88+
random permutation (PRPi) into part of a distinguisher against a
89+
sampling procedure. **)
90+
local module type Sample_t = {
91+
proc sample(X: D -> bool): D
92+
}.
93+
94+
local module Direct : Sample_t = {
95+
proc sample(X) = {
96+
var r;
97+
98+
r <@ S.direct((), fun _=> X);
99+
return r;
100+
}
101+
}.
102+
103+
local module Indirect : Sample_t = {
104+
proc sample(X) = {
105+
var r;
106+
107+
r <@ S.indirect((), fun _=> X);
108+
return r;
109+
}
110+
}.
111+
112+
local equiv eq_Direct_Indirect:
113+
Direct.sample ~ Indirect.sample: ={X} ==> ={res}.
114+
proof.
115+
by proc; call ll_direct_indirect_eq; auto; rewrite dD_ll.
116+
qed.
117+
118+
local module PRPi' (S:Sample_t) = {
119+
proc init = PRPi.init
120+
121+
proc f(x:D): D = {
122+
var r;
123+
if (x \notin PRPi.m) {
124+
r <@ S.sample(rng PRPi.m);
125+
PRPi.m.[x] <- r;
126+
}
127+
return oget PRPi.m.[x];
128+
}
129+
}.
130+
131+
(* We need bad flags *)
132+
local module PRFi'_bad = {
133+
var bad: bool
134+
135+
proc init() = {
136+
PRPi.init();
137+
bad <- false;
138+
}
139+
140+
proc f(x: D): D = {
141+
var r;
142+
143+
if (x \notin PRPi.m) {
144+
r <$ dD;
145+
bad <- bad \/ rng PRPi.m r;
146+
PRPi.m.[x] <- r;
147+
}
148+
return oget PRPi.m.[x];
149+
}
150+
}.
151+
152+
local module PRPi'_bad = {
153+
proc init() = {
154+
PRPi.init();
155+
PRFi'_bad.bad <- false;
156+
}
157+
158+
proc f(x: D): D = {
159+
var r;
160+
161+
if (x \notin PRPi.m) {
162+
r <$ dD;
163+
if (rng PRPi.m r) {
164+
PRFi'_bad.bad <- true;
165+
r <$ dD \ (rng PRPi.m);
166+
}
167+
PRPi.m.[x] <- r;
168+
}
169+
return oget PRPi.m.[x];
170+
}
171+
}.
172+
173+
(* FIXME: lift out and share with Strong RP RF *)
174+
local lemma notin_supportIP (P : 'a -> bool) (d : 'a distr):
175+
(exists a, support d a /\ !P a) <=> mu d P < mu d predT.
176+
proof.
177+
rewrite (mu_split _ predT P) /predI /predT /predC /=.
178+
rewrite (exists_eq (fun a => support d a /\ !P a) (fun a => !P a /\ a \in d)) /=.
179+
+ by move=> a /=; rewrite andbC.
180+
by rewrite -(witness_support (predC P)) -/(predC _) /#.
181+
qed.
182+
183+
local lemma excepted_lossless (m:(D,D) fmap):
184+
(exists x, x \notin m) =>
185+
mu (dD \ (rng m)) predT = 1%r.
186+
proof.
187+
move=> /endo_dom_rng [x h]; rewrite dexcepted_ll // 1:dD_ll.
188+
by rewrite -dD_ll; apply: notin_supportIP; exists x; rewrite dD_fu.
189+
qed.
190+
191+
(** We split this into extremely small steps, but this is not
192+
necessary **)
193+
local lemma pr_PRPi_PRPi'_Direct &m:
194+
Pr[IND(PRPi, D).main() @ &m: res]
195+
= Pr[IND(PRPi'(Direct), D).main() @ &m:res].
196+
proof.
197+
byequiv=> //; proc; call (: ={PRPi.m}).
198+
+ by proc; if=> //; inline *; auto.
199+
by inline *; auto.
200+
qed.
201+
202+
local lemma pr_PRPi'_Direct_PRPi'_Indirect &m:
203+
Pr[IND(PRPi'(Direct), D).main() @ &m: res]
204+
= Pr[IND(PRPi'(Indirect), D).main() @ &m: res].
205+
proof.
206+
byequiv=> //; proc; call (: ={PRPi.m}).
207+
+ proc; if=> //; auto.
208+
by call eq_Direct_Indirect; auto=> |>; rewrite dD_ll.
209+
by inline *; auto.
210+
qed.
211+
212+
local lemma pr_PRPi'_Indirect_PRPi'_bad &m:
213+
Pr[IND(PRPi'(Indirect), D).main() @ &m: res]
214+
= Pr[IND(PRPi'_bad, D).main() @ &m: res].
215+
proof.
216+
byequiv=> //; proc; call (: ={PRPi.m}).
217+
+ proc; if=> //; inline *.
218+
do ! cfold {1} 1.
219+
seq 1 1: (#pre /\ r1{1} = r{2}); 1:by auto.
220+
by if=> //; auto.
221+
by inline *; auto.
222+
qed.
223+
224+
local lemma pr_PRFi'_PRFi &m:
225+
Pr[IND(PRFi, D).main() @ &m: res]
226+
= Pr[IND(PRFi'_bad, D).main() @ &m: res].
227+
proof.
228+
byequiv=> //; proc; call (: ={m}(PRFi, PRPi)); 1:by sim.
229+
by inline *; auto.
230+
qed.
231+
232+
local lemma pr_PRPi'_Indirect_PRFi &m:
233+
`| Pr[IND(PRPi'_bad, D).main() @ &m: res]
234+
- Pr[IND(PRFi, D).main() @ &m: res]|
235+
<= Pr[IND(PRFi'_bad, D).main() @ &m: PRFi'_bad.bad].
236+
proof.
237+
rewrite pr_PRFi'_PRFi; byequiv: PRFi'_bad.bad=> //=; 2:smt().
238+
proc.
239+
call (: PRFi'_bad.bad
240+
, !PRFi'_bad.bad{2} /\ ={PRPi.m} /\ ={PRFi'_bad.bad}
241+
, PRFi'_bad.bad{2} /\ ={PRFi'_bad.bad}).
242+
+ exact: D_ll.
243+
+ proc; if=> //=; inline *.
244+
seq 1 1: (#pre /\ ={r}); 1:by auto.
245+
if {1}; auto=> |> &1 _ x_notin_m _.
246+
by apply: excepted_lossless; exists x{1}.
247+
+ move=> &2 bad; proc; if; 2:by auto.
248+
seq 1: (PRFi'_bad.bad{2} /\ PRFi'_bad.bad = PRFi'_bad.bad{2} /\ x \notin PRPi.m)=> //.
249+
+ by auto=> |>; rewrite dD_ll.
250+
+ if; auto=> |> &1 _ x_notin_m _ @/predT /=.
251+
by apply: excepted_lossless; exists x{1}.
252+
+ by hoare; auto.
253+
+ by move=> &1; proc; if; auto=> |>; rewrite dD_ll.
254+
by inline *; auto=> |> /#.
255+
qed.
256+
257+
import StdBigop.Bigreal BRA.
258+
259+
local lemma pr_PRFi'_bad &m:
260+
Pr[IND(PRFi'_bad, D).main() @ &m: PRFi'_bad.bad]
261+
<= (q^2 - q)%r / 2%r * hmin_dD.
262+
proof.
263+
(** This is easier than instantiating Birthday bound, still **)
264+
have ->: Pr[IND(PRFi'_bad, D).main() @ &m: PRFi'_bad.bad]
265+
= Pr[IND(Count(PRFi'_bad), D).main() @ &m: PRFi'_bad.bad /\ Count.count <= q].
266+
+ byequiv (: ={glob D} ==> ={PRFi'_bad.bad} /\ Count.count{2} <= q)=> //.
267+
conseq (: _ ==> ={PRFi'_bad.bad}) _ (: _ ==> Count.count <= q)=> //.
268+
+ by proc; call (D_count PRFi'_bad 0); inline *; auto.
269+
proc; call (: ={PRPi.m, PRFi'_bad.bad})=> //.
270+
+ by proc; inline *; sp; if; auto.
271+
by inline *; auto.
272+
fel 1 (Count.count) (fun i=> i%r * hmin_dD) q
273+
PRFi'_bad.bad
274+
[] (card (frng PRPi.m) <= Count.count)=> //.
275+
+ by rewrite -mulr_suml sumidE 1:ge0_q mulrDr expr2 mulrN1.
276+
+ by inline *; auto=> |>; rewrite frng0 fcards0.
277+
+ exlim Count.count=> c; conseq (: _: <= (c%r * hmin_dD))=> //.
278+
proc; inline *; sp; if=> //.
279+
+ wp; rnd (rng PRPi.m); auto=> |> &0 ge0_count lt_count_q.
280+
move=> _ size_rng_m _; rewrite (mu_eq _ _ (mem (frng PRPi.m{0}))).
281+
+ by move=> x; rewrite mem_frng.
282+
apply: (ler_trans ((card (frng PRPi.m{0}))%r * hmin_dD)).
283+
+ by apply: Mu_mem.mu_mem_le=> x _; exact: hmin_dDP.
284+
apply: ler_wpmul2r; 1:exact: ge0_hmin_dD.
285+
by apply: le_fromint=> /#.
286+
by hoare; auto=> |>; smt(ge0_hmin_dD).
287+
+ move=> c; proc; inline *; sp; if; auto=> |>; 2:smt().
288+
move=> &0 card_rng_m x_notin_m r _; split=> [/#|].
289+
have ->: frng PRPi.m.[x <- r]{0} = frng PRPi.m{0} `|` fset1 r.
290+
+ apply: fsetP=> z; rewrite in_fsetU1 !mem_frng.
291+
rewrite rng_set; congr; congr.
292+
apply: fmap_eqP=> z0; rewrite remE.
293+
case: (z0 = x{0})=> |>.
294+
by move: x_notin_m; rewrite domE=> /= ->.
295+
by rewrite fcardU1; smt().
296+
qed.
297+
298+
lemma RP_RF_Switching &m:
299+
`| Pr[IND(PRPi, D).main() @ &m: res]
300+
- Pr[IND(PRFi, D).main() @ &m: res]|
301+
<= (q ^ 2 - q)%r / 2%r * hmin_dD.
302+
proof.
303+
rewrite (pr_PRPi_PRPi'_Direct &m) (pr_PRPi'_Direct_PRPi'_Indirect &m).
304+
rewrite (pr_PRPi'_Indirect_PRPi'_bad &m).
305+
smt(pr_PRPi'_Indirect_PRFi pr_PRFi'_bad).
306+
qed.
307+
end section.

0 commit comments

Comments
 (0)