https://github.com/EasyCrypt/easycrypt
Raw File
Tip revision: d3179246f8365b8ee7b73167a12e954f2bdfeb91 authored by Benjamin Gregoire on 14 December 2022, 09:56:45 UTC
Merge branch 'main' into deploy-quantum
Tip revision: d317924
composition_cost.ec
require import AllCore Real Distr.
require import CHoareTactic.
import StdBigop Bigint BIA.

type ('a, 'b) sum = [
  | Left of 'a
  | Right of 'b
].

op getl (s:('a, 'b) sum) = 
  with s = Left a  => Some a
  with s = Right _ => None.

op getr (s:('a, 'b) sum) = 
  with s = Left a  => None
  with s = Right b => Some b.

schema cost_getl ['a 'b] `{P} {s:('a, 'b) sum} : cost[P:getl s] = N 2 + cost[P:s].
schema cost_getr ['a 'b] `{P} {s:('a, 'b) sum} : cost[P:getr s] = N 2 + cost[P:s].
hint simplify cost_getl, cost_getr.

op smap (fa:'a -> 'c) (fb:'b -> 'd) (s: ('a, 'b) sum) = 
  with s = Left a  => Left (fa a)
  with s = Right b => Right (fb b).

schema cost_obind_getr ['a 'b] `{P} {e : ('a,'b)sum option}: 
  cost[P : obind getr e] = cost[P : e] + N 3.
hint simplify cost_obind_getr.

schema cost_obind_getl ['a 'b] `{P} {e : ('a,'b)sum option}: 
  cost[P : obind getl e] = cost[P : e] + N 3.
hint simplify cost_obind_getl.

schema cost_omap_Left ['a 'b] `{P} {e : 'a option}:
  cost[P : omap (Left<:'a,'b>) e] = cost[P:e] + N 2.
hint simplify cost_omap_Left.

schema cost_omap_Right ['a 'b] `{P} {e : 'b option}:
  cost[P : omap (Right<:'a,'b>) e] = cost[P:e] + N 2.
hint simplify cost_omap_Right.

schema cost_Left ['a 'b] `{P} {e:'a}:
  cost[P: Left<:'a, 'b> e] = cost [P: e] + N 1.

schema cost_Right ['a 'b] `{P} {e:'b}:
  cost[P: Right<:'a, 'b> e] = cost [P: e] + N 1.
hint simplify cost_Left.
hint simplify cost_Right.

type cenv = {
  cd    : int; (* self complexity of the distinguisher *)
  ci    : int; (* number of call to inputs             *)
  co    : int; (* number of call to outputs            *)
  cs    : int; (* number of call to step               *)
  cb    : int; (* number of call to backdoor           *)
}.
  
op cenv_pos (c:cenv) = 
   0 <= c.`cd /\
   0 <= c.`ci /\
   0 <= c.`co /\
   0 <= c.`cs /\
   0 <= c.`cb.

type cprot = {
  cpinit     : int;
  cpinputs   : int;
  cpoutputs  : int;
  cpstep     : int;
  cpbackdoor : int;
}.

op cprot_pos (cp:cprot) = 
  0 <= cp.`cpinit     /\
  0 <= cp.`cpinputs   /\
  0 <= cp.`cpoutputs  /\
  0 <= cp.`cpstep     /\
  0 <= cp.`cpbackdoor.

abstract theory ProtocolType.

(*
     
exists S, forall Z

We take the formalisation using dummy adversary 
      ------------------------      ------------------------
      |       Z              |      |       Z              |
      ------------------|    |      ------------------|    |
            : I/O       |    |            : I/O       |    |
      |-------------|   |    |      |-------------|   |    |
      |     P       |   |    |      |     P'      |   |    |
      |             |   |    |      |   |---------|   |    |
      |             | - |    |      |   |     : L     |    |
      |             | M |    |      |   | |-------|   |    |
      |             |   |    |      |   | |   S   | - |    |
      |-------------|   |----|      |-- | |-------| M |----|

Z communicates with P/P' and S 
P' (can also be a functionality F) communicates with S

*)
      
type inputs.         (* Z can send inputs to P/F *)
type ask_outputs.    (* Z can ask outputs to P/F *)
type outputs.        

type step.           (* Z can ask to do step *)
type ask_backdoor.   (* Z can ask backdoors to P/S, S can ask backdoors to F*)
type backdoor.

end ProtocolType.

abstract theory EXEC_MODEL.

clone include ProtocolType.

module type IO = {
  proc inputs (i:inputs) : unit
  proc outputs(o:ask_outputs) : outputs option
}.

module type BACKDOORS = {
  proc step (m:step) : unit
  proc backdoor (m:ask_backdoor) : backdoor option
}.

module type E_INTERFACE = {
  include IO
  include BACKDOORS
}.

module type PROTOCOL = {
  proc init() : unit
  include E_INTERFACE
}.

module type ENV (I:E_INTERFACE) = {
  proc distinguish () : bool
}.

module UC_emul (E:ENV) (P:PROTOCOL) = {
  proc main() = {
    var b;
    P.init();
    b <@ E(P).distinguish();
    return b;
  }
}.

abstract theory C.

  op c : cenv.

  axiom c_pos : cenv_pos c.

  module type CENV (I : E_INTERFACE) = {
    proc distinguish() : bool `{N c.`cd, I.inputs:c.`ci, I.outputs:c.`co, I.step:c.`cs, I.backdoor:c.`cb}
  }.

end C.

abstract theory CP.

  op cp : cprot.

  axiom cp_pos : cprot_pos cp.

  module type CPROTOCOL  = {
    proc init() : unit `{N cp.`cpinit}
    
    proc inputs(i : inputs) : unit `{N cp.`cpinputs}
    
    proc outputs(o : ask_outputs) : outputs option `{N cp.`cpoutputs}
    
    proc step(m : step) : unit `{N cp.`cpstep}
    
    proc backdoor(m : ask_backdoor) : backdoor option `{N cp.`cpbackdoor}
  }.

end CP.

end EXEC_MODEL.

type csim = {
  cinit : int;
  cstep : int;
  cs_s  : int;
  cs_b  : int;
  cbackdoor : int;
  cb_s  : int;
  cb_b  : int;
}.

