https://github.com/EasyCrypt/easycrypt
Raw File
Tip revision: 14a56f1344b9c6391ef1dab22b2208838943e0cf authored by Christian Doczkal on 08 December 2021, 12:53:47 UTC
add lemma RO_LRO and generalize RO_FinRO_D
Tip revision: 14a56f1
RealIdealCCA.ec
require import AllCore List Distr DBool DInterval.
require (*--*) Oracle_PKE.

import Distr.MRat.

(* This file provides games for the Real/Ideal variant of
IND-CCA2. The Real game provides the adversary with access to an
encryption oracle, in the Ideal game the encryption oracle always
encrypts the same (fixed) message. 

Unlike Oracle_PKE, we do not work with an abstract [Scheme]
module. Instead, we require distributions [dkeyseed, dencseed] and
operators that allow implementing a [Scheme] module *) 

(* FIXME: We use an inner theory to avoid the name-clash with the Real theory. *)
theory CCA.

type pkey, skey, plaintext, ciphertext, encseed, keyseed.

op dkeyseed : { keyseed distr | is_uniform dkeyseed } as dkeyseed_d.
op dencseed : { encseed distr | is_uniform dencseed } as dencseed_d.

op Ndec : int.
op Nenc : { int | 0 < Nenc } as Nenc_gt0.

op enc : plaintext * pkey * encseed -> ciphertext.
op dec : ciphertext * skey -> plaintext option.
op pkgen : keyseed -> pkey.
op skgen : keyseed -> skey.
op m0 : plaintext.

axiom encK (k : keyseed) (m : plaintext) (e : encseed) :
  dec (enc(m, pkgen k, e), skgen k) = Some m.

clone import Oracle_PKE as PKE with
  type pkey <- pkey,
  type skey <- skey,
  type plaintext <- plaintext,
  type ciphertext <- ciphertext
  proof*.

clone import IND_CCA2 as C with
  op Ndec <- Ndec,
  op Nenc <- Nenc,
  axiom Nenc_gt0 <- Nenc_gt0
  proof*.

module type Oracle = {
  proc pk () : pkey
  proc enc (_ : plaintext) : ciphertext
  proc dec (_ : ciphertext)  : plaintext option
}.

module type Oracle_i = {
  include Oracle
  proc init() : unit
}.

(* "Real" oracle, enc encrypts the given message *)
module Real : Oracle = {
  var pk : pkey
  var sk : skey

  proc init() : unit = {
    var ks;

    ks <$ dkeyseed;
    pk <- pkgen ks;
    sk <- skgen ks;
  }

  proc pk () = {
    return pk;
  }

  proc enc (m : plaintext) : ciphertext = {
    var e, c;

    e <$ dencseed;
    c <- enc(m, pk, e);
    return c;
  }
  proc dec (c : ciphertext) : plaintext option = {
    var m;

    m <- dec(c, sk);
    return m;
  }
}.

(* Ideal orcale, discards m and always encrypts m0 

In the case of a ciphertext collision (e.g. a collision in dencseed),
the last given message is returned *)

module Ideal : Oracle = {
  import var Real
  var cs : (ciphertext * plaintext) list

  proc init() : unit = {
    var ks;

    ks <$ dkeyseed;
    pk <- pkgen ks;
    sk <- skgen ks;
    cs <- [];
  }

  proc pk () = {
    return pk;
  }

  proc enc (m : plaintext) : ciphertext = {
    var e, c;

    e <$ dencseed;
    c <- enc(m0, pk, e);
    cs <- (c, m) :: cs;
    return c;
  }

  proc dec (c : ciphertext) : plaintext option = {
    var m;

    m <- assoc cs c;
    if (m = None) {
      m <- dec(c, sk);
    }
    return m;
  }
}.

(* Same as above, but in the case of a ciphertext collision, the
message returned by decryption is chosen randomly. *) 

module IdealS : Oracle = {
  import var Real
  include var Ideal [-dec]

  proc dec (c : ciphertext) : plaintext option = {
    var fcs, m, p;

    fcs <- List.filter (fun p => fst p = c) cs;
    if (fcs <> []) {
      p <$ drat fcs;
      m <- Some (snd p);
    } else {
      m <- dec(c, sk);
    }
    return m;
  }
}.

module type Adversary (G : Oracle) = {
  proc main () : bool
}.

module Game (O : Oracle_i, A : Adversary) = {

  proc main() = {
    var r;

    O.init();
    r <@ A(O).main();
    return r;
  }
}.

(*-------------------------------*)

module CountICCA (O : Oracle) = {
  var ndec, nenc : int

  proc init () : unit = {
    ndec <- 0;
    nenc <- 0;
  }

