|
| 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