op csim_pos (c:csim) = 
  0 <= c.`cinit /\
  0 <= c.`cstep /\
  0 <= c.`cs_s  /\ 
  0 <= c.`cs_b  /\ 
  0 <= c.`cbackdoor /\ 
  0 <= c.`cb_s  /\
  0 <= c.`cb_b.

abstract theory REAL_IDEAL.

  clone EXEC_MODEL as REAL.
  clone EXEC_MODEL as IDEAL with
    type inputs      <- REAL.inputs,
    type ask_outputs <- REAL.ask_outputs,
    type outputs     <- REAL.outputs.        

  module type SIMULATOR (FB:IDEAL.BACKDOORS) = {
    proc init() : unit {}
    include REAL.BACKDOORS
  }.

  (* Compose a functionality with a simulator --> protocol *)

  module CompS(F:IDEAL.PROTOCOL, S:SIMULATOR) : REAL.PROTOCOL = {
    proc init() = {
      F.init();
      S(F).init();
    }
    include F [ inputs, outputs]
    include S(F) [step, backdoor]
  }.

  abstract theory C.
    clone include REAL.C.

    op csi : csim.
    axiom csi_pos : csim_pos csi.

    module type CSIMULATOR (FB:IDEAL.BACKDOORS) = {
      proc init() : unit {} `{N csi.`cinit}
      proc step(m : REAL.step) : unit `{N csi.`cstep, FB.step:csi.`cs_s, FB.backdoor:csi.`cs_b}
      proc backdoor(m : REAL.ask_backdoor) : REAL.backdoor option `{N csi.`cbackdoor, FB.step:csi.`cb_s, FB.backdoor:csi.`cb_b}
    }.

  end C.

end REAL_IDEAL.

abstract theory NON_DUMMY.

  clone REAL_IDEAL as RI.

  (* We put the non-dummy adversary inside the protocol. *)

  type step_A.
  type ask_A.
  type get_A.

  clone REAL_IDEAL as NONDUMMY_RI with
    type REAL.inputs        <- RI.REAL.inputs,
    type REAL.ask_outputs   <- RI.REAL.ask_outputs,
    type REAL.outputs       <- RI.REAL.outputs,
    type REAL.step          <- step_A,
    type REAL.ask_backdoor  <- ask_A, 
    type REAL.backdoor      <- get_A,
    type IDEAL.step         <- RI.IDEAL.step,
    type IDEAL.ask_backdoor <- RI.IDEAL.ask_backdoor,
    type IDEAL.backdoor     <- RI.IDEAL.backdoor.

  (* A Can't have an init, which seems fine *)
  module type ADV(B : RI.REAL.BACKDOORS) = { 
     include NONDUMMY_RI.REAL.BACKDOORS
  }.

  module A_PROTOCOL(A : ADV, P : RI.REAL.PROTOCOL) : NONDUMMY_RI.REAL.PROTOCOL = {
     proc init() : unit = {
         P.init();
     }
     include P [inputs, outputs]
     include A(P) [step,backdoor]
  }.

(*  
  module P : DUMMY_RI.REAL.PROTOCOL.
  module F : DUMMY_RI.IDEAL.PROTOCOL.

  op eps : real.

  axiom security : 
    exists (S<: NONDUMMY_RI.SIMULATOR),
       forall (Z<: NONDUMMY_RI.REAL.ENV),
         `| Pr[NONDUMMY_RI.REAL.UC_emul(Z,A_PROTOCOL(A,P)).main() @ &m : res] -
            Pr[NONDUMMY_RI.REAL.UC_emul(Z,NONDUMMY_RI.CompS(F,S)).main() @ &m : res] | <= eps.
*)

end NON_DUMMY.

theory NONDUMMY_EQUIV_DUMMY.

  clone import NON_DUMMY as NON_DUMMY.
  
  type cadv = {
    cas   : int;
    cas_s : int;
    cas_b : int;
    cab   : int;
    cab_s : int;
    cab_b : int
  }.

  op cadv_pos cA = 
    0 <= cA.`cas   /\
    0 <= cA.`cas_s /\
    0 <= cA.`cas_b /\
    0 <= cA.`cab   /\
    0 <= cA.`cab_s /\
    0 <= cA.`cab_b.
  
   module (SeqSA(A:ADV, S:RI.SIMULATOR): NONDUMMY_RI.SIMULATOR) (B:NONDUMMY_RI.IDEAL.BACKDOORS) = {
     proc init () = {
       S(B).init();
     }

     include A(S(B))
  }.

  module SeqZA(Z:NONDUMMY_RI.REAL.ENV) (A:ADV) (I: RI.REAL.E_INTERFACE) = { 
    module IA = {
      proc inputs  = I.inputs
      proc outputs = I.outputs
      proc step    = A(I).step
      proc backdoor = A(I).backdoor
    }
    proc distinguish = Z(IA).distinguish
  }.

  op csa (cA:cadv) (cS:csim) = {|
    cinit = cS.`cinit;
    cstep = cA.`cas + cS.`cstep * cA.`cas_s + cS.`cbackdoor * cA.`cas_b;
    cs_s  = cS.`cs_s * cA.`cas_s + cS.`cb_s * cA.`cas_b;
    cs_b  = cS.`cs_b * cA.`cas_s + cS.`cb_b * cA.`cas_b;
    cbackdoor =  cA.`cab + cS.`cstep * cA.`cab_s + cS.`cbackdoor * cA.`cab_b;
    cb_s  = cS.`cs_s * cA.`cab_s + cS.`cb_s * cA.`cab_b;
    cb_b  = cS.`cs_b * cA.`cab_s + cS.`cb_b * cA.`cab_b;
  |}.

  lemma dummy_nondummy (P<:RI.REAL.PROTOCOL) (F<:RI.IDEAL.PROTOCOL) &m eps (cS: csim) :
    csim_pos cS => 
    forall (S<: RI.SIMULATOR
                 [init : `{N cS.`cinit} {},
                  step : `{N cS.`cstep, #FB.step : cS.`cs_s, #FB.backdoor : cS.`cs_b},
                  backdoor : `{N cS.`cbackdoor, #FB.step: cS.`cb_s, #FB.backdoor : cS.`cb_b}]
                 {-F}),
      (forall (Z<: RI.REAL.ENV {-P,-F,-S}),
        `| Pr[RI.REAL.UC_emul(Z,P).main() @ &m : res] -
           Pr[RI.REAL.UC_emul(Z,RI.CompS(F,S)).main() @ &m : res] | <= eps) =>

    forall (cA:cadv) (A<:ADV [step : `{N cA.`cas, #B.step: cA.`cas_s, #B.backdoor: cA.`cas_b},
                              backdoor : `{N cA.`cab, #B.step: cA.`cab_s, #B.backdoor: cA.`cab_b}]
                              {-F, -P, -S}),
     cadv_pos cA =>                       
     let csa = csa cA cS in
     exists (S1<: NONDUMMY_RI.SIMULATOR 
                  [init : `{N csa.`cinit} {},
                   step : `{N csa.`cstep, #FB.step : csa.`cs_s, #FB.backdoor : csa.`cs_b},
                   backdoor : `{N csa.`cbackdoor, #FB.step: csa.`cb_s, #FB.backdoor : csa.`cb_b}]
                  {+S, + A, -F}), (* Remark the -F, is redoundant since S and A cannot use it *)
      forall (Z<: NONDUMMY_RI.REAL.ENV {-A, -P, -F, -S1}),
        `| Pr[NONDUMMY_RI.REAL.UC_emul(Z,A_PROTOCOL(A,P)).main() @ &m : res] -
           Pr[NONDUMMY_RI.REAL.UC_emul(Z,NONDUMMY_RI.CompS(F,S1)).main() @ &m : res] | <= eps.
  proof.
    move=> hcS S hS cA A hcA csa.
    exists (SeqSA(A,S)); split.
    + (split; last split) => kb ks FB hkb hks.
      + by proc; call (:true; time []); skip => /= /#. 
      + move=> /=; proc true : time 
              [S(FB).step : [N (cS.`cstep + ks * cS.`cs_s + kb * cS.`cs_b)],
               S(FB).backdoor : [N (cS.`cbackdoor + ks * cS.`cb_s + kb * cS.`cb_b)]] => //=.
        + by rewrite !bigi_constz /#. 
        + move=> ???.
          proc true : time [FB.step : [N ks],
                            FB.backdoor : [N kb]] => //=.
          + by rewrite !bigi_constz /#.
          + by move=> ???;proc true : time [].
          by move=> ???;proc true : time [].
        move=> ???.
        proc true : time [FB.step : [N ks],
                          FB.backdoor : [N kb]] => //=.
        + by rewrite !bigi_constz /#.
        + by move=> ???;proc true : time [].
        by move=> ???;proc true : time [].
      move=> /=; proc true : time 
              [S(FB).step : [N (cS.`cstep + ks * cS.`cs_s + kb * cS.`cs_b)],
               S(FB).backdoor : [N (cS.`cbackdoor + ks * cS.`cb_s + kb * cS.`cb_b)]] => //=.
      + by rewrite !bigi_constz /#. 
      + move=> ???.
        proc true : time [FB.step : [N ks],
                          FB.backdoor : [N kb]] => //=.
        + by rewrite !bigi_constz /#.
        + by move=> ???;proc true : time [].
        by move=> ???;proc true : time [].
      move=> ???.
      proc true : time [FB.step : [N ks],
                        FB.backdoor : [N kb]] => //=.
      + by rewrite !bigi_constz /#.
      + by move=> ???;proc true : time [].
      by move=> ???;proc true : time [].
    move=> Z.
    have := hS (SeqZA(Z,A)).
    have -> : Pr[RI.REAL.UC_emul(SeqZA(Z, A), P).main() @ &m : res] = 
              Pr[NONDUMMY_RI.REAL.UC_emul(Z, A_PROTOCOL(A, P)).main() @ &m : res].
    + by byequiv => //; proc; inline *; sim.
    have -> // : Pr[RI.REAL.UC_emul(SeqZA(Z, A), RI.CompS(F, S)).main() @ &m : res] = 
                 Pr[NONDUMMY_RI.REAL.UC_emul(Z, NONDUMMY_RI.CompS(F, SeqSA(A, S))).main() @ &m : res].
    by byequiv => //; proc; inline *; sim.
  qed.

  theory NONDUMMY_DUMMY.

  clone import NON_DUMMY as ASSUMPTION1 with
      type step_A = RI.REAL.step,
      type ask_A  = RI.REAL.ask_backdoor,
      type get_A  = RI.REAL.backdoor.

  module (DUMMY_A : ADV) (B :RI.REAL.BACKDOORS)  = {
     proc step(s : RI.REAL.step) : unit = {
        B.step(s);
     }

     proc backdoor(b : RI.REAL.ask_backdoor) : RI.REAL.backdoor option = {
        var l;
        l <@ B.backdoor(b);
        return l;
     }
  }.

  (* A module with no exported procedure, this is used to encode any set of variable *)
  module type MEM = { }.

  lemma nondummy_dummy (P<:RI.REAL.PROTOCOL) (F<:RI.IDEAL.PROTOCOL) &m eps (cS: cadv -> csim) (Mem <: MEM):
    (forall cA, cadv_pos cA => csim_pos (cS cA)) => 
    (forall (cA:cadv) (A<:ADV [step : `{N cA.`cas, #B.step: cA.`cas_s, #B.backdoor: cA.`cas_b},
                     backdoor : `{N cA.`cab, #B.step: cA.`cab_s, #B.backdoor: cA.`cab_b} ]),
     let csa = cS cA in
     exists (S<: NONDUMMY_RI.SIMULATOR 
                  [init : `{N csa.`cinit} {},
                   step : `{N csa.`cstep, #FB.step : csa.`cs_s, #FB.backdoor : csa.`cs_b},
                   backdoor : `{N csa.`cbackdoor, #FB.step: csa.`cb_s, #FB.backdoor : csa.`cb_b}]
                  {+Mem, -F}),
      forall (Z<: NONDUMMY_RI.REAL.ENV {-A, -P, -F, -S}),
        `| Pr[NONDUMMY_RI.REAL.UC_emul(Z,A_PROTOCOL(A,P)).main() @ &m : res] -
           Pr[NONDUMMY_RI.REAL.UC_emul(Z,NONDUMMY_RI.CompS(F,S)).main() @ &m : res] | <= eps) =>
    let cdA = {| cas = 0; cas_s = 1; cas_b = 0; cab = 0; cab_s = 0; cab_b = 1 |} in
    let csd = cS cdA in
    (exists (S<: RI.SIMULATOR
                 [init : `{N csd.`cinit} {},
                  step : `{N csd.`cstep, #FB.step : csd.`cs_s, #FB.backdoor : csd.`cs_b},
                  backdoor : `{N csd.`cbackdoor, #FB.step: csd.`cb_s, #FB.backdoor : csd.`cb_b}]
                 {+Mem, -F}),
      forall (Z<: RI.REAL.ENV {-P,-F,-S}),
        `| Pr[RI.REAL.UC_emul(Z,P).main() @ &m : res] -
           Pr[RI.REAL.UC_emul(Z,RI.CompS(F,S)).main() @ &m : res] | <= eps).
  proof.
    move=> hcS hA cdA csd.  
    have /= [S hS]:= hA cdA (<:DUMMY_A) _ _.     
    + by move=> kBb kBs B ??; proc; call(:true; time []); auto.
    + by move=> kBb kBs B ??; proc; call(:true; time []); auto.
    exists S; split.
    + (split; last split) => kBb kBs FB ??.
      + by proc true : time []. 
      + proc true : time [FB.step : [N kBs],FB.backdoor :[N kBb]] => //=.
        + have cS_pos := hcS cdA _; 1: done.
          by rewrite !bigi_constz /#. 
        + by move=> *; proc true : time [].
        by move=> *; proc true : time [].
      proc true : time [FB.step : [N kBs],FB.backdoor :[N kBb]] => //=.
      + have cS_pos := hcS cdA _; 1: done.
        by rewrite !bigi_constz /#. 
      + by move=> *; proc true : time [].
      by move=> *; proc true : time [].                 
    move=> Z; have := hS Z. 
    have -> : Pr[NONDUMMY_RI.REAL.UC_emul(Z, A_PROTOCOL(DUMMY_A, P)).main() @ &m : res] = 
              Pr[RI.REAL.UC_emul(Z, P).main() @ &m : res].
    + byequiv => //; last by move=> ?? ->.
      proc; call (_: ={glob P}).
      + by proc *; inline *; call (:true).
      + by proc *; inline *; call (:true).
      + by proc *; inline *; call (:true); auto.
      + by proc *; inline *; wp; call (:true); auto.
      by inline *; call(:true); auto => /> ?? 4!->.
    have -> // : Pr[NONDUMMY_RI.REAL.UC_emul(Z, NONDUMMY_RI.CompS(F, S)).main() @ &m : res] =
              Pr[RI.REAL.UC_emul(Z, RI.CompS(F, S)).main() @ &m : res].
    byequiv => //; last by move=> ?? ->.
    proc; call (_: ={glob F, glob S}).
    + by proc *; inline *; call (:true).
    + by proc *; inline *; call (:true).
    + by proc *; inline *; call (: ={glob F}); auto; proc true.
    + by proc *; inline *; call (: ={glob F}); auto; proc true.
    by inline *; call(: ={glob F}); call(: true); auto => /> ?? 6!->.
  qed.

  end NONDUMMY_DUMMY.

end NONDUMMY_EQUIV_DUMMY.

abstract theory TRANSITIVITY.

  (* H1 := P2 realizes P1 /\ H2 := P3 realizes P2 => P3 realizes P1 *)

  clone REAL_IDEAL as H1.

  clone REAL_IDEAL as H2 with
    type REAL.inputs       <- H1.REAL.inputs,
    type REAL.ask_outputs  <- H1.REAL.ask_outputs,
    type REAL.outputs      <- H1.REAL.outputs,
    type REAL.step         <- H1.IDEAL.step,
    type REAL.ask_backdoor <- H1.IDEAL.ask_backdoor, 
    type REAL.backdoor     <- H1.IDEAL.backdoor.

  clone REAL_IDEAL as GOAL with
    type REAL.inputs       <- H1.REAL.inputs,
    type REAL.ask_outputs  <- H1.REAL.ask_outputs,
    type REAL.outputs      <- H1.REAL.outputs,
    type REAL.step         <- H1.REAL.step,
    type REAL.ask_backdoor <- H1.REAL.ask_backdoor, 
    type REAL.backdoor     <- H1.REAL.backdoor,
    type IDEAL.step         <- H2.IDEAL.step,
    type IDEAL.ask_backdoor <- H2.IDEAL.ask_backdoor, 
    type IDEAL.backdoor     <- H2.IDEAL.backdoor.

   module (SeqS(S1:H2.SIMULATOR, S2:H1.SIMULATOR): GOAL.SIMULATOR) (FB:GOAL.IDEAL.BACKDOORS) = {
     proc init () = {
       S1(FB).init();
       S2(S1(FB)).init();
     }

     include S2(S1(FB)) [-init] 
   }.

  module (CompZS(Z:GOAL.REAL.ENV)(S:H1.SIMULATOR) : H2.REAL.ENV) (I:H2.REAL.E_INTERFACE) = {
    module IZ = {
      include I [inputs, outputs]
      include S(I) 
    }

    proc distinguish() = {
      var b;
      IZ.init();
      b <@ Z(IZ).distinguish();
      return b;
    }
    
  }.

  section TRANS.
     declare module P1 <: GOAL.REAL.PROTOCOL.
     declare module P2 <: H1.IDEAL.PROTOCOL.
     declare module P3 <: H2.IDEAL.PROTOCOL.
     declare module S12 <: H1.SIMULATOR {-P2, -P3}.
     declare module S23 <: H2.SIMULATOR {-P3, -S12}.
  
     declare module Z <: GOAL.REAL.ENV { -P1, -P2, -P3, -S12, -S23}.
 
     lemma uc_transitivity &m : 
       `| Pr[GOAL.REAL.UC_emul(Z, P1).main() @ &m : res] - 
          Pr[GOAL.REAL.UC_emul(Z, GOAL.CompS(P3, SeqS(S23, S12))).main() @ &m : res] | <= 
       `| Pr[  H1.REAL.UC_emul(Z, P1).main() @ &m : res] - 
          Pr[  H1.REAL.UC_emul(Z, H1.CompS(P2, S12)).main() @ &m : res] | + 
       `| Pr[  H2.REAL.UC_emul(CompZS(Z,S12),P2).main() @ &m : res] - 
          Pr[  H2.REAL.UC_emul(CompZS(Z,S12),H2.CompS(P3, S23)).main() @ &m : res] |.
     proof.
      have -> : Pr[GOAL.REAL.UC_emul(Z,P1).main() @ &m : res] = Pr[H1.REAL.UC_emul(Z,P1).main() @ &m : res].
      + by byequiv => //; sim.
      have -> : 
        Pr[H1.REAL.UC_emul(Z, H1.CompS(P2, S12)).main() @ &m : res] = 
        Pr[H2.REAL.UC_emul(CompZS(Z,S12),P2).main() @ &m : res].
      + by byequiv => //; proc; inline *; wp; sim.
      have -> /#: 
        Pr[GOAL.REAL.UC_emul(Z, GOAL.CompS(P3, SeqS(S23, S12))).main() @ &m : res] = 
        Pr[H2.REAL.UC_emul(CompZS(Z, S12), H2.CompS(P3, S23)).main() @ &m : res].
      by byequiv => //; proc; inline *; wp; sim. 
    qed.

   end section TRANS.

   abstract theory C.

   op cz   : cenv.
   op cs12 : csim.
   op cs23 : csim.

   axiom cz_pos   : cenv_pos cz.
   axiom cs12_pos : csim_pos cs12.
   axiom cs23_pos : csim_pos cs23.

   op cz23 = {|
     cd = cz.`cd + cs12.`cinit + cz.`cs * cs12.`cstep + cz.`cb * cs12.`cbackdoor; 
     ci = cz.`ci;
     co = cz.`co;
     cs = cz.`cs * cs12.`cs_s + cz.`cb * cs12.`cb_s;
     cb = cz.`cs * cs12.`cs_b + cz.`cb * cs12.`cb_b;
   |}.

   op cs13 = {| 
       cinit = cs12.`cinit + cs23.`cinit;
       cstep = cs12.`cstep + cs23.`cstep * cs12.`cs_s + cs23.`cbackdoor * cs12.`cs_b;
       cs_s  = cs12.`cs_s * cs23.`cs_s + cs12.`cs_b * cs23.`cb_s;
       cs_b  = cs12.`cs_s * cs23.`cs_b + cs12.`cs_b * cs23.`cb_b;
       cbackdoor = cs12.`cbackdoor + cs23.`cstep * cs12.`cb_s + cs23.`cbackdoor * cs12.`cb_b;
       cb_s  = cs12.`cb_s * cs23.`cs_s + cs12.`cb_b * cs23.`cb_s;
       cb_b  = cs12.`cb_s * cs23.`cs_b + cs12.`cb_b * cs23.`cb_b;
     |}.

   clone H1.C as CH1 with
     op c <- cz,
     op csi <- cs12
     proof * by smt(cz_pos cs12_pos cs23_pos).
   
   clone H2.C as CH2 with 
     op c <- cz23,
     op csi <- cs23
     proof * by smt(cz_pos cs12_pos cs23_pos).

   clone GOAL.C as CGOAL with
     op c <- cz,
     op csi <- cs13 
     proof * by smt (cz_pos cs12_pos cs23_pos).

   section.

   declare module P1  <: GOAL.REAL.PROTOCOL.
   declare module P2  <: H1.IDEAL.PROTOCOL.
   declare module P3  <: H2.IDEAL.PROTOCOL.
   declare module S12 <: CH1.CSIMULATOR {-P2, -P3}.
   declare module S23 <: CH2.CSIMULATOR {-P3, -S12}.

   lemma cost_S23_step (kbackdoor kstep : int) (FB <: GOAL.IDEAL.BACKDOORS[backdoor : `{N kbackdoor} , step : `{N kstep} ]) :
     choare[S23(FB).step] time [N cs23.`cstep; FB.step : cs23.`cs_s; FB.backdoor : cs23.`cs_b].
   proof.
     proc true : time [FB.step : [N kstep], FB.backdoor : [N kbackdoor]] => //=.
     + by rewrite !bigi_constz; smt(cs23_pos). 
     + by move=> /= *; proc true : time [].
     by move=> /= *; proc true : time [].
   qed.

   lemma cost_S23_backdoor (kbackdoor kstep : int) (FB <: GOAL.IDEAL.BACKDOORS[backdoor : `{N kbackdoor} , step : `{N kstep} ]) :
       choare[S23(FB).backdoor] time [N cs23.`cbackdoor; FB.step : cs23.`cb_s; FB.backdoor : cs23.`cb_b].
   proof.
     proc true : time [FB.step : [N kstep], FB.backdoor : [N kbackdoor]] => //=.
     + by rewrite !bigi_constz; smt(cs23_pos). 
     + by move=> /= *; proc true : time [].
     by move=> /= *; proc true : time [].
   qed.

   lemma cost_SeqS_init (FB <: GOAL.IDEAL.BACKDOORS):
      choare[SeqS(S23, S12, FB).init] time [N cs13.`cinit].
   proof. proc; call(:true);call(:true);skip => /#. qed.
  
   lemma cost_SeqS_step (kbackdoor kstep : int) (FB <: GOAL.IDEAL.BACKDOORS[backdoor : `{N kbackdoor} , step : `{N kstep} ]): 
      choare[S12(S23(FB)).step] time [N cs13.`cstep; FB.step : cs13.`cs_s; FB.backdoor : cs13.`cs_b].
   proof.
     proc true: time [S23(FB).step: [N cs23.`cstep; FB.step: cs23.`cs_s; FB.backdoor: cs23.`cs_b],
                      S23(FB).backdoor: [N cs23.`cbackdoor; FB.step: cs23.`cb_s; FB.backdoor: cs23.`cb_b]] => //=.
     + by rewrite !bigi_constz; smt(cs12_pos).
     + by move=> ???; apply (cost_S23_step kbackdoor kstep FB).
     by move=> ???; apply (cost_S23_backdoor kbackdoor kstep FB).
   qed.

   lemma cost_SeqS_backdoor (kbackdoor kstep : int) (FB <: GOAL.IDEAL.BACKDOORS[backdoor : `{N kbackdoor} , step : `{N kstep} ]): 
      choare[S12(S23(FB)).backdoor] time [N cs13.`cbackdoor; FB.step : cs13.`cb_s; FB.backdoor : cs13.`cb_b].
   proof.
     proc true: time [S23(FB).step: [N cs23.`cstep; FB.step: cs23.`cs_s; FB.backdoor: cs23.`cs_b],
                      S23(FB).backdoor: [N cs23.`cbackdoor; FB.step: cs23.`cb_s; FB.backdoor: cs23.`cb_b]] => //=.
     + by rewrite !bigi_constz; smt(cs12_pos). 
     + move=> *; apply (cost_S23_step kbackdoor kstep FB).
     by move=> *; apply (cost_S23_backdoor kbackdoor kstep FB).
   qed. 

   lemma ex_uc_transitivity &m (e1 e2:real):
      (forall (Z<:CH1.CENV{-P1, -P2, -S12, -P3, -S23}),
          `| Pr[H1.REAL.UC_emul(Z,P1).main() @ &m : res] - 
             Pr[H1.REAL.UC_emul(Z,H1.CompS(P2, S12)).main() @ &m : res] | <= e1) =>
      (forall (Z<:CH2.CENV{-P2,-P3,-S23, -P1}),
          `| Pr[H2.REAL.UC_emul(Z,P2).main() @ &m : res] - 
             Pr[H2.REAL.UC_emul(Z,H2.CompS(P3, S23)).main() @ &m : res] | <= e2) =>
      (exists (S13<:CGOAL.CSIMULATOR {+S12,+S23, -P3}),
        forall (Z<:CGOAL.CENV{-P1,-P2,-P3,-S12,-S23}),
          `| Pr[GOAL.REAL.UC_emul(Z,P1).main() @ &m : res] - 
             Pr[GOAL.REAL.UC_emul(Z,GOAL.CompS(P3, S13)).main() @ &m : res] | <= e1 + e2).
   proof.
     move=> hS12 hS23. 
     exists (SeqS(S23, S12)) => /=. 
     split.
     + split.
       + by move=> k1 k2 FB *; apply (cost_SeqS_init FB). 
       split.
       + by move=> k1 k2 FB *; apply (cost_SeqS_step k1 k2 FB).
       by move=> k1 k2 FB *; apply (cost_SeqS_backdoor k1 k2 FB).
     move=> Z.
     have := uc_transitivity P1 P2 P3 S12 S23 Z &m.
     have := hS12 Z. 
     have /# := hS23 (CompZS(Z,S12)) _.
     move=> kb ks ko ki I ????.
     proc; inline *.
     call (_:true; time [CompZS(Z, S12, I).IZ.inputs:['0; I.inputs: 1],
                         CompZS(Z, S12, I).IZ.outputs:['0; I.outputs: 1],
                         CompZS(Z, S12, I).IZ.step:
                                 [             N cs12.`cstep; 
                                  I.step     : cs12.`cs_s; 
                                  I.backdoor : cs12.`cs_b],
                         CompZS(Z, S12, I).IZ.backdoor:
                                 [             N cs12.`cbackdoor; 
                                  I.step     : cs12.`cb_s; 
                                  I.backdoor : cs12.`cb_b]]).
     + by move=> /= *; proc true : time []. 
     + by move=> /= *; proc true : time [].
     + move=> * /=.
       proc true: time [I.step :[N ks], I.backdoor : [N kb]] => //.
       + by rewrite !bigi_constz; smt(cs12_pos).
       + by move=> /= *; proc true : time [].
       by move=> /= *; proc true : time []. 
     + move=> * /=.
       proc true: time [I.step :[N ks], I.backdoor : [N kb]] => //.
       + by rewrite !bigi_constz; smt(cs12_pos).
       + by move=> /= *; proc true : time [].
       by move=> /= *; proc true : time []. 
     call (:true); skip => /=.
     by rewrite !big1_eq /= !bigi_constz /=;smt(cz_pos).
   qed.

  end section.

  end C.

end TRANSITIVITY.

theory COMPOSITION.

  (* Pi realizes f => R^Pi realizes R^f 
      
  ____IO___
  |    ____|    
  | R  |_f_| - backdoor
  |________| - backdoor *)

  clone REAL_IDEAL as Pi.

  clone EXEC_MODEL as R.

  clone REAL_IDEAL as RPi with
    type REAL.inputs       <- R.inputs,
    type REAL.ask_outputs  <- R.ask_outputs,
    type REAL.outputs      <- R.outputs,  
    type REAL.step         = (R.step, Pi.REAL.step) sum,
    type REAL.ask_backdoor = (R.ask_backdoor, Pi.REAL.ask_backdoor) sum,
    type REAL.backdoor     = (R.backdoor, Pi.REAL.backdoor) sum,
    type IDEAL.step         = (R.step, Pi.IDEAL.step) sum,
    type IDEAL.ask_backdoor = (R.ask_backdoor, Pi.IDEAL.ask_backdoor) sum,
    type IDEAL.backdoor     = (R.backdoor, Pi.IDEAL.backdoor) sum.

  module type RHO (P:Pi.REAL.IO) = {
    proc init() : unit {}
    include R.PROTOCOL [-init]
  }.

  module CompR_I(Rho:RHO) (P:Pi.REAL.E_INTERFACE) : RPi.REAL.E_INTERFACE = {

    include Rho(P) [inputs, outputs]

    proc step (m:RPi.REAL.step) : unit = {
      match (m) with
      | Left  m1 => { Rho(P).step(m1); }
      | Right m2 => { P.step(m2); }
      end;
    }

    proc backdoor (m:RPi.REAL.ask_backdoor) : RPi.REAL.backdoor option = {
      var r1, r2, r;
      match (m) with
      | Left m1  => { r1 <@ Rho(P).backdoor(m1); r <- omap Left r1; }
      | Right m2 => { r2 <@ P.backdoor(m2);      r <- omap Right r2; }
      end;
      return r;
    }
  }.

  module CompRP(Rho:RHO) (P:Pi.REAL.PROTOCOL) : RPi.REAL.PROTOCOL = {
    
    proc init () = {
      P.init();
      Rho(P).init();
    }
    include CompR_I(Rho,P) 
  }.

  module CompRF(Rho:RHO) (F:Pi.IDEAL.PROTOCOL) : RPi.IDEAL.PROTOCOL = {
    
    proc init () = {
      F.init();
      Rho(F).init();
    }
    
    include Rho(F) [inputs, outputs]

    proc step (m:RPi.IDEAL.step) : unit = {
      match (m) with
      | Left  m1 => { Rho(F).step(m1); }
      | Right m2 => { F.step(m2); }
      end;
    }

    proc backdoor (m:RPi.IDEAL.ask_backdoor) : RPi.IDEAL.backdoor option = {
      var r1, r2, r;
      match (m) with
      | Left m1  => { r1 <@ Rho(F).backdoor(m1); r <- omap Left r1; }
      | Right m2 => { r2 <@ F.backdoor(m2); r <- omap Right r2; }
      end;
      return r;
    }

  }.

  module (Sid(Sf:Pi.SIMULATOR) : RPi.SIMULATOR) (FB : RPi.IDEAL.BACKDOORS) = {
  
    module FBPi = {
      proc step(m:Pi.IDEAL.step) : unit = {
        FB.step(Right m);
      }
      
      proc backdoor(m:Pi.IDEAL.ask_backdoor) : Pi.IDEAL.backdoor option = {
        var r;
        r <@ FB.backdoor (Right m);
        return obind getr r;
      }
    }
    
    proc init = Sf(FBPi).init 

    proc step(m:RPi.REAL.step) : unit = {
      match (m) with
      | Left m1  => { FB.step(Left m1); }
      | Right m2 => { Sf(FBPi).step(m2); }
      end;
    }
    
    proc backdoor(m:RPi.REAL.ask_backdoor) : RPi.REAL.backdoor option = {
      var r1, r2;
      var r :RPi.REAL.backdoor option;
      match (m) with
      | Left  m1 => { r1 <@ FB.backdoor(Left m1); r <- omap Left (obind getl r1); } 
      | Right m2 => { r2 <@ Sf(FBPi).backdoor(m2); r <- omap Right r2; }
      end;
      return r;
    }
  }.

  module (CompZR(Z:RPi.REAL.ENV, Rho:RHO):Pi.REAL.ENV) (I : Pi.REAL.E_INTERFACE) = {
    proc distinguish() : bool = {
      var b;
      Rho(I).init();
      b <@ Z(CompR_I(Rho,I)).distinguish();
      return b;
    }
  }.
      
  section COMP.
 
    declare module P  <: Pi.REAL.PROTOCOL.
    declare module Ff <: Pi.IDEAL.PROTOCOL.
    declare module Sf <: Pi.SIMULATOR {-Ff}.

    declare module Rho<: RHO {-P, -Ff, -Sf}.
 
    declare module Z<: RPi.REAL.ENV {-Rho, -P, -Ff, -Sf}.

    lemma compose &m : 
      Pr[RPi.REAL.UC_emul(Z,CompRP(Rho,P)).main() @ &m : res] - 
        Pr[RPi.REAL.UC_emul(Z,RPi.CompS(CompRF(Rho,Ff), Sid(Sf))).main() @ &m : res] = 
      Pr[Pi.REAL.UC_emul(CompZR(Z,Rho), P).main() @ &m : res] - 
        Pr[Pi.REAL.UC_emul(CompZR(Z,Rho), Pi.CompS(Ff,Sf)).main() @ &m : res].
    proof.
      have -> :  
        Pr[RPi.REAL.UC_emul(Z,CompRP(Rho,P)).main() @ &m : res] =
        Pr[Pi.REAL.UC_emul(CompZR(Z,Rho), P).main() @ &m : res].
      + by byequiv => //; proc; inline *; wp; sim.
      have -> // : 
        Pr[RPi.REAL.UC_emul(Z,RPi.CompS(CompRF(Rho,Ff), Sid(Sf))).main() @ &m : res] =
        Pr[Pi.REAL.UC_emul(CompZR(Z,Rho), Pi.CompS(Ff,Sf)).main() @ &m : res]. 
      byequiv => //; proc; inline *; wp.
      call (_ : = {glob Rho, glob Ff, glob Sf}); 1,2:sim. 
      + proc; match; 1,2: smt().
        + move => m1 m2; inline *; match Left {1} 2.  
          + by auto => /#.
          call (_: ={glob Ff}); 1,2:sim.
          by auto => /> /#.
        move => m1 m2; call (_: ={glob Rho, glob Ff}); last by auto => />.
        + proc *; inline *; match Right {1} 3; 1: by auto => /#.
          by call (_: true); auto => /> /#.
        proc *; inline *;match Right {1} 3; 1: by auto => /#.
        by wp; call (_: true); auto => /> /#.
      + proc; match; 1,2: smt().
        + move=> m1 m2; wp; inline *; match Left {1} 2; 1: by auto => /#.
          by wp; call (_: ={glob Ff}); 1,2:sim; auto => /> /#.
        move=> m1 m2; wp; inline *; call (_: ={glob Rho, glob Ff}). 
        + proc *; inline *; match Right {1} 3; 1: by auto => /#.
          by call (_: true); auto => /> /#.
        + proc *; inline *; match Right {1} 3; 1: by auto => /#.
          by wp; call (_: true); auto => /> /#.       
        by auto => />.
      by swap{1} 2 1; sim />. 
    qed.
 
  end section COMP.

  abstract theory C.

  type crho = {
    cr_init : int;
    cr_in   : int;
    cr_in_i : int;
    cr_in_o : int;
    cr_out  : int;
    cr_out_i : int;
    cr_out_o : int;
    cr_st    : int;
    cr_st_i  : int;
    cr_st_o  : int;
    cr_bk    : int;
    cr_bk_i  : int;
    cr_bk_o  : int;
  }.

  op crho_pos (cr: crho) = 
    0 <= cr.`cr_init  /\
    0 <= cr.`cr_in    /\
    0 <= cr.`cr_in_i  /\
    0 <= cr.`cr_in_o  /\
    0 <= cr.`cr_out   /\
    0 <= cr.`cr_out_i /\
    0 <= cr.`cr_out_o /\
    0 <= cr.`cr_st    /\
    0 <= cr.`cr_st_i  /\
    0 <= cr.`cr_st_o  /\
    0 <= cr.`cr_bk    /\
    0 <= cr.`cr_bk_i  /\
    0 <= cr.`cr_bk_o.
  
  op cr : crho.
  axiom cr_pos : crho_pos cr.

  module type CRHO(P : Pi.REAL.IO)  = {
    proc init() : unit {} `{ N cr.`cr_init}
  
    proc inputs(i : R.inputs) : unit `{N cr.`cr_in, P.inputs : cr.`cr_in_i, P.outputs : cr.`cr_in_o} 
  
    proc outputs(o : R.ask_outputs) : R.outputs option `{N cr.`cr_out, P.inputs : cr.`cr_out_i, P.outputs : cr.`cr_out_o} 
  
    proc step(m : R.step) : unit `{N cr.`cr_st, P.inputs : cr.`cr_st_i, P.outputs : cr.`cr_st_o} 
  
    proc backdoor(m : R.ask_backdoor) : R.backdoor option `{N cr.`cr_bk, P.inputs : cr.`cr_bk_i, P.outputs : cr.`cr_bk_o} 
  }.

  op csi : csim.
  axiom csi_pos : csim_pos csi.

  op rcsi = {|
    cinit = csi.`cinit;
    cstep = max 2 (1 + csi.`cs_s + csi.`cs_b * 4 + csi.`cstep) ;
    cs_s  = max 1 csi.`cs_s;
    cs_b  = csi.`cs_b;
    cbackdoor = max 7 (3 + csi.`cb_s + csi.`cb_b * 4 + csi.`cbackdoor + csi.`cstep);
    cb_s = csi.`cb_s;
    cb_b = max 1 csi.`cb_b; 
  |}.

  lemma rcsi_pos : csim_pos rcsi by smt(csi_pos).

  (* complexity or the environement for the reduction CompRP(Rho,P) *)
  op rc : cenv.
  axiom rc_pos : cenv_pos rc.

  (* complexity or the environement of the reduction *)
  op c = {|
    cd = cr.`cr_init + rc.`cd + rc.`ci * cr.`cr_in + rc.`co * cr.`cr_out + rc.`cs * (1 + cr.`cr_st) + rc.`cb * (3 + cr.`cr_bk);
    ci = cr.`cr_in_i * rc.`ci + cr.`cr_out_i * rc.`co + cr.`cr_st_i * rc.`cs + cr.`cr_bk_i * rc.`cb;
    co = cr.`cr_in_o * rc.`ci + cr.`cr_out_o * rc.`co + cr.`cr_st_o * rc.`cs + cr.`cr_bk_o * rc.`cb;
    cs = rc.`cs;
    cb = rc.`cb;
  |}.  

  lemma c_pos : cenv_pos c by smt (rc_pos cr_pos). 

  clone Pi.C as CPi with
    op csi <- csi,
    op c   <- c
    proof csi_pos by apply csi_pos
    proof c_pos by apply c_pos.

  clone RPi.C as CRPi with
    op csi <- rcsi,
    op c <- rc
    proof csi_pos by apply rcsi_pos
    proof c_pos by apply rc_pos.

  section.

   declare module P  <: Pi.REAL.PROTOCOL.
   declare module Ff <: Pi.IDEAL.PROTOCOL.
   declare module Sf <: CPi.CSIMULATOR {-Ff}.

   declare module Rho<: CRHO {-P, -Ff, -Sf}.

   lemma ex_compose e &m : 
     (forall (Z <: CPi.CENV {-P, -Ff, -Sf}),
       `|Pr[Pi.REAL.UC_emul(Z, P).main() @ &m : res] - 
          Pr[Pi.REAL.UC_emul(Z, Pi.CompS(Ff,Sf)).main() @ &m : res]| <= e) =>
     exists (S<:CRPi.CSIMULATOR{+Sf}),
     (forall (Z <: CRPi.CENV {-Rho,-P,-Ff,-Sf}),
       `|Pr[RPi.REAL.UC_emul(Z,CompRP(Rho,P)).main() @ &m : res] - 
          Pr[RPi.REAL.UC_emul(Z,RPi.CompS(CompRF(Rho,Ff), S)).main() @ &m : res]| <= e).
   proof.
     move=> h; exists (Sid(Sf)); split.
     (* Complexity of the simulator *)
     + split.
       + by move=> kb ks FB hkb hks; proc true : time [].
       split.
       + move=> kb ks FB hkb hks; proc.
         exlim m => -[] mi. 
         + match Left 1; [1: by auto; smt() | 2: done].
           call(:true; time []); auto => />; smt(csi_pos).  
         match Right 1; [1: by auto; smt() | 2: done].
         call (:true; time [Sid(Sf, FB).FBPi.step : ['1; FB.step : 1], Sid(Sf, FB).FBPi.backdoor : [N 4; FB.backdoor : 1]]).
         + by move=> *; proc; call (:true; time []); skip => />.
         + by move=> *; proc; call (:true; time []); skip => />.
         by skip => &hr />; rewrite !bigi_constz /=; smt(csi_pos).
       move=> kb ks FB hkb hks; proc.
       exlim m => -[] mi.  
       + match Left 1; [1: by auto; smt() | 2: done].
         wp; call(:true; time []); auto => />; smt(csi_pos).
       match Right 1; [1: by auto; smt() | 2: done].
       wp;call (:true; time [Sid(Sf, FB).FBPi.step : ['1; FB.step : 1], Sid(Sf, FB).FBPi.backdoor : [N 4; FB.backdoor : 1]]).
       + by move=> *; proc; call (:true; time []); skip => />.
       + by move=> *; proc; call (:true; time []); skip => />.
       skip => &hr />; rewrite !bigi_constz /=; smt(csi_pos).
     move=> Z; rewrite (compose P Ff Sf Rho Z &m).
     (* Complexity of the environment *)
     apply (h (CompZR(Z,Rho))).
     move=> kb ks ko ki I hkb hks hko hki; proc.
     call(:true;time[CompR_I(Rho, I).inputs   : [N cr.`cr_in; I.inputs : cr.`cr_in_i; I.outputs : cr.`cr_in_o],
                     CompR_I(Rho, I).outputs  : [N cr.`cr_out; I.inputs : cr.`cr_out_i; I.outputs : cr.`cr_out_o],
                     CompR_I(Rho, I).step     : [N (1 + cr.`cr_st); I.inputs : cr.`cr_st_i; I.outputs : cr.`cr_st_o; I.step : 1],
                     CompR_I(Rho, I).backdoor : [N (3 + cr.`cr_bk); I.inputs : cr.`cr_bk_i; I.outputs : cr.`cr_bk_o; I.backdoor : 1]]).
     + move=> *; proc true : time [I.inputs: [N ki], I.outputs: [N ko]] => />.
       + by rewrite !bigi_constz /=; smt (cr_pos).
       + by move=> *; proc true : time [].
       by move=> *; proc true : time [].
     + move=> *; proc true : time [I.inputs : [N ki], I.outputs : [N ko]] => />.
       + rewrite !bigi_constz /=; smt (cr_pos). 
       + by move=> *; proc true : time [].
       by move=> *; proc true : time [].
     + move=> *; proc.
       exlim m => -[] mi. 
       + match Left 1; [1: by auto; smt() | 2: done].
         call(:true; time [I.inputs : [N ki], I.outputs : [N ko]]).
         + by move=> *; proc true : time [].
         + by move=> *; proc true : time [].
         skip => />; rewrite !bigi_constz /=; smt(cr_pos). 
       match Right 1; [1: by auto; smt() | 2: done].
       by call (:true); skip => />; smt (cr_pos).
     + move=> *; proc.
       exlim m => -[] mi. 
       + match Left 1; [1: by auto; smt() | 2: done].
         wp; call(:true; time [I.inputs : [N ki], I.outputs : [N ko]]).
         + by move=> *; proc true : time [].
         + by move=> *; proc true : time [].
         skip => />; rewrite !bigi_constz /=; smt(cr_pos).
       match Right 1; [1: by auto; smt() | 2: done].
       by wp; call (:true); skip => />; smt (cr_pos). 
     call (:true); skip => />.
     rewrite !bigi_constz /=; smt(rc_pos).
   qed.
  
  end section.
  
  end C.

  clone import TRANSITIVITY as TRANS with 
    type H1.REAL.inputs       <- R.inputs,
    type H1.REAL.ask_outputs  <- R.ask_outputs,
    type H1.REAL.outputs      <- R.outputs,
    type H1.REAL.step         <- RPi.REAL.step,
    type H1.REAL.ask_backdoor <- RPi.REAL.ask_backdoor, 
    type H1.REAL.backdoor     <- RPi.REAL.backdoor,
    type H1.IDEAL.step         <- RPi.IDEAL.step,
    type H1.IDEAL.ask_backdoor <- RPi.IDEAL.ask_backdoor, 
    type H1.IDEAL.backdoor     <- RPi.IDEAL.backdoor.

  section StrongCOMP.

    declare module P  <: Pi.REAL.PROTOCOL.
    declare module Ff <: Pi.IDEAL.PROTOCOL.
    declare module Sf <: Pi.SIMULATOR {-Ff}.

    declare module Rho<: RHO {-P, -Ff, -Sf}.
    declare module F <: H2.IDEAL.PROTOCOL {-Sf}.
    declare module S <: H2.SIMULATOR {-F, -Sf}.

    declare module Z<: RPi.REAL.ENV {-Rho, -P, -Ff, -Sf, -F, -S}.

    lemma strong_compose &m : 
      `| Pr[GOAL.REAL.UC_emul(Z,CompRP(Rho,P)).main() @ &m : res] - 
         Pr[GOAL.REAL.UC_emul(Z,GOAL.CompS(F, SeqS(S, Sid(Sf)))).main() @ &m : res] |
       <= 
      `| Pr[Pi.REAL.UC_emul(CompZR(Z,Rho), P).main() @ &m : res] - 
         Pr[Pi.REAL.UC_emul(CompZR(Z,Rho), Pi.CompS(Ff,Sf)).main() @ &m : res] | + 
      `| Pr[H2.REAL.UC_emul(CompZS(Z, Sid(Sf)), CompRF(Rho,Ff)).main() @ &m : res] - 
         Pr[H2.REAL.UC_emul(CompZS(Z, Sid(Sf)), H2.CompS(F,S)).main() @ &m : res] |.
    proof.
      have h1 := uc_transitivity (CompRP(Rho,P)) (CompRF(Rho,Ff)) F (Sid(Sf)) S Z &m.
      have h2 := compose P Ff Sf Rho Z &m.
      apply (StdOrder.RealOrder.ler_trans _ _ _ h1) => {h1}.
      have -> : Pr[H1.REAL.UC_emul(Z, CompRP(Rho, P)).main() @ &m : res] = 
                Pr[Pi.REAL.UC_emul(CompZR(Z, Rho), P).main() @ &m : res].
      + by byequiv=> //; proc; inline *; wp; sim.
      have -> // : Pr[H1.REAL.UC_emul(Z, H1.CompS(CompRF(Rho, Ff), Sid(Sf))).main() @ &m : res] =
                   Pr[Pi.REAL.UC_emul(CompZR(Z, Rho), Pi.CompS(Ff, Sf)).main() @ &m : res].
      byequiv=> //; proc; inline *; wp. swap{1} 2 1.
      call (_: ={glob Rho, glob Ff,glob Sf}); 1,2:sim.
      + proc; match; 1,2: smt().
        + move=> m1 m2; inline *; match Left {1} 2; 1: by auto => /> /#.
          call (_: ={glob Ff} ); 1,2:sim.
          by auto => /#.
        move=> m1 m2; inline *; call (_: ={glob Rho, glob Ff}). 
        + proc *; inline *; match Right {1} 3; 1: by auto => /> /#.
          by call (_: true); auto => /> /#.
        + proc *; inline *; match Right {1} 3; 1: by auto => /> /#.
          auto; call (_: true);auto => /> /#.
        by auto => />.
      + proc; match;1,2: smt().
        + move=> m1 m2; inline *; match Left {1} 2; 1: by auto => /> /#.
          wp;call (_: ={glob Ff} ); 1,2:sim.
          by auto => /#.
        move=> m1 m2; inline *; wp; call (_: ={glob Rho, glob Ff}). 
        + proc *; inline *; match Right {1} 3; 1: by auto => /> /#.
          by call (_: true); auto => /> /#.
        + proc *; inline *; match Right {1} 3; 1: by auto => /> /#.
          auto; call (_: true);auto => /> /#.
        by auto => />.
      sim />.
     qed.

  end section StrongCOMP.

  abstract theory SC.

  op csi : csim.

  axiom csi_pos : csim_pos csi.

  op rcsi : csim =
    {| cinit = csi.`cinit; cstep = max 2 (1 + csi.`cs_s + csi.`cs_b * 4 + csi.`cstep); cs_s =
      max 1 csi.`cs_s; cs_b = csi.`cs_b; cbackdoor =
      max 7 (3 + csi.`cb_s + csi.`cb_b * 4 + csi.`cbackdoor + csi.`cstep); cb_s = csi.`cb_s; cb_b =
      max 1 csi.`cb_b; |}.
  
  lemma rcsi_pos : csim_pos rcsi by smt (csi_pos).

  clone TRANS.C as CT
    with op cs12 <- rcsi
    proof cs12_pos by apply rcsi_pos.

  clone COMPOSITION.C as C0 with 
    op rc <- CT.cz,
    op csi <- csi
    proof rc_pos by apply CT.cz_pos
    proof csi_pos by apply csi_pos.

  section.  
    declare module P  <: Pi.REAL.PROTOCOL.
    declare module Ff <: Pi.IDEAL.PROTOCOL.
    declare module Sf <: C0.CPi.CSIMULATOR {-Ff}.

    declare module Rho<: C0.CRHO {-P, -Ff, -Sf}.
    declare module F <: H2.IDEAL.PROTOCOL {-Sf}.
    declare module S <: CT.CH2.CSIMULATOR {-F, -Sf}.

    lemma ex_strong_compose &m e1 e2 : 
      (forall (Z <: C0.CPi.CENV {-P, -Ff, -Sf}),
        `| Pr[Pi.REAL.UC_emul(Z, P).main() @ &m : res] - 
         Pr[Pi.REAL.UC_emul(Z, Pi.CompS(Ff,Sf)).main() @ &m : res] | <= e1) =>
      (forall (Z <: CT.CH2.CENV {-CompRF(Rho,Ff), -F, -S}),
        `| Pr[H2.REAL.UC_emul(Z, CompRF(Rho,Ff)).main() @ &m : res] - 
           Pr[H2.REAL.UC_emul(Z, H2.CompS(F,S)).main() @ &m : res] | <= e2) =>
      exists (S' <: CT.CGOAL.CSIMULATOR {+S, +Sf, -F}), 
      (forall (Z <: CT.CGOAL.CENV {-Rho, -P, -Ff, -Sf, -F, -S}),
        `| Pr[GOAL.REAL.UC_emul(Z,CompRP(Rho,P)).main() @ &m : res] - 
           Pr[GOAL.REAL.UC_emul(Z,GOAL.CompS(F, S')).main() @ &m : res] | <= e1 + e2).
    proof.
      move=> hP HR.
      have [Srho HSrho]:= C0.ex_compose P Ff Sf Rho e1 &m _; 1: by move=> Z0; apply (hP Z0).
      have := CT.ex_uc_transitivity (CompRP(Rho,P)) (CompRF(Rho,Ff)) F Srho S &m e1 e2 _ _.
      + move=> Z1; have := HSrho Z1.
        + have -> : Pr[RPi.REAL.UC_emul(Z1, CompRP(Rho, P)).main() @ &m : res] = 
                    Pr[H1.REAL.UC_emul(Z1, CompRP(Rho, P)).main() @ &m : res].
          + by byequiv => //; sim.
          have -> // : Pr[RPi.REAL.UC_emul(Z1, RPi.CompS(CompRF(Rho, Ff), Srho)).main() @ &m : res] = 
                    Pr[H1.REAL.UC_emul(Z1, H1.CompS(CompRF(Rho, Ff), Srho)).main() @ &m : res].
          + by byequiv => //; sim.
      + by move=> Z1; apply (HR Z1).
      by move=> [S' hS']; exists S' => Z; apply (hS' Z).
    qed.

  end section.

  end SC.

end COMPOSITION.

abstract theory PARA.
  
  clone EXEC_MODEL as Pi1.
  clone EXEC_MODEL as Pi2.

  clone EXEC_MODEL as Pi12 with
    type inputs         = (Pi1.inputs, Pi2.inputs) sum,
    type ask_outputs    = (Pi1.ask_outputs, Pi2.ask_outputs) sum,
    type outputs        = (Pi1.outputs, Pi2.outputs) sum,
    type step           = (Pi1.step, Pi2.step) sum,
    type ask_backdoor   = (Pi1.ask_backdoor, Pi2.ask_backdoor) sum,
    type backdoor       = (Pi1.backdoor, Pi2.backdoor) sum.

  (* Build the parallel composition of Pi1 and Pi2 *)
  
  module EIPara(P1:Pi1.E_INTERFACE, P2:Pi2.E_INTERFACE) : Pi12.E_INTERFACE = {
   
    proc inputs(i: Pi12.inputs) = {
      match i with
      | Left i => P1.inputs(i);
      | Right i => P2.inputs(i);
      end;
    }

    proc outputs(o : Pi12.ask_outputs) : Pi12.outputs option = {
      var r1, r2, r;
      match o with
      | Left o  => { r1 <@ P1.outputs(o); r <- omap Left r1; }
      | Right o => { r2 <@ P2.outputs(o); r <- omap Right r2; }
      end;
      return r;
    }
  
    proc step(m : Pi12.step) : unit = {
      match m with
      | Left m  => P1.step(m);
      | Right m => P2.step(m);
      end;
    }
  
    proc backdoor(m : Pi12.ask_backdoor) : Pi12.backdoor option = {
      var r1, r2, r;
      match m with 
      | Left m  => { r1 <@ P1.backdoor(m); r <- omap Left r1; }
      | Right m => { r2 <@ P2.backdoor(m); r <- omap Right r2; }
      end;
      return r;
    }
 
  }.

  module PPara (P1:Pi1.PROTOCOL, P2:Pi2.PROTOCOL) : Pi12.PROTOCOL = {
    proc init() = {
      P1.init();
      P2.init();
    }

    include EIPara(P1,P2)
  }.

end PARA.

abstract theory PARA_IR.

  clone EXEC_MODEL as Pi1.
  clone REAL_IDEAL as Pi2.

  clone PARA as R with
    type Pi1.inputs        <- PARA_IR.Pi1.inputs,
    type Pi1.ask_outputs   <- PARA_IR.Pi1.ask_outputs,
    type Pi1.outputs       <- PARA_IR.Pi1.outputs,
    type Pi1.step          <- PARA_IR.Pi1.step,
    type Pi1.ask_backdoor  <- PARA_IR.Pi1.ask_backdoor,
    type Pi1.backdoor      <- PARA_IR.Pi1.backdoor,
    type Pi2.inputs        <- PARA_IR.Pi2.REAL.inputs,
    type Pi2.ask_outputs   <- PARA_IR.Pi2.REAL.ask_outputs,
    type Pi2.outputs       <- PARA_IR.Pi2.REAL.outputs,
    type Pi2.step          <- PARA_IR.Pi2.REAL.step,
    type Pi2.ask_backdoor  <- PARA_IR.Pi2.REAL.ask_backdoor,
    type Pi2.backdoor      <- PARA_IR.Pi2.REAL.backdoor.

  clone PARA as I with
    type Pi1.inputs        <- PARA_IR.Pi1.inputs,
    type Pi1.ask_outputs   <- PARA_IR.Pi1.ask_outputs,
    type Pi1.outputs       <- PARA_IR.Pi1.outputs,
    type Pi1.step          <- PARA_IR.Pi1.step,
    type Pi1.ask_backdoor  <- PARA_IR.Pi1.ask_backdoor,
    type Pi1.backdoor      <- PARA_IR.Pi1.backdoor,
    type Pi2.inputs        <- PARA_IR.Pi2.REAL.inputs,
    type Pi2.ask_outputs   <- PARA_IR.Pi2.REAL.ask_outputs,
    type Pi2.outputs       <- PARA_IR.Pi2.REAL.outputs,
    type Pi2.step          <- PARA_IR.Pi2.IDEAL.step,
    type Pi2.ask_backdoor  <- PARA_IR.Pi2.IDEAL.ask_backdoor,
    type Pi2.backdoor      <- PARA_IR.Pi2.IDEAL.backdoor.

  clone REAL_IDEAL as RI with
    type REAL.inputs         <- R.Pi12.inputs,
    type REAL.ask_outputs    <- R.Pi12.ask_outputs,
    type REAL.outputs        <- R.Pi12.outputs,
    type REAL.step           <- R.Pi12.step,
    type REAL.ask_backdoor   <- R.Pi12.ask_backdoor,
    type REAL.backdoor       <- R.Pi12.backdoor,
    type IDEAL.step          <- I.Pi12.step,
    type IDEAL.ask_backdoor  <- I.Pi12.ask_backdoor,
    type IDEAL.backdoor      <- I.Pi12.backdoor.
   
  module (Sid(Sf:Pi2.SIMULATOR) : RI.SIMULATOR) (FB : RI.IDEAL.BACKDOORS) = {
  
    module FBPi = {
      proc step(m:Pi2.IDEAL.step) : unit = {
        FB.step(Right m);
      }
      
      proc backdoor(m:Pi2.IDEAL.ask_backdoor) : Pi2.IDEAL.backdoor option = {
        var r;
        r <@ FB.backdoor (Right m);
        return obind getr r;
      }
    }
    
    proc init = Sf(FBPi).init 
  
    proc step(m:R.Pi12.step) : unit = {
      match (m) with
      | Left m1  => { FB.step(Left m1); }
      | Right m2 => { Sf(FBPi).step(m2); }
      end;
    }
    
    proc backdoor(m:R.Pi12.ask_backdoor) : R.Pi12.backdoor option = {
      var r1, r2, r;
      match (m) with
      | Left  m1 => { r1 <@ FB.backdoor(Left m1); r <- omap Left (obind getl r1); } 
      | Right m2 => { r2 <@ Sf(FBPi).backdoor(m2); r <- omap Right r2; }
      end;
      return r;
    }
  }.

  module (CompZR(Z:RI.REAL.ENV, F1: Pi1.PROTOCOL) : Pi2.REAL.ENV) (P2:Pi2.REAL.E_INTERFACE) = {
    proc distinguish() = {
      var b;
      F1.init();
      b <@ Z(R.EIPara(F1,P2)).distinguish();
      return b;
    }
  }.
  
  section PROOF.

    declare module F1 <: Pi1.PROTOCOL.
    declare module P2 <: Pi2.REAL.PROTOCOL {-F1}.
    declare module F2 <: Pi2.IDEAL.PROTOCOL {-F1, -P2}.
    declare module S2 <: Pi2.SIMULATOR {-F1, -P2, -F2}.

    declare module Z  <: RI.REAL.ENV { -F1, -P2, -F2, -S2 }.

    equiv F2_step : Sid(S2, I.PPara(F1, F2)).FBPi.step ~ F2.step : ={arg, glob F2, glob F1} ==>  ={res, glob F2, glob F1}.
    proof.
      proc *; inline *; sp; match Right {1} 1; 1: by auto; smt(). 
      by call(:true); skip.
    qed.

    equiv F2_backdoor: Sid(S2, I.PPara(F1, F2)).FBPi.backdoor ~ F2.backdoor : ={arg, glob F2, glob F1} ==>  ={res, glob F2, glob F1}.
    proof.
      proc *; inline *; sp; match Right {1} 1; 1: by auto; smt(). 
      by wp; call(:true); skip => /> /#.
    qed.

    lemma compose &m : 
      Pr[RI.REAL.UC_emul(Z, R.PPara(F1, P2)).main() @ &m : res] -
      Pr[RI.REAL.UC_emul(Z, RI.CompS(I.PPara(F1,F2), Sid(S2))).main() @ &m : res] = 
      Pr[Pi2.REAL.UC_emul(CompZR(Z,F1), P2).main() @ &m : res] -
      Pr[Pi2.REAL.UC_emul(CompZR(Z,F1), Pi2.CompS(F2, S2)).main() @ &m : res].
    proof.
      congr.
      + by byequiv => //; proc; inline *; swap{1} 1 1; sim.
      congr.
      byequiv => //; proc; inline *; wp.
      call (: ={glob F1, glob F2, glob S2}); 1..2: sim.
      + proc; inline *; match => *; 1..2: smt().
        + sp; match Left {1} 1; 1: by auto; smt().
          by call(:true); skip.
        call(: ={glob F2, glob F1}); last by auto.
        + by apply F2_step.
        by apply F2_backdoor.
      + proc; inline *; match => *; 1..2: smt().
        + wp; sp; match Left {1} 1; 1: by auto; smt().
          by wp; call(:true); skip => /#.
        wp; call(: ={glob F2, glob F1}); last by auto.
        + by apply F2_step.
        by apply F2_backdoor.
      swap{2} 3 -2; sim />.
    qed.

  end section PROOF.

  abstract theory C.

  op cz  : cenv.  (* complexity of Z   *)
  op cf1 : cprot. (* complexity of F1  *)
  op cs2 : csim.  (* complexity of S2  *)

  axiom cz_pos   : cenv_pos cz.
  axiom cf1_pos  : cprot_pos cf1.
  axiom cs2_pos  : csim_pos cs2.

  clone Pi1.CP as CPi1 with 
    op cp <- cf1 
    proof * by smt (cf1_pos).

  clone RI.C as CRI with
    op c <- cz,
    (* complexity of the simulator for Para(F1,P2) *)
    op csi = {|
        cinit = cs2.`cinit;
        cstep = max 2 (1 + cs2.`cstep + cs2.`cs_s + 4 * cs2.`cs_b);
        cs_s  = max 1 cs2.`cs_s;
        cs_b  = cs2.`cs_b; 
        cbackdoor = max 7 (3 + cs2.`cbackdoor + cs2.`cb_s + 4 * cs2.`cb_b);
        cb_s  = cs2.`cb_s;
        cb_b  = max 1 cs2.`cb_b;
      |}
    proof *by smt(cz_pos cs2_pos).
  
  clone Pi2.C as CPi2 with
    op c = {|
        cd = cf1.`cpinit + cz.`cd + 
             (1 + cf1.`cpinputs) * cz.`ci + (4 + cf1.`cpoutputs) * cz.`co + (1 + cf1.`cpstep) * cz.`cs + (4 + cf1.`cpbackdoor) * cz.`cb;
        ci = cz.`ci;
        co = cz.`co;
        cs = cz.`cs;
        cb = cz.`cb;
      |},
    op csi <- cs2
    proof * by smt(cf1_pos cz_pos cs2_pos).

  section.

    declare module F1 <: CPi1.CPROTOCOL.
    declare module P2 <: Pi2.REAL.PROTOCOL {-F1}.
    declare module F2 <: Pi2.IDEAL.PROTOCOL {-F1, -P2}.
    declare module S2 <: CPi2.CSIMULATOR {-F1, -P2, -F2}.

    lemma ex_compose &m e : 
      (forall (Z<:CPi2.CENV{-P2, -F2, -S2}), 
         `|Pr[Pi2.REAL.UC_emul(Z, P2).main() @ &m : res] - 
            Pr[Pi2.REAL.UC_emul(Z, Pi2.CompS(F2,S2)).main() @ &m : res]| <= e) =>
      exists (S <: CRI.CSIMULATOR { +S2, -I.PPara(F1,F2)}), 
        forall (Z <: CRI.CENV{-F1, -P2, -F2, -S2}), 
         `|Pr[RI.REAL.UC_emul(Z, R.PPara(F1, P2)).main() @ &m : res] -
             Pr[RI.REAL.UC_emul(Z, RI.CompS(I.PPara(F1,F2), S)).main() @ &m : res]| <= e.
    proof.
      move=> h.
      exists (Sid(S2)); split.
      + split.
        + by move=> kbackdoor kstep FB hkb hks; proc true : time [].
        split => kbackdoor kstep FB hkb hks.
        + proc; exlim m => -[] mi. 
          + match Left 1; [1: by auto; smt() | 2: done].
            call(:true; time []); auto => />; smt(cs2_pos). 
          match Right 1; [1: by auto; smt() | 2: done].
          call(:true; time [Sid(S2, FB).FBPi.step     : [ '1; FB.step : 1],
                            Sid(S2, FB).FBPi.backdoor : [ N 4; FB.backdoor : 1]]).
          + by move=> * /=; proc; call (:true; time []); auto.
          + by move=> * /=; proc; call (:true; time []); auto.     
          skip => />; rewrite !bigi_constz /=; smt(cs2_pos). 
        proc; exlim m => -[] mi. 
        + match Left 1; [1:by auto; smt() | 2: done].
          wp; call(:true; time []); auto => />; smt(cs2_pos).
        match Right 1; [1: by auto; smt() | 2: done].
        wp; call(:true; time [Sid(S2, FB).FBPi.step     : [ '1; FB.step : 1],
                              Sid(S2, FB).FBPi.backdoor : [ N 4; FB.backdoor : 1]]).
        + by move=> * /=; proc; call (:true; time []); auto.
        + by move=> * /=; proc; call (:true; time []); auto.     
        skip => />; rewrite !bigi_constz /=; smt(cs2_pos).
      move=> Z.
      rewrite (compose F1 P2 F2 S2 Z).
      apply (h (CompZR(Z, F1))) => kbackdoor kstep koutputs kinputs I hkb hks hko hki.
      proc.
      call (:true; time [R.EIPara(F1, I).inputs   : [N (1 + cf1.`cpinputs)  ; I.inputs   : 1],
                         R.EIPara(F1, I).outputs  : [N (4 + cf1.`cpoutputs) ; I.outputs  : 1],
                         R.EIPara(F1, I).step     : [N (1 + cf1.`cpstep)    ; I.step     : 1],
                         R.EIPara(F1, I).backdoor : [N (4 + cf1.`cpbackdoor); I.backdoor : 1]]) => *.
      + proc; exlim i => -[] ii.
        + match Left 1; [1: by auto => /# | 2:done]. 
          by call(:true; time []); auto => /> /#.
        match Right 1; [1: by auto => /# | 2:done].
        by call(:true; time []); auto => />; smt (cf1_pos).
      + proc; exlim o => -[] oi.
        + match Left 1; [1: by auto => /# | 2:done]. 
          by wp; call(:true; time []); auto => /> /#.
        match Right 1; [1: by auto => /# | 2:done].
        by wp; call(:true; time []); auto => />; smt (cf1_pos).
      + proc; exlim m => -[] mi.
        + match Left 1; [1: by auto => /# | 2:done]. 
          by call(:true; time []); auto => /> /#.
        match Right 1; [1: by auto => /# | 2:done].
        by call(:true; time []); auto => />; smt (cf1_pos).
      + proc; exlim m => -[] mi.
        + match Left 1; [1: by auto => /# | 2:done]. 
          by wp; call(:true; time []); auto => /> /#.
        match Right 1; [1: by auto => /# | 2:done].
        by wp; call(:true; time []); auto => />; smt (cf1_pos).
      call(:true; time []); skip => />.
      rewrite !bigi_constz /=; smt(cz_pos cf1_pos).
    qed.
  end section.
  end C.

end PARA_IR.

abstract theory PARA_RI.

  clone REAL_IDEAL as Pi1.
  clone EXEC_MODEL as Pi2.

  clone PARA as R with
    type Pi1.inputs        <- PARA_RI.Pi1.REAL.inputs,
    type Pi1.ask_outputs   <- PARA_RI.Pi1.REAL.ask_outputs,
    type Pi1.outputs       <- PARA_RI.Pi1.REAL.outputs,
    type Pi1.step          <- PARA_RI.Pi1.REAL.step,
    type Pi1.ask_backdoor  <- PARA_RI.Pi1.REAL.ask_backdoor,
    type Pi1.backdoor      <- PARA_RI.Pi1.REAL.backdoor,
    type Pi2.inputs        <- PARA_RI.Pi2.inputs,
    type Pi2.ask_outputs   <- PARA_RI.Pi2.ask_outputs,
    type Pi2.outputs       <- PARA_RI.Pi2.outputs,
    type Pi2.step          <- PARA_RI.Pi2.step,
    type Pi2.ask_backdoor  <- PARA_RI.Pi2.ask_backdoor,
    type Pi2.backdoor      <- PARA_RI.Pi2.backdoor.

  clone PARA as I with
    type Pi1.inputs        <- PARA_RI.Pi1.REAL.inputs,
    type Pi1.ask_outputs   <- PARA_RI.Pi1.REAL.ask_outputs,
    type Pi1.outputs       <- PARA_RI.Pi1.REAL.outputs,
    type Pi1.step          <- PARA_RI.Pi1.IDEAL.step,
    type Pi1.ask_backdoor  <- PARA_RI.Pi1.IDEAL.ask_backdoor,
    type Pi1.backdoor      <- PARA_RI.Pi1.IDEAL.backdoor,
    type Pi2.inputs        <- PARA_RI.Pi2.inputs,
    type Pi2.ask_outputs   <- PARA_RI.Pi2.ask_outputs,
    type Pi2.outputs       <- PARA_RI.Pi2.outputs,
    type Pi2.step          <- PARA_RI.Pi2.step,
    type Pi2.ask_backdoor  <- PARA_RI.Pi2.ask_backdoor,
    type Pi2.backdoor      <- PARA_RI.Pi2.backdoor.

  clone REAL_IDEAL as RI with
    type REAL.inputs         <- R.Pi12.inputs,
    type REAL.ask_outputs    <- R.Pi12.ask_outputs,
    type REAL.outputs        <- R.Pi12.outputs,
    type REAL.step           <- R.Pi12.step,
    type REAL.ask_backdoor   <- R.Pi12.ask_backdoor,
    type REAL.backdoor       <- R.Pi12.backdoor,
    type IDEAL.step          <- I.Pi12.step,
    type IDEAL.ask_backdoor  <- I.Pi12.ask_backdoor,
    type IDEAL.backdoor      <- I.Pi12.backdoor.
   
  module (Sid(Sf:Pi1.SIMULATOR) : RI.SIMULATOR) (FB : RI.IDEAL.BACKDOORS) = {
  
    module FBPi = {
      proc step(m:Pi1.IDEAL.step) : unit = {
        FB.step(Left m);
      }
      
      proc backdoor(m:Pi1.IDEAL.ask_backdoor) : Pi1.IDEAL.backdoor option = {
        var r;
        r <@ FB.backdoor (Left m);
        return obind getl r;
      }
    }
    
    proc init = Sf(FBPi).init 

    proc step(m:R.Pi12.step) : unit = {
      match (m) with
      | Left m1  => { Sf(FBPi).step(m1); }
      | Right m2 => { FB.step(Right m2); }
      end;
    }
    
    proc backdoor(m:R.Pi12.ask_backdoor) : R.Pi12.backdoor option = {
      var r1, r2, r;
      match (m) with
      | Left  m1 => { r1 <@ Sf(FBPi).backdoor(m1); r <- omap Left r1; } 
      | Right m2 => { r2 <@ FB.backdoor(Right m2); r <- omap Right (obind getr r2); }
      end;
      return r;
    }
  }.

  module (CompZR(Z:RI.REAL.ENV, F2: Pi2.PROTOCOL) : Pi1.REAL.ENV) (P1:Pi1.REAL.E_INTERFACE) = {
    proc distinguish() = {
      var b;
      F2.init();
      b <@ Z(R.EIPara(P1,F2)).distinguish();
      return b;
    }
  }.
  
  section PROOF.

    declare module F2 <: Pi2.PROTOCOL.
    declare module P1 <: Pi1.REAL.PROTOCOL {-F2}.
    declare module F1 <: Pi1.IDEAL.PROTOCOL {-F2, -P1}.
    declare module S1 <: Pi1.SIMULATOR {-F2, -P1, -F1}.

    declare module Z  <: RI.REAL.ENV { -F2, -P1, -F1, -S1 }.

    equiv F1_step : Sid(S1, I.PPara(F1, F2)).FBPi.step ~ F1.step : ={arg, glob F2, glob F1} ==>  ={res, glob F2, glob F1}.
    proof.
      proc *; inline *; sp; match Left {1} 1; 1: by auto; smt(). 
      by call(:true); skip.
    qed.

    equiv F1_backdoor: Sid(S1, I.PPara(F1, F2)).FBPi.backdoor ~ F1.backdoor : ={arg, glob F2, glob F1} ==>  ={res, glob F2, glob F1}.
    proof.
      proc *; inline *; sp; match Left {1} 1; 1: by auto; smt(). 
      by wp; call(:true); skip => /> /#.
    qed.

    lemma compose &m : 
      Pr[RI.REAL.UC_emul(Z, R.PPara(P1, F2)).main() @ &m : res] -
      Pr[RI.REAL.UC_emul(Z, RI.CompS(I.PPara(F1,F2), Sid(S1))).main() @ &m : res] = 
      Pr[Pi1.REAL.UC_emul(CompZR(Z,F2), P1).main() @ &m : res] -
      Pr[Pi1.REAL.UC_emul(CompZR(Z,F2), Pi1.CompS(F1, S1)).main() @ &m : res].
    proof.
      congr.
      + byequiv => //; proc; inline *; sim.
      congr.
      byequiv => //; proc; inline *; wp.
      call (: ={glob F1, glob F2, glob S1}); 1..2: sim.
      + proc; inline *; match => *; 1..2: smt().
        + call(: ={glob F2, glob F1}); last by auto.
          + by apply F1_step.
          by apply F1_backdoor.
        sp; match Right {1} 1; 1: by auto; smt().
        by call(:true); skip.
      + proc; inline *; match => *; 1..2: smt().
        + wp; call(: ={glob F2, glob F1}); last by auto.
          + by apply F1_step.
          by apply F1_backdoor.
        wp; sp; match Right {1} 1; 1: by auto; smt().
        by wp; call(:true); skip => /#.
      swap{2} 3 -1; sim />.
    qed.

  end section PROOF.

  abstract theory C.

  op cz  : cenv.  (* complexity of Z   *)
  op cf2 : cprot. (* complexity of F2  *)
  op cs1 : csim.  (* complexity of S1  *)

  axiom cz_pos   : cenv_pos cz.
  axiom cf2_pos  : cprot_pos cf2.
  axiom cs1_pos  : csim_pos cs1.

  clone Pi2.CP as CPi2 with 
    op cp <- cf2 
    proof * by smt (cf2_pos).

  clone RI.C as CRI with
    op c <- cz,
    (* complexity of the simulator for Para(F1,P2) *)
    op csi = {|
        cinit = cs1.`cinit;
        cstep = max 2 (1 + cs1.`cstep + cs1.`cs_s + 4 * cs1.`cs_b);
        cs_s  = max 1 cs1.`cs_s;
        cs_b  = cs1.`cs_b; 
        cbackdoor = max 7 (3 + cs1.`cbackdoor + cs1.`cb_s + 4 * cs1.`cb_b);
        cb_s  = cs1.`cb_s;
        cb_b  = max 1 cs1.`cb_b;
      |}
    proof *by smt(cz_pos cs1_pos).
  
  clone Pi1.C as CPi1 with
    op c = {|
        cd = cf2.`cpinit + cz.`cd + 
             (1 + cf2.`cpinputs) * cz.`ci + (4 + cf2.`cpoutputs) * cz.`co + (1 + cf2.`cpstep) * cz.`cs + (4 + cf2.`cpbackdoor) * cz.`cb;
        ci = cz.`ci;
        co = cz.`co;
        cs = cz.`cs;
        cb = cz.`cb;
      |},
    op csi <- cs1
    proof * by smt(cf2_pos cz_pos cs1_pos).

  section.

    declare module F2 <: CPi2.CPROTOCOL.
    declare module P1 <: Pi1.REAL.PROTOCOL {-F2}.
    declare module F1 <: Pi1.IDEAL.PROTOCOL {-F2, -P1}.
    declare module S1 <: CPi1.CSIMULATOR {-F2, -P1}.

    lemma ex_compose &m e : 
      (forall (Z<:CPi1.CENV{-P1, -F1, -S1}), 
         `|Pr[Pi1.REAL.UC_emul(Z, P1).main() @ &m : res] - 
            Pr[Pi1.REAL.UC_emul(Z, Pi1.CompS(F1,S1)).main() @ &m : res]| <= e) =>
      exists (S <: CRI.CSIMULATOR { +S1, -I.PPara(F1,F2) }), 
        forall (Z <: CRI.CENV{-F2, -P1, -F1, -S1}), 
         `|Pr[RI.REAL.UC_emul(Z, R.PPara(P1, F2)).main() @ &m : res] -
            Pr[RI.REAL.UC_emul(Z, RI.CompS(I.PPara(F1,F2), S)).main() @ &m : res]| <= e.
    proof.
      move=> h.
      exists (Sid(S1)); split.
      + split.
        + by move=> kbackdoor kstep FB hkb hks; proc true : time [].
        split => kbackdoor kstep FB hkb hks.
        + proc; exlim m => -[] mi. 
          + match Left 1; [1: by auto; smt() | 2:done] .
            call(:true; time [Sid(S1, FB).FBPi.step     : [ '1; FB.step : 1],
                              Sid(S1, FB).FBPi.backdoor : [ N 4; FB.backdoor : 1]]).
            + by move=> * /=; proc; call (:true; time []); auto.
            + by move=> * /=; proc; call (:true; time []); auto.     
            skip => />; rewrite !bigi_constz /=; smt(cs1_pos). 
          match Right 1; [1: by auto; smt() | 2:done].
          call(:true; time []); auto => />; smt(cs1_pos). 
        proc; exlim m => -[] mi. 
        + match Left 1; [1: by auto; smt() | 2:done].
          wp; call(:true; time [Sid(S1, FB).FBPi.step     : [ '1; FB.step : 1],
                                Sid(S1, FB).FBPi.backdoor : [ N 4; FB.backdoor : 1]]).
          + by move=> * /=; proc; call (:true; time []); auto.
          + by move=> * /=; proc; call (:true; time []); auto.     
          skip => />; rewrite !bigi_constz /=; smt(cs1_pos).
        match Right 1; [1: by auto; smt() | 2:done].
        wp; call(:true; time []); auto => />; smt(cs1_pos).
      move=> Z.
      rewrite (compose F2 P1 F1 S1 Z).
      apply (h (CompZR(Z, F2))) => kbackdoor kstep koutputs kinputs I hkb hks hko hki.
      proc.
      call (:true; time [R.EIPara(I, F2).inputs   : [N (1 + cf2.`cpinputs)  ; I.inputs   : 1],
                         R.EIPara(I, F2).outputs  : [N (4 + cf2.`cpoutputs) ; I.outputs  : 1],
                         R.EIPara(I, F2).step     : [N (1 + cf2.`cpstep)    ; I.step     : 1],
                         R.EIPara(I, F2).backdoor : [N (4 + cf2.`cpbackdoor); I.backdoor : 1]]) => *.
      + proc; exlim i => -[] ii.
        + match Left 1; [1: by auto => /# | 2:done]. 
          by call(:true; time []); auto => />; smt (cf2_pos).
        match Right 1; [1: by auto => /# | 2:done].
        by call(:true; time []); auto => /> /#.
      + proc; exlim o => -[] oi.
        + match Left 1; [1: by auto => /# | 2:done]. 
          by wp; call(:true; time []); auto => />; smt (cf2_pos).
        match Right 1; [1: by auto => /# | 2:done].
        by wp; call(:true; time []); auto => /> /#.
      + proc; exlim m => -[] mi.
        + match Left 1; [1: by auto => /# | 2:done]. 
          by call(:true; time []); auto => />; smt (cf2_pos).
        match Right 1; [1: by auto => /# | 2:done].
        by call(:true; time []); auto => /> /#.
      + proc; exlim m => -[] mi.
        + match Left 1; [1: by auto => /# | 2:done]. 
          by wp; call(:true; time []); auto => />; smt (cf2_pos).
        match Right 1; [1: by auto => /# | 2:done].
        by wp; call(:true; time []); auto => /> /#.
      call(:true; time []); skip => />.
      rewrite !bigi_constz /=; smt(cz_pos cf2_pos).
    qed.
  end section.
  end C.

end PARA_RI.
back to top