  proc pk = O.pk

  proc enc (m : plaintext) : ciphertext = {
    var c;

    c <@ O.enc(m);
    nenc <- nenc + 1;
    return c;
  }

  proc dec (c : ciphertext) : plaintext option = {
    var m;

    m <@ O.dec(c);
    ndec <- ndec + 1;
    return m;
  }
}.

module CountAdv (A : Adversary) (O : Oracle) = {
  proc main() = {
    var b;

    CountICCA(O).init();
    b <@ A(CountICCA(O)).main();
    return b;
  }
}.

(* Adversary for the bound |Real - Ideal| < |CCA_L - CCA_R| *)
module B (A : Adversary) (O : CCA_Oracle) = {
  var cs : (ciphertext * plaintext) list
  var pk : pkey

  module O' : Oracle = {

    proc init (p : pkey) = { pk <- p; }

    proc pk () = { return pk; }

    proc enc (m : plaintext) = {
      var c;

      c <- O.l_or_r(m, m0);
      cs <- (c, m) :: cs;
      return c;
    }

    proc dec (c : ciphertext) = {
      var m;

      m <- assoc cs c;
      if (m = None) {
        m <- O.dec(c);
      }
      return m;
    }
  }

  proc main (pk : pkey) : bool = {
    var r;

    cs <- [];
    O'.init(pk);
    r <@ A(O').main();
    return r;
  }
}.

(* Adversary for the bound |Real - IdealS| < |CCA_L - CCA_R| *)
module BS (A : Adversary) (O : CCA_Oracle) = {
  var cs : (ciphertext * plaintext) list
  var pk : pkey

  module O' : Oracle = {

    proc init (p : pkey) = { pk <- p; }

    proc pk () = { return pk; }

    proc enc (m : plaintext) = {
      var c;

      c <- O.l_or_r(m, m0);
      cs <- (c, m) :: cs;
      return c;
    }

    proc dec (c : ciphertext) : plaintext option = {
      var fcs, m, p;

      fcs <- List.filter (fun p => fst p = c) cs;
      if (fcs <> []) {
        p <$ drat fcs;
        m <- Some (snd p);
      } else {
        m <- O.dec(c);
      }
      return m;
    }
  }

  proc main (pk : pkey) : bool = {
    var r;

    cs <- [];
    O'.init(pk);
    r <@ A(O').main();
    return r;
  }
}.

section.

declare module A <: Adversary {Real, Ideal, IdealS,
                              C.CCA_Oracle, B, BS, CountCCA, CountICCA}.

module S : Scheme = {
  proc kg () : pkey * skey = {
    var ks, pk, sk;

    ks <$ dkeyseed;
    pk <- pkgen ks;
    sk <- skgen ks;
    return (pk, sk);
  }

  proc enc (pk : pkey, m : plaintext) : ciphertext = {
    var e, c;

    e <$ dencseed;
    c <- enc (m, pk, e);
    return c;
  }

  proc dec(sk : skey, c : ciphertext) : plaintext option = {
    var m;

    m <- dec (c, sk);
    return m;
  }
}.

declare axiom A_bound (O <: Oracle {A, CountICCA}) : hoare [CountAdv(A, O).main :
               true ==> CountICCA.ndec <= Ndec /\ CountICCA.nenc <= Nenc].

