https://github.com/EasyCrypt/easycrypt
Tip revision: 3cef12a5d08bbaf7b486bd02ca6c194f4f167bea authored by Manuel Barbosa on 10 October 2023, 09:38:20 UTC
Making this branch work with same Why3 version as main
Making this branch work with same Why3 version as main
Tip revision: 3cef12a
FDH_quantum.ec
require import AllCore List Distr DBool DList DProd CHoareTactic FunSamplingLib.
require (* *) T_QROM T_EUF T_PERM.
(* *) import StdOrder RField RealOrder StdBigop Bigreal DInterval.
(* --------------------------------------------------------------------------- *)
clone import T_EUF as EUF.
clone import T_QROM as QROM with
type from <- msg.
clone import DFunBiasedSingle with
type X <- msg,
theory MUFF <- MUFF.
clone include T_PERM with
type pkey <- pkey,
type skey <- skey,
type dom <- sign,
type codom <- hash.
(* --------------------------------------------------------------------------- *)
(* Security definition of existencial unforgeability in the QROM *)
module type Sign_QROM (H:QRO) = {
proc kg() : pkey * skey
proc sign(sk : skey, m : msg) : sign
proc verify(pk : pkey, m : msg, s : sign) : bool
}.
quantum module type AdvEUF_QROM (H:QRO) (S:OrclSign) = {
proc main(pk:pkey) : msg * sign
}.
module Wrap (A:AdvEUF_QROM) (H:QRO) (S:OrclSign) = {
var ch : int
var cs : int
module Hc = {
quantum proc h {x:msg} = {
quantum var h;
ch <- ch+1;
h <@ H.h{x};
return h;
}
}
module Sc = {
proc sign (m:msg) = {
var s;
cs <- cs+1;
s <@ S.sign(m);
return s;
}
}
proc main(pk:pkey) = {
var ms : msg * sign;
ch <- 0; cs <- 0;
ms <@ A(Hc, Sc).main(pk);
return ms;
}
}.
module EUF_QROM (A:AdvEUF_QROM) (S:Sign_QROM) = {
proc main() = {
var b;
QRO.init();
b <@ EUF(Wrap(A, QRO), S(QRO)).main();
return b;
}
}.
(* --------------------------------------------------------------------------- *)
(* Security definition of existencial unforgeability in the QROM *)
module (FDH:Sign_QROM) (H:QRO) = {
proc kg () = {
var k;
k <$ kg;
return k;
}
proc sign(sk:skey, m:msg) = {
var h;
h <@ H.h{m};
return finv sk h;
}
proc verify(pk:pkey, m:msg, s:sign) = {
var h;
h <@ H.h{m};
return h = f pk s;
}
}.
(* --------------------------------------------------------------------------- *)
(* Maximal number of queries to the sign/hash oracle *)
op qs : { int | 0 < qs } as gt0_qs.
op qh : { int | 0 <= qh } as ge0_qh.
op q = qs + qh + 1.
(* --------------------------------------------------------------------------- *)
(* Generic start of the proof used for OW and ClawFree *)
section.
declare module A <: AdvEUF_QROM { -QRO, -EUF, -Wrap}.
declare axiom hoare_bound (H<:QRO{-A,-Wrap}) (S<:OrclSign{-A,-Wrap}) :
hoare [Wrap(A, H, S).main : true ==> Wrap.cs <= qs /\ Wrap.ch <= qh].
hoare hoare_bound1 : Wrap(A, QRO, EUF(Wrap(A, QRO), FDH(QRO)).Os).main :
QRO.ch = 0 /\ size EUF.log = 0 ==>
QRO.ch <= qs + qh /\ size EUF.log <= qs.
proof.
conseq (: true ==> Wrap.cs <= qs /\ Wrap.ch <= qh)
(: QRO.ch = 0 /\ size EUF.log = 0 ==> QRO.ch = Wrap.cs + Wrap.ch /\
size EUF.log = Wrap.cs).
+ smt().
+ proc.
call (: QRO.ch = Wrap.cs + Wrap.ch /\ size EUF.log = Wrap.cs).
+ by proc; inline *; auto => /> *; split; ring.
+ by proc; inline *; auto => /> *; ring.
by auto.
by apply (hoare_bound QRO (<:EUF(Wrap(A, QRO), FDH(QRO)).Os)).
qed.
hoare EUF_QROM_bound : EUF_QROM(A,FDH).main :
true ==> res => ! (EUF.m \in EUF.log) /\ size EUF.log <= qs /\ QRO.ch <= q.
proof.
proc. inline EUF(Wrap(A, QRO), FDH(QRO)).main FDH(QRO).verify QRO.h; wp.
call hoare_bound1.
by inline *;auto => /> /#.
qed.
end section.
abstract theory START.
module EUF_QROM' (A:AdvEUF_QROM) = {
var bf : msg -> bool
proc main(lam_:real) = {
var b;
bf <$ dbfun lam_;
b <@ EUF_QROM(A, FDH).main();
return b /\ (bf EUF.m /\ (forall m', m' \in EUF.log => !bf m') /\ size EUF.log <= qs );
}
}.
section.
declare module A <: AdvEUF_QROM { -QRO, -EUF, - EUF_QROM'}.
declare axiom hoare_bound (H<:QRO{-A, -Wrap}) (S<:OrclSign{-A, -Wrap}) :
hoare[ Wrap(A, H, S).main : true ==> Wrap.cs <= qs /\ Wrap.ch <= qh].
lemma l1 lam &m:
0%r <= lam <= 1%r =>
Pr[EUF_QROM'(A).main(lam) @ &m : res] >=
lam * (1%r - lam)^qs * Pr[EUF_QROM(A,FDH).main() @ &m : res].
proof.
move=> lam_bound.
byphoare (:(glob A){m} = glob A /\ lam_ = lam ==> _) => //.
proc.
swap 1 1.
seq 1 : b Pr[EUF_QROM(A,FDH).main() @ &m : res] (lam * (1%r - lam) ^ qs)
_ 0%r (lam_ = lam /\ (b => !EUF.m \in EUF.log /\ size EUF.log <= qs)).
+ by call (EUF_QROM_bound A hoare_bound); skip => /> /#.
+ call (: (glob A){m} = glob A ==> res); 2: by auto.
bypr => &m0 ?.
byequiv (: ={glob A} ==> ={res}) => //; 1: sim.
+ rnd (fun t => t EUF.m /\ forall (m' : msg), m' \in EUF.log => ! t m').
by skip => /> &hr h /h /> *; apply: pr_dbfun_l_pow.
+ by auto.
smt().
qed.
end section.
end START.
import QROM.MUFF.
(* --------------------------------------------------------------------------*)
op [lossless uniform full] dsign : sign distr.
(* FIXME: this should be provable because f is bijective *)
axiom dhash_dsign h s: mu1 dhash h = mu1 dsign s.
op dfsign : (msg -> sign) distr = dfun (fun _ => dsign).
lemma dfsign_ll: is_lossless dfsign.
proof. apply dfun_ll => ?;apply dsign_ll. qed.
lemma dfsign_uni: is_uniform dfsign.
proof. apply dfun_uni => ?; apply dsign_uni. qed.
lemma dfsign_fu: is_full dfsign.
proof. apply dfun_fu => ?; apply dsign_fu. qed.
hint solve 0 random : dfsign_ll dfsign_uni dfsign_fu.
lemma dfsign_dfhash (hs: msg -> sign) (hh:msg -> hash) : mu1 dfsign hs = mu1 dfhash hh.
proof. by rewrite !dfun1E;apply BRM.eq_big => //= x _; rewrite eq_sym; apply dhash_dsign. qed.
(* --------------------------------------------------------------------------- *)
(* First proof : reducing to claw free *)
(* --------------------------------------------------------------------------- *)
abstract theory FDH_CF.
clone import T_ClawFree.
op lam : real.
axiom lam_bound : 0%r < lam < 1%r.
module B (A:AdvEUF_QROM) = {
import var EUF
var hs : msg -> sign
var bf : msg -> bool
module Os = {
proc sign(m:msg) = {
log <- m :: log;
return hs m;
}
}
proc main(pk:pkey) = {
var m, s;
bf <$ dbfun lam;
hs <$ dfsign;
QRO.h <- (fun m => if bf m then P2.f pk (hs m) else f pk (hs m));
log <- [];
(m,s) <@ A(QRO, Os).main(pk);
return (s, hs m);
}
}.
section.
declare module A <: AdvEUF_QROM { -EUF, -QRO, -B, -Wrap}.
declare axiom hoare_bound (H<:QRO{-A, -Wrap}) (S<:OrclSign{-A, -Wrap}) :
hoare[ Wrap(A, H, S).main : true ==> Wrap.cs <= qs /\ Wrap.ch <= qh].
declare axiom A_ll (H <: QRO{-A}) (S <: OrclSign{-A}) :
islossless S.sign => islossless H.h => islossless A(H, S).main.
local clone import START.
import var EUF.
local lemma l3 &m :
Pr[EUF_QROM'(A).main(lam) @ &m : res] <= Pr[ClawFree(B(A)).main() @ &m : res].
proof.
byequiv => //; proc.
inline *; wp.
call (: (exists m', m' \in log /\ B.bf m'),
(={log, QRO.h} /\ EUF_QROM'.bf{1} = B.bf{2} /\
QRO.h{2} = (fun m => let hs = B.hs{2} m in if B.bf{2} m then P2.f pk{1} hs else f pk{1} hs) /\
(pk,sk){1} \in kg),
(exists m', m' \in log /\ EUF_QROM'.bf m'){1} = (exists m', m' \in log /\ B.bf m'){2}).
+ by apply A_ll.
+ by proc; inline *; auto => />; smt (finv_f).
+ by move=> *; proc; inline *; auto => /> /#.
+ by move=> *; proc; inline *;auto => /> /#.
+ by proc; inline *; auto.
+ by move=> *; proc; inline *; auto => /> /#.
+ by move=> *; proc; inline *; auto.
swap{1} 4 -3; wp.
rnd (fun h => fun m => if B.bf m then P2.finv sk (h m) else finv sk (h m)){2}
(fun hs => fun m => if B.bf m then P2.f pk (hs m) else f pk (hs m)){2}.
auto => /> {&m} k hk t _; split.
+ move=> hs _; apply fun_ext; smt (P2.finv_f finv_f).
smt (dfsign_dfhash P2.f_finv f_finv).
qed.
lemma conclusion &m :
Pr[EUF_QROM(A, FDH).main() @ &m : res] <=
Pr[ClawFree(B(A)).main() @ &m : res] / (lam * (1%r - lam) ^ qs).
proof.
have : lam * (1%r - lam) ^ qs * Pr[EUF_QROM(A, FDH).main() @ &m : res] <=
Pr[ClawFree(B(A)).main() @ &m : res].
+ move: (l1 A hoare_bound lam &m) lam_bound (l3 &m) => /#.
rewrite -ler_pdivl_mull; smt(expr_gt0 lam_bound).
qed.
end section.
end FDH_CF.
(* The minimun of the above bound is found for lam = 1/(qs+1) *)
abstract theory FDH_CF_instanciate.
clone include FDH_CF with
op lam = 1%r/(qs+1)%r
proof lam_bound by smt(gt0_qs).
end FDH_CF_instanciate.
(* --------------------------------------------------------------------------- *)
(* Second proof : reducing to OW, proof based on semi-constant *)
(* --------------------------------------------------------------------------- *)
abstract theory FDH_OW_semi_constant.
clone import T_OW with
type init <- real,
op dcodom <- dhash.
module B(A:AdvEUF_QROM) : AdvOW = {
import var EUF
var hs : msg -> sign
var bf : msg -> bool
module Os = {
proc sign(m:msg) = {
log <- m :: log;
return hs m;
}
}
proc main(pk:pkey, y:hash, lam:real) : sign = {
var m,s;
bf <$ dbfun lam;
hs <$ dfsign;
QRO.h <- fun m => if bf m then y else f pk (hs m);
EUF.log <- [];
(m,s) <@ A(QRO, Os).main(pk);
return s;
}
}.
clone import SemiConstDistr with
op k <- qs.
section OW.
declare module A <: AdvEUF_QROM { -QRO, -EUF, -B, -Wrap, -SCD}.
declare axiom A_ll (H <: QRO{-A}) (S <: OrclSign{-A}) :
islossless S.sign => islossless H.h => islossless A(H, S).main.
declare axiom hoare_bound (H<:QRO{-A, -Wrap}) (S<:OrclSign{-A, -Wrap}) :
hoare[ Wrap(A, H, S).main : true ==> Wrap.cs <= qs /\ Wrap.ch <= qh].
local clone import START.
local module (ASCD:AdvSCD) (H:QRO) = {
import var EUF
module Os = {
proc sign(m:msg) = {
var hm;
log <- m::log;
hm <@ H.h{m};
return finv sk hm;
}
}
proc main() = {
var b, s, hm;
(pk, sk) <$ kg;
log <- [];
(m,s) <@ Wrap(A, H, Os).main(pk);
hm <@ H.h{m};
b <- hm = f pk s;
return (b, m, log);
}
}.
import var EUF B SCD.
local lemma l3 lam &m :
Pr[EUF_QROM'(A).main(lam) @ &m : res] =
Pr[SCD(ASCD)._F0(lam) @ &m : res].
proof.
byequiv => //.
proc; inline *; wp.
call (: ={QRO.h, sk, log}).
+ by proc ; inline *; auto.
+ by proc; inline *;auto => />;skip => />.
swap{2} 4 1; auto; rnd{2}; auto => /> /#.
qed.
local hoare hoare_bound1 : Wrap(A, QRO, ASCD(QRO).Os).main :
QRO.ch = 0 ==>
QRO.ch <= qs + qh.
proof.
conseq (: true ==> Wrap.cs <= qs /\ Wrap.ch <= qh)
(: QRO.ch = 0 ==> QRO.ch = Wrap.cs + Wrap.ch).
+ smt().
+ proc.
call (: QRO.ch = Wrap.cs + Wrap.ch).
+ by proc; inline *; auto => /> *; ring.
+ by proc; inline *; auto => /> *; ring.
by auto.
by apply (hoare_bound QRO (<:ASCD(QRO).Os)).
qed.
local lemma l4 lam &m :
0.0 <= lam <= 1.0 =>
`| Pr[SCD(ASCD)._F0(lam) @ &m : res] -
Pr[SCD(ASCD)._F1(lam) @ &m: res] |
<= (2%r * q%r + qs%r + 1%r)^4/ 6%r * lam^2.
proof.
move=> lam_bound.
apply (advantage q lam ASCD &m lam_bound _).
proc.
inline ASCD(QRO).main QRO.h; wp.
call hoare_bound1; inline *; auto => /> /#.
qed.
local lemma l5 lam &m :
Pr[SCD(ASCD)._F1(lam) @ &m: res] <= Pr[OW(B(A)).main(lam) @ &m : res].
proof.
byequiv (: ={glob A} /\ p{1} = i{2} ==> res{1} => res{2}) => //.
proc; inline *; wp.
(* Proof using upto bad *)
call (: (exists m', m' \in log /\ B.bf m'),
(={log, QRO.h} /\ SCD.bf{1} = B.bf{2} /\
(forall m', !SCD.bf{1} m' => QRO.h{1} m' = f pk{1} (hs{2} m')) /\
(pk,sk){1} \in kg),
(exists m', m' \in log /\ SCD.bf m'){1} =
(exists m', m' \in log /\ B.bf m'){2}
).
+ exact A_ll.
+ by proc; inline *; auto => />; smt(finv_f).
+ by move=> *; proc; inline *; auto => /> /#.
+ by move=> *; proc; inline *; auto => /> /#.
+ proc; inline *; auto => />; skip => /#.
+ by move=> *; proc; inline *; auto => /> /#.
+ by move=> *; proc; inline *; auto => /> /#.
swap{1}6 -5. swap{1} 5 -3; wp.
rnd (fun (h:msg -> hash) => fun m => finv EUF.sk{1} (h m))
(fun (h:msg -> sign) => fun m => f EUF.pk{1} (h m)).
rewrite /good; auto => /> &2 *; split.
+ by move=> *; apply fun_ext => ?; smt(finv_f).
smt(dfsign_dfhash f_finv finv_f).
qed.
(* Fixed: not sure if this is the best bound *)
lemma conclusion lam &m :
0.0 < lam < 1.0 =>
Pr[EUF_QROM(A,FDH).main() @ &m : res] <=
Pr[OW(B(A)).main(lam) @ &m : res] / (lam * (1%r - lam) ^ qs) +
(2*qh + 3*qs + 3)%r^4/ 6%r * lam / (1%r - lam) ^ qs.
proof.
move=> lam_bound.
have : lam * (1%r - lam) ^ qs * Pr[EUF_QROM(A,FDH).main() @ &m : res] <=
Pr[OW(B(A)).main(lam) @ &m : res] + (2%r * q%r + qs%r + 1%r)^4/ 6%r * lam^2.
+ by move: (l1 A hoare_bound lam &m) lam_bound (l3 lam &m) (l4 lam &m) (l5 lam &m) => /#.
rewrite /q !fromintD -ler_pdivl_mull; 1: smt (expr_gt0).
move=> h; apply/(ler_trans _ _ _ h)/lerr_eq; field => //; smt (expr_gt0).
qed.
(* Proof of the Zhandry's bound *)
lemma conclusion_z &m :
let eps = Pr[EUF_QROM(A,FDH).main() @ &m : res] in
let k = ((2.0*qh%r + 3.0 * qs%r + 3.0)^4 + 6.0*qs%r)/6.0 in
let lam = eps / (2.0 * k) in
eps ^ 2 / (4.0 * k) <= Pr[OW(B(A)).main(lam) @ &m : res].
proof.
move=> eps k lam.
case: (Pr[EUF_QROM(A, FDH).main() @ &m : res] = 0.0).
+ by move=> -> /=; rewrite expr0z /=; smt(mu_bounded).
rewrite -/eps => h0eps.
apply: ler_trans (l5 lam &m).
have : Pr[SCD(ASCD)._F0(lam) @ &m : res] - (2%r * q%r + qs%r + 1%r)^4/ 6%r * lam^2 <=
Pr[SCD(ASCD)._F1(lam) @ &m : res].
+ by have := l4 lam &m; smt (mu_bounded gt0_qs ge0_qh expr_ge0).
apply: ler_trans.
rewrite -(l3 lam &m).
apply (ler_trans
(lam * (1%r - lam) ^ qs * eps - (2%r * q%r + qs%r + 1%r)^4 / 6%r * lam ^ 2)); last first. + have := l1 A hoare_bound lam &m _ ; by smt(mu_bounded gt0_qs ge0_qh expr_ge0).
apply (@ler_trans
(lam * eps - qs%r * lam ^2 - (2%r * q%r + qs%r + 1%r)^4 / 6%r * lam ^ 2)).
have H: forall n qq, 0 < n => 0 <= qq => qq%r^n = qq%r^(n-1)*qq%r by smt(exprS).
move : gt0_qs ge0_qh => *; have Hq : 0 < q by smt().
+ apply lerr_eq; rewrite /lam /q /k !fromintD; field => //;
do !(rewrite H 1,2:/# /= ?expr0 /=); 1,2: by smt().
rewrite ler_add2.
apply (ler_trans (lam * (1%r -qs%r*lam) * eps)); last first.
+ apply ler_wpmul2r; 1: smt(mu_bounded).
apply ler_wpmul2l; 1: smt(mu_bounded gt0_qs ge0_qh expr_ge0).
smt (le_binomial mu_bounded gt0_qs ge0_qh expr_ge0).
rewrite expr2.
have /# : qs%r * (lam * lam) * eps <= qs%r * (lam * lam).
rewrite -{2}(mulr1 (qs%r * (lam * lam))).
by apply ler_wpmul2l; smt(mu_bounded gt0_qs ge0_qh).
qed.
end section OW.
end FDH_OW_semi_constant.
(* --------------------------------------------------------------------------- *)
(* Third proof : reducing to OW, proof based on small-range *)
(* --------------------------------------------------------------------------- *)
(*
abstract theory FDH_OW_SmallRange.
(* FIXME: be undep of lam *)
clone import T_OW with
type init <- int,
op dcodom <- dhash.
clone import SmallRange.
module B (A:AdvEUF_QROM) : AdvOW = {
import var EUF
var hs : msg -> sign
module Os = {
proc sign(m:msg) = {
log <- m :: log;
return hs m;
}
}
proc main(pk_:pkey, y:hash, r:int) = {
var s, fr, rs2, rs, rh;
log <- [];
rs2 <$ dlist dsign (r-1);
rs <- witness :: rs2;
rh <- y :: map (f pk_) rs2;
fr <$ difun r;
hs <- fun x => nth witness rs (fr x);
QRO.h <- fun x => nth witness rh (fr x);
(m,s) <@ A(QRO, Os).main(pk_);
return s;
}
}.
section OW.
declare module A : AdvEUF_QROM { -QRO, -EUF, -B, -SR, -Wrap}.
axiom hoare_bound (H<:QRO{-A, -Wrap}) (S<:OrclSign{-A, -Wrap}) :
hoare[ Wrap(A, H, S).main : true ==> Wrap.cs <= qs /\ Wrap.ch <= qh].
axiom A_ll (H <: QRO{-A}) (S <: OrclSign{-A}) :
islossless S.sign => islossless H.h => islossless A(H, S).main.
module EUF_QROM' (A:AdvEUF_QROM) = {
proc main(r_:int) = {
var b;
SR.fr <$ difun r_;
b <@ EUF_QROM(A, FDH).main();
return b /\ (SR.fr EUF.m = 0 /\ (forall m', m' \in EUF.log => SR.fr m' <> 0) );
}
}.
lemma l1 r &m:
1 <= r => let lam = inv r%r in
Pr[EUF_QROM'(A).main(r) @ &m : res] >=
lam * (1%r - lam)^qs * Pr[EUF_QROM(A,FDH).main() @ &m : res].
proof.
move=> hr lam.
byphoare (:(glob A){m} = glob A /\ r_ = r ==> _) => //.
proc.
swap 1 1.
seq 1 : b Pr[EUF_QROM(A,FDH).main() @ &m : res] (lam * (1%r - lam) ^ qs)
_ 0%r (r_ = r /\ (b => !EUF.m \in EUF.log /\ size EUF.log <= qs)).
+ by call (EUF_QROM_bound A hoare_bound); skip => /> /#.
+ call (: (glob A){m} = glob A ==> res); 2: by auto.
bypr => &m0 ?.
byequiv (: ={glob A} ==> ={res}) => //; 1: sim.
+ rnd (fun t => t EUF.m = 0 /\ forall (m' : msg), m' \in EUF.log => t m' <> 0).
skip => /> &hr h /h /> *.
have /= h1 := dfunE_mem_le lam (fun _ => [0..r-1]) [EUF.m{hr}] EUF.log{hr} (fun _ i => i = 0) _ _ _.
+ by rewrite /= dinter_ll /#.
+ by rewrite /= dinter1E /#.
+ smt().
have -> :
(fun (t : msg -> int) => t EUF.m{hr} = 0 /\ forall (m' : msg), m' \in EUF.log{hr} => t m' <> 0) =
(fun (f : msg -> int) =>
(forall (x : msg), x = EUF.m{hr} => f x = 0) /\ forall (x : msg), x \in EUF.log{hr} => f x <> 0).
+ by apply fun_ext => z /#.
apply: ler_trans h1.
by rewrite expr1 ler_wpmul2l 1:/# ler_wiexpn2l; smt(size_ge0).
+ by auto.
smt().
qed.
local module (ASRr:AdvSRr) (H:SRr) = {
var bad : bool
import var EUF
module Os = {
proc sign(m:msg) = {
var i, hm;
log <- m::log;
(i,hm) <@ H.h{m};
bad <- bad \/ i = 0;
return finv sk hm;
}
}
module H' = {
quantum proc h{x:msg} : hash = {
quantum var i, hx;
(i,hx) <@ H.h{x};
return hx;
}
}
proc main(r_:int) = {
var s, i, hm, b;
(pk, sk) <$ kg;
log <- [];
bad <- false;
(m,s) <@ Wrap(A, H', Os).main(pk);
(i,hm) <@ H.h{m};
bad <- bad \/ i <> 0;
b <- hm = f pk s /\ !m \in log /\ !bad;
return b;
}
}.
import var EUF B SR.
local lemma l2 r &m :
0 < r =>
Pr[EUF_QROM'(A).main(r) @ &m : res] =
Pr[IND_SRr(SROr, ASRr).main(r) @ &m : res].
proof.
move=> hr; byequiv => //.
proc; inline *; wp.
call (: ={QRO.h, SR.fr, log, EUF.sk} /\ (ASRr.bad = exists m', m' \in log /\ SR.fr m' = 0){2}).
+ by proc ; inline *; auto => /> /#.
+ by proc; inline *;auto => />;skip => />.
auto => /> /#.
qed.
local hoare hoare_bound1 : Wrap(A, ASRr(SROr).H', ASRr(SROr).Os).main :
QRO.ch = 0 ==>
QRO.ch <= qs + qh.
proof.
conseq (: true ==> Wrap.cs <= qs /\ Wrap.ch <= qh)
(: QRO.ch = 0 ==> QRO.ch = Wrap.cs + Wrap.ch).
+ smt().
+ proc.
call (: QRO.ch = Wrap.cs + Wrap.ch).
+ by proc; inline *; auto => /> *; ring.
+ by proc; inline *; auto => /> *; ring.
by auto.
by apply (hoare_bound (<:ASRr(SROr).H') (<: ASRr(SROr).Os)).
qed.
local lemma l3 r &m :
0 < r =>
`| Pr[IND_SRr(SROr, ASRr).main(r) @ &m : res] -
Pr[IND_SRr(SRr , ASRr).main(r) @ &m : res] |
<= (54 * q^3)%r / r%r.
proof.
move=> h0r; apply (advantage_r q r ASRr &m h0r _).
proc; inline ASRr(SROr).main SROr.h.
wp; call hoare_bound1; inline *; auto => /> /#.
qed.
local clone import DList.Program with
type t <- hash,
op d <- dhash.
local lemma l4 r0_ &m :
0 < r0_ =>
Pr[IND_SRr(SRr , ASRr).main(r0_) @ &m : res] <= Pr[OW(B(A)).main(r0_) @ &m : res].
proof.
move=> hr;
byequiv (: ={glob A} /\ r{1} = r0_ /\ ={arg} ==> res{1} => res{2}) => //.
proc; inline *; wp; symmetry.
(* Proof using upto bad *)
call (_: ASRr.bad,
={QRO.h, EUF.log} /\
(forall m, SR.fr{2} m <> 0 => B.hs{1} m = (finv EUF.sk (QRO.h m)){2})) => //.
+ by apply A_ll.
+ by proc; inline *; auto => /> /#.
+ by move=> *; islossless.
+ by move=> *; proc; inline *; auto => /> /#.
+ by proc; inline *; auto.
+ by move=> *; islossless.
+ by move=> *; proc; inline *; auto.
swap{2} 7 -5.
swap{1} [8..9] 1. swap{1} 5 -4. swap{1} [4..6] 2.
wp; sp; rnd => /=.
seq 1 1 : (#pre /\ ={sk} /\ pk{1} = EUF.pk{2} /\ ((pk,sk) \in kg){1}).
+ by rnd; skip => /> [].
conseq (: rh{2} = (y :: map (f pk) rs2){1} /\ size rs2{1} = r{1} - 1 ).
+ move=> /> &2 hpk rs2 y hsize fr /dfun_supp /= hfr; split.
+ move=> m' hm'; have /DInterval.supp_dinter hfrm' := hfr m'.
rewrite (nth_map witness witness (f EUF.pk{2})); smt(finv_f).
smt (finv_f f_finv mapP).
transitivity * {2} { rh <@ Sample.sample(r0); } => //; 1:smt(); last by inline *;auto.
transitivity {2} { rh <@ SampleCons.sample(r0); }
(r{1} = r0{2} /\ 0 <= r{1} - 1 /\ ={sk} /\ pk{1} = EUF.pk{2} /\ (pk,sk){1} \in kg ==>
rh{2} = (y :: map (f pk) rs2){1} /\ (size rs2 = r - 1){1})
(={r0} /\ 0 < r0{1} ==> ={rh}) => //;1: smt(); last first.
+ by symmetry; call Sample_SampleCons_eq; skip.
inline *; swap{2} 3 -1; wp; sp.
rnd (map (f pk{1})) (map (finv sk{1})); rnd; skip => />.
move=> &2 hr0 hk y hy; split.
+ move=> rs2 ?; rewrite -map_comp -{1}(map_id rs2); apply eq_map.
by move=> x; rewrite /(\o) (f_finv _ _ hk).
move=> _; split.
+ move=> rs hrs. rewrite !dlist1E 1,2:// size_map.
case: (r0{2} - 1 = size rs) => // hsz.
rewrite BRM.big_map; apply BRM.eq_big => //= h _.
by rewrite /(\o); apply dhash_dsign.
move=> _ rs2; rewrite supp_dlist 1:// => -[] ^ hs -> /= ?; split.
+ by rewrite supp_dlist 1:// size_map hs /= allP => *; apply dhash_fu.
move=> ?; rewrite -map_comp -{1}(map_id rs2); apply eq_map.
by move=> x; rewrite /(\o) (finv_f _ _ hk).
qed.
lemma conclusion r0 &m :
1 < r0 => let lam = inv r0%r in
Pr[EUF_QROM(A,FDH).main() @ &m : res] <=
Pr[OW(B(A)).main(r0) @ &m : res] / (lam * (1%r - lam) ^ qs) +
(54 * q^3)%r / (1%r - lam) ^ qs.
proof.
move=> h1r0 lam.
have h0r0 : 0 < r0 by smt().
have : lam * (1%r - lam) ^ qs * Pr[EUF_QROM(A,FDH).main() @ &m : res] <=
Pr[OW(B(A)).main(r0) @ &m : res] + (54 * q^3)%r * lam.
+ have := l1 r0 &m _; 1: smt().
by move: (l2 r0 &m h0r0) (l3 r0 &m h0r0) (l4 r0 &m h0r0) => /#.
have lam_bound : 0.0 < lam < 1.0 by smt().
rewrite -ler_pdivl_mull; 1: smt (expr_gt0).
move=> h; apply/(ler_trans _ _ _ h)/lerr_eq; field => //; smt (expr_gt0).
qed.
(* The minimum is found in r0 = qs + 1 *)
*)
(* We can prove the Zhandry bound *)
(* Remark Zhandry use r = 2k / eps, this is not a integer.
We are forced to use the floor and this make the final bound a little less good. *)
abstract theory FDH_OW_small_range.
clone import T_OW with
type init <- int,
op dcodom <- dhash.
clone import SmallRange.
import DFunSmallRange.
module B (A:AdvEUF_QROM) : AdvOW = {
import var EUF
var hs : msg -> sign
module Os = {
proc sign(m:msg) = {
log <- m :: log;
return hs m;
}
}
proc main(pk_:pkey, y:hash, r:int) = {
var s, i, fr, rs1, rs2, rs, rh;
log <- [];
i <$ [0 .. r-1];
rs1 <$ dlist dsign i;
rs2 <$ dlist dsign (r-i-1);
rs <- rs1 ++ witness :: rs2;
rh <- map (f pk_) rs1 ++ y :: map (f pk_) rs2;
fr <$ difun_R;
hs <- fun x => nth witness rs (r2i (fr x));
QRO.h <- fun x => nth witness rh (r2i (fr x));
(m,s) <@ A(QRO, Os).main(pk_);
return s;
}
}.
clone import Collision with op q <- q.
section OW.
declare module A <: AdvEUF_QROM { -QRO, -EUF , -B, -Wrap, -SR}.
declare axiom hoare_bound (H<:QRO{-A, -Wrap}) (S<:OrclSign{-A, -Wrap}) :
hoare[ Wrap(A, H, S).main : true ==> Wrap.cs <= qs /\ Wrap.ch <= qh].
declare axiom A_ll (H <: QRO{-A}) (S <: OrclSign{-A}) :
islossless S.sign => islossless H.h => islossless A(H, S).main.
local module G1 (H:QRO) = {
var logs : (hash * msg) list
var hm : hash
module FDH = {
proc kg () = {
var k;
k <$ kg;
return k;
}
proc sign(sk:skey, m:msg) = {
var h;
h <@ H.h{m};
logs <- (h,m) :: logs;
return finv sk h;
}
proc verify(pk:pkey, m:msg, s:sign) = {
hm <@ H.h{m};
return hm = f pk s;
}
}
proc main() = {
var b;
logs <- [];
b <@ EUF(Wrap(A, H), FDH).main();
return b;
}
}.
clone import T_PRF.
local lemma EUF_G1 &m :
Pr[EUF_QROM(A,FDH).main() @ &m : res] = Pr[IND_QRO(QRO,G1).main() @ &m : res].
proof.
byequiv => //; proc; inline *.
wp; call (: ={EUF.sk, EUF.log, QRO.h}).
+ by proc; inline *; auto.
+ by sim.
auto => />.
qed.
local module G1_col (H:QRO) = {
proc main() = {
var b;
b <@ G1(H).main();
return (EUF.m, oget (assoc G1.logs G1.hm));
}
}.
local hoare hoare_bound1 : Wrap(A, QRO, EUF(Wrap(A, QRO), G1(QRO).FDH).Os).main :
QRO.ch = 0 ==>
QRO.ch <= qs + qh.
proof.
conseq (: true ==> Wrap.cs <= qs /\ Wrap.ch <= qh)
(: QRO.ch = 0 ==> QRO.ch = Wrap.cs + Wrap.ch).
+ smt().
+ proc.
call (: QRO.ch = Wrap.cs + Wrap.ch).
+ by proc; inline *; auto => /> *; ring.
+ by proc; inline *; auto => /> *; ring.
by auto.
by apply (hoare_bound QRO (<: EUF(Wrap(A, QRO), G1(QRO).FDH).Os)).
qed.
local lemma G1_split &m :
let r1 = mu1 dhash witness in
Pr[IND_QRO(QRO,G1).main() @ &m : res] <=
Pr[IND_QRO(QRO,G1).main() @ &m : res /\ ! (G1.hm \in unzip1 G1.logs) ] +
(27 *(q +2)^3)%r * r1.
proof.
rewrite Pr[mu_split (G1.hm \in map fst G1.logs)].
have : Pr[IND_QRO(QRO, G1).main() @ &m : res /\ (G1.hm \in unzip1 G1.logs)] <=
Pr[Col(G1_col).main() @ &m : res].
+ byequiv=> //; proc; inline *; wp.
call (: ={EUF.sk, EUF.log, QRO.h, G1.logs} /\
(G1.logs = map (fun m => (QRO.h m, m)) EUF.log){1}).
+ by proc; inline *; auto.
+ by sim />.
auto => />; smt(assocP mapP).
have /# := pr_col G1_col &m _.
proc.
inline G1_col(QRO).main G1(QRO).main EUF(Wrap(A, QRO), G1(QRO).FDH).main
G1(QRO).FDH.verify QRO.h; wp.
by call hoare_bound1; inline *; auto => /> /#.
qed.
local module G2(H:SR) = {
proc main () = {
var b;
b <@ G1(H).main();
return b /\ !(G1.hm \in unzip1 G1.logs);
}
}.
local lemma G1_SRO &m :
Pr[IND_QRO(QRO,G1).main() @ &m : res /\ ! (G1.hm \in unzip1 G1.logs) ] =
Pr[IND_SR (SRO,G2).main() @ &m : res].
proof.
byequiv => //; proc; inline G2(SRO).main; wp.
sim 1 1 : (b{1} = b0{2} /\ ={G1.hm, G1.logs}).
inline *; auto => />; apply dfun_ll => //=; apply Rt.dunifin_ll.
qed.
local hoare hoare_bound2 : Wrap(A, SRO, EUF(Wrap(A, SRO), G1(SRO).FDH).Os).main :
QRO.ch = 0 ==>
QRO.ch <= qs + qh.
proof.
conseq (: true ==> Wrap.cs <= qs /\ Wrap.ch <= qh)
(: QRO.ch = 0 ==> QRO.ch = Wrap.cs + Wrap.ch).
+ smt().
+ proc.
call (: QRO.ch = Wrap.cs + Wrap.ch).
+ by proc; inline *; auto => /> *; ring.
+ by proc; inline *; auto => /> *; ring.
by auto.
by apply (hoare_bound SRO (<: EUF(Wrap(A, SRO), G1(SRO).FDH).Os)).
qed.
local lemma SRO_SR &m :
`| Pr[IND_SR (SRO,G2).main() @ &m : res] - Pr[IND_SR (SR,G2).main() @ &m : res] | <=
(27 * q^3)%r / _r%r.
proof.
apply (advantage q G2 &m Rt.Support.card_gt0).
proc.
inline G2(SRO).main G1(SRO).main EUF(Wrap(A, SRO), G1(SRO).FDH).main
G1(SRO).FDH.verify QRO.h; wp.
by call hoare_bound2; inline *; auto => /> /#.
qed.
local module G3 = {
var i : int
proc main () = {
var b;
b <@ IND_SR (SR,G2).main();
i <$ [0.._r-1];
return b /\ r2i (SR.fr EUF.m) = i;
}
}.
local lemma G3_SR &m :
Pr[G3.main() @ &m : res] = 1.0/_r%r * Pr[IND_SR (SR,G2).main() @ &m : res].
proof.
byphoare (:(glob A){m} = glob A ==> _) => //.
proc.
seq 1 : b Pr[IND_SR(SR,G2).main() @ &m : res] (inv _r%r)
_ 0%r (0 <= r2i (SR.fr EUF.m) < _r).
+ inline *; wp.
conseq (: forall x, 0 <= r2i (SR.fr x) < _r).
+ by move=> /> ? m0 /(_ m0.`1) />.
call (: true) => //; auto => /> rh _ fr /dfun_supp /= h _ _ x; 1: by smt(r2i_range).
+ call (: (glob A){m} = glob A ==> res); 2: by auto.
bypr => &m0 h.
byequiv (: ={glob A, arg} ==> ={res, EUF.m, EUF.log, SR.fr}) => //; 1: by sim.
by move: h => />.
+ by conseq />; rnd (pred1 (r2i (SR.fr EUF.m))); skip => />; smt (DInterval.dinter1E).
+ by conseq (:false) => /> //.
smt().
qed.
local clone import DList.Program with
type t <- hash,
op d <- dhash.
local module S = {
proc sample2(n1:int, n2:int) = {
var rh1, y, rh2;
(rh1,y,rh2) <@ Sample.sample2(n1,n2);
return rh1 ++ y :: rh2;
}
}.
local lemma G3_OW &m:
Pr[G3.main() @ &m : res] <= Pr[OW(B(A)).main(_r) @ &m : res].
proof.
byequiv=> //; proc; inline *.
swap{1} -24; wp.
symmetry.
call (_: exists m', m' \in EUF.log /\ r2i (SR.fr m') = G3.i,
={QRO.h, EUF.log} /\
(G1.logs = map (fun m => (QRO.h m, m)) EUF.log){2} /\
(forall m, r2i (SR.fr{2} m) <> G3.i{2} =>
B.hs{1} m = (finv EUF.sk (QRO.h m)){2}),
(G1.logs = map (fun m => (QRO.h m, m)) EUF.log){2}) => //.
+ by apply A_ll.
+ by proc; inline *; auto => /> /#.
+ by move=> *; proc; auto.
+ by move=> *; proc; inline *; auto => /> /#.
+ by proc; inline *; auto.
+ by move=> *; proc; auto.
+ by move=> *; proc; inline *; auto.
swap{2} 7 -6; wp.
swap{1} [10..11] 1.
swap{1} 5 -4.
swap{1} [4..6] 4; wp.
swap{2} [2..2] -2; sp.
swap{1} 2 2.
rnd.
seq 2 2 : (#pre /\ k{2} = (pk,sk){1} /\ G3.i{2} = i0{1} /\ 0 <= G3.i{2} < _r /\
k{2} \in kg).
+ by auto => />; smt(DInterval.supp_dinter).
conseq (: SR.rh{2} = (map (f pk) rs1 ++ y :: map (f pk) rs2){1} /\
size rs1{1} = i0{1} /\ size rs2{1} = r{1} - i0{1} - 1).
+ move=> /> &1 h0i hir hk rs1 rs2 y <<- ? fr /dfun_supp /= hfr; split.
+ move=> m' hm'; rewrite !nth_cat size_map /=.
have hfrm' := hfr m'.
case: (r2i (fr m') < size rs1) => ?.
+ rewrite (nth_map witness witness (f pk{1}) (r2i (fr m'))); 1: by smt(r2i_range).
by rewrite (finv_f _ _ hk) //.
rewrite (nth_map witness witness (f pk{1})); 1: by smt(r2i_range).
by smt(finv_f).
smt (finv_f f_finv nth_cat size_map mapP map_comp).
transitivity * {2} { SR.rh <@ Sample.sample(_r); } => //; 1:smt(); last by inline *;auto.
transitivity {2} { SR.rh <@ S.sample2(G3.i, _r - G3.i - 1); }
(r{1} = _r /\ i0{1} = G3.i{2} /\ (pk,sk){1} \in kg /\ 0 <= G3.i{2} < _r ==>
SR.rh{2} = map (f pk{1}) rs1{1} ++ y{1} :: map (f pk{1}) rs2{1} /\
size rs1{1} = i0{1} /\ size rs2{1} = r{1} - i0{1} - 1)
(0 <= G3.i{1} < _r ==> ={SR.rh}) => //;1: smt(); last first.
+ symmetry; inline S.sample2; wp; ecall (Sample_Sample2 n1{2} n2{2}); wp; skip => /> /#.
inline *; wp; sp.
rnd (map (f pk{1})) (map (finv sk{1})); rnd; rnd (map (f pk{1})) (map (finv sk{1})); skip => />.
move=> &1 &2 hk hi0 hir; split.
+ move=> rs1 ?; rewrite -map_comp -{1}(map_id rs1); apply eq_map.
by move=> x; rewrite /(\o) (f_finv _ _ hk).
move=> _; split.
+ move=> rs hrs. rewrite !dlist1E 1,2:// size_map.
case: (G3.i{2} = size rs) => // hsz.
rewrite BRM.big_map; apply BRM.eq_big => //= h _.
by rewrite /(\o); apply dhash_dsign.
move=> _ rs1; rewrite supp_dlist 1:// => -[] <<- ?; split.
+ rewrite supp_dlist 1:// size_map /= allP => *; apply dhash_fu.
move=> _; split.
+ rewrite -map_comp -{1}(map_id rs1); apply eq_map.
by move=> x; rewrite /(\o) (finv_f _ _ hk).
move=> _ y hy; split.
+ move=> rs2 ?; rewrite -map_comp -{1}(map_id rs2); apply eq_map.
by move=> x; rewrite /(\o) (f_finv _ _ hk).
move=> _; split.
+ move=> rs hrs. rewrite !dlist1E 1,2:/# size_map.
case: (_r - size rs1 - 1 = size rs) => // hsz.
rewrite BRM.big_map; apply BRM.eq_big => //= h _.
by rewrite /(\o); apply dhash_dsign.
move=> _ rs2; rewrite supp_dlist 1:/# => -[] hrs2 ?; split.
+ rewrite supp_dlist 1:/# size_map hrs2 /= allP => *; apply dhash_fu.
move=> _; split => //.
rewrite -map_comp -{1}(map_id rs2); apply eq_map.
by move=> x; rewrite /(\o) (finv_f _ _ hk).
qed.
(* Remark:
[mu1 dhash witness] is the probability of obtaining an element in dhash,
this is negligeable.
Now assume that the hypothesis: (3 * k)%r * rhash <= eps
We have eps < (3 * k)%r * rhash.
So eps is negligeable.
*)
lemma conclusion &m :
let eps = Pr[EUF_QROM(A,FDH).main() @ &m : res] in
let k = 54 * (q + 2)^3 in
_r = 3 * floor (k%r / eps) =>
let rhash = mu1 dhash witness in
(3 * k)%r * rhash <= eps =>
eps ^2 / (6 * k)%r <= Pr[OW(B(A)).main(_r) @ &m : res].
proof.
move=> eps k Hr rhash hhe.
have h0rhash: 0.0 < rhash by smt(dhash_fu).
have gt0_q3 : 1 <= (q + 2) ^ 3 by apply IntOrder.exprn_ege1 => //; smt (gt0_qs ge0_qh).
have heps : 0%r < eps by smt().
have gt0_r : 0 < _r.
+ smt(floor_bound mu_bounded).
apply: ler_trans (G3_OW &m).
rewrite (G3_SR &m).
apply (ler_trans (eps/(3*k)%r * Pr[IND_SR(SR, G2).main() @ &m : res])); last first.
+ apply ler_wpmul2r; 1: smt(mu_bounded).
have h0k: 0%r < k%r by smt().
have hepsk : 0%r <= eps/k%r by smt().
have -> : eps/(3*k)%r = inv((3*k)%r/eps) by smt().
by rewrite /= lef_pinv; smt (floor_bound).
have -> : eps ^ 2 / (6 * k)%r = eps / (3 * k)%r * (eps/2%r) by field => //; smt().
apply ler_wpmul2l; 1: smt().
apply (ler_trans (Pr[IND_SR(SRO, G2).main() @ &m : res] - (27*(q+2)^3)%r/_r%r)); last first.
+ by have := SRO_SR &m; smt (IntOrder.ler_pexp gt0_qs ge0_qh).
apply (ler_trans (eps - (27 * (q + 2) ^ 3)%r / _r%r - (27 * (q + 2) ^ 3)%r / _r%r));
last first.
+ rewrite ler_add2r.
have := EUF_G1 &m; rewrite -/eps => ->.
have /= := G1_split &m. rewrite -/rhash.
have -> := G1_SRO &m.
have /#: (27 * (q + 2) ^ 3)%r * rhash <= (27 * (q + 2) ^ 3)%r / _r%r.
apply ler_wpmul2l; 1:smt().
apply (ler_trans (eps / (3*k)%r)).
+ by apply ler_pdivl_mulr; [ smt() | rewrite mulrC].
have -> : eps/(3*k)%r = inv((3*k)%r/eps) by smt().
by rewrite /= lef_pinv; smt (floor_bound).
have -> : eps - (27 * (q + 2) ^ 3)%r / _r%r - (27 * (q + 2) ^ 3)%r / _r%r =
eps - k%r/_r%r by smt().
have /# : k%r / _r%r <= eps/2%r.
rewrite -/k ler_pdivl_mulr 1:// mulrC mulrA ler_pdivr_mulr 1:/#.
rewrite (mulrC eps) -ler_pdivr_mulr 1:// -mulrA mulrC -ler_pdivl_mulr 1://.
have : 3%r <= k%r / eps.
+ apply (ler_trans k%r); 1: smt().
rewrite ler_pdivl_mulr 1://; have /#: eps <= 1%r by smt(mu_bounded).
smt (floor_bound).
qed.
end section OW.
end FDH_OW_small_range.