OAEP.eca
require import AllCore List FSet OldFMap.
require import Distr DBool DProd.
require (*--*) Ring ROM.
pragma +implicits.
pragma -oldip.
(* Type for G's outputs (and H's inputs) *)
clone import Ring.ZModule as GTag
rename [type] "t" as "gtag".
op dgtag: { gtag distr | is_lossless dgtag
/\ is_uniform dgtag
/\ is_full dgtag } as dgtag_ll_uni_fu.
lemma dgtag_ll : is_lossless dgtag by smt(dgtag_ll_uni_fu).
lemma dgtag_uni : is_uniform dgtag by smt(dgtag_ll_uni_fu).
lemma dgtag_fu : is_full dgtag by smt(dgtag_ll_uni_fu).
lemma dgtag_funi: is_funiform dgtag by smt(dgtag_ll_uni_fu).
(* Type for H's outputs (and G's inputs) *)
clone import Ring.ZModule as HTag
rename [type] "t" as "htag".
op dhtag: { htag distr | is_lossless dhtag
/\ is_uniform dhtag
/\ is_full dhtag } as dhtag_ll_uni_fu.
lemma dhtag_ll : is_lossless dhtag by smt(dhtag_ll_uni_fu).
lemma dhtag_uni : is_uniform dhtag by smt(dhtag_ll_uni_fu).
lemma dhtag_fu : is_full dhtag by smt(dhtag_ll_uni_fu).
lemma dhtag_funi: is_funiform dhtag by smt(dhtag_ll_uni_fu).
(* The type of plaintext must be injected strictly into gtag: *)
(* redundancy is crucial for OAEP to work! *)
type ptxt.
op pad : ptxt -> gtag.
op unpad: gtag -> ptxt option.
axiom padK : pcancel pad unpad.
axiom unpadK: ocancel unpad pad.
(* The permutation works on pairs of GTag and HTag *)
clone import Ring.ZModule as D with
type t <- gtag * htag,
op zeror <- (GTag.zeror,HTag.zeror),
op (+) (x y : gtag * htag) <- ((x.`1 + y.`1),(x.`2 + y.`2)),
op [-] (x : gtag * htag) <- (-x.`1,-x.`2)
proof *.
realize addrA. by move=> x y z /=; rewrite GTag.addrA HTag.addrA. qed.
realize addrC. by move=> x y /=; rewrite GTag.addrC HTag.addrC. qed.
realize add0r. by move=> [x1 x2] /=; rewrite GTag.add0r HTag.add0r. qed.
realize addNr. by move=> [x1 x2] /=; rewrite GTag.addNr HTag.addNr. qed.
op dd = dgtag `*` dhtag.
lemma dd_ll : is_lossless dd by rewrite dprod_ll dgtag_ll dhtag_ll.
lemma dd_uni : is_uniform dd by rewrite dprod_uni 1:dgtag_uni// dhtag_uni.
lemma dd_fu : is_full dd by rewrite dprod_fu dgtag_fu dhtag_fu.
lemma dd_funi: is_funiform dd by rewrite dprod_funi 1:dgtag_funi// dhtag_funi.
(* The permutation itself (with abstract keys) *)
type pkey, skey.
op dkeys : { (pkey * skey) distr | is_lossless dkeys } as dkeys_ll.
op f : pkey -> gtag * htag -> gtag * htag.
op fi: skey -> gtag * htag -> gtag * htag.
axiom fK pk sk : (pk,sk) \in dkeys => cancel (f pk) (fi sk).
axiom fiK pk sk: (pk,sk) \in dkeys => cancel (fi sk) (f pk).
(* The G random oracle *)
clone import ROM.Lazy as G with
type from <- htag,
type to <- gtag,
op dsample _ <- dgtag
proof *
rename
"RO" as "G"
"Oracle" as "Gt".
import Types.
(* The H random oracle *)
clone import ROM.Lazy as H with
type from <- gtag,
type to <- htag,
op dsample _ <- dhtag
proof *
rename
"RO" as "H"
"Oracle" as "Ht".
import Types.
(* ------------------------------------------------------------------------ *)
module OAEP (H : AH) (G : AG) = {
proc kg() = {
var pk, sk;
(pk,sk) <$ dkeys;
return (pk,sk);
}
proc enc(pk : pkey, m : ptxt) = {
var r, s, t;
r <$ dhtag;
s <@ G.o(r);
s <- s + (pad m);
t <@ H.o(s);
t <- t + r;
return f pk (s,t);
}
proc dec(sk : skey, c : gtag * htag) = {
var s, t, r, m;
(s,t) <- fi sk c;
r <@ H.o(s);
r <- r + t;
m <@ G.o(r);
m <- m + s;
return unpad m;
}
}.
(* ------------------------------------------------------------------------ *)
module type PKE = {
proc kg(): pkey * skey
proc enc(pk : pkey, m : ptxt): gtag * htag
proc dec(sk : skey, c : gtag * htag): ptxt option
}.
module type HG_PKE (H : AH) (G : AG) = {
proc kg(): pkey * skey
proc enc(pk : pkey, m : ptxt): gtag * htag
proc dec(sk : skey, c : gtag * htag): ptxt option
}.
module type APKE = {
proc dec(c : gtag * htag): ptxt option
}.
module type CCA_ADV (H : AH) (G : AG) (S : APKE) = {
proc choose(pk : pkey): ptxt * ptxt
proc guess(c : gtag * htag): bool
}.
module IND_CCA (H : Ht) (G : Gt) (S : HG_PKE) (A : CCA_ADV) = {
var sk : skey
var c' : gtag * htag
module Sc = {
proc dec(c : gtag * htag) = {
var r <- None;
r <@ S(H,G).dec(sk,c);
return r;
}
}
module Sg = {
proc dec(c : gtag * htag) = {
var r <- None;
if (c <> c') {
r <@ S(H,G).dec(sk,c);
}
return r;
}
}
proc main() = {
var pk, m0, m1, b, b';
H.init();
G.init();
(pk,sk) <$ dkeys;
(m0,m1) <@ A(H,G,Sc).choose(pk);
b <$ {0,1};
c' <@ S(H,G).enc(pk,b?m0:m1);
b' <@ A(H,G,Sg).guess(c');
return b = b';
}
}.
(* ------------------------------------------------------------------------ *)
(* A quick note here: our inverter *does not* take over the random oracles, *)
(* but simply uses them. This does not really make a difference if this is *)
(* the end of the proof, but will make a huge difference if we then compose *)
(* this proof with another one that requires the random oracles to be true *)
(* random oracles. It may be a good idea to write something about this... *)
(* ------------------------------------------------------------------------ *)
module type Inverter (H : AH) (G : AG) = {
proc invert(pk : pkey, c : gtag * htag): gtag list (* could use fset *)
}.
module SPDOW (H : Ht) (G : Gt) (I : Inverter) = {
proc main() = {
var pk, sk, s, t, ss;
H.init();
G.init();
(pk,sk) <$ dkeys;
(s,t) <$ dd;
ss <@ I(H,G).invert(pk,f pk (s,t));
return s \in ss;
}
}.
(*
op extract (p : 'x -> bool) (xs : 'x list) =
with xs = [] => None
with xs = x::xs =>
if p x
then Some x
else extract p xs.
op extract2 (p : 'x -> 'y -> bool) (xs : 'x list) (ys : 'y list) =
with xs = [] => None
with xs = x::xs =>
let y = extract (p x) ys in
if y <> None
then Some (x,oget y)
else extract2 p xs ys.
lemma extract_correct (p : 'x -> bool) xs a:
extract p xs = Some a
=> p a.
proof. by elim: xs=> //= x xs ih; case: (p x). qed.
lemma extract2_correct (p : 'x -> 'y -> bool) xs ys a b:
extract2 p xs ys = Some (a,b)
=> p a b.
proof.
elim: xs=> //= x xs ih.
case: {-1}(extract (p x) ys) (eq_refl (extract (p x) ys))=> // y.
by move=> /extract_correct; rewrite oget_some=> /= + [#] <- <-.
qed.
module I (A : CCA_ADV) (H : AH) (G : AG) = {
var pk : pkey
var logH: (gtag * htag) list
var logG: (htag * gtag) list
module H' = {
proc o(x : gtag) = {
var r;
r <@ H.o(x);
logH <- (x,r)::logH;
return r;
}
}
module G' = {
proc o(x : htag) = {
var r;
r <@ G.o(x);
logG <- (x,r)::logG;
return r;
}
}
module S = {
proc dec(c : gtag * htag): ptxt option = {
var sg, s, g;
var m <- None;
(* Could probably reduce asymptotic and proof complexity here. *)
sg <- extract2 (fun (sh : gtag * htag) (rg : htag * gtag)=>
let (s,h) = sh in
let (r,g) = rg in
c = f pk (s,r + h)
/\ unpad (s + g) <> None)
logH logG;
if (sg <> None) {
(s,g) <- ((oget sg).`1.`1,(oget sg).`2.`2);
m <- unpad (s + g);
}
return m;
}
}
proc invert(pk' : pkey, c : gtag * htag) = {
var m0, m1;
logH <- [];
logG <- [];
pk <- pk';
(m0,m1) <@ A(H,G,S).choose(pk);
A(H,G,S).guess(c);
return logH;
}
}.
*)
section.
declare module A <: CCA_ADV {H, G,(* I,*) IND_CCA}.
axiom A_choose_ll (H <: AH {A}) (G <: AG {A}) (S <: APKE {A}):
islossless S.dec => islossless G.o => islossless H.o =>
islossless A(H,G,S).choose.
axiom A_guess_ll (H <: AH {A}) (G <: AG {A}) (S <: APKE {A}):
islossless S.dec => islossless G.o => islossless H.o =>
islossless A(H,G,S).guess.
local module Game0 (H : Ht) (G : Gt) = {
var sk: skey
var c': gtag * htag
module Sc = {
proc dec(c : gtag * htag) = {
var r <- None;
r <@ OAEP(H,G).dec(sk,c);
return r;
}
}
module Sg = {
proc dec(c : gtag * htag) = {
var r <- None;
if (c <> c') {
r <@ OAEP(H,G).dec(sk,c);
}
return r;
}
}
proc main() = {
var pk, m0, m1, b, b', r, s, t;
H.init();
G.init();
(pk,sk) <$ dkeys;
(m0,m1) <@ A(H,G,Sc).choose(pk);
b <$ {0,1};
r <$ dhtag;
s <@ G.o(r);
s <- s + pad (b?m0:m1);
t <@ H.o(s);
t <- t + r;
c' <- f pk (s,t);
b' <@ A(H,G,Sg).guess(c');
return b = b';
}
}.
local lemma eq_INDCCA_Game0 (H <: Ht {A, Game0, IND_CCA})
(G <: Gt {A, H, Game0, IND_CCA}):
equiv [IND_CCA(H,G,OAEP,A).main ~ Game0(H,G).main:
={glob A, glob H, glob G} ==> ={res}].
proof.
proc; inline OAEP(H,G).enc.
sim: (={glob H, glob G, b, b'}).
swap{1} 7 2; wp.
by sim: (={glob A, glob H, glob G, b, s, r, m0, m1} /\ pk0{1} = pk{2} /\ ={sk}(IND_CCA,Game0)).
qed.
local module Game1 = {
var sk : skey
var r : htag
var c' : gtag * htag
module Sc = {
proc dec(c : gtag * htag) = {
var p <- None;
p <@ OAEP(H,G).dec(sk,c);
return p;
}
}
module Sg = {
proc dec(c : gtag * htag) = {
var p <- None;
if (c <> c') {
p <@ OAEP(H,G).dec(sk,c);
}
return p;
}
}
proc main() = {
var pk, m0, m1, b, b', s, t;
H.init();
G.init();
(pk,sk) <$ dkeys;
r <$ dhtag;
s <$ dgtag;
(m0,m1) <@ A(H,G,Sc).choose(pk);
t <@ H.o(s);
t <- t + r;
c' <- f pk (s,t);
b <$ {0,1};
b' <@ A(H,G,Sg).guess(c');
return b = b';
}
}.
(** TODO: This is generic: move it somewhere more appropriate
after seeing if it can't be generalized further. **)
local lemma eq_upto_G_o P:
equiv [G.o ~ G.o:
(forall x, x \in dom G.m{2} => !P x) /\
={x} /\
eq_except G.m{1} G.m{2} P
==> (forall x, x \in dom G.m{2} => !P x) =>
={res} /\
eq_except G.m{1} G.m{2} P].
proof.
proc; auto=> /> &1 &2 not_bad eqe g _; split.
+ rewrite !getP_eq dom_set eq_except_set=> //= x_notin_m2 x_in_m1 /(_ x{2}).
rewrite in_fsetU in_fset1=> //= x_notin_P.
have /iffLR /(_ eqe x{2} x_notin_P) eq_mx:= eq_exceptP G.m{1} G.m{2} P.
by move: x_in_m1 x_notin_m2; rewrite !in_dom eq_mx.
rewrite !getP_eq oget_some=> ^ + /not_bad x_notin_P.
have /iffLR /(_ eqe x{2} x_notin_P) eq_mx:= eq_exceptP G.m{1} G.m{2} P.
by rewrite !in_dom eq_mx.
qed.
(** TODO: This should be easier to deduce from the
above--rewrite in pre/post, maybe based on conseq? **)
local lemma eq_upto1_G_o r:
equiv [G.o ~ G.o:
!(r \in dom G.m){2} /\
={x} /\
eq_except G.m{1} G.m{2} (pred1 r)
==> !(r \in dom G.m){2} =>
={res} /\
eq_except G.m{1} G.m{2} (pred1 r)].
proof. by conseq (eq_upto_G_o (pred1 r))=> /#. qed.
local equiv eq_upto_OAEP_dec: OAEP(H,G).dec ~ OAEP(H,G).dec:
!(Game1.r \in dom G.m){2} /\
={glob H, c, sk} /\
={sk, c'}(Game0,Game1) /\
eq_except G.m{1} G.m{2} (pred1 Game1.r{2})
==> !(Game1.r \in dom G.m){2} =>
={glob H, res} /\
={sk, c'}(Game0,Game1) /\
eq_except G.m{1} G.m{2} (pred1 Game1.r{2}).
proof.
proc; exists * Game1.r{2}; elim *=> r.
wp; call (eq_upto1_G_o r).
wp; call H_o_eq.
by auto=> /#.
qed.
local lemma OAEP_dec_ll (H <: Ht) (G <: Gt):
islossless H.o =>
islossless G.o =>
islossless OAEP(H,G).dec.
proof.
by move=> H_o_ll G_o_ll; proc; wp; call G_o_ll; wp; call H_o_ll; auto.
qed.
local equiv eq_Game0_Game1: Game0(H,G).main ~ Game1.main:
={glob A} ==> !(Game1.r \in dom G.m){2} => ={res}.
proof.
proc; swap{2} [4..5] 1. swap{2} 10 -5.
call (: Game1.r \in dom G.m,
={glob H} /\
={sk, c'}(Game0,Game1) /\
eq_except G.m{1} G.m{2} (pred1 Game1.r{2})).
+ exact/A_guess_ll.
+ by proc; sp; if=> //=; call eq_upto_OAEP_dec; auto=> /#.
+ by move=> _ _; proc; sp; if=> //=; call (OAEP_dec_ll H G (H_o_ll dhtag_ll) (G_o_ll dgtag_ll)).
+ move=> _; conseq (: true) (: Game1.r \in dom G.m ==> Game1.r \in dom G.m)=> //=.
+ by proc; inline *; sp; if=> //=; auto=> />; smt(dom_set in_fsetU).
by proc; sp; if=> //=; call (OAEP_dec_ll H G (H_o_ll dhtag_ll) (G_o_ll dgtag_ll)).
+ by proc *; exists * Game1.r{2}; elim *=> r; call (eq_upto1_G_o r).
+ move=> _ _; exact/(G_o_ll dgtag_ll).
+ move=> _; conseq (G_o_ll dgtag_ll) (: Game1.r \in dom G.m ==> Game1.r \in dom G.m)=> //=.
by proc; auto=> />; smt(dom_set in_fsetU).
+ by conseq H_o_eq.
+ move=> _ _; exact/(H_o_ll dhtag_ll).
+ by move=> />; conseq (H_o_ll dhtag_ll) (: true).
inline H.o G.o; wp; rnd; wp.
rnd (fun x=> x + pad (if b then m0 else m1){2})
(fun x=> x - pad (if b then m0 else m1){2}).
auto.
call (: ={glob H, glob G} /\ ={sk}(Game0,Game1)).
+ by sim.
+ by sim.
+ by sim.
inline *; auto=> /> [pk sk] vk [m0 m1] /= Gm1 Gm2 b _ r _; split=> [g _ /=|_].
+ by rewrite subrK.
split=> [g _|_ g _].
+ exact/dgtag_funi.
rewrite dgtag_fu /= -addrA subrr addr0 /= !getP /= oget_some /=.
by rewrite set_eq_except /= /#.
qed.
local lemma pr_INDCCA_Game1 &m:
Pr[IND_CCA(H,G,OAEP,A).main() @ &m: res] - 1%r/2%r
<= Pr[Game1.main() @ &m: Game1.r \in dom G.m].
proof.
have ->: Pr[IND_CCA(H,G,OAEP,A).main() @ &m: res]
= Pr[Game0(H,G).main() @ &m: res].
+ by byequiv (eq_INDCCA_Game0 H G).
rewrite StdOrder.RealOrder.ler_subl_addl.
have <-: Pr[Game1.main() @ &m: res] = 1%r/2%r.
+ byphoare=> //=; proc; swap 10 1; rnd (pred1 b').
conseq (: true)=> //=.
+ by move=> b'; rewrite dbool1E.
call (A_guess_ll H G (<: Game1.Sg) _ _ _).
+ proc; sp; if=> //=; call (: true)=> //=.
by wp; call (G_o_ll dgtag_ll); wp; call (H_o_ll dhtag_ll); auto.
+ exact/(G_o_ll dgtag_ll).
+ exact/(H_o_ll dhtag_ll).
auto; call (H_o_ll dhtag_ll).
call (A_choose_ll H G (<: Game1.Sc) _ _ _).
+ proc; call (: true); auto=> //=.
by wp; call (G_o_ll dgtag_ll); wp; call (H_o_ll dhtag_ll); auto.
+ exact/(G_o_ll dgtag_ll).
+ exact/(H_o_ll dhtag_ll).
auto; call G_init_ll; auto; call H_init_ll; auto=> />.
by rewrite dkeys_ll dhtag_ll dgtag_ll.
by byequiv eq_Game0_Game1=> /#.
qed.
local module Game1_1 (A : CCA_ADV) = {
var sk : skey
var r : htag
var c' : gtag * htag
var bad: bool
var bad': bool
module S = {
proc dec(c : gtag * htag) = {
var s, t, r, g;
var p <- None;
(s,t) <- fi sk c;
g <$ dgtag;
if (s \in dom H.m) {
r <@ H.o(s);
r <- r + t;
if (r \in dom G.m) {
g <- oget G.m.[r];
p <- unpad (g + s);
} else {
G.m.[r] <- g;
bad <- bad \/ unpad (g + s) <> None;
}
} else {
r <@ H.o(s);
r <- r + t;
bad' <- bad' \/ r \in dom G.m;
if (!r \in dom G.m) {
G.m.[r] <- g;
bad <- bad \/ unpad (g + s) <> None;
}
}
return p;
}
}
module Sg = {
proc dec(c : gtag * htag) = {
var p <- None;
if (c <> c') {
p <@ S.dec(c);
}
return p;
}
}
proc main() = {
var pk, m0, m1, b, b', s, t;
bad <- false;
bad' <- false;
G.init();
H.init();
(pk,sk) <$ dkeys;
r <$ dhtag;
s <$ dgtag;
(m0,m1) <@ A(H,G,S).choose(pk);
t <@ H.o(s);
t <- t + r;
c' <- f pk (s,t);
b <$ {0,1};
b' <@ A(H,G,Sg).guess(c');
return b = b';
}
}.
local equiv eq_Game1_Game1_1: Game1.main ~ Game1_1(A).main:
={glob A} ==> !Game1_1.bad{2} /\ !Game1_1.bad'{2}=> ={res}.
proof.
proc; inline *.
call (: Game1_1.bad \/ Game1_1.bad',
={glob G, glob H}
/\ ={sk, c'}(Game1,Game1_1)).
+ exact/A_guess_ll.
+ proc; sp; if=> //=; inline OAEP(H,G).dec Game1_1(A).S.dec.
sp; case: ((s \in dom H.m){2}).
+ rcondt{2} 2; first by auto.
swap{2} 1 2.
seq 2 2: ( !Game1_1.bad{2}
/\ !Game1_1.bad'{2}
/\ ={glob G, glob H, s, t, p, r}
/\ ={sk, c'}(Game1,Game1_1)
/\ p0{2} = p{2}
/\ p{2} = None).
+ by inline *; auto=> /> &1 &2 <- [#] 2!-> /#.
case: ((r \in dom G.m){2}).
+ rcondt{2} 2; first by auto.
inline G.o; rcondf{1} 3; first by auto.
by auto.
rcondf{2} 2; first by auto.
inline G.o; rcondt{1} 3; first by auto.
auto=> /> &2 not_bad not_bad' r_notin_mG g _.
by rewrite not_bad not_bad' getP_eq.
rcondf{2} 2; first by auto.
swap{2} 1 2.
inline *; auto=> /> &1 &2 <- [#] ->> ->> /> /negb_or [] -> -> _ _ h _ g _.
by rewrite !getP_eq=> ->.
+ by move=> _ _; proc; sp; if=> //=; call (OAEP_dec_ll H G (H_o_ll dhtag_ll) (G_o_ll dgtag_ll)).
+ move=> />; conseq (: true) (: Game1_1.bad \/ Game1_1.bad')=> //=.
+ proc; inline *; sp; if=> //=.
+ seq 4: (Game1_1.bad \/ Game1_1.bad'); first by auto.
by if=> //=; wp; auto=> /> &hr [|] ->.
proc; inline *; sp; if=> //=.
+ seq 4: true=> //=; first by auto; rewrite dgtag_ll.
by if=> //=; auto; rewrite dhtag_ll.
+ by proc; inline *; auto.
+ by move=> _ _; exact/(G_o_ll dgtag_ll).
+ by move=> />; conseq (G_o_ll dgtag_ll).
+ by proc; inline *; auto.
+ by move=> _ _; exact/(H_o_ll dhtag_ll).
+ by move=> _; conseq (H_o_ll dhtag_ll).
auto.
call (: Game1_1.bad \/ Game1_1.bad',
={glob G, glob H}
/\ ={sk}(Game1,Game1_1)).
+ exact/A_choose_ll.
+ proc; inline OAEP(H,G).dec Game1_1(A).S.dec.
sp; case: ((s \in dom H.m){2}).
+ rcondt{2} 2; first by auto.
swap{2} 1 2.
seq 2 2: ( !Game1_1.bad{2}
/\ !Game1_1.bad'{2}
/\ ={glob G, glob H, s, t, r}
/\ ={sk}(Game1,Game1_1)
/\ p{2} = None).
+ by inline *; auto=> /> &1 &2 <- [#] 2!-> /#.
case: ((r \in dom G.m){2}).
+ rcondt{2} 2; first by auto.
inline G.o; rcondf{1} 3; first by auto.
by auto=> />; rewrite dgtag_ll.
rcondf{2} 2; first by auto.
inline *; auto=> /> &2 not_bad not_bad' r_notin_mG g _.
by rewrite not_bad not_bad' getP_eq.
rcondf{2} 2; first by auto.
swap{2} 1 2.
inline *; auto=> /> &1 &2 <- [#] ->> ->> /> /negb_or [#] -> -> _ h _ g _.
by rewrite !getP_eq=> ->.
+ by move=> _ _; proc; call (OAEP_dec_ll H G (H_o_ll dhtag_ll) (G_o_ll dgtag_ll)); auto.
+ move=> />; conseq (: true) (: Game1_1.bad \/ Game1_1.bad')=> //=.
+ proc; inline *; seq 3: (Game1_1.bad \/ Game1_1.bad'); first by auto.
by if=> //=; auto=> /> &hr [|] ->.
proc; inline *.
+ seq 3: true=> //=; first by auto; rewrite dgtag_ll.
by if=> //=; auto; rewrite dhtag_ll.
+ by proc; inline *; auto.
+ by move=> _ _; exact/(G_o_ll dgtag_ll).
+ by move=> _; conseq (G_o_ll dgtag_ll).
+ by proc; inline *; auto.
+ by move=> _ _; exact/(H_o_ll dhtag_ll).
+ by move=> _; conseq (H_o_ll dhtag_ll).
auto=> /> [pk sk] vk h _ g _ [m0L m1L] [m0R m1R] gAL mGL mHL gAR bad bad' mGR mHR upto.
move=> h' _; rewrite !getP_eq /=; split.
+ move=> g_notin_mHR; split.
+ by move=> g_notin_mHL b' _; split=> [/upto [#] <*>|_] /#.
by move=> g_in_MHL b' _; split=> [/upto [#] <*>|_] /#.
move=> g_in_mHR; split.
+ by move=> g_notin_mHL b' _; split=> [/upto [#] <*>|_] /#.
by move=> g_in_mHL b' _; split=> [/upto [#] <*> /=|_] /#.
qed.
lemma mu_dgtag_xor (P : gtag -> bool) (s : gtag):
mu dgtag (fun x=> P (x + s))
= mu dgtag P.
proof.
rewrite {2}(: dgtag = dmap dgtag (fun x=> x + s)).
+ apply/eq_distr=> x; rewrite dmap1E /(\o).
have ->: (fun x0=> x0 + s = x) = pred1 (x - s).
+ by apply/fun_ext=> x0; smt(@GTag).
by apply/dgtag_funi.
by rewrite dmapE /(\o).
qed.
lemma mu_dhtag_xor (P : htag -> bool) (t : htag):
mu dhtag (fun x=> P (x + t))
= mu dhtag P.
proof.
rewrite {2}(: dhtag = dmap dhtag (fun x=> x + t)).
+ apply/eq_distr=> x; rewrite dmap1E /(\o).
have ->: (fun x0=> x0 + t = x) = pred1 (x - t).
+ by apply/fun_ext=> x0; smt(@HTag).
by apply/dhtag_funi.
by rewrite dmapE /(\o).
qed.
local phoare pBad_Game1_1_dec (A <: CCA_ADV):
[Game1_1(A).S.dec: !Game1_1.bad ==> Game1_1.bad]
<= (mu dgtag (fun x=> unpad x <> None)).
proof.
proc.
seq 3: (unpad (g + s) <> None) (mu dgtag (fun x=> unpad x <> None)) 1%r _ 0%r (!Game1_1.bad)=> //=.
+ by auto.
+ rnd (fun x=> unpad (x + s) <> None); auto.
by move=> &hr _; rewrite (@mu_dgtag_xor (fun x=> unpad x <> None)).
by hoare; if=> //=; inline *; auto=> />.
qed.
local module DecA (H : AH) (G : AG) (S : APKE) = {
var qD : int
module S' = {
proc dec(c : gtag * htag) = {
var p;
qD <- qD + 1;
p <@ S.dec(c);
return p;
}
}
proc choose = A(H,G,S').choose
proc guess = A(H,G,S').guess
}.
local lemma prBad_Game1_1 qD &m:
DecA.qD{m} = 0 =>
0 < qD =>
Pr[Game1_1(DecA).main() @ &m: Game1_1.bad /\ DecA.qD <= qD] <= qD%r * mu dgtag (fun x=> unpad x <> None).
proof.
move=> DecAqD_0 gt0_qD.
fel 4 (DecA.qD) (fun x, mu dgtag (fun x=> unpad x <> None)) qD Game1_1.bad []=> //=.
+ rewrite StdBigop.Bigreal.BRA.big_const count_predT size_range /max gt0_qD /=.
rewrite StdRing.RField.mulrC -StdRing.RField.intmulr /intmul -StdRing.RField.AddMonoid.iteropE.
by have ->: !qD < 0 by smt().
+ by inline *; auto=> />; rewrite dom0 fcards0.
+ by proc; call (pBad_Game1_1_dec DecA); auto.
+ by move=> c; proc; call (: true)=> //=; auto=> /#.
+ proc; inline Game1_1(DecA).Sg.dec; sp; if=> //=.
+ by wp; call (pBad_Game1_1_dec DecA); auto.
by hoare; auto; smt(mu_bounded).
+ by move=> c; proc; call (: true)=> //=; auto=> /#.
qed.
local phoare pBad'_Game1_1_dec (A <: CCA_ADV):
[Game1_1(A).S.dec: !Game1_1.bad' ==> Game1_1.bad']
<= (mu dhtag (fun x=> x \in dom G.m)).
proof.
exists* (dom G.m); elim*=> S; conseq (: _ : <=(mu dhtag (fun x=> x \in S)))=> //=.
proc; seq 3: (S = dom G.m /\ !Game1_1.bad') 1%r (mu dhtag (fun x=> x \in S)) 0%r _=> //=.
+ if=> //=.
+ by hoare; [smt(mu_bounded) | conseq (:true)].
seq 1: (r + t \in S) (mu dhtag (fun x=> x + t \in S)) 1%r _ 0%r
(!Game1_1.bad' /\ S = dom G.m)=> //=.
+ by conseq (: true).
+ inline *; rcondt 3; 1:by auto.
wp; rnd (fun x=> x + t \in S); auto=> &m _; split=> [|_ v _] //=.
by rewrite !getP_eq.
+ by hoare; auto=> /> &m -> ->.
by move=> &m _; rewrite (@mu_dhtag_xor (fun x=> x \in S)).
by hoare; auto.
qed.
lemma mu_dhtag_mem (S : htag fset):
mu dhtag (mem S)
= mu1 dhtag witness * (card S)%r.
proof.
elim/fset_ind: S.
+ by rewrite fcards0 /= -(@mu_eq _ pred0) 2:mu0=> // x; rewrite in_fset0.
move=> h S h_notin_S ih; rewrite fcardU fcard1.
have->: S `&` fset1 h = fset0.
+ by apply/fsetP=> x; rewrite in_fsetI in_fset1 in_fset0; case: (x = h).
rewrite fcards0 /= -(@mu_eq _ (predU (mem S) (pred1 h))).
+ by move=> x; rewrite in_fsetU in_fset1.
rewrite mu_or.
have ->: predI (mem S) (pred1 h) = pred0.
+ apply/fun_ext=> x; rewrite /predI /pred1 /pred0; case: (x = h)=> />.
+ by rewrite h_notin_S.
by rewrite mu0 /= ih (@dhtag_funi h witness) /#.
qed.
local module DecGA (H : AH) (G : AG) (S : APKE) = {
var qD : int
var qG : int
module G' = {
proc o(x : htag) = {
var g;
qG <- qG + 1;
g <@ G.o(x);
return g;
}
}
module S' = {
proc dec(c : gtag * htag) = {
var p;
qD <- qD + 1;
p <@ S.dec(c);
return p;
}
}
proc choose = A(H,G',S').choose
proc guess = A(H,G',S').guess
}.
(** FIXME: This is not completely good because of limitations of fel, but gives confidence that a final version would go through, and gives hints as to useful features for fel to have
e.g.: if !P is stable by the oracles (i.e. {!P} o {!P} for all oracles) and P is in the event, then P is a 'free' precondition to all oracle-based proof obligations **)
local lemma prBad'_Game1_1 qD qG &m:
DecGA.qG{m} = 0
=> DecGA.qD{m} = 0
=> 0 < qD
=> 0 < qG
=> Pr[Game1_1(DecGA).main() @ &m: Game1_1.bad' /\ DecGA.qD <= qD /\ card (dom G.m) <= qD + qG]
<= qD%r * (qD + qG)%r * mu1 dhtag witness.
proof.
move=> DecGA_qG0 DecGA_gD0 gt0_qD gt0_qG.
fel 4 (DecGA.qD) (fun x, (qD + qG)%r * mu1 dhtag witness) qD Game1_1.bad' [DecGA(H, G, Game1_1(DecGA).S).S'.dec: (card (dom G.m) <= qD + qG); DecGA(H, G, Game1_1(DecGA).Sg).S'.dec: (card (dom G.m) <= qD + qG)]=> //=.
+ rewrite StdBigop.Bigreal.BRA.big_const count_predT size_range /max gt0_qD /=.
rewrite -StdRing.RField.mulrA (@StdRing.RField.mulrC qD%r).
rewrite -StdRing.RField.intmulr /intmul -StdRing.RField.AddMonoid.iteropE.
by have ->: !qD < 0 by smt().
+ by inline *; auto=> />; rewrite dom0 fcards0.
+ conseq (: _ : <= (mu dhtag (mem (dom G.m)))).
+ move=> &hr [#] _ _ _; rewrite mu_dhtag_mem StdRing.RField.mulrC -le_fromint.
by move: gt0_qG gt0_qD; rewrite -!lt_fromint; smt.
by proc; call (pBad'_Game1_1_dec DecGA); auto.
+ by move=> c; proc; call (: true)=> //=; auto=> /#.
+ admit. (* at the moment, bad can still be set after more than (qD+qG) entries in the ROM... EXCEPT for the fact that the lemma explicitly limits the number of entries to (qD+qG) *)
+ conseq (: _ : <= (mu dhtag (mem (dom G.m)))).
+ move=> &hr [#] _ _ _; rewrite mu_dhtag_mem StdRing.RField.mulrC -le_fromint.
by move: gt0_qG gt0_qD; rewrite -!lt_fromint; smt.
proc; inline Game1_1(DecGA).Sg.dec.
case: (c <> Game1_1.c').
+ rcondt 4; first by auto.
by wp; call (pBad'_Game1_1_dec DecGA); auto.
hoare; first by smt(mu_bounded).
by rcondf 4; auto.
+ by move=> c; proc; call (: true)=> //=; auto=> /#.
admit. (* at the moment, bad can still be set after more than (qD+qG) entries in the ROM... EXCEPT for the fact that the lemma explicitly limits the number of entries to (qD+qG) *)
qed.
local module Game2 (A : CCA_ADV) = {
var sk : skey
var r : htag
var c' : gtag * htag
module S = {
proc dec(c : gtag * htag) = {
var s, t, r, g;
var p <- None;
(s,t) <- fi sk c;
g <$ dgtag;
if (s \in dom H.m) {
r <@ H.o(s);
r <- r + t;
if (r \in dom G.m) {
g <- oget G.m.[r];
p <- unpad (g + s);
} else {
G.m.[r] <- g;
}
} else {
r <@ H.o(s);
r <- r + t;
if (!r \in dom G.m) {
G.m.[r] <- g;
}
}
return p;
}
}
module Sg = {
proc dec(c : gtag * htag) = {
var p <- None;
if (c <> c') {
p <@ S.dec(c);
}
return p;
}
}
proc main() = {
var pk, m0, m1, b, b', s, t;
G.init();
H.init();
(pk,sk) <$ dkeys;
r <$ dhtag;
s <$ dgtag;
(m0,m1) <@ A(H,G,S).choose(pk);
t <@ H.o(s);
t <- t + r;
c' <- f pk (s,t);
b <$ {0,1};
b' <@ A(H,G,Sg).guess(c');
return b = b';
}
}.
local equiv eq1_1__2: Game1_1(A).main ~ Game2(A).main:
={glob A} ==> ={res}.
proof. by sim. qed.
end section.