https://github.com/EasyCrypt/easycrypt
Raw File
Tip revision: e5f25487cf1d55ccd5a25aff871bcc0731bce05d authored by Pierre-Yves Strub on 30 January 2023, 08:24:39 UTC
Activate warning "unused unfolds" by default
Tip revision: e5f2548
hashed_elgamal_std.ec
(* -------------------------------------------------------------------- *)
require import AllCore Int Real Distr DBool CHoareTactic.
require (*--*) DiffieHellman BitWord PKE_CPA.


(* ---------------- Sane Default Behaviours --------------------------- *)
pragma +implicits.

(* ---------------------- Let's Get Started --------------------------- *)
op k : { int | 0 < k } as gt0_k.

clone import BitWord as Bits with
  op n <- k
proof gt0_n by exact/gt0_k
rename
  "word" as "bits"
  "dunifin" as "dbits".
import DWord.

(** Assumption: DDH **)
(*** WARNING: DiffieHellman is not up to speed with latest developments ***)
clone DiffieHellman as DH.
import DH.DDH DH.G DH.GP DH.FD DH.GP.ZModE.

(** Assumption Entropy Smoothing *)
theory EntropySmoothing.
  type hkey.

  op dhkey: { hkey distr | is_lossless dhkey } as dhkey_ll.
  hint exact random : dhkey_ll.

  op cdhkey : { int | 0 <= cdhkey} as ge0_cdhkey.
  schema cost_dhkey `{P} : cost[P: dhkey] = N cdhkey.
  hint simplify cost_dhkey.

  op hash : hkey -> group -> bits.
  op chash : {int | 0 <= chash } as ge0_chash.
  schema cost_hash `{P} {k : hkey, g : group} : cost [P:hash k g] = cost [P: k] + cost[P:g] + N chash.
  hint simplify  cost_hash.

  module type AdvES = {
    proc guess(_: hkey * bits) : bool
  }.

  module ES0 (A : AdvES) = {
    proc main () : bool = {
      var b, hk, h;
      hk <$ dhkey;
      h  <$ dbits;
      b  <@ A.guess(hk, h);
      return b;
    }
  }.

  module ES1 (A : AdvES) = {
    proc main () : bool = {
      var b, hk, z;
      hk <$ dhkey;
      z  <$ dt;
      b  <@ A.guess(hk, hash hk (g ^ z));
      return b;
    }
  }.
end EntropySmoothing.
import EntropySmoothing.

(** Construction: a PKE **)
type pkey = hkey * group.
type skey = hkey * exp.
type ptxt = bits.
type ctxt = group * bits.

clone import PKE_CPA as PKE_ with
  type pkey <- pkey,
  type skey <- skey,
  type ptxt <- ptxt,
  type ctxt <- ctxt.

(** Concrete Construction: Hashed ElGammal **)
module Hashed_ElGamal : Scheme = {
  proc kg() = {
    var hk, sk;

    hk <$ dhkey;
    sk <$ dt;
    return ((hk,g ^ sk), (hk, sk));
  }

  proc enc(pk: pkey, m: ptxt) = {
    var y, h;

    y <$ dt;
    h <- hash pk.`1 (pk.`2 ^ y);
    return (g ^ y, h +^ m);
  }

  proc dec(sk:skey, c:ctxt): ptxt option = {
    var gy, h, hm;

    (gy, hm) <- c;
    h        <- hash sk.`1 (gy ^ sk.`2);
    return Some (h +^ hm);
  }
}.

(** Exact security *)
module DDHAdv(A:Adversary) = {
  proc guess (gx, gy, gz) : bool = {
    var hk, m0, m1, b, b', h;
    hk       <$ dhkey;
    (m0, m1) <@ A.choose((hk, gx));
    b        <$ {0,1};
    h        <- hash hk gz;
    b'       <@ A.guess(gy, h +^ (b?m1:m0));
    return b' = b;
  }
}.

module ESAdv(A:Adversary) = {
  proc guess (hk, h) : bool = {
    var x, y, m0, m1, b, b';
    x        <$ dt;
    y        <$ dt;
    (m0, m1) <@ A.choose((hk, g ^ x));
    b        <$ {0,1};
    b'       <@ A.guess(g ^ y, h +^ (b?m1:m0));
    return b' = b;
  }
}.