equiv AB_bound (O <: CCA_Oracle{CountICCA, CountCCA, A, B}) :
  C.CountAdv(B(A), O).main ~ CountAdv(A, B(A, O).O').main :
  ={glob A, glob O} /\ B.cs{2} = [] /\ arg{1} = B.pk{2} ==>
  CountCCA.ndec{1} <= CountICCA.ndec{2} /\ CountCCA.nenc{1} = CountICCA.nenc{2}.
proof.
proc. inline*; sp; auto.
call (: ={glob O, glob B} /\ CountCCA.ndec{1} <= CountICCA.ndec{2} /\
        CountCCA.nenc{1} = CountICCA.nenc{2}) => //.
- by proc; auto.
- by proc; inline *; sp; auto; call (: true); auto => /> /#.
- proc; inline *; sp; auto.
  if; auto => />; 2: by smt().
  by sp; call (: true); auto => /> /#.
qed.

lemma B_bound (O <: CCA_Oracle{CountCCA, CountICCA, A, B}) :
  hoare [C.CountAdv(B(A), O).main :
    true ==> CountCCA.ndec <= Ndec /\ CountCCA.nenc <= Nenc].
proof.
by conseq (AB_bound (<: O)) (A_bound (<: B(A, O).O')) => // /#.
qed.

local module Real' : Oracle = {
  import var Real
  include var Ideal [-enc]

  proc enc (m : plaintext) : ciphertext = {
    var e, c;

    e <$ dencseed;
    c <- enc(m, pk, e);
    cs <- (c, m) :: cs;
    return c;
  }
}.

local module RealS' : Oracle = {
  import var Real
  import var Ideal
  include Real' [-dec]

  proc dec (c : ciphertext) : plaintext option = {
    var fcs, m, p;

    fcs <- List.filter (fun p => fst p = c) cs;
    if (fcs <> []) {
      p <$ drat fcs;
      m <- Some (snd p);
    } else {
      m <- dec(c, sk);
    }
    return m;
  }
}.

local equiv Real_Real' :
  Game(Real, A).main ~ Game(Real', A).main : ={glob A} ==> ={res}.
proof.
proc; inline *.
call (: ={glob Real} /\
        (exists ks, (Real.pk, Real.sk){2} = (pkgen ks, skgen ks)) /\
        (forall c m, assoc Ideal.cs c = Some m => dec(c, Real.sk) = Some m){2}).
- by proc; auto.
- proc; wp; rnd; auto=> /> &m2 ks Hcs e _ c m'.
  rewrite assoc_cons.
  case: (c = enc (m{m2}, pkgen ks, e)) => />; 1: by rewrite encK.
  by move => _; apply: Hcs.
- by proc; wp; skip => /> &m2 ks Hcs /#.
- by wp; rnd; skip => /> /#.
qed.

local equiv Real'_CCA_L :
  Game(Real', A).main ~ CCA_L(S, B(A)).main : ={glob A} ==> ={res}.
proof.
proc; inline *; wp.
call (: ={pk, sk}(Real, CCA_Oracle) /\ unzip1 Ideal.cs{1} = CCA_Oracle.cs{2} /\
        (forall c m,assoc Ideal.cs c = Some m => dec(c, Real.sk) = Some m){1} /\
        (exists ks, (CCA_Oracle.pk, CCA_Oracle.sk){2} = (pkgen ks, skgen ks)) /\
        Ideal.cs{1} = B.cs{2} /\ B.pk{2} = CCA_Oracle.pk{2}).
- by proc; inline *; auto => />.
- proc; inline *; auto => /> &m1 &m2 Hcs ks ? ? e _ c m'.
  by rewrite assoc_cons; case : (c = enc (m{m2}, pkgen ks, e)) => />; smt(encK).
- proc; inline *; auto => /> &m1 &m2 Hcs ks ? ?.
  by rewrite -assocTP.
- by wp; rnd; skip => /> /#.
qed.

equiv Ideal_CCA_R :
  Game(Ideal, A).main ~ CCA_R(S, B(A)).main : ={glob A} ==> ={res}.
proof.
proc; inline *; wp.
call (: ={pk, sk}(Real, CCA_Oracle) /\ unzip1 Ideal.cs{1} = CCA_Oracle.cs{2} /\
        (forall c m,
           assoc Ideal.cs c = Some m => dec(c, Real.sk) = Some m0){1} /\
        (exists ks, (CCA_Oracle.pk, CCA_Oracle.sk){2} = (pkgen ks, skgen ks)) /\
        Ideal.cs{1} = B.cs{2} /\ B.pk{2} = CCA_Oracle.pk{2}).
- by proc; inline *; auto => />.
- proc; inline *; auto => /> &m1 &m2 Hcs ks ? ? e _ c m'.
  by rewrite assoc_cons; case : (c = enc (m0, pkgen ks, e)) => />; smt(encK).
- proc; inline *; auto => /> &m1 &m2 Hcs ks ? ?.
  by rewrite -assocTP.
- by wp; rnd; skip => /> /#.
qed.

lemma ICCA_CCALR &m :
  Pr[Game(Real, A).main() @ &m : res] - Pr[Game(Ideal, A).main() @ &m : res] =
  Pr[CCA_L(S, B(A)).main() @ &m : res] - Pr[CCA_R(S, B(A)).main() @ &m : res].
proof.
have -> : Pr[Game(Real, A).main() @ &m : res] = Pr[Game(Real', A).main() @ &m : res].
  by byequiv => //; conseq Real_Real'.
have -> : Pr[Game(Real', A).main() @ &m : res] = Pr[CCA_L(S, B(A)).main() @ &m : res].
  by byequiv => //; conseq  Real'_CCA_L.
have -> : Pr[Game(Ideal, A).main() @ &m : res] = Pr[CCA_R(S, B(A)).main() @ &m : res]. 
  by byequiv => //; conseq Ideal_CCA_R.
smt().
qed.

local equiv Real_RealS' :
  Game(Real, A).main ~ Game(RealS', A).main : ={glob A} ==> ={res}.
proof.
proc; inline *.
call (: ={glob Real} /\
        (exists ks, (Real.pk, Real.sk){2} = (pkgen ks, skgen ks)) /\
        (forall c p,
           p \in drat (List.filter (fun p => fst p = c) Ideal.cs) =>
           dec(c, Real.sk) = Some (snd p)){2}).
- by proc; auto.
- proc; wp; rnd; auto => /> &m2 ks csP e ? c ?.
  case: (enc (m{m2}, pkgen ks, e) = c); 2: by move => _; apply csP.
  rewrite supp_drat in_cons.
  move => ? [|]; 1: smt(encK).
  by rewrite -supp_drat; apply csP.
- proc; sp; if{2}; 2: by auto => />.
  auto => /> &m2 ? Hcs ?; split; 1: smt(drat_ll).
  by move => _; apply Hcs.
- by wp; rnd; skip => />; smt(supp_drat).
qed.

local equiv RealS'_CCA_L :
  Game(RealS', A).main ~ CCA_L(S, BS(A)).main : ={glob A} ==> ={res}.
proof.
proc; inline *; wp.
call (: ={pk, sk}(Real, CCA_Oracle) /\ unzip1 Ideal.cs{1} = CCA_Oracle.cs{2} /\
        (forall c m, (c, m) \in List.filter (fun p => fst p = c) Ideal.cs =>
                       dec(c, Real.sk) = Some m){1} /\
        (exists ks, (CCA_Oracle.pk, CCA_Oracle.sk){2} = (pkgen ks, skgen ks)) /\
        Ideal.cs{1} = BS.cs{2} /\ BS.pk{2} = CCA_Oracle.pk{2}).
- by proc; inline *; auto => />.
- proc; inline *; auto => /> &m1 &m2 Hcs ks skP ? e _ c ?.
  case: (enc (m{m2}, pkgen ks, e) = c).
  + move => ? [|?]; 1: smt(encK).
    by rewrite -skP; apply (Hcs c); smt().
  + by move => _ ?; rewrite -skP; apply (Hcs c); smt().
- proc; inline *; sp; auto; if; auto => /> &m1 &m2 5?.
  rewrite mem_map_fst; smt(mem_filter).
- by wp; rnd; skip => /> /#.
qed.

equiv IdealS_CCA_R :
  Game(IdealS, A).main ~ CCA_R(S, BS(A)).main : ={glob A} ==> ={res}.
proof.
proc; inline *; wp.
call (: ={pk, sk}(Real, CCA_Oracle) /\ unzip1 Ideal.cs{1} = CCA_Oracle.cs{2} /\
        (forall c m, (c, m) \in List.filter (fun p => fst p = c)
                                                   Ideal.cs =>
                       dec(c, Real.sk) = Some m0){1} /\
        (exists ks, (CCA_Oracle.pk, CCA_Oracle.sk){2} = (pkgen ks, skgen ks)) /\
        Ideal.cs{1} = BS.cs{2} /\ BS.pk{2} = CCA_Oracle.pk{2}).
- by proc; inline *; auto => />.
- proc; inline *; auto => /> &m1 &m2 Hcs ks skP ? e _ c m'.
  case: (enc (m0, pkgen ks, e) = c); 1: smt(encK).
  by move => _ ?; rewrite -skP; apply (Hcs c m'); smt().
- proc; inline *; sp; auto; if; auto => /> &m1 &m2 5?.
  rewrite mem_map_fst; smt(mem_filter).
- by wp; rnd; skip => /> /#.
qed.

lemma ICCA_CCALR' &m :
  Pr[Game(Real, A).main() @ &m : res] - Pr[Game(IdealS, A).main() @ &m : res] =
  Pr[CCA_L(S, BS(A)).main() @ &m : res] - Pr[CCA_R(S, BS(A)).main() @ &m : res].
proof.
have -> : Pr[Game(Real, A).main() @ &m : res] =
          Pr[Game(RealS', A).main() @ &m : res].
- byequiv => //; conseq (: ={glob A} ==> ={res}) => //; exact Real_RealS'.
have -> : Pr[Game(RealS', A).main() @ &m : res] =
          Pr[CCA_L(S, BS(A)).main() @ &m : res].
- byequiv => //; conseq (: ={glob A} ==> ={res}) => //; exact RealS'_CCA_L.
have -> : Pr[Game(IdealS, A).main() @ &m : res] =
          Pr[CCA_R(S, BS(A)).main() @ &m : res]; 2: by smt().
byequiv => //; conseq (: ={glob A} ==> ={res}) => //; exact IdealS_CCA_R.
qed.

end section.

end CCA.
back to top