https://github.com/EasyCrypt/easycrypt
Tip revision: 9b906a364f732efd3b8a29f5316c0fd22ede833c authored by Pierre-Yves Strub on 10 February 2020, 09:45:49 UTC
Merge branch '1.0' into deploy-lamport
Merge branch '1.0' into deploy-lamport
Tip revision: 9b906a3
Lamport.ec
require import AllCore Distr DBool DInterval List.
from Jasmin require import JArray.
require (* *) PlugAndPray.
abstract theory EUF_onetime.
type Sk.
type Pk.
type Sig.
type Msg.
module type Scheme = {
proc keyGen () : Pk * Sk
proc sign (m:Msg, sk:Sk) : Sig
proc verify (m:Msg, sig:Sig, pk:Pk) : bool
}.
module type Forger = {
proc choose(pk : Pk) : Msg
proc forge(sig : Sig) : Msg * Sig
}.
module EUF(S:Scheme, F :Forger) ={
proc main(): bool ={
var sk:Sk;
var pk:Pk;
var msg,msg' : Msg;
var sig,sig' : Sig;
var valid : bool;
(pk,sk) <@ S.keyGen();
msg <@ F.choose(pk);
sig <@ S.sign(msg,sk);
(msg',sig') <@ F.forge(sig);
valid <@ S.verify(msg',sig',pk);
return (msg <> msg' /\ valid);
}
}.
end EUF_onetime.
type D.
type R.
type Sk = D * D.
type Pk = R * R.
type Sig = D.
type Msg = bool.
op H : D -> R.
op challenge : D distr.
axiom chal_ll : is_lossless challenge.
hint exact : chal_ll.
(* One wayness for H *)
module type Inverter = {
proc invert(y : R) : D
}.
module OW(I :Inverter) ={
proc main(): bool ={
var x,x':D;
var y : R;
x <$ challenge;
y <- H x;
x' <- I.invert(y);
return (H x = H x');
}
}.
(* One-Time Existential UF *)
clone import EUF_onetime as EUF1 with
type Sk <- Sk,
type Pk <- Pk,
type Sig <- Sig,
type Msg <- Msg.
module Lamport : EUF1.Scheme = {
proc keyGen() : Pk * Sk = {
var sk0,sk1;
sk0 <$ challenge;
sk1 <$ challenge;
return ((H sk0, H sk1), (sk0,sk1));
}
proc sign(m : Msg, sk : Sk) : Sig = {
var sk0, sk1, sig;
(sk0,sk1) <- sk;
sig <- if m
then sk1
else sk0;
return sig;
}
proc verify(m : Msg, sig : Sig, pk : Pk) : bool = {
var pk0, pk1;
(pk0,pk1) <- pk;
return H sig = if m
then pk1
else pk0;
}
}.
(* Reduction *)
module I(F : Forger) : Inverter = {
proc invert(y : R) : D = {
var sk:Sk;
var pk:Pk;
var b;
var msg,msg' : Msg;
var sig,sig' : Sig;
(pk,sk) <@ Lamport.keyGen();
b <$ {0,1};
pk <- if b
then (fst pk, y)
else (y, snd pk);
msg <@ F.choose(pk);
sig <@ Lamport.sign(msg,sk);
(msg',sig') <@ F.forge(sig);
return sig';
}
}.
(* We modify the security game so that it
has the same probability space as the
inverter. *)
section PROOFS.
declare module F: Forger.
axiom forge_ll : islossless F.forge.
local module EUF_guess ={
var correct_guess : bool
var msg : bool
proc isolate() = {
var sk:Sk;
var pk:Pk;
var msg' : Msg;
var sig,sig' : Sig;
var valid : bool;
(pk,sk) <@ Lamport.keyGen();
msg <@ F.choose(pk);
sig <@ Lamport.sign(msg,sk);
(msg',sig') <@ F.forge(sig);
valid <@ Lamport.verify(msg',sig',pk);
return (msg <> msg' /\ valid);
}
proc main(): bool = {
var r, b;
r <@ isolate();
b <$ {0,1};
correct_guess <- msg <> b;
return r;
}
}.
local lemma same_prob &mem :
Pr [ EUF_guess.main() @ &mem : res ] =
Pr [ EUF(Lamport, F).main() @ &mem : res].
proof.
byequiv => />; proc.
inline EUF_guess.isolate.
wp; rnd {1}; wp.
by sim : (={msg',valid} /\ EUF_guess.msg{1} = msg{2}).
qed.
local lemma lower_prob &mem :
Pr [ EUF_guess.main() @ &mem : res /\ EUF_guess.correct_guess ] <=
Pr [ OW(I(F)).main() @ &mem : res ].
proof.
byequiv => />.
proc.
inline EUF_guess.isolate I(F).invert Lamport.verify.
swap {1} 11 -10; swap {2} 5 -4.
seq 1 1 : (={b, glob F}); 1: by sim.
seq 1 5 :
(#pre /\ ={pk} /\
(if b{1} then sk.`1{1} = sk.`1{2} else sk.`2{1} = sk.`2{2}) /\
(if b{1} then pk{2}.`2 else pk{2}.`1) = H x{2}).
+ inline *; wp.
case (b{1}).
+ by rnd{2}; swap {1} 2 -1; auto; smt(chal_ll).
by swap {2} 5 -1; rnd{2}; auto; smt (chal_ll).
wp; seq 1 1 : (#pre /\ EUF_guess.msg{1} = msg{2}); 1: by sim />.
case (EUF_guess.msg{1} <> b{1}).
(* correct guess*)
+ by inline *; call (_:true); auto => />; smt().
(* incorrect guess *)
conseq (:true) => />.
by call{1} forge_ll; call{2} forge_ll; inline*; auto.
qed.
(* 1/2 loss *)
local lemma prob_loss &mem :
Pr [ EUF_guess.main() @ &mem : res /\ EUF_guess.correct_guess ]
= 1%r / 2%r * Pr [ EUF_guess.main() @ &mem : res ].
proof.
byphoare (_: (glob F) = (glob F){mem} ==> res /\ EUF_guess.correct_guess) => //.
proc; wp.
seq 1 : r (Pr[EUF_guess.main() @ &mem : res]) (1%r/2%r) _ 0%r => //.
+ call (_: (glob F) = (glob F){mem} ==> res).
bypr => &m _.
byequiv => //.
proc; inline EUF_guess.isolate; wp; rnd{2}; wp.
by sim : (={EUF_guess.msg,msg',valid}).
+ by auto.
+ rnd (fun b => EUF_guess.msg <> b); auto => /> ??; smt(dboolE).
by conseq (_ : _ ==> false) => />.
qed.
(* Theorem statement *)
lemma lamport_euf &mem :
Pr [ EUF(Lamport, F).main() @ &mem : res ] <= 2%r * Pr [ OW(I(F)).main() @ &mem : res].
proof.
rewrite -(same_prob &mem).
have := prob_loss &mem; have := lower_prob &mem; smt().
qed.
end section PROOFS.
(* Lamport for messages of size [size] *)
op n : {int | 0 < n } as gt0_n.
clone import PolyArray as Array with
op size <- n
proof ge0_size by smt (gt0_n).
type Sk_n = D Array.t * D Array.t.
type Pk_n = R Array.t * R Array.t.
type Sig_n = D Array.t.
type Msg_n = bool Array.t.
clone import EUF_onetime as EUFn with
type Pk <- Pk_n,
type Sk <- Sk_n,
type Sig <- Sig_n,
type Msg <- Msg_n.
module Lamport_n : EUFn.Scheme = {
proc keyGen() : Pk_n * Sk_n = {
var sk0, sk1;
var i;
i = 0;
sk0 <- witness;
sk1 <- witness;
while (i < n) {
sk0.[i] <$ challenge;
sk1.[i] <$ challenge;
i <- i + 1;
}
return ((Array.map H sk0, Array.map H sk1), (sk0,sk1));
}
proc sign(m : Msg_n, sk : Sk_n) : Sig_n = {
var sk0, sk1, sig;
(sk0,sk1) <- sk;
sig <- Array.init (fun i => if m.[i] then sk1.[i] else sk0.[i]);
return sig;
}
proc verify(m : Msg_n, sig : Sig_n, pk : Pk_n) : bool = {
var pk0, pk1;
(pk0,pk1) <- pk;
return Array.map H sig = Array.init (fun i => if m.[i] then pk1.[i] else pk0.[i]);
}
}.
module EUFn_1 (F:EUFn.Forger) : EUF1.Forger = {
var k : int
var sk : Sk_n
var m : Msg_n
proc choose(pk : Pk) : Msg = {
var i, sk0, sk1, pk0, pk1;
k <$ [0..n -1];
i = 0;
sk0 <- witness;
sk1 <- witness;
while (i < n) {
if (i <> k) {
sk0.[i] <$ challenge;
sk1.[i] <$ challenge;
}
i <- i + 1;
}
sk <- (sk0, sk1);
pk0 <- Array.init (fun i => if i = k then pk.`1 else H sk0.[i]);
pk1 <- Array.init (fun i => if i = k then pk.`2 else H sk1.[i]);
m <@ F.choose((pk0,pk1));
return m.[k];
}
proc forge(sig:Sig) : Msg * Sig = {
var sk0, sk1, sign, sign';
(sk0,sk1) <- sk;
sign <- Array.init (fun i => if i = k then sig
else if m.[i] then sk1.[i] else sk0.[i]);
(m,sign') <@ F.forge(sign);
return (m.[k], sign'.[k]);
}
}.
section PROOFS_n.
declare module F: EUFn.Forger {EUFn_1}.
axiom forge_ll : islossless F.forge.
local clone import PlugAndPray as PlugAndPray0 with
type tval <- int,
op indices = range 0 n,
type tin <- unit,
type tres <- bool
proof *.
realize indices_not_nil.
proof. smt (range_ltn gt0_n). qed.
local module G:Game = {
var msg : Msg_n
var msg' : Msg_n
proc main(): bool ={
var sk:Sk_n;
var pk:Pk_n;
var sig,sig' : Sig_n;
var valid : bool;
(pk,sk) <@ Lamport_n.keyGen();
msg <@ F.choose(pk);
sig <@ Lamport_n.sign(msg,sk);
(msg',sig') <@ F.forge(sig);
valid <@ Lamport_n.verify(msg',sig',pk);
return (msg <> msg' /\ valid);
}
}.
op diff_msg (msg msg':Msg_n) =
List.find (fun i => msg.[i] <> msg'.[i]) (range 0 n).
lemma card_n : card = n.
proof. rewrite undup_id 1:range_uniq size_range; smt(gt0_n). qed.
lemma diff_msg_has (msg msg':Msg_n) :
msg' <> msg =>
has (fun (i : int) => msg'.[i] <> msg.[i]) (range 0 n).
proof.
rewrite hasP => hn.
case : (exists (x : int), (x \in range 0 n) /\ (fun (i : int) => msg'.[i] <> msg.[i]) x) => //.
rewrite negb_exists => h; apply hn.
apply Array.tP => i /mem_range hi.
by have := h i; rewrite /= hi.
qed.
lemma diff_msg_bound msg msg' :
msg' <> msg =>
0 <= diff_msg msg' msg < n.
proof.
rewrite /diff_msg => hn.
have {3}<-: size (range 0 n) = n.
+ by rewrite size_range; smt(gt0_n).
by rewrite find_ge0 -has_find diff_msg_has.
qed.
lemma diff_msg_in msg msg' :
msg' <> msg => diff_msg msg' msg \in indices.
proof. by move=> hn; rewrite mem_range diff_msg_bound. qed.
lemma diff_msg_neq msg msg' :
msg' <> msg => msg'.[diff_msg msg' msg] <> msg.[diff_msg msg' msg].
proof.
rewrite /diff_msg => hn.
have /= := nth_find witness (fun i => msg'.[i] <> msg.[i]) (range 0 n) _.
+ by apply diff_msg_has.
by rewrite nth_range //=; apply diff_msg_bound.
qed.
local lemma guess_euf1 &mem :
let psi = fun (g:glob G) (b:bool) =>
let (msg', msg, gF) = g in
diff_msg msg msg' in
let phi = fun (g:glob G) (b:bool) =>
let (msg', msg, gF) = g in
msg <> msg' /\ b in
Pr[Guess(G).main() @ &mem : phi (glob G) res.`2 /\ res.`1 = psi (glob G) res.`2] <=
Pr[EUF1.EUF(Lamport, EUFn_1(F)).main () @ &mem : res].
proof.
move=> /=.
byequiv => //;proc.
inline G.main EUFn_1(F).forge EUFn_1(F).choose.
swap{1} 8 -7; swap{2} 3 -2.
seq 3 10 : (={glob F} /\ pk{1} = (pk00, pk1){2} /\ i{1} = EUFn_1.k{2} /\
pk00{2}.[i{1}] = pk{2}.`1 /\
pk1{2}.[i{1}] = pk{2}.`2 /\
sk{2} = (sk.`1.[i], sk.`2.[i]){1} /\
(forall j, 0 <= j < n => j <> i{1} => sk.`1{1}.[j] = EUFn_1.sk{2}.`1.[j] /\
sk.`2{1}.[j] = EUFn_1.sk{2}.`2.[j])).
+ inline Lamport_n.keyGen.
swap{2} 3 3; wp; swap{2} 2 1.
splitwhile{1} 6 : (i0 < i).
splitwhile{2} 7 : (i < EUFn_1.k).
swap{2} 3 2. swap{2} [5..6] 1.
seq 6 5 : (={glob F, sk0, sk1} /\ i0{1} = i{2} /\ i{1} = EUFn_1.k{2} /\ i{2} = i{1} /\ 0 <= i{1} < n).
+ while (={sk0, sk1} /\ i0{1} = i{2} /\ i{2} <= i{1} /\ i{1} = EUFn_1.k{2}).
+ by rcondt{2} ^if; auto => />; smt().
by auto => />; smt (supp_dinter).
rcondt{1} ^while; 1: by auto;smt().
rcondt{2} ^while; 1: by auto; conseq (:true) => />; auto.
rcondf{2} ^if; 1: by auto; conseq(:true) => />; auto.
inline{2} Lamport.keyGen; swap{2} [3..4] 2; wp.
while ((sk0.[i], sk1.[i]){1} = (sk01,sk11){2} /\ 0 <= i{2} /\
i0{1} = i{2} /\ i{1} < i{2} <= n /\ i{1} = EUFn_1.k{2} /\
(forall j, 0 <= j < i{2} => j <> i{1} => sk0{1}.[j] = sk0{2}.[j] /\
sk1{1}.[j] = sk1{2}.[j])).
+ by rcondt{2} ^if; auto; smt (Array.get_setE).
by auto; smt (Array.get_setE Array.mapE Array.tP Array.initiE).
move=> /=; inline *; wp.
call (:true); wp; call(:true); skip => /> &1 &2 h1 h2 h ?; split.
+ by apply Array.tP => *;smt (Array.initiE).
move=> heqi rL rR Fl Fr /> hn heq.
rewrite (diff_msg_neq _ _ hn) /=.
have <- := Array.mapiE H rR.`2 (diff_msg result_R rR.`1).
+ by apply diff_msg_bound.
by rewrite heq Array.initiE 1:diff_msg_bound //= h1 h2.
qed.
lemma lamport_n_euf &mem :
Pr[EUFn.EUF(Lamport_n,F).main() @ &mem : res] <=
2%r * n%r * Pr[OW(I(EUFn_1(F))).main() @ &mem : res].
proof.
pose psi (g:glob G) (b:bool) :=
let (msg', msg, gF) = g in
diff_msg msg msg'.
pose phi (g:glob G) (b:bool) :=
let (msg', msg, gF) = g in
msg <> msg' /\ b.
have -> : Pr[EUFn.EUF(Lamport_n,F).main() @ &mem : res] =
Pr[G.main() @ &mem : phi (glob G) res].
+ byequiv => //;proc.
sim : (msg{1} = G.msg{2} /\ msg'{1} = G.msg'{2} /\ ={valid}) => />.
have -> := PBound_mult G phi psi () &mem.
+ by rewrite /psi => -[msg msg' F] o [] /= hn ho; apply diff_msg_in.
rewrite card_n.
have /= := (guess_euf1 &mem).
have := lamport_euf (EUFn_1(F)) _ &mem.
+ by islossless; apply forge_ll.
have : 0%r < n%r; smt(gt0_n).
qed.
end section PROOFS_n.