section Security.
  declare module A <: Adversary.
  declare axiom Ac_ll: islossless A.choose.
  declare axiom Ag_ll: islossless A.guess.

  local lemma cpa_ddh0 &m:
      Pr[CPA(Hashed_ElGamal,A).main() @ &m : res]
    = Pr[DDH0(DDHAdv(A)).main() @ &m : res].
  proof.
  byequiv=> //; proc; inline *.
  swap{1} 1 1; swap{1} 8 -6; swap{2} 6 -3.
  auto; call (: true).
  auto; call (: true).
  by auto=> /> sk _ y _ hk _ [m0 m1] b _ /=; rewrite expM.
  qed.

  local lemma ddh1_es1 &m:
      Pr[DDH1(DDHAdv(A)).main() @ &m : res]
    = Pr[ES1(ESAdv(A)).main() @ &m : res].
  proof.
  byequiv=> //; proc; inline *.
  swap{1} 7 -3; swap{2} [5..6] -4; swap{2} 4 -1.
  auto; call (: true).
  auto; call (:true).
  by auto.
  qed.

  local module Gb = {
    proc main () : bool = {
      var hk, x, y, v,m0, m1, b, b';
      hk      <$ dhkey;
      x       <$ dt;
      y       <$ dt;
      (m0,m1) <@ A.choose(hk, g ^ x);
      v       <$ dbits;
      b'      <@ A.guess(g ^ y, v);
      b       <$ {0,1};
      return b' = b;
    }
  }.

  local lemma es0_Gb &m:
      Pr[ES0(ESAdv(A)).main() @ &m : res]
    = Pr[Gb.main()@ &m : res].
  proof.
  byequiv=> //; proc; inline *.
  swap{1} 2 1. swap{1} [3..4] 4. swap{2} 7 -2.
  auto; call (: true); wp.
  rnd (fun w, w +^ (b0{1} ? m1{1} : m0{1})).
  auto; call (: true).
  by auto=> /> *; split => *; algebra.
  qed.

  local lemma Gb_half &m:
     Pr[Gb.main()@ &m : res] = 1%r/2%r.
  proof.
  byphoare=> //; proc.
  rnd (pred1 b')=> /=; conseq (_:_ ==> true).
  + by move=> /> b; rewrite dbool1E pred1E.
  call Ag_ll; auto.
  by call Ac_ll; auto=> />; rewrite dhkey_ll dt_ll dbits_ll.
  qed.

  lemma conclusion &m :
       `| Pr[CPA(Hashed_ElGamal,A).main() @ &m : res] - 1%r/2%r |
    <= `| Pr[DDH0(DDHAdv(A)).main() @ &m : res]
          - Pr[DDH1(DDHAdv(A)).main() @ &m : res] |
       + `| Pr[ES0(ESAdv(A)).main() @ &m : res]
            - Pr[ES1(ESAdv(A)).main() @ &m : res]|.
  proof.
  rewrite (cpa_ddh0 &m) (ddh1_es1 &m) (es0_Gb &m) (Gb_half &m).
  smt(@Real).
  qed.
end section Security.

print conclusion.

(* -------------------------------------------------------------------- *)
abstract theory Cost.

clone include AllCore.Cost.
clone include Bool.Cost.
clone include Bits.Cost.
clone include DBool.Cost.
clone include DH.GP.Cost.
clone include DH.FD.Cost.
clone include DH.GP.ZModE.Cost.

op cddh = 3 + cxor + chash + cdbool + cdhkey.
op cguess = 3 + 2*cgpow + cxor + cdbool + 2 * cdt.

lemma ex_conclusion (kc kg: int) (A <: Adversary[choose : `{N kc} , guess : `{N kg}]) &m :
  0 <= kc => 0 <= kg =>
  exists (Dddh <: DH.DDH.Adversary [guess : `{N (cddh + kg + kc)}])
         (Des <: AdvES[guess: `{N (cguess + kg + kc) }]),
   `|Pr[CPA(Hashed_ElGamal, A).main() @ &m : res] - 1%r / 2%r| <=
   `|Pr[DDH0(Dddh).main() @ &m : res] - Pr[DDH1(Dddh).main() @ &m : res]| +
   `|Pr[ES0(Des).main() @ &m : res] - Pr[ES1(Des).main() @ &m : res]|.
proof.
  move=> ge0_kc ge0_kg.
  exists (DDHAdv(A)); split; last first.
  exists (ESAdv(A)); split; last first.
  apply (conclusion A _ _ &m).
  + conseq (_ : _ : time [N kc]).
    by proc true : time[].
  + conseq (_ : true ==> true : time [N kg]).
    by proc true : time[].
  + proc. move => /=.
    call (:true; time []); rnd; call(:true; time []); do 2!rnd; skip => />.
    rewrite dt_ll dbool_ll /=. smt (ge0_cg ge0_cxor ge0_cdbool ge0_cdt).
  proc; call (:true; time []); wp; rnd; call(:true; time []); rnd; skip => />.
  rewrite dhkey_ll dbool_ll /=. smt (ge0_cxor ge0_cdbool ge0_chash ge0_cdhkey).
qed.

end Cost.
back to top