https://github.com/EasyCrypt/easycrypt
Raw File
Tip revision: 29bfa6a843867a07c6b55780879c19b310a50f2c authored by Alley Stoughton on 02 July 2022, 09:50:09 UTC
Saving work.
Tip revision: 29bfa6a
dh_enc_cost.ec
require import AllCore SmtMap Distr RndO CHoareTactic.
require import Composition_cost.

import CoreMap Bigxint.

(* Parties have some sort of identifier *)
type party.

(* 2 party Protocols have roles *)
type role = [ | I | R ].

(* Most protocols will pack data in packages *)
type 'a pkg = party * party * 'a.

op snd (p : 'a pkg) = p.`1.
op rcv (p : 'a pkg) = p.`2.
op pld (p : 'a pkg) = p.`3.

lemma pkgid (p p' : unit pkg): p = (snd p',rcv p', ()) => p = p' by smt().

schema cost_rcv ['a] `{P} {p : 'a pkg} : cost [P : rcv p] = '1 + cost [P : p].
schema cost_snd ['a] `{P} {p : 'a pkg} : cost [P : snd p] = '1 + cost [P : p].
schema cost_pld ['a] `{P} {p:'a pkg} : cost[P:pld p] = '1 + cost [P:p].
schema cost_tt : cost [true : tt] = '0.

hint simplify cost_pld, cost_rcv, cost_snd, cost_tt.

schema cost_eqI `{P} {r:role} : cost [P : r = I] = '1 + cost [P : r].
schema cost_eqR `{P} {r:role} : cost [P : r = R] = '1 + cost [P : r].
hint simplify cost_eqI, cost_eqR.

schema cost_eqNone ['a] `{P} {x:'a option} : cost [P: x = None] = '1 + cost [P : x].
hint simplify cost_eqNone.

schema cost_eqLefttt `{P} {r: (unit,unit) sum} : cost [P : r = Left tt] = '1 + cost [P : r].
schema cost_eqRighttt `{P} {r: (unit,unit) sum} : cost [P : r = Right tt] = '1 + cost [P : r].
hint simplify cost_eqLefttt, cost_eqRighttt.


(****************************************************************)
(*                    CHANNEL FUNCTIONALITIES                   *)
(****************************************************************)

type 'a stlkg = [
   | Ch_Init
   | Ch_Blocked1 of 'a
   | Ch_Wait of 'a
   | Ch_Blocked2 of 'a
   | Ch_Available of 'a 
].

schema cost_eqSRI ['a] {r2: ('a stlkg, 'a stlkg) sum option} : cost[true : r2 = Some (Right Ch_Init)] = N 3 + cost [true : r2].
hint simplify cost_eqSRI.

theory FChan.

type msg.

type state = (msg pkg) stlkg.

op leakpkg (p : msg pkg) : unit pkg = (snd p, rcv p, ()).

op init_st : state = Ch_Init.

op get_msg (st : state) (r : role) (p' : unit pkg)  = 
    with st = Ch_Init => None
    with st = Ch_Blocked1 _ => None
    with st = Ch_Wait _ => None
    with st = Ch_Blocked2 _ => None
    with st = Ch_Available p => if leakpkg p = p' /\ r = R then Some (pld p) else None.
 
op unblock (st : state) =
    with st = Ch_Init => st
    with st = Ch_Blocked1 p => Ch_Wait p
    with st = Ch_Wait _ => st
    with st = Ch_Blocked2 p => Ch_Available p
    with st = Ch_Available _ => st.

theory FAuth.

op set_msg (st : state) (r : role) (p : msg pkg) = 
    with st = Ch_Init => if r = I then Ch_Blocked2 p else st
    with st = Ch_Blocked1 _ => st
    with st = Ch_Wait _ => st
    with st = Ch_Blocked2 _ => st
    with st = Ch_Available _ => st.

type leakage = (msg pkg) stlkg.

op leak (st : state) = Some st.

schema cost_witness_msgpkg : cost [true: witness <:msg pkg>] = '1.
hint simplify cost_witness_msgpkg.

schema cost_init_st : cost [true : init_st] = '1.
schema cost_set_msg {r : role, p : msg pkg, st: state } : 
  cost [true : set_msg st r p] = N 3 + cost[true : st] + cost [true : r] + cost [true : p].
schema cost_get_msg {r : role, p : unit pkg, st: state } :
   cost [true :  get_msg st r p] = N 9 + cost[true : st] + cost [true : r] + cost [true : p].
schema cost_unblock {st:state} : cost [true : unblock st] = N 2 + cost[true : st].
schema cost_leak    {st: state} : cost [true : leak st] = '1 + cost[true : st].
hint simplify cost_init_st, cost_set_msg, cost_get_msg, cost_unblock, cost_leak.

clone import REAL_IDEAL as FAuthWorlds with
    type REAL.inputs <- role * msg pkg,
    type REAL.ask_outputs <- role * unit pkg,
    type REAL.outputs <- msg,
    type IDEAL.step <- unit,
    type IDEAL.ask_backdoor <- unit,
    type IDEAL.backdoor <- leakage.

import IDEAL.

module FAuth : PROTOCOL = {
   var st : state

   proc init() : unit = { st <- init_st; }

   proc inputs(r : role, p : msg pkg) : unit = {
      st <- set_msg st r p;
   }

   proc outputs(r : role, p : unit pkg) : msg option = {
      return get_msg st r p;
   }

   proc step() : unit = {
      st <- unblock st;
   }

   proc backdoor() : leakage option = {
      return leak st;
   }

}.

end FAuth.

(* We need to replace this with general parallel composition of
   functionalitites *)
theory F2Auth.

clone FAuth as FAuthLR.

clone FAuth as FAuthRL.

clone import PARA with
    type Pi1.inputs        <- role * msg pkg,
    type Pi1.ask_outputs   <- role * unit pkg,
    type Pi1.outputs       <- msg,
    type Pi1.step          <- unit,
    type Pi1.ask_backdoor  <- unit,
    type Pi1.backdoor      <- (msg pkg) stlkg,
    type Pi2.inputs        <- role * msg pkg,
    type Pi2.ask_outputs   <- role * unit pkg,
    type Pi2.outputs       <- msg,
    type Pi2.step          <- unit,
    type Pi2.ask_backdoor  <- unit,
    type Pi2.backdoor      <- (msg pkg) stlkg.

module F2Auth = PPara(FAuthLR.FAuth,FAuthRL.FAuth).

end F2Auth.

theory FSC.

type leakage = (unit pkg) stlkg.

op set_msg (st : state) (r : role) (p : msg pkg) = 
    with st = Ch_Init => if r = I then Ch_Blocked1 p else st
    with st = Ch_Blocked1 _ => st
    with st = Ch_Wait p' => if r = R /\ snd p' = snd p /\ rcv p' = rcv p then Ch_Blocked2 p' else st
    with st = Ch_Blocked2 _ => st
    with st = Ch_Available _ => st.

op leak (st : state) = 
    with st = Ch_Init => Some Ch_Init
    with st = Ch_Blocked1 p => Some (Ch_Blocked1 (leakpkg p))
    with st = Ch_Wait p => Some (Ch_Wait (leakpkg p))
    with st = Ch_Blocked2 p => Some (Ch_Blocked2 (leakpkg p))
    with st = Ch_Available p => Some (Ch_Available (leakpkg p)).

clone import REAL_IDEAL as FSCWorlds with
    type REAL.inputs <- role * msg pkg,
    type REAL.ask_outputs <- role * unit pkg,
    type REAL.outputs <- msg,
    type IDEAL.step <- unit,
    type IDEAL.ask_backdoor <- unit,
    type IDEAL.backdoor <- leakage.

import IDEAL.

module FSC : PROTOCOL = {
   var st : state

   proc init() : unit = { st <- init_st; }

   proc inputs(r : role, p : msg pkg) : unit = {
      st <- set_msg st r p;
   }

   proc outputs(r : role, p : unit pkg) : msg option = {
      return get_msg st r p;
   }

   proc step() : unit = {
      st <- unblock st;
   }

   proc backdoor() : leakage option = {
      return leak st;
   }

}.

end FSC.

end FChan.

(****************************************************************)
(*                  KEY EXCHANGE FUNCTIONALITY                  *)
(****************************************************************)


theory FKE.

type key.

op gen : key distr.

type kestate = [
  | KE_Init
  | KE_Blocked1 of unit pkg
  | KE_Wait of unit pkg
  | KE_Blocked2 of unit pkg
  | KE_Available of unit pkg
].

type state = {
  key : key;
  kst : kestate;
}.

type leakage = kestate.

op init key = {| key = key; kst = KE_Init |}.

schema cost_init `{P} {k:key} : cost[P:init k] = N 2 + cost[P:k].
hint simplify cost_init.

(* This is a hack *)
op kstp st = st.`kst.

(* Both parties need to provide the same party ids and roles
   for the protocol to start *)
op party_start (st : state) (r : role)  (p' : unit pkg) = 
 match (kstp st) with
    |  KE_Init        => if r = I
                         then {| st with kst = KE_Blocked1 p' |} 
                         else st
    |  KE_Blocked1 p  => st
    |  KE_Wait p      => if r = R /\ p = p' 
                         then {| st with kst = KE_Blocked2 p |} 
                         else st
    |  KE_Blocked2 p  => st
    |  KE_Available p => st
    end.

op party_output (st : state) (r : role) = 
    match (kstp st) with
    |  KE_Init        => None
    |  KE_Blocked1 p  =>  None
    |  KE_Wait p      => None
    |  KE_Blocked2 p  => if r = R
                         then Some st.`key
                         else None
    |  KE_Available p => if r = I \/ r = R
                         then Some st.`key
                         else None
    end.

op unblock st = 
    match (kstp st) with
    |  KE_Init        => st
    |  KE_Blocked1 p  => {| st with kst = KE_Wait p |} 
    |  KE_Wait p      => st
    |  KE_Blocked2 p  => {| st with kst = KE_Available p |} 
    |  KE_Available p => st
    end.

op leak st = Some st.`kst.

schema cost_leak `{P} {st:state} : cost [P:leak st] = '1 + cost[P:st].
hint simplify cost_leak.

schema cost_unblock `{P} {s:state} : cost[P : unblock s] = N 2 + cost[P : s].
hint simplify cost_unblock.

clone import REAL_IDEAL as FKEWorlds with
    type REAL.inputs <- role * unit pkg,
    type REAL.ask_outputs <- role,
    type REAL.outputs <- key,
    type IDEAL.step <- unit,
    type IDEAL.ask_backdoor <- unit,
    type IDEAL.backdoor <- leakage.

import IDEAL.

module FKE : PROTOCOL = {
   var st : state

   proc init() : unit = {
      var k;
      k <$ gen;
      st <- init k;
   }

   proc inputs(r : role, p : unit pkg) : unit = {
      st <- party_start st r p;
   }

   proc outputs(r : role) : key option = {
      return party_output st r;
   }

   proc step() : unit = {
      st <- unblock st;
   }

   proc backdoor() : leakage option = {
      return leak st;
   }

}.

(* The following subtheory defines a joint
    ideal functionality that combines FKE with
    FAuth in parallel. *)

theory FKEAuth.

clone import FChan.

clone import PARA with
    type Pi1.inputs        <- role * msg pkg,
    type Pi1.ask_outputs   <- role * unit pkg,
    type Pi1.outputs       <- msg,
    type Pi1.step          <- unit,
    type Pi1.ask_backdoor  <- unit,
    type Pi1.backdoor      <- (msg pkg) stlkg,
    type Pi2.inputs        <- role * unit pkg,
    type Pi2.ask_outputs   <- role,
    type Pi2.outputs       <- key,
    type Pi2.step          <- unit,
    type Pi2.ask_backdoor  <- unit,
    type Pi2.backdoor      <- kestate.

module FKEAuth = PPara(FAuth.FAuth,FKE).

end FKEAuth.

end FKE.

(****************************************************************)
(*                  DIFFIE - HELLMAN PROTOCOL                   *)
(****************************************************************)


require import DiffieHellman.

clone include AllCore.Cost.
clone include G.Cost.
clone include G.FD.Cost.
clone include G.FD.FDistr.Cost.
clone include Bool.Cost.

schema cost_dapply : cost[true : dapply (fun (x : t) => g ^ x) FDistr.dt] = 
                          N (cdt + cgpow).

op adv_ddh : int -> real.

axiom adv_ddh_max cddh : 
   forall (A <:DDH.Adversary [ guess : `{N cddh}]) &m, 
     `| Pr[DDH.DDH0(A).main() @&m : res] - Pr[DDH.DDH1(A).main() @&m : res] | <= adv_ddh cddh.

theory DHKE. 

import DDH.

clone import FChan as HybFChan with
     type msg <- group.

(* This allows us to define a hybrid protocol that uses F2Auth.
   We don't intend to instantiate this functionality.  *)
clone import COMPOSITION with
   type Pi.REAL.inputs = (role * group pkg, role * group pkg) sum,
   type Pi.REAL.ask_outputs = (role * unit pkg, role * unit pkg) sum,
   type Pi.REAL.outputs = (group, group) sum,
   type Pi.IDEAL.step =  (unit,unit) sum,
   type Pi.IDEAL.ask_backdoor =  (unit,unit) sum,
   type Pi.IDEAL.backdoor = (state,state) sum,
   type R.inputs = role * unit pkg,
   type R.ask_outputs = role,
   type R.outputs = group,
   type R.step = role,
   type R.ask_backdoor = role,
   type R.backdoor = unit.

import RPi.

type istate = [
   | IInit
   | ISent
   | IDone
].

type rstate = [
   | RWaiting
   | RDone
].

schema cost_eqIInit `{P} {i:istate} : cost[P : i = IInit] = '1 + cost[P: i].
schema cost_eqISent `{P} {i:istate} : cost[P : i = ISent] = '1 + cost[P: i].
schema cost_eqRWaiting `{P} {i:rstate} : cost[P : i = RWaiting] = '1 + cost[P: i].
hint simplify cost_eqIInit, cost_eqISent, cost_eqRWaiting.

schema cost_witness_UPKG `{P} : cost[P : witness <:unit pkg>] = '1.
hint simplify cost_witness_UPKG.

module (DHKE : RHO) (Auth: Pi.REAL.IO) = {

   module Initiator = {
     var p : unit pkg
     var st : istate
     var _X : group
     var _x : F.t
     var _K  : group option

     proc init() : unit = {
        p <- witness;
        st <- IInit;
        _x <$ FDistr.dt;
        _X <- g^_x;
        _K <- None;
     }
  
     proc inputs(_p : unit pkg) : unit = { 
        if (st = IInit) {
           p <- _p;
           Auth.inputs(Left (I, (snd p, rcv p, _X))); 
           st <- ISent;
        }
     }
  
     proc outputs() : group option = { 
        return _K;
     }

     proc step() : unit = { 
        var _Y;
        if (st = ISent) {
           _Y <@ Auth.outputs(Right (R, (rcv p, snd p, ())));
           if (_Y <> None) {
              _K <- Some (oget (getr (oget _Y)) ^ _x);
              st <- IDone;
           }
        }
     }
  
     proc backdoor() : unit option = { 
         return None;
     }
   }

   module Responder = {
     var p : unit pkg
     var st : rstate
     var _Y : group
     var _y : F.t
     var _K  : group option

     proc init() : unit = {
        p <- witness;
        st <- RWaiting;
        _y <$ FDistr.dt;
        _Y <- g^_y;
        _K <- None;
     }
  
     proc inputs(_p : unit pkg) : unit = { 
        var _X;
        if (st = RWaiting) {
           _X <@ Auth.outputs(Left (R,_p));
           if (_X <> None) {
              p <- _p;
              Auth.inputs(Right (I, (rcv p, snd p, _Y))); 
              _K <- Some (oget (getl (oget _X)) ^ _y);
              st <- RDone;
           }
        }
     }
  
     proc outputs() : group option = { 
        return _K;
     }
  
     proc step() : unit = { 
     }
  
     proc backdoor() : unit option = { 
         return None;
     }
   }

   (* Now the protocol *)

   proc init() : unit = {
     Initiator.init();
     Responder.init();
   }

   proc inputs(r : role, p : unit pkg) : unit = {
      if (r = I) {
            Initiator.inputs(p);
      } else {
            Responder.inputs(p);
      }  
   }

   proc outputs(r : role) : group option = {
      var rr;
      rr <- None;
      if (r = I) {
         rr <@ Initiator.outputs();
      } else {
         rr <@ Responder.outputs();
      }
      return rr;
   }

   proc step(r : role) : unit = {
      if (r = I) {
         Initiator.step();
      } else {
         Responder.step();
      }
   }

   proc backdoor(r : role) : unit option = {
      var rr;
      if (r = I) {
         rr <@ Initiator.backdoor();
      } else {
         rr <@ Responder.backdoor();
      }
      return rr;
   }

}.

(* To have the type of the simulator we need now to clone FKE. *)

clone import FKE with
  type key <- group,
  op gen <- dapply (fun x => g^x) FDistr.dt,
  type FKEWorlds.REAL.step <- IDEAL.step,
  type FKEWorlds.REAL.ask_backdoor <- IDEAL.ask_backdoor,
  type FKEWorlds.REAL.backdoor <- IDEAL.backdoor,
  type FKEAuth.FChan.msg <- group.

clone import F2Auth as SimAuth.

import FKEWorlds.

module (DHKE_SIM : SIMULATOR) (FB : IDEAL.BACKDOORS) = {
   var _Keager : group

   var _X : group
   var _Y : group

   proc init() : unit = {
     var x,y;

     x <$ FDistr.dt;
     y <$ FDistr.dt;

     _X <- g^x;
     _Y <- g^y;

     SimAuth.F2Auth.init();
   }

   proc step(s : RPi.IDEAL.step) : unit = {
      var lk ,__Y;
      match (s) with
      | Left r => { 
         (* Stepping Rho *)
         lk <@ FB.backdoor();
         if (lk <> None) {
            match (oget lk) with 
            | KE_Init => {
                 (* Nothing happened on the inputs side,
                    so nothing to do *)
              }
            | KE_Blocked1 i => {
                 (* Initiator was given first input, but
                    this was not yet delivered. We do
                    nothing *)
              } 
            | KE_Wait i => {
                 (* Nothing happened on the receiver 
                    side yet, we wait *)
              }
            | KE_Blocked2 i => {
                 (* We got an input on the receiver 
                    side for sure.
                    The sender may now be terminating in the
                    real world, easy to check because we will
                    have a message to read also on the initiator
                    end of things *)
                    if (r = I) {    
                         __Y <@ SimAuth.F2Auth.outputs(Right (R,(rcv i, snd i, ())));
                        if (__Y <> None) {
                           FB.step();
                        }
                    }
              }
            | KE_Available i => {
                 (* Nothing to do *)
              }
            end;
         }
        }
      | Right r => { 
         (* Stepping F2Auth *)
         lk <@ FB.backdoor();
         if (lk <> None) {
            match (oget lk) with 
            | KE_Init => {
                 (* Nothing happened on the inputs side,
                    so nothing to do *)
              }
            | KE_Blocked1 i => {
                    if (r = Left ()) {
                        (* Initiator was given first input, and
                           this is now being delivered. *)
                        SimAuth.F2Auth.inputs(Left (I, (snd i, rcv i, _X))); 
                        SimAuth.F2Auth.step(Left ()); 
                        FB.step();
                    }                 
              } 
            | KE_Wait i => {
                        (* Nothing to do *)
              }
            | KE_Blocked2 i => {
                    if (r = Right ()) {
                      (* We got an input on the receiver 
                      side for sure.
                      This step to the channel is
                      the moment in which the message 
                      the receiver sent gets delivered. *)
                       SimAuth.F2Auth.inputs(Right (I, (rcv i, snd i, _Y))); 
                       SimAuth.F2Auth.step(Right ()); 
                    }                 
              }
            | KE_Available i => {
                 (* Nothing to do *)
              }
            end;
         }
        }
      end;
   }

   proc backdoor(b : RPi.IDEAL.ask_backdoor) : RPi.IDEAL.backdoor option = {
    var r2 : Pi.IDEAL.backdoor option;
    var r : (R.backdoor, Pi.IDEAL.backdoor) sum option;
    var lk; 
    
    match (b) with
    | Left m1 => { 
         r <- None;
         }
    | Right m2 => {
         (* Since things are out of sync we may need to cheat *)
         r <- None;
         lk <@ FB.backdoor();
         if (lk <> None) {
            match (oget lk) with 
            | KE_Init => {
               (* All in sync *)
               r2 <@ SimAuth.F2Auth.backdoor(m2);              
               r <- omap Right r2;       
              }
            | KE_Blocked1 i => {
               if (m2 = Left ()) { 
                  (* Initiator channel *)          
                  r <- Some (Right (Left (Ch_Blocked2 (snd i, rcv i,_X)))); 
               }
               else {
                  (* Responder channel *)          
                  r <- Some (Right (Right Ch_Init)); 
               }
              } 
            | KE_Wait i => {
               (* All in sync *)
               r2 <@  SimAuth.F2Auth.backdoor(m2);              
               r <- omap Right r2;       
              }
            | KE_Blocked2 i => {
               if (m2 = Left ()) {  
                  (* Initiator channel *)         
                  r <- Some (Right (Left (Ch_Available (snd i, rcv i, _X)))); 
               }
               else {
                  (* Responder channel *)
                  r2 <@  SimAuth.F2Auth.backdoor(Right ()); 
                  if (r2 = Some (Right Ch_Init)) {
                     r <- Some (Right (Right (Ch_Blocked2 (rcv i, snd i, _Y)))); 
                  } else {
                     r <- Some (Right (Right (Ch_Available (rcv i, snd i, _Y)))); 
                  }
               }
              }
            | KE_Available i => {
               (* All in sync *)
               r2 <@  SimAuth.F2Auth.backdoor(m2);              
               r <- omap Right r2;       
              }
            end;
         }
    }
    end;
    return r;
   }

}.

(**************************)
(* We now write the proof *)
(**************************)

(* EAGER SAMPLE IN RHO *)

module type RHOEager (P : Pi.REAL.IO) = {
  proc init(__X : group ,__Y : group, __Z : group) : unit {}
  
  proc inputs(i : R.inputs) : unit {P.inputs, P.outputs}
  
  proc outputs(o : R.ask_outputs) : R.outputs option {P.inputs, P.outputs}
  
  proc step(m : R.step) : unit {P.inputs, P.outputs}
  
  proc backdoor(m : R.ask_backdoor) : R.backdoor option {P.inputs, P.outputs}
}.

module (DHKE_Eager : RHOEager) (Auth: Pi.REAL.IO) = {

   module Initiator = {

     proc init(__X : group) : unit = {
        DHKE.Initiator.p <- witness;
        DHKE.Initiator.st <- IInit;
        DHKE.Initiator._X <- __X;
        DHKE.Initiator._K <- None;
     }

     proc step() : unit = { 
        var _Y;
        if (DHKE.Initiator.st = ISent) {
           _Y <@ Auth.outputs(Right (R, (rcv DHKE.Initiator.p, snd DHKE.Initiator.p, ())));
           if (_Y <> None) {
              DHKE.Initiator._K <- Some DHKE_SIM._Keager;
              DHKE.Initiator.st <- IDone;
           }
        }
     }

     include DHKE(Auth).Initiator [ -init, step]
   }

   module Responder = {

     proc init(__Y) : unit = {
        DHKE.Responder.p <- witness;
        DHKE.Responder.st <- RWaiting;
        DHKE.Responder._K <- None;
        DHKE.Responder._Y <- __Y;
     }
  
     proc inputs(p : unit pkg) : unit = { 
        var _X;
        if (DHKE.Responder.st = RWaiting) {
           _X <@ Auth.outputs(Left (R, p));
           if (_X <> None) {
              DHKE.Responder.p <- p;
              Auth.inputs(Right (I, (rcv DHKE.Responder.p, snd DHKE.Responder.p, DHKE.Responder._Y))); 
              DHKE.Responder._K <- Some DHKE_SIM._Keager;
              DHKE.Responder.st <- RDone;
           }
        }
     }
     include DHKE(Auth).Responder [ -init, inputs]
   }

   (* Now the protocol with eager sampling *)

   proc init(__X, __Y, __K : group) : unit = {
     DHKE_SIM._Keager <- __K;
     Initiator.init(__X);
     Responder.init(__Y);    
   }

   include DHKE(Auth) [ -init, inputs, step]

   proc inputs(r : role, p : unit pkg) : unit = {
      if (r = I) {
           Initiator.inputs(p);
      } else {
           Responder.inputs(p); 
     }
   }

   proc step(r : role) : unit = {
      if (r = I) {
         Initiator.step();
      } else {
         Responder.step();
      }
   }

}.

(* Now composition with eager sampling *)

module CompRFEager (Rho : RHOEager, F : Pi.IDEAL.PROTOCOL) = {
  proc init(__X : group, __Y : group, __K : group) : unit = {
    F.init();
    Rho(F).init(__X, __Y, __K);
  }
  
  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 : R.backdoor option;
    var r2 : Pi.IDEAL.backdoor option;
    var r : (R.backdoor, Pi.IDEAL.backdoor) sum option;
    
    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;
  }
}.

(* Finally the game with eager sampling and DDH attacker type *)

module type EagerPROTOCOL = {
   include REAL.PROTOCOL [-init]
   proc init(__X : group, __Y : group, __K : group) : unit
}.

module UC_emul_DDH(E : REAL.ENV, P : EagerPROTOCOL) : Adversary = {
  proc guess(__X : group, __Y : group, __K : group) : bool = {
    var b : bool;
    
    P.init(__X,__Y,__K);
    b <@ E(P).distinguish();
    
    return b;
  }
}.

section.

(* Security proof first uses DDH to replace the key with a random
   group element in the real-world. Then it shows that the simulator
   perfectly emulates this modified game *)

declare module Z <: REAL.ENV {-DHKE_SIM, -FKE, 
                              -HybFChan.F2Auth.FAuthLR.FAuth,
                              -HybFChan.F2Auth.FAuthRL.FAuth,
                              -DHKE}.

lemma hop1 &m : 
   Pr[ REAL.UC_emul(Z,CompRF(DHKE,HybFChan.F2Auth.F2Auth)).main() @ &m : res] =
   Pr[ DDH0(UC_emul_DDH(Z,CompRFEager(DHKE_Eager,HybFChan.F2Auth.F2Auth))).main() @ &m : res].
proof.
byequiv => //.
proc.
inline *.
swap {1} 5 -4.
swap {1} 10 -8.
seq 2 2 : (={glob Z} /\ DHKE.Initiator._x{1} = x{2} /\ DHKE.Responder._y{1} = y{2}).
by sim />.
wp;call (_: ={glob HybFChan.F2Auth.F2Auth,
           DHKE.Initiator.p,DHKE.Initiator.st,DHKE.Initiator._X,
           DHKE.Initiator._K,
           DHKE.Responder.p,DHKE.Responder.st,DHKE.Responder._Y,
           DHKE.Responder._K} /\
           DHKE_SIM._Keager{2} = DHKE.Responder._Y{1}^DHKE.Initiator._x{1} /\
           DHKE_SIM._Keager{2} = DHKE.Initiator._X{1}^DHKE.Responder._y{1} /\

           (match DHKE.Initiator.st{1} with
            | IInit => 
                  HybFChan.F2Auth.FAuthLR.FAuth.st{1} = Ch_Init /\
                  HybFChan.F2Auth.FAuthRL.FAuth.st{1} = Ch_Init 
            | ISent => 
                 match (HybFChan.F2Auth.FAuthLR.FAuth.st{1}) with
                 | Ch_Init => true
                 | Ch_Blocked1 p => true
                 | Ch_Wait p => true
                 | Ch_Blocked2 p   => p = (snd DHKE.Initiator.p{1}, rcv DHKE.Initiator.p{1}, DHKE.Initiator._X{1})
                 | Ch_Available p => p = (snd DHKE.Initiator.p{1}, rcv DHKE.Initiator.p{1}, DHKE.Initiator._X{1})
                 end /\

                 match HybFChan.F2Auth.FAuthRL.FAuth.st{1} with
                 | Ch_Init => true
                 | Ch_Blocked1 p => true
                 | Ch_Wait p => true
                 | Ch_Blocked2 p   => p = (rcv DHKE.Responder.p{1}, snd DHKE.Responder.p{1}, DHKE.Responder._Y{1})
                 | Ch_Available p => p = (rcv DHKE.Responder.p{1}, snd DHKE.Responder.p{1}, DHKE.Responder._Y{1})
                 end
            | IDone =>  DHKE.Responder.st{1} = RDone /\
                        DHKE.Initiator._K{1} = Some (DHKE.Responder._Y{1}^DHKE.Initiator._x{1})
            end) /\
           (match DHKE.Responder.st{1} with
            | RWaiting => DHKE.Initiator.st{1} = IInit \/ 
                  (DHKE.Initiator.st{1} = ISent /\ HybFChan.F2Auth.FAuthRL.FAuth.st{1} = Ch_Init)
            | RDone => 
                 (DHKE.Initiator.st{1} = ISent \/ DHKE.Initiator.st{1} = IDone)  /\
                  DHKE.Responder._K{1} = Some (DHKE.Initiator._X{1}^DHKE.Responder._y{1})
               end)
); last first.

(* Init *)
auto => />; smt(@G).

(* Now the call *)
+ by proc;inline *; auto => /> /#.
+ by sim />.
+ proc; match; first 2 by auto.
  + move => m1 m1_0; inline *.
    sp 1 1; if => //=; if => //=.
    by sp 1 1; match; auto => /#.
  + move => m1 m1_0; inline *; auto => /#.
by proc; inline *; match; auto => /#.
qed.

(* we'll need a complex relation on states *)
op staterel (p1 : unit pkg) ist1 rst1 (kst2 : kestate) 
            (fLR1 fRL1 fLR2 fRL2 : HybFChan.state) _Xp _Yp _KI _KR (_K : group) =
    match kst2 with
    (* KE INITIAL STATE: NOTHING HAPPENED YET *)
    | KE_Init =>
        (ist1 = IInit)   /\ (rst1 = RWaiting) /\ 
        (fLR1 = Ch_Init) /\ (fRL1 = Ch_Init) /\ 
        (fLR2 = Ch_Init) /\ (fRL2 = Ch_Init) /\ 
        (_KI = None)     /\ (_KR = None)
    (* KE BLOCKED1 STATE: ENV PROVIDED INPUT TO INITIATOR BUT X NOT YET DELIVERED *)
    | KE_Blocked1 p => 
        p1 = p /\ 
        (ist1 = ISent)         /\ (rst1 = RWaiting) /\ 
        (fLR1 = Ch_Blocked2 _Xp) /\ (fRL1 = Ch_Init)  /\ 
        (fLR2 = Ch_Init)       /\ (fRL2 = Ch_Init)   /\ 
        (_KI = None)           /\ (_KR = None)
    (* KE WAITING STATE: ENV PROVIDED INPUT TO INITIATOR AND X DELIVERED. *)
    | KE_Wait p => 
        p1 = p /\ 
        (ist1 = ISent)           /\ (rst1 = RWaiting) /\ 
        (fLR1 = Ch_Available _Xp) /\ (fRL1 = Ch_Init) /\ 
        (fLR2 = Ch_Available _Xp) /\ (fRL2 = Ch_Init) /\ 
        (_KI = None)             /\ (_KR = None)
    (* KE BLOCKED2 STATE: ENV PROVIDED INPUT TO BOTH PARTIES AND Y YES/NO DELIVERED *)
    | KE_Blocked2 p => 
        p1 = p /\ 
        (ist1 = ISent) /\ (rst1 = RDone) /\ 
        (fLR1 = Ch_Available _Xp)         /\ 
        (fRL2 = Ch_Init \/ fRL2 = Ch_Available _Yp) /\
              (fRL2 = Ch_Init          => fRL1 = Ch_Blocked2 _Yp)   /\
              (fRL2 = Ch_Available _Yp => fRL1 = Ch_Available _Yp) /\
        (fLR2 = Ch_Available _Xp) /\ 
        (_KI = None) /\ (_KR = Some _K)
    (* KE AVAILABLE STATE: ALeft DONE *)
    | KE_Available p => 
        p1 = p /\ 
        (ist1 = IDone) /\ (rst1 = RDone) /\ 
        (fLR1 = Ch_Available _Xp) /\ (fRL1 = Ch_Available _Yp) /\ 
        (fLR2 = Ch_Available _Xp) /\ (fRL2 = Ch_Available _Yp) /\ 
        (_KI = Some _K)  /\ (_KR = Some _K)
    end.


lemma hop2 &m : 
   Pr[ DDH1(UC_emul_DDH(Z,CompRFEager(DHKE_Eager,HybFChan.F2Auth.F2Auth))).main() @ &m : res] =
   Pr[ REAL.UC_emul(Z,CompS(FKE, DHKE_SIM)).main() @ &m : res].
proof.
byequiv => //.
proc. 

(* Initializations *)
inline {1} UC_emul_DDH(Z, CompRFEager(DHKE_Eager, HybFChan.F2Auth.F2Auth)).guess.
inline {2} CompS(FKE, DHKE_SIM).init. 
seq 7 2 : (={glob Z} /\
           HybFChan.F2Auth.FAuthLR.FAuth.st{1} = SimAuth.FAuthLR.FAuth.st{2} /\
           HybFChan.F2Auth.FAuthRL.FAuth.st{1} = SimAuth.FAuthRL.FAuth.st{2} /\
           FAuthLR.FAuth.st{2} = Ch_Init /\
           FAuthRL.FAuth.st{2} = Ch_Init /\
           DHKE.Initiator._K{1} = None /\
           DHKE.Responder._K{1} = None /\
           DHKE.Initiator._X{1} = DHKE_SIM._X{2} /\
           DHKE.Responder._Y{1} = DHKE_SIM._Y{2}  /\
           DHKE_SIM._Keager{1} = FKE.st.`key{2} /\
           DHKE.Initiator.st{1} = IInit /\
           DHKE.Responder.st{1} = RWaiting /\
           FKE.st.`kst{2}  = KE_Init
           ).
+ inline *.
  wp; swap {1} 3 -2; rnd; rnd;  wp; rnd (fun x => g^x) (fun x => log x). 
  auto => />; progress; 1,4:algebra. 
  + by rewrite dmapE /(\o) /pred1; congr; apply fun_ext => z; algebra.
  + by rewrite supp_dmap; exists zL; trivial.
  + by rewrite /init.
  + by rewrite /init.
(* Calling the environment *)
wp; call (_: 
   DHKE.Initiator._X{1} = DHKE_SIM._X{2} /\
   DHKE.Responder._Y{1} = DHKE_SIM._Y{2} /\
   DHKE_SIM._Keager{1} = FKE.st{2}.`key /\
   staterel DHKE.Initiator.p{1}
            DHKE.Initiator.st{1} 
            DHKE.Responder.st{1} 
            FKE.st{2}.`kst 
            HybFChan.F2Auth.FAuthLR.FAuth.st{1}
            HybFChan.F2Auth.FAuthRL.FAuth.st{1}
            FAuthLR.FAuth.st{2}
            FAuthRL.FAuth.st{2}
            (snd DHKE.Initiator.p{1}, rcv DHKE.Initiator.p{1}, DHKE.Initiator._X{1})
            (rcv DHKE.Initiator.p{1}, snd DHKE.Initiator.p{1}, DHKE.Responder._Y{1})
            DHKE.Initiator._K{1}
            DHKE.Responder._K{1}
            DHKE_SIM._Keager{1}).

(* INPUTS *)
(* PY: SMT doesn't catch this without rewrite *)
+ by proc; inline *;wp;skip; rewrite /staterel; smt(pkgid). 

(* OUTPUTS *)
+ by proc;inline *;auto => />; rewrite /staterel => /#. 

(* STEP *)
+ proc.
  match; first 2 by smt().
  (* We step Rho *)
  + by move => m1 p; inline *;wp;skip; smt(). 
  (* We step F2Auth *)

    move => m2 p;inline *; auto => /> &1 &2.
    rewrite /staterel /leak oget_some /unblock /kstp.
    by case (FKE.st{2}.`kst) => /> /#. 

(* BACKDOOR *)
+ proc.
  match; first 2 by smt().
  (* Backdoor Rho *)
  + by move => *; inline *;auto => /#.   
  (* Backdoor F2Auth *)
  move=> m2 p; inline *; auto => /> &1 &2.
  rewrite /staterel /leak oget_some /unblock /kstp /rcv. 
  by case (FKE.st{2}.`kst) => /> /#. 

(* WRAP-UP CALL *)
by auto => /#.
qed.

(* THIS IS THE MAIN DH UC SECURITY THEOREM *)

lemma KEAdv &m :
      `| Pr[ REAL.UC_emul(Z,CompRF(DHKE,HybFChan.F2Auth.F2Auth)).main() @ &m : res] - 
         Pr[ REAL.UC_emul(Z,CompS(FKE, DHKE_SIM)).main() @ &m : res] | = 
      `| Pr[ DDH0(UC_emul_DDH(Z,CompRFEager(DHKE_Eager,HybFChan.F2Auth.F2Auth))).main() @ &m : res] - 
         Pr[ DDH1(UC_emul_DDH(Z,CompRFEager(DHKE_Eager,HybFChan.F2Auth.F2Auth))).main() @ &m : res] |.
proof. by rewrite (hop1 &m) (hop2 &m). qed.

end section.

abstract theory C.

clone FKEWorlds.C as RC
  with op csi = {|
    cinit     = 2 * (1 + cgpow + cdt);
    cstep     = 26; 
    cs_s      = 1; 
    cs_b      = 1;
    cbackdoor = 20;
    cb_s      = 0;
    cb_b      = 1; 
  |}
  proof csi_pos by smt (ge0_cg ge0_cf ge0_cdt) .

op cddh =  8 + RC.c.`cd + RC.c.`ci * 29 + RC.c.`co * 2 + RC.c.`cs * 23 + RC.c.`cb * 5.

lemma KEAdv_ex &m :
    exists (S <: RC.CSIMULATOR {+DHKE_SIM}), 
      forall (Z <: RC.CENV { -HybFChan.F2Auth.FAuthLR.FAuth, -HybFChan.F2Auth.FAuthRL.FAuth, -DHKE, -FKE, -DHKE_SIM}),
      `| Pr[ REAL.UC_emul(Z,CompRF(DHKE,HybFChan.F2Auth.F2Auth)).main() @ &m : res] - 
         Pr[ REAL.UC_emul(Z,CompS(FKE, S)).main() @ &m : res] | <= adv_ddh cddh.
proof. 
  exists DHKE_SIM; split.
  + split; [ | split] => kb ks FB hkb hks.
    + proc; inline *; wp; do !rnd; skip => />.
      rewrite FDistr.dt_ll /#.
    + proc; exlim s => -[]r.
      + match Left 1; [1: by auto; smt() | 2: done].
        seq 1 : true time [ N 25; FB.backdoor : 0; FB.step : 1 ]; 1: done.
        + by call (:true); auto => />. 
        if => //. 
        + exlim (oget lk) => -[|i|i|i|i].
          + by match KE_Init 1 => //=; auto => /> /#.
          + by match KE_Blocked1 1 => //=; auto => /> /#. 
          + by match KE_Wait 1 => //=; auto => /> /#.
          + match KE_Blocked2 1 => //=; auto => />; 1: smt().
            if => //.
            + seq 1 : true time [N 2; FB.step : 1] => //.
              + by inline *; sp 1 => //=; match Right 1 => //=; auto => /> /#.
              by if => //; 1: call (:true); auto => />.
            by auto => /> /#.          
          by match KE_Available 1 => //=; auto => /> /#.
        by auto=> /> /#.
      match Right 1; [1: by auto; smt() | 2: done].
      seq 1 : true time [ N 20; FB.backdoor : 0; FB.step : 1 ]; 1: done.
      + by call (:true); auto => />. 
      if => //.
      + exlim (oget lk) => -[|i|i|i|i].
        + by match KE_Init 1 => //=; auto => /> /#. 
        + match KE_Blocked1 1 => //=; auto => />; 1: smt().
          if => //.
          + call(:true); inline *; sp 1 => //=.
            by match Left 1 => //=; auto => /> /#. 
          by auto => /> /#.
        + by match KE_Wait 1 => //=; auto => /> /#. 
        + match KE_Blocked2 1 => //=; auto => />; 1: smt().
          if => //.
          + inline *; sp 1 => //=.
            by match Right 1 => //=; auto => /> /#. 
          by auto => /> /#. 
        match KE_Available 1 => //=; auto => /> /#.
      by auto=> /> /#. 
    proc.
    exlim b => -[] mi. 
    + match Left 1; [1: by auto; smt() | 2: done].
      by auto => /> /#.
    match Right 1; [1: by auto; smt() | 2: done].
    seq 2 : true time [N 18] => //.
    + by call (:true); auto => />.
    by inline *; auto => /> /#.     

  move=> Z. have ->:= KEAdv Z &m.
  apply (adv_ddh_max cddh (UC_emul_DDH(Z, CompRFEager(DHKE_Eager, HybFChan.F2Auth.PARA.PPara(HybFChan.F2Auth.FAuthLR.FAuth, HybFChan.F2Auth.FAuthRL.FAuth))))).
  proc; inline *.
  call (:true; time [
     CompRFEager(DHKE_Eager, HybFChan.F2Auth.PARA.PPara(HybFChan.F2Auth.FAuthLR.FAuth, HybFChan.F2Auth.FAuthRL.FAuth)).inputs : [N 29],
     CompRFEager(DHKE_Eager, HybFChan.F2Auth.PARA.PPara(HybFChan.F2Auth.FAuthLR.FAuth, HybFChan.F2Auth.FAuthRL.FAuth)).outputs : [N 2],
     CompRFEager(DHKE_Eager, HybFChan.F2Auth.PARA.PPara(HybFChan.F2Auth.FAuthLR.FAuth, HybFChan.F2Auth.FAuthRL.FAuth)).step : [N 23],
     CompRFEager(DHKE_Eager, HybFChan.F2Auth.PARA.PPara(HybFChan.F2Auth.FAuthLR.FAuth, HybFChan.F2Auth.FAuthRL.FAuth)).backdoor : [N 5] ]) => /= *.
  + by proc; inline *; auto => />.
  + by proc; inline *; auto => />.
  + by proc; inline *; auto => /> /#.
  + by proc; inline *; auto => /> /#.
  auto => />.
  rewrite !bigi_constz; smt (RC.c_pos).
qed.

end C.

end DHKE.

(****************************************************************)
(*                  SECURE CHANNEL BASED ON OTP                 *)
(****************************************************************)

theory OTP.

type msg = group.
type key = group.
type cph = group.

op gen = dapply (fun x => g^x) FDistr.dt.

op enc(m : msg, k : key) = m * k.
op dec(c : cph, k : key) = c / k.

lemma correctness m k :
   dec (enc m k) k = m.
proof. rewrite /dec /enc -div_def log_mul -{2}(gpow_log m); congr; ring. qed.

schema cost_dec `{P} {c : cph, k : key} : cost [P: dec c k] = N cgdiv + cost[P:c] + cost[P:k].
schema cost_enc `{P} {c : cph, k : key} : cost [P: enc c k] = N cgmul + cost[P:c] + cost[P:k].
hint simplify cost_dec, cost_enc.

import DHKE.FKE.

(* To have the type of the simulator we need now to clone ideal
   secure channel functionality FSC. *)

clone import FChan with
  type msg <- group,
  type FSC.FSCWorlds.REAL.step <- (role, (unit, unit) sum) sum,
  type FSC.FSCWorlds.REAL.ask_backdoor <- (role, (unit, unit) sum) sum,
  type FSC.FSCWorlds.REAL.backdoor <- (unit,  (FKEAuth.FChan.state, kestate) sum) sum.

(* This allows us to define a hybrid protocol that uses FKEAuth. *)
clone import COMPOSITION as C_OTP with
   type Pi.REAL.inputs = (role * group pkg,role * unit pkg) sum,
   type Pi.REAL.ask_outputs = (role * unit pkg, role) sum,
   type Pi.REAL.outputs = (group,group) sum,
   (**************************************************)
   (* We look ahead to an instantiation based on DHKE *)
   type Pi.REAL.step = (unit, (role, (unit, unit) sum) sum) sum,
   type Pi.REAL.ask_backdoor = (unit, (role, (unit, unit) sum) sum) sum,
   type Pi.REAL.backdoor = (FKEAuth.FChan.state, (unit, (DHKE.HybFChan.state,
DHKE.HybFChan.state) sum) sum) sum,
   (**************************************************)
   type Pi.IDEAL.step = (unit,unit) sum,
   type Pi.IDEAL.ask_backdoor = (unit, unit) sum,
   type Pi.IDEAL.backdoor = (FKEAuth.FChan.state,kestate) sum,
   type R.inputs = role * msg pkg,
   type R.ask_outputs = role * unit pkg,
   type R.outputs = msg,
   type R.step = role,
   type R.ask_backdoor = role,
   type R.backdoor = unit,
   (* We also set the stage for transitivity *)
   type TRANS.H2.IDEAL.step = unit,
   type TRANS.H2.IDEAL.ask_backdoor = unit,
   type TRANS.H2.IDEAL.backdoor = FChan.FSC.leakage.

import RPi.

type iscstate = [
   | ISCInit
   | ISCSentKE
   | ISCDone
].

schema cost_eqISCInit   `{P} {i:iscstate} : cost [P : i = ISCInit] = '1 + cost [P : i].
schema cost_eqISCSentKE `{P} {i:iscstate} : cost [P : i = ISCSentKE] = '1 + cost [P : i].
schema cost_eqISCDone   `{P} {i:iscstate} : cost [P : i = ISCDone] = '1 + cost [P : i].
hint simplify cost_eqISCInit, cost_eqISCSentKE, cost_eqISCDone.

module (OTP : RHO) (KEAuth: Pi.REAL.IO) = {

   module Initiator = {
     var p : msg pkg
     var st : iscstate

     proc init() : unit = {
        p <- witness;
        st <- ISCInit;
     }
  
     (* To avoid confusion we only accept one input on initiator side *)
     proc inputs(_p : msg pkg) : unit = { 
        if (st = ISCInit) {
           p <- _p;
           KEAuth.inputs(Right (I, (snd p, rcv p, ()))); 
           st <- ISCSentKE;
        }
     }
  
     proc outputs() : group option = { return None; }

     (* Initiator needs to be stepped to be aware of KE completion *)
     proc step() : unit = { 
        var _K;
        if (st = ISCSentKE) {
           _K <@ KEAuth.outputs(Right I);
           if (_K <> None) {
              KEAuth.inputs(Left (I, (snd p, rcv p, enc (pld p) (oget (getr (oget _K))))));
              st <- ISCDone;
           }
        }
     }
  
     proc backdoor() : unit option = { return None; }
   }

   module Responder = {

     proc init() : unit = { }
  
     (* We let KE functionality manage inputs, only one will
        be accepted, and it must mach the one previously given
        by initiator *)
     proc inputs(_p : msg pkg) : unit = { 
        KEAuth.inputs(Right (R, (snd _p, rcv _p, ())));
     }

     (* Output becomes available whenever the authentication
        channel is unblocked *)  
     proc outputs(_p : unit pkg) : group option = {
        var rr,m,_K;
        rr <- None; 
        m <@ KEAuth.outputs(Left (R,  (snd _p, rcv _p, ())));
        if (m <> None) {
           _K <@ KEAuth.outputs(Right R);
           rr <- Some (dec (oget (getl (oget m))) (oget (getr (oget _K))));
        }
        return rr;
     }
  
     proc step() : unit = { }
  
     proc backdoor() : unit option = { return None; }
   }

   (* Now the protocol *)

   proc init() : unit = {
     Initiator.init();
     Responder.init();
   }

   proc inputs(r : role, p : msg pkg) : unit = {
      if (r = I) {
            Initiator.inputs(p);
      } else {
            Responder.inputs(p);
      }  
   }

   proc outputs(r : role, p : unit pkg) : group option = {
      var rr;
      rr <- None;
      if (r = I) {
         rr <@ Initiator.outputs();
      } else {
         rr <@ Responder.outputs(p);
      }
      return rr;
   }

   proc step(r : role) : unit = {
      if (r = I) {
         Initiator.step();
      } else {
         Responder.step();
      }
   }

   proc backdoor(r : role) : unit option = {
      var rr;
      if (r = I) {
         rr <@ Initiator.backdoor();
      } else {
         rr <@ Responder.backdoor();
      }
      return rr;
   }
}.


(* The simulator uses it's own copy of the low level
   ideal functionalities, which it uses in the simulation *)
clone import FKEAuth as SimKEAuth.

import FSC.
import FSCWorlds.

module (OTP_SIM : SIMULATOR) (FB : IDEAL.BACKDOORS) = {

   proc init() : unit = {
     SimKEAuth.FKEAuth.init();
   }

   proc step(s : RPi.IDEAL.step) : unit = {
     var lsc ,lke, lfa,fresh;
     match s with
     | Left m1 => {
           (* Stepping rho *)
           lsc <@ FB.backdoor();
           match (oget lsc) with
           | Ch_Init => { (* Nothing happended yet *) }
           | Ch_Blocked1 p => { (* Initiator has provided input, but responder still did not *) }
           | Ch_Wait p => { (* Initiator has provided input, but responder still did not *) }
           | Ch_Blocked2 p => {
               if (m1 = I) {
                   (* Stepping initiator when both already provided input.
                      Step causes initiator to progress if KE just finished *)
                   lfa <@ SimKEAuth.FKEAuth.backdoor(Left ());
                   match (oget (getl (oget lfa))) with
                   | Ch_Init => {
                     lke <@ SimKEAuth.FKEAuth.backdoor(Right ());
                      match  (oget (getr (oget lke))) with
                      | KE_Init => {}
                      | KE_Blocked1 p => {}
                      | KE_Wait p => {}
                      | KE_Blocked2 p => {}
                      | KE_Available p => {
                            fresh <$ dapply (fun x => g^x) FDistr.dt;
                            SimKEAuth.FKEAuth.inputs(Left (I, (snd p, rcv p, enc  (g^F.zero) fresh)));
                        }
                      end; 
                     }
                   | Ch_Blocked1 p' => { }
                   | Ch_Wait p' => { }
                   | Ch_Blocked2 p' => { }
                   | Ch_Available p' => { }
                   end;
               } else {
                   (* Stepping responder has no effect *)
               }
             }
           | Ch_Available p => { }
           end;
             
       }
     | Right m2 => {
           match m2 with
           | Left m21 => {
              (* Stepping FAuth: pass it along but check if receiver done. *)
                 SimKEAuth.FKEAuth.step(Left ());
                 lfa <@ SimKEAuth.FKEAuth.backdoor(Left ());
                 match (oget (getl (oget lfa))) with
                 | Ch_Init => { }
                 | Ch_Blocked1 p' => { }
                 | Ch_Wait p' => { }
                 | Ch_Blocked2 p' => { }
                 | Ch_Available p' => { FB.step(); }
                 end;
             }
           | Right m22 => {
              (* Stepping FKE: Just pass it along, but check if inputs
                 needs to be given first. *)
                 lsc <@ FB.backdoor();
                 match (oget lsc) with
                 | Ch_Init => { }
                 | Ch_Blocked1 p' => { 
                    lke <@ SimKEAuth.FKEAuth.backdoor(Right ());
                     match  (oget (getr (oget lke))) with
                     | KE_Init => {  SimKEAuth.FKEAuth.inputs(Right (I,(snd p', rcv p', ()))); FB.step(); }
                     | KE_Blocked1 p => {}
                     | KE_Wait p => { }
                     | KE_Blocked2 p => {}
                     | KE_Available p => { }
                     end; 
                   }
                 | Ch_Wait p' => { }
                 | Ch_Blocked2 p' => {
                    lke <@ SimKEAuth.FKEAuth.backdoor(Right ());
                     match  (oget (getr (oget lke))) with
                     | KE_Init => {}
                     | KE_Blocked1 p => {}
                     | KE_Wait p => { SimKEAuth.FKEAuth.inputs(Right (R,p));  }
                     | KE_Blocked2 p => {}
                     | KE_Available p => { }
                     end; 
                    }
                 | Ch_Available p' => { }
                 end;
                 SimKEAuth.FKEAuth.step(Right ());
             }
           end;
       }
     end;
   }

   proc backdoor(b : RPi.IDEAL.ask_backdoor) : RPi.IDEAL.backdoor option = {
       var rr,lsc,lfa,lke;
       rr <- None;
       match b with 
       | Left b1 => {
             (* Rho has no leakage *)
         } 
       | Right b2 => {
            match b2 with
            | Left c1 => {
               (* Auth backdoor: just type conversions *)
                 lfa <@ SimKEAuth.FKEAuth.backdoor(Left ());
                 match (oget (getl (oget lfa))) with
                 | Ch_Init => { 
                     rr <- Some (Right (Left (Ch_Init))); 
                   }
                 | Ch_Blocked1 p' => { 
                     rr <- Some (Right (Left (Ch_Blocked1 p'))); 
                   }
                 | Ch_Wait p' => {  
                     rr <- Some (Right (Left (Ch_Wait p')));  
                   }
                 | Ch_Blocked2 p' => { 
                     rr <- Some (Right (Left (Ch_Blocked2 p')));  
                   }
                 | Ch_Available p' => {  
                     rr <- Some (Right (Left (Ch_Available p')));  
                   }
                 end;
          }
          | Right c2 => {
               (* Key exchange leakage: sim sometimes lags behind, but we don't 
                  step FKE here. *)
               lsc <@ FB.backdoor();
               match (oget lsc) with
               | Ch_Init => { rr <- Some (Right (Right KE_Init)); }
               | Ch_Blocked1 p => { rr <- Some (Right (Right (KE_Blocked1 p))); }
               | Ch_Wait p => { rr <- Some (Right (Right (KE_Wait p))); }
               | Ch_Blocked2 p => {
                  lfa <@ SimKEAuth.FKEAuth.backdoor(Left ());
                  match (oget (getl (oget lfa))) with
                  | Ch_Init => {
                    lke <@ SimKEAuth.FKEAuth.backdoor(Right ());
                     match  (oget (getr (oget lke))) with
                     | KE_Init => { rr <- Some (Right (Right (KE_Blocked2 p))); }
                     | KE_Blocked1 p' => { rr <- Some (Right (Right (KE_Blocked2 p))); }
                     | KE_Wait p' => { rr <- Some (Right (Right (KE_Blocked2 p))); }
                     | KE_Blocked2 p' => { rr <- Some (Right (Right (KE_Blocked2 p))); }
                     | KE_Available p' => { rr <- Some (Right (Right (KE_Available p))); }
                     end; 
                    }
                  | Ch_Blocked1 p' => { }
                  | Ch_Wait p' => { }
                  | Ch_Blocked2 p' => { 
                       rr <- Some (Right (Right (KE_Available p))); 
                    }
                  | Ch_Available p' => { 
                       rr <- Some (Right (Right (KE_Available p))); 
                    }
                  end;
                }
             | Ch_Available p => { rr <- Some (Right (Right (KE_Available p))); }
             end;
             }
          end;
         } 
       end;
       return rr;
   }

}.

section.

(* AS A PROOF INSTRUMENT WE NEED TO MOVE THE SAMPLING OF THE AGREED KEY TO WHERE THE 
   SIMULATOR DOES IT *)
module (OTPLazy : RHO) (KEAuth: Pi.REAL.IO) = {
   module Initiator = {
     include OTP(KEAuth).Initiator [- step]

     proc step() : unit = { 
        var _K,fresh;
        if (OTP.Initiator.st = ISCSentKE) {
           _K <@ DHKE.FKE.FKEAuth.FKEAuth.outputs(Right I);
           if (_K <> None) {
              fresh <$  dapply (fun x => g^x) FDistr.dt;
              (* WE PUNCH THROUGH THE INTERFACE TO HARDWIRE A FRESH KEY *)
              DHKE.FKE.FKE.st <- {| DHKE.FKE.FKE.st with key = fresh |};
              DHKE.FKE.FKEAuth.FKEAuth.inputs(Left (I, 
                 (snd OTP.Initiator.p, rcv OTP.Initiator.p, enc (pld OTP.Initiator.p) fresh)));
              OTP.Initiator.st <- ISCDone;
           }
        }
     }
   }


   (* Now the protocol *)

   include OTP(KEAuth) [- init,step]

   proc init() : unit = {
     Initiator.init();
     OTP(KEAuth).Responder.init();
   }

   proc step(r : role) : unit = {
      if (r = I) {
         Initiator.step();
      } else {
         OTP(KEAuth).Responder.step();
      }
   }
}.

declare module Z <: REAL.ENV {-FKE, -FSC, -FChan.FAuth.FAuth, -OTP_SIM, 
                             -OTP, -DHKE.FKE.FKEAuth.FChan.FAuth.FAuth, -OTPLazy}.

(* To prove that our change of sampling in OTP is sound we use a generic
   eager sampling module *)
local clone import GenEager with
  type from <- unit, 
  type to   <- group, 
  op sampleto <- fun (x:unit) => dapply (fun (x : t) => g ^ x) FDistr.dt
  proof *.
realize sampleto_ll.
proof. by move => _; rewrite /= weight_dmap FDistr.dt_ll. qed.

(* We rewrite our new version of OTP using this module *)
local module OTP_AUX (O:RO) (KEAuth: Pi.REAL.IO) = {
   module Initiator = {
     include OTP(KEAuth).Initiator [- step]

     proc step() : unit = { 
        var _K,fresh;
        if (OTP.Initiator.st = ISCSentKE) {
           _K <@ DHKE.FKE.FKEAuth.FKEAuth.outputs(Right I);
           if (_K <> None) {
              fresh <@ O.get();
              (* WE PUNCH THROUGH THE INTERFACE TO HARDWIRE A FRESH KEY *)
              DHKE.FKE.FKE.st <- {| DHKE.FKE.FKE.st with key = fresh |};
              DHKE.FKE.FKEAuth.FKEAuth.inputs(Left (I, 
                 (snd OTP.Initiator.p, rcv OTP.Initiator.p, enc (pld OTP.Initiator.p) fresh)));
              OTP.Initiator.st <- ISCDone;
           }
        }
     }
  }


   (* Now the protocol *)

   include OTP(KEAuth) [- init,step]

   proc init() : unit = {
     O.init();
     O.sample();
     Initiator.init();
     OTP(KEAuth).Responder.init();
   }

   proc step(r : role) : unit = {
      if (r = I) {
         Initiator.step();
      } else {
         OTP(KEAuth).Responder.step();
      }
   }
}.

local module D (O:RO) = {
   proc distinguish = REAL.UC_emul(Z,CompRF(OTP_AUX(O),DHKE.FKE.FKEAuth.FKEAuth)).main
}.

lemma OTPAdv1 &m :
      Pr[ REAL.UC_emul(Z,CompRF(OTP,DHKE.FKE.FKEAuth.FKEAuth)).main() @ &m : res] =
      Pr[ REAL.UC_emul(Z,CompRF(OTPLazy,DHKE.FKE.FKEAuth.FKEAuth)).main() @ &m : res].
proof.
  have -> : 
    Pr[ REAL.UC_emul(Z,CompRF(OTP,DHKE.FKE.FKEAuth.FKEAuth)).main() @ &m : res] =
    Pr[ D(RO).distinguish() @ &m : res].
  + byequiv (_: ={glob Z} ==> ={res}) => //.
    proc.
    call (_: ={glob DHKE.FKE.FKEAuth.FChan.FAuth.FAuth, glob OTP} /\ 
            FKE.st{1}.`kst = FKE.st{2}.`kst /\
            () \in RO.m{2} /\
            FKE.st{1}.`key = oget RO.m{2}.[()] /\
            (DHKE.FKE.FKEAuth.FChan.FAuth.FAuth.st{2} <> Ch_Init => 
                   FKE.st{2}.`key = oget RO.m{2}.[()])
             ); conseq />.
    + proc => /=; inline *; wp; skip => /> &1 &2; rewrite /party_start /kstp /=. 
      progress; smt (). 

    + proc => /=; inline *; wp; skip => /> &1 &2; rewrite /party_output /kstp /= /#.    
    + proc; match; 1,2 : smt().
      + move=> m1 m2.      
        inline OTP(DHKE.FKE.FKEAuth.FKEAuth).step OTP_AUX(RO, DHKE.FKE.FKEAuth.FKEAuth).step.
        sp 1 1; if => //; conseq />; last by sim.
        inline *.
        if => //.
        match Right {1} 2; 1: by auto => /#.
        match Right {2} 2; 1: by auto => /#.
        sp; if; last done.
        + move => /> &1 &2.
          rewrite /get_as_Right /= /party_output /kstp => <-.
          by case: (FKE.st{1}.`kst) => />.
        rcondf{2} 3; 1: by auto.
        match Left {1} 2; 1: auto => /#.
        match Left {2} 6; 1: auto => /#.
        auto => /> &1 &2 ?? heq.
        rewrite /party_output /kstp /get_as_Left /get_as_Right /= heq.
        case : (FKE.st{1}.`kst) => />; rewrite //=. 
        rewrite /= dmap_ll //=.
        by apply  FDistr.dt_ll.
      move => m1 m2. 
      inline DHKE.FKE.FKEAuth.FKEAuth.step.
      sp 1 1; match; 1,2: smt().
      + by move=> *; inline *; auto => /#.
      move=> tt1 tt2; inline *; auto => /> &1 &2 heq ??.
      by rewrite /unblock /kstp heq; case: (FKE.st{2}.`kst) => />.
    + proc.
      match; 1,2 : smt().
      + by move=> m1 m2; inline *; sim; auto.
      by move=> m1 m2; inline *; auto => /> /#.
    inline *; auto => />; rewrite /is_lossless weight_dmap FDistr.dt_ll /= => *.
    by rewrite get_setE /init /= mem_empty /= mem_set.
  have -> : Pr[D(RO).distinguish() @ &m : res] = Pr[D(LRO).distinguish() @ &m : res].
  + byequiv (RO_LRO_D(D)) => //.
  byequiv (_: ={glob Z} ==> ={res}) => //.
  proc.
  call (_: ={glob DHKE.FKE.FKEAuth.FChan.FAuth.FAuth, glob OTP, glob FKE} /\ 
            () \in RO.m{1} = (OTP.Initiator.st{1} = ISCDone)); conseq />; 2,4: by sim.
  + proc; if => //; conseq />; 2: by sim.
    by inline *; auto => />.
  + proc; match; 1,2 : smt().
    + move=> m1 m2; inline *; sp 1 1; if => //.
      if => //.
      match Right {1} 2; 1: by auto => /#.
      match Right {2} 2; 1: by auto => /#.
      sp; if => //; auto => /> &1 &2 -> /= ??.
      by rewrite get_setE /= mem_set.
    by move=> m1 m2;conseq />; inline *; sim; auto.
  by inline *; auto => /> ?; rewrite mem_empty.
qed.

(* THE INVARIANT RELATES THE STATES OF THE REAL AND SIMULATED
   FUNCTIONALITIES *)

op invotp ip1 kst1 kst2 (cst2 : group pkg stlkg) ast2 ast1 ist1 =
   match cst2 with
   | Ch_Init => (* We just started, no inputs, all funcs in init *)
                kst1.`kst = kst2.`kst /\
                kst2.`kst = KE_Init /\
                ast2 = FChan.init_st /\
                ast1 = DHKE.FKE.FKEAuth.FChan.init_st /\ 
                ist1 = ISCInit
   | Ch_Blocked1 p => 
                (* There has been a initiator input, but nothing else,
                   so things only moved in the real world key exchance *)
                ip1 = p /\ 
                kst1.`kst = KE_Blocked1 (snd p,rcv p, ()) /\
                kst2.`kst = KE_Init /\
                ast2 = FChan.init_st /\ 
                ast1 = Ch_Init /\ 
                ist1 = ISCSentKE 
   | Ch_Wait p => 
                (* KE has been stepped to wait in the real world and simulator
                   picks it up to make everything match again at waiting state *)
                ip1 = p /\ 
                kst1.`kst = kst2.`kst /\
                kst1.`kst = KE_Wait (snd p,rcv p, ()) /\
                ast2 = FChan.init_st /\ 
                ast1 = Ch_Init /\ 
                ist1 = ISCSentKE
   | Ch_Blocked2 p => 
                ip1 = p  /\
                (* We create a disjunction in the invariant based on the state of various 
                    functionalities *)

                (
                (* We may have just come from wait state *)
                   (kst1.`kst = KE_Blocked2 (snd p,rcv p, ()) /\ 
                     kst2.`kst = KE_Wait (snd p,rcv p, ()) /\ 
                      ast1 = Ch_Init /\
                       ist1 = ISCSentKE /\ ast2 = Ch_Init) \/
                (* Key Exchange finished but nobody knows: we get here through steps to ke *)
                   (kst1.`kst = KE_Available (snd p,rcv p, ()) /\ 
                     kst2.`kst = KE_Available (snd p,rcv p, ()) /\ 
                      ast1 = Ch_Init /\
                       ist1 = ISCSentKE /\ ast2 = Ch_Init) \/
                (* Key Exchange finished and initiator did its part but still undelivered *)
                   (kst1.`kst = KE_Available (snd p,rcv p, ()) /\ 
                     kst2.`kst = KE_Available (snd p,rcv p, ()) /\
                      ast1 = Ch_Blocked2 
                                     (snd p, rcv p, enc (pld p) kst1.`key) /\
                      ast2 = Ch_Blocked2 (snd p, rcv p, enc (pld p) kst1.`key) /\
                       ist1 = ISCDone)
                 ) 
   | Ch_Available p => 
                ip1 = p /\ 
                kst1.`kst = KE_Available (snd p,rcv p, ()) /\
                kst2.`kst = KE_Available (snd p,rcv p, ()) /\
                ast1 = Ch_Available 
                                    (snd p, rcv p, enc (pld p) kst1.`key) /\
                ist1 = ISCDone /\ 
                ast2 = Ch_Available (snd p, rcv p, enc (pld p) kst1.`key)
   end.

lemma alg_lem1 (a b c : group):
   a = a * inv b * c * b * inv c.
proof. rewrite log_bij !(Ring.rw_algebra, inv_def); ring. qed.

lemma alg_lem2 (a b c : group):
   a = a * b * inv c * inv b * c.
proof. rewrite log_bij !(Ring.rw_algebra, inv_def); ring. qed.

lemma sup_lem1 (a b c : group):
   a \in gen =>
     mu1 gen a = mu1 gen (a * inv b * c).
have genu : (is_uniform gen); first by apply dmap_uni;smt(@DiffieHellman).
have genl : (is_lossless gen); first by apply dmap_ll;smt(@DiffieHellman).
rewrite !(mu1_uni_ll _ _ genu genl). 
move => agen; rewrite agen //=.
have -> // : (a * inv b * c \in gen).
rewrite /gen supp_dmap.
by exists ((log a) - (log b) + (log c));smt(@DiffieHellman).
qed.

lemma sup_lem2 (a b c : group):
    a \in gen =>
      a * b * inv c \in gen.
proof.
rewrite /gen //=  => *; rewrite supp_dmap. 
by exists (log a + log b + (log (inv c)));smt(@DiffieHellman).
qed.

lemma enc_lem (a b c : group):
    enc b a = enc c (a * b * inv c).
proof.
rewrite /enc; rewrite log_bij !(Ring.rw_algebra, inv_def); ring. 
qed.


lemma aux ['a] (b : bool) (u v : 'a) :
  b => (if b then u else v) = u
by case b. 
lemma aux2 ['a] (b : bool) (u v : 'a) :
  !b => (if b then u else v) = v
by case b.

lemma OTPAdv2 &m :
      Pr[ REAL.UC_emul(Z,CompRF(OTPLazy,DHKE.FKE.FKEAuth.FKEAuth)).main() @ &m : res] =
         Pr[ REAL.UC_emul(Z,CompS(FSC, OTP_SIM)).main() @ &m : res].
byequiv => //.
proc.
inline *.
swap {1} [2..3] -1.
swap {2} [3..4] -2.
seq 2 2 : (={glob Z,glob FKE} /\ FKE.st.`kst{2} = KE_Init); first by auto => /#.
call (_: invotp OTP.Initiator.p{1}
                FKE.st{1}
                FKE.st{2} 
                FSC.st{2}
                FChan.FAuth.FAuth.st{2} 
                DHKE.FKE.FKEAuth.FChan.FAuth.FAuth.st{1} 
                OTP.Initiator.st{1}); last by auto => />.

(* INPUTS *)
+ by proc;inline *;wp;skip;rewrite /invotp; smt().

(* OUTPUTS *)
+ proc;inline *.
  wp; skip => /> &1 &2; rewrite /rcv /= /party_output /invotp /kstp /=.

  (* MERGE-COST: old proof *)
  (* smt (correctness). *)

  (* MERGE-COST: new proof *)
  progress; 1,3: smt ().
  move :H; case (FSC.st{2}); case (FKE.st{1}.`kst) => />.
  smt (correctness).
  smt (correctness).
  smt (correctness).
  smt (correctness).
  smt (correctness).
  smt (correctness).
  smt (correctness).
  smt (correctness).  
  smt (correctness).
  move :H0 H1.
  case (OTP.Initiator.p{1}); case (p{2}) => />. 
  rewrite /leakpkg /pld /rcv /snd /=. 
  smt(correctness).
  (* MERGE-COST: end *)


(* Step *)
+ proc;inline *.
  match; first 2 by smt().
  (* STEP RHO *)
  + move => m1 m1_0.
    sp 1 0; if{1}.
    (* STEP INITIATOR *)
    + sp 0 1; match{2}.
      + by rcondf {1} 1; move => *; wp;skip;smt().
      + move => p. 
        rcondt {1} 1; first by move => *; wp;skip;smt().
        by sp 1 0; match{1};  by move => *;rcondf {1} 5; move => *;wp;skip;smt().
      + move => p.
        rcondt {1} 1; first by move => *; wp;skip;smt().
        by sp 1 0; match{1};  by move => *;rcondf {1} 5; move => *;wp;skip;smt().
      + move => p.
        rcondt {2} 1; first by move => *; wp;skip;smt().
        if{1}.
        + seq 0 3 : (#pre /\  (oget (getl (oget lfa{2}))) = Ch_Init); 
            first by wp;skip;smt().
          match {2}; last 4 by move => p'; exfalso; smt().
          seq 3 3 : (#pre /\ 
              (_K{1} <> None => 
                 FKE.st{1}.`kst = KE_Available (snd OTP.Initiator.p{1}, 
                                                rcv OTP.Initiator.p{1},()) /\
                (oget (getr (oget lke{2}))) = FKE.st{1}.`kst) /\
              (_K{1} = None => 
                FKE.st{1}.`kst <> KE_Available (snd OTP.Initiator.p{1}, 
                                                rcv OTP.Initiator.p{1},()) /\
                (oget (getr (oget lke{2}))) = KE_Wait (snd OTP.Initiator.p{1}, 
                                                        rcv OTP.Initiator.p{1},()))); 
            first by wp;skip;rewrite /invotp /#.
          case (_K{1} = None).
          + rcondf {1} 1; first by auto => /#.
            by match {2}; auto => /#.
          rcondt {1} 1; first by auto => />.
          match {2}; first 4 by auto => /#.
          move => p0.
          swap {1} 2 1.
          seq 2 2 : (#pre /\ ={i} /\ 
                    i{1} = Left (I,  (snd OTP.Initiator.p{1},  
                                        rcv OTP.Initiator.p{1}, 
                                        enc (pld OTP.Initiator.p{1}) fresh{1}))).
          + wp; rnd (fun (fresh1 : group) => 
                       fresh1 * (pld OTP.Initiator.p{1}) * (inv (g ^ F.zero))) 
                    (fun (fresh2 : group) => 
                       fresh2 * (inv (pld OTP.Initiator.p{1})) * (g ^ F.zero)).

            (* MERGE-COST: old proof *)
            (* by wp;skip;smt(alg_lem1 sup_lem1 sup_lem2 alg_lem2 enc_lem). *)

            (* MERGE-COST: new proof *)
            wp;skip. 
            move => />. 
            progress.
            smt(alg_lem1 alg_lem2 enc_lem). 
            by apply sup_lem1.
            by apply sup_lem2.
            smt(alg_lem1 alg_lem2 enc_lem).
            smt(alg_lem1 alg_lem2 enc_lem).
            smt(alg_lem1 alg_lem2 enc_lem).
            smt(alg_lem1 alg_lem2 enc_lem).
            (* MERGE-COST: end *)

          by wp;skip;rewrite /invotp;smt().
        seq 0 3 : (#pre /\ (oget (getl (oget lfa{2}))) <> Ch_Init); 
          first by wp;skip;smt().
        by match {2}; 1: (by exfalso => /#); auto => /#.
      by move => p; rcondf{1} 1; [ by move => *;auto => /#| by auto=> />].
    (* STEP RESPONDER *)
    sp 0 1; match{2}; first 3 by auto;rewrite /invotp /#.
    + by move => p;rcondf{2} 1; auto; rewrite /invotp /#.
    by auto; rewrite /invotp /#.

  (* STEP HYBRID FUNC *)
  move => m2 m2_0.
  sp 1 0;match; 1,2: (by smt()). 
  (* FAuth *)
  + auto; rewrite /invotp /#.
  (* KE *)
  move => *;wp;skip;rewrite /invotp => /> &1 &2.
  by case: (FSC.st{2}) => /> /#. 

(* BACKDOOR *)
proc.
sp 0 1; match; first 2 by smt().

(* Backdooring the protocol *)
+ by move => m1 b1; inline *; wp;skip;rewrite /invotp; smt().

(* Backdooring the hybrid functionalities *)
move => m2 b2; inline *.
by sp 1 0; match; 1,2: (by smt()); auto; rewrite /invotp /#.
qed.

(* Main theorem for secure channel based on OTP using ideal functionalities
   for key exchange and authenticated message transfer. Simulation is perfect. *)
lemma OTPAdv &m :
      Pr[ REAL.UC_emul(Z,CompRF(OTP,DHKE.FKE.FKEAuth.FKEAuth)).main() @ &m : res] =
         Pr[ REAL.UC_emul(Z,CompS(FSC, OTP_SIM)).main() @ &m : res].
proof. by rewrite (OTPAdv1 &m) -(OTPAdv2 &m). qed.

end section.

(* NOW WE MOVE TO INSTANTIATING FKE WITH DHKE *)

clone import PARA_IR with
    type Pi1.inputs        <- role * group pkg,
    type Pi1.ask_outputs   <- role * unit pkg,
    type Pi1.outputs       <- group,
    type Pi1.step          <- unit,
    type Pi1.ask_backdoor  <- unit,
    type Pi1.backdoor      <- DHKE.FKE.FKEAuth.FChan.state,
    type Pi2.REAL.inputs        <- role * unit pkg,
    type Pi2.REAL.ask_outputs   <- role,
    type Pi2.REAL.outputs       <- group,
    type Pi2.REAL.step          <- (role,(unit,unit) sum) sum,
    type Pi2.REAL.ask_backdoor  <- (role,(unit,unit) sum) sum,
    type Pi2.REAL.backdoor      <- (unit, (DHKE.HybFChan.state, DHKE.HybFChan.state) sum) sum,
    type Pi2.IDEAL.step          <- unit,
    type Pi2.IDEAL.ask_backdoor  <- unit,
    type Pi2.IDEAL.backdoor      <- DHKE.FKE.kestate. 


(* We re-express OTP as an instance of PARA_IR. *)
module I_PARA = I.PPara(DHKE.FKE.FKEAuth.FChan.FAuth.FAuth, DHKE.FKE.FKE).
module I_OTP = C_OTP.CompRF(OTP,I_PARA).

(* This also allows us to express an instantiated KE *)
module DHKE_ = DHKE.COMPOSITION.CompRF(DHKE.DHKE,DHKE.HybFChan.F2Auth.F2Auth).
module R_PARA = R.PPara(DHKE.FKE.FKEAuth.FChan.FAuth.FAuth, DHKE_).
(* And also to express parametrizing OTP with this *)
module R_OTP = C_OTP.CompRP(OTP,R_PARA).

op cz : cenv.
axiom cz_pos : cenv_pos cz.

clone PARA_IR.C as PIC with
  op cz = {|
    cd = 2 + cz.`cd + 9 * cz.`ci + (22 + cgdiv) * cz.`co + (19 + cgmul) * cz.`cs + 5 * cz.`cb;
    ci = cz.`cs + cz.`ci;
    co = cz.`cs + 2 * cz.`co;
    cs = cz.`cs;
    cb = cz.`cb;
  |},
  op cf1 = {|
    cpinit     = 1;
    cpinputs   = 3;
    cpoutputs  = 9;
    cpstep     = 2;
    cpbackdoor = 1;
  |},
  op cs2 = {|
    cinit     = 2 * (1 + cgpow + cdt);
    cstep     = 26; 
    cs_s      = 1; 
    cs_b      = 1;
    cbackdoor = 20;
    cb_s      = 0;
    cb_b      = 1; 
  |}
  proof cz_pos by smt (cz_pos ge0_cg)
  proof cf1_pos by done
  proof cs2_pos by smt (ge0_cg ge0_cf ge0_cdt).

clone DHKE.C as DHKEC with
  op RC.c <- PIC.CPi2.c
  proof RC.c_pos by apply PIC.CPi2.c_pos. 

(* THIS PROOF IS ALL BOILER PLATE: WE APPLY THE REFINEMENT OF UC THEOREM FOR INSTANTIATING
   ONE OF THE FUNCTIONALITIES IN A PARALLEL COMPOSITION OF FUNCTIONALITIES, AND THEN PROVE THAT
   EVERYTHING MATCHES SYNTACTICALLY. THE THEOREM STATES THAT THE DHKE SIMULATOR CAN BE USED TO
   CONSTRUCT A SIMULATOR FOR THE INSTANTIATED PROTOCOL, WHICH UC REALIZES THE PROTOCOL WITH
   AN IDEAL FKE. BELOW WE APPLY TRANSITIVITY TO DERIVE THE MAIN THEOREM. *)

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

lemma InstOTPAdv &m :    
  exists (S <: RPi.SIMULATOR {+DHKE.DHKE_SIM}
               [init : {} `{N csi.`cinit},
                step : `{N csi.`cstep, #FB.step : csi.`cs_s, #FB.backdoor : csi.`cs_b},
                backdoor : `{N csi.`cbackdoor, #FB.step : csi.`cb_s, #FB.backdoor : csi.`cb_b}]) , 
      forall (Z <:  C_OTP.RPi.REAL.ENV {-OTP, -FKE, -DHKE.FKE.FKEAuth.FChan.FAuth.FAuth,
                              -DHKE.DHKE_SIM, -DHKE_, -FSC, -FChan.FAuth.FAuth}
                  [distinguish : {#I.inputs, #I.outputs, #I.step, #I.backdoor}
                               `{N cz.`cd,
                                 #I.inputs : cz.`ci,
                                 #I.outputs : cz.`co,
                                 #I.step : cz.`cs,
                                 #I.backdoor : cz.`cb}
              ]), 
      `| Pr[ C_OTP.RPi.REAL.UC_emul(Z,R_OTP).main() @ &m : res] -
         Pr[ C_OTP.RPi.REAL.UC_emul(Z,C_OTP.RPi.CompS(I_OTP, S)) .main() @ &m : res] | <= adv_ddh DHKEC.cddh.
proof.
have [S h] := DHKEC.KEAdv_ex &m.
have := PIC.ex_compose DHKE.FKE.FKEAuth.FChan.FAuth.FAuth _ _ _ _ _ DHKE_ DHKE.FKE.FKE S &m (adv_ddh DHKEC.cddh) _;
  1..5: by proc; auto => />.
move=> /= Z; have := h Z.
have -> : 
    Pr[FKEWorlds.REAL.UC_emul(Z, DHKE.COMPOSITION.CompRF(DHKE.DHKE, DHKE.HybFChan.F2Auth.PARA.PPara(DHKE.HybFChan.F2Auth.FAuthLR.FAuth, DHKE.HybFChan.F2Auth.FAuthRL.FAuth))).main
     () @ &m : res] =
   Pr[Pi2.REAL.UC_emul(Z, DHKE.COMPOSITION.CompRF(DHKE.DHKE, DHKE.HybFChan.F2Auth.PARA.PPara(DHKE.HybFChan.F2Auth.FAuthLR.FAuth, DHKE.HybFChan.F2Auth.FAuthRL.FAuth))).main () @ &m : res].
+ by byequiv => //; sim.
have -> //: Pr[FKEWorlds.REAL.UC_emul(Z, FKEWorlds.CompS(FKE, S)).main() @ &m : res] = Pr[Pi2.REAL.UC_emul(Z, Pi2.CompS(FKE, S)).main() @ &m : res].
+ by byequiv => //; sim.
move=> [S1 hS1].
exists (C_OTP.Sid(S1)); split.
+ split; [|split] => kb ks FB *.
  + by proc true : time []. 
  + proc; exlim m => -[] mi.
    + match Left 1; [auto; smt() | done |].
      by call (:true); auto => /> /#. 
    match Right 1;[auto; smt() | done |].
    call (:true; time [C_OTP.Sid(S1, FB).FBPi.step : [N 1; FB.step : 1], 
                       C_OTP.Sid(S1, FB).FBPi.backdoor : [N 4; FB.backdoor : 1]]).
    + by move=> *; proc; call(:true); auto.
    + by move=> *; proc; call(:true); auto.
    auto => />.
    rewrite !bigi_constz; 1..2 : smt (PIC.CRI.csi_pos).
    by rewrite /= !StdBigop.Bigint.bigi_constz; smt (PIC.CRI.csi_pos).
  proc; exlim m => -[] mi.
  + match Left 1; [auto; smt() | done |].
    by wp; call (:true); auto => />. 
  match Right 1;[auto; smt() | done |].
  wp;call (:true; time [C_OTP.Sid(S1, FB).FBPi.step : [N 1; FB.step : 1], 
                        C_OTP.Sid(S1, FB).FBPi.backdoor : [N 4; FB.backdoor : 1]]).
  + by move=> *; proc; call(:true); auto.
  + by move=> *; proc; call(:true); auto.
  auto => />.
  rewrite !bigi_constz; 1..2 : smt (PIC.CRI.csi_pos).
  by rewrite /= !StdBigop.Bigint.bigi_constz; smt (PIC.CRI.csi_pos).
move => Z.
have -> : Pr[ C_OTP.RPi.REAL.UC_emul(Z,R_OTP).main() @ &m : res] = 
          Pr[ RI.REAL.UC_emul(C_OTP.CompZR(Z, OTP), R.PPara(DHKE.FKE.FKEAuth.FChan.FAuth.FAuth, DHKE_)).main () 
               @ &m : res].
+ byequiv => //.
  proc;inline *.
  wp;call(_: ={glob OTP, glob DHKE.DHKE, glob DHKE.HybFChan.F2Auth.F2Auth, 
               glob DHKE.FKE.FKEAuth.FChan.FAuth.FAuth}); first 4 by sim.
  by auto.
have -> : Pr[RPi.REAL.UC_emul(Z, RPi.CompS(I_OTP, C_OTP.Sid(S1))).main() @ &m : res] = 
          Pr[RI.REAL.UC_emul(C_OTP.CompZR(Z, OTP), RI.CompS(I.PPara(DHKE.FKE.FKEAuth.FChan.FAuth.FAuth, FKE), S1)).main() @ &m : res]. 
+ byequiv => //. 
  proc;inline *.
  wp;call(_: ={glob S1, glob OTP, glob DHKE.FKE.FKEAuth.FChan.FAuth.FAuth, glob FKE}); 
     first 2 by sim.
  + proc; match; [1,2: by smt()]; move=> *;inline *; auto.
    call (: ={glob S1, glob OTP, glob DHKE.FKE.FKEAuth.FChan.FAuth.FAuth, glob FKE}).
    + by proc; inline *; match Right {1} 2; auto => /#.
    + by proc; inline *; match Right {1} 2; auto => /#.
    by auto => />.
  + proc; match; 1,2: by smt().
    + by move => *; inline *;auto => /> /#.
    move => *; inline *.
    wp; call (: ={glob S1, glob OTP, glob DHKE.FKE.FKEAuth.FChan.FAuth.FAuth, glob FKE}).
    + by proc; inline *; match Right {1} 2; auto => /#.
    + by proc; inline *; match Right {1} 2; auto => /#.
    by auto => />.
  wp; call (:true); auto => />.
apply (hS1 (C_OTP.CompZR(Z, OTP))). 
move=> kb ks ko ki I0 * {S h S1 hS1}.
proc.
call (:true; time [ CompR_I(OTP, I0).inputs  : [N 9; I0.inputs : 1], 
                    CompR_I(OTP, I0).outputs : [N (22 + cgdiv); I0.outputs : 2], 
                    CompR_I(OTP, I0).step    : [N (19 + cgmul); I0.inputs : 1; I0.outputs : 1; I0.step : 1], 
                    CompR_I(OTP, I0).backdoor : [N 5; I0.backdoor : 1]]) => /= *.
+ proc; inline *;if => //.
  + sp => //; if => //; auto; last by smt().
    by call (:true); auto => />.
  by call(:true);auto => />. 
+ proc; inline *; sp => //; if => //; auto => />; 1: smt(ge0_cg).
  seq 3 : true time [N (13 + cgdiv); I0.outputs : 1] => //.
  + by call (:true); auto => /> /#.
  if => //=; auto => />; last smt(ge0_cg).
  by call(:true); auto => /> /#.
+ proc; exlim m => -[] mi.
  + match Left 1; [auto; smt() | done |].
    inline *; sp => //; if => //; last by auto; smt (ge0_cg).
    if => //; last by auto; smt (ge0_cg).
    seq 1 : true time [N (14 + cgmul); I0.inputs : 1] => //.
    + by call(:true); auto => /> /#.
    if => //; auto; 2: smt(ge0_cg).
    call (:true); auto => /> /#.
  match Right 1; [auto; smt() | done |].
  call(:true); auto => />; smt (ge0_cg).
+ proc. exlim m => -[] mi.
  + match Left 1; [auto; smt() | done |].
    by inline *; auto => /#.
  match Right 1; [auto; smt() | done |].
  by wp; call (:true); auto.  
inline *; auto => />.
rewrite !bigi_constz; 1..4:smt(cz_pos). 
rewrite /= !StdBigop.Bigint.bigi_constz; smt (cz_pos).
qed.

import DHKE.
(* import COMPOSITION. *)
import FKE.
import FKEAuth. 

(****************************************************************)
(*  MAIN THEOREM: ASSUMING 3 AUTHENTICATED COMMUNICATIONS       *)
(*  DHKE + OTP UC REALIZES A ONE-SHOT SECURE CHANNEL.           *)
(****************************************************************)

(* Again, the steps in this proof are all boiler plate; we just apply transitivity.
   We could have equality here, but we need to have a strengthened transitivity lemma for perfect simulators. *)

clone C_OTP.TRANS.C as COTPTC with
  op cz   <- cz,
  op cs12 <- csi,
  op cs23 <- {|
    cinit     = 3 + cdt + cgpow;
    cstep     = 34 + cgmul + 2 * cgpow + cdt;
    cs_s      = 1;
    cs_b      = 1;
    cbackdoor = 25;
    cb_s      = 0;
    cb_b      = 1;
    |}
  proof cz_pos by apply cz_pos
  proof cs12_pos by smt (PIC.CRI.csi_pos)
  proof cs23_pos by smt (ge0_cf ge0_cg ge0_cdt).
 
(* add proof *)

(* hint simplify cost_dapply. *)


schema cost_party_start `{P} {s:state, r:role, p : unit pkg}: cost[P: party_start s r p] = N 5 + cost[P:s] + cost[P:r] + cost[P:p].
hint simplify cost_party_start.

lemma MainTheorem &m : 
  exists (S <: TRANS.GOAL.SIMULATOR),
    forall (Z <: RPi.REAL.ENV {-S, -FSC, -CompRP(OTP,R.PPara(FChan.FAuth.FAuth, COMPOSITION.CompRF(DHKE,HybFChan.F2Auth.F2Auth)))}
      [distinguish : 
        `{N cz.`cd,
          #I.inputs : cz.`ci,
          #I.outputs : cz.`co,
          #I.step : cz.`cs,
          #I.backdoor : cz.`cb}]
    ),   
      `| Pr[ RPi.REAL.UC_emul(Z,CompRP(OTP,R.PPara(FChan.FAuth.FAuth, COMPOSITION.CompRF(DHKE,HybFChan.F2Auth.F2Auth)))).main() @ &m : res] -
         Pr[ RPi.REAL.UC_emul(Z,TRANS.GOAL.CompS(FSC, S)).main() @ &m : res] | <=
       adv_ddh (11 + cz.`cd + 42 * cz.`ci + (52 + cgdiv) * cz.`co + (93 + cgmul) * cz.`cs + 15 * cz.`cb).
proof.
have -> : 11 + cz.`cd + 42 * cz.`ci + (52 + cgdiv) * cz.`co + (93 + cgmul) * cz.`cs + 15 * cz.`cb = DHKEC.cddh by smt().
have [S hS]:= InstOTPAdv &m.
have [S1 hS1]:= COTPTC.ex_uc_transitivity
      (CompRP(OTP, R.PPara(FChan.FAuth.FAuth, COMPOSITION.CompRF(DHKE, HybFChan.F2Auth.F2Auth)))) 
      (CompRF(OTP,FKEAuth)) FSC S OTP_SIM _ _ _ &m (adv_ddh DHKEC.cddh) 0%r _ _.
+ move=> kb ks FB *.
  instantiate h := (cost_dapply {k : group}).
  proc; inline *; wp; rnd; 1: by rewrite h.
  by auto => />; rewrite h dmap_ll 1:FDistr.dt_ll /= /#.
+ move=> kb ks FB *.                  
  proc. 
  exlim (s) => -[]mi.
  + match Left 1;[auto; smt() | done |].
    seq 1 : true time [N (33 + cgmul + 2 * cgpow + cdt)] => //.
    + by call (:true); auto => /> /#. 
    inline *; wp.
    exlim (oget lsc) => -[|p|p|p|p].
    + match Ch_Init 1; [auto; smt() | done |].
      by auto => />; smt(ge0_cf ge0_cg ge0_cdt).
    + match Ch_Blocked1 1; [auto; smt() | done |].
      by auto => />; smt(ge0_cf ge0_cg ge0_cdt).
    + match Ch_Wait 1; [auto; smt() | done |].
      auto => />; smt(ge0_cf ge0_cg ge0_cdt).
    + match Ch_Blocked2 1; [auto; smt() | done |].
      if => //.
      + sp => //.
        match Left 1; [auto; smt() | done |].
        sp => //.
        exlim (oget (getl (oget lfa))) => -[|p'|p'|p'|p'].
        + match Ch_Init 1; [auto; smt() | done |].
          sp => //.
          match Right 1; [auto; smt() | done |].
          sp => //.
          exlim (oget (getr (oget lke))) => -[|p1|p1|p1|p1].
          + match KE_Init 1; [auto; smt() | done |].
            auto; smt(ge0_cf ge0_cg ge0_cdt).
          + match KE_Blocked1 1; [auto; smt() | done |].
            auto => />; smt(ge0_cf ge0_cg ge0_cdt).
          + match KE_Wait 1; [auto; smt() | done |].
            auto => />; smt(ge0_cf ge0_cg ge0_cdt).
          + match KE_Blocked2 1; [auto; smt() | done |].
            auto => />; smt(ge0_cf ge0_cg ge0_cdt).
          match KE_Available 1; [auto; smt() | done |].
          seq 2 : (i =  Left (I, (snd p2, rcv p2, enc (g ^ F.zero) fresh))) time [N 4] => //.
          + instantiate h := (cost_dapply {m2, m3 : unit, r3, r4 : role, p0, p1, p2 : unit pkg, fresh : group, 
                                           p : group pkg, r1, r10 : group pkg stlkg option, r2, r20 : kestate option,
                                           r, r0 : (group pkg stlkg, kestate) sum option, lsc : Top.OTP.FChan.FSC.leakage option, 
                                           m1 : C_OTP.R.step, s : RPi.IDEAL.step, i : SimKEAuth.PARA.Pi12.inputs,
                                           m, m0 : SimKEAuth.PARA.Pi12.ask_backdoor, lke, lfa : SimKEAuth.PARA.Pi12.backdoor option}).
             wp; rnd => />; 1: by rewrite h.
             auto => /> *; rewrite h dmap_ll 1:FDistr.dt_ll /=; smt(ge0_cf ge0_cg).
          by match Left 1; [auto; smt() | done | auto => />].
        + match Ch_Blocked1 1; [auto; smt() | done |].
          auto; smt(ge0_cf ge0_cg ge0_cdt).
        + match Ch_Wait 1; [auto; smt() | done |].
          auto; smt(ge0_cf ge0_cg ge0_cdt).
        + match Ch_Blocked2 1; [auto; smt() | done |].
          auto; smt(ge0_cf ge0_cg ge0_cdt).
        match Ch_Available 1; [auto; smt() | done |].
        auto; smt(ge0_cf ge0_cg ge0_cdt).
      auto; smt(ge0_cf ge0_cg ge0_cdt).
    match Ch_Available 1; [auto; smt() | done |].
    auto; smt(ge0_cf ge0_cg ge0_cdt).
  match Right 1;[auto; smt() | done |].
  inline *.
  exlim m2 => -[] m2i.
  + match Left 1; [auto; smt() | done |].
    sp 1 => //.
    match Left 1; [auto; smt() | done |].
    sp 3 => //.
    match Left 1; [auto; smt() | done|].
    sp => //.
    exlim (oget (getl (oget lfa))) => -[|p'|p'|p'|p'].
    + match Ch_Init 1; [auto; smt() | done |].
      by auto => /=; smt(ge0_cf ge0_cg ge0_cdt).
    + by match Ch_Blocked1 1; auto; smt(ge0_cf ge0_cg ge0_cdt).
    + by match Ch_Wait 1; auto; smt(ge0_cf ge0_cg ge0_cdt).
    + by match Ch_Blocked2 1; auto; smt(ge0_cf ge0_cg ge0_cdt).
    match Ch_Available 1; [auto; smt() | done |].
    by call (:true); auto; smt(ge0_cf ge0_cg ge0_cdt).
  match Right 1; [auto; smt() | done |].
  seq 1 : true time [N 28; FB.step : 1] => //.
  + by call(:true); auto => />; smt(ge0_cf ge0_cg ge0_cdt).
  seq 1 : true time [N 4] => //; last by sp 1 => //; match Right 1; auto.
  exlim (oget lsc) => -[|p'|p'|p'|p'].
  + match Ch_Init 1; auto; smt(ge0_cf ge0_cg ge0_cdt).
  + match Ch_Blocked1 1;[auto; smt() | done |].
    sp 1 => //; match Right 1; [auto; smt() | done |].
    sp 4 => //.
    exlim  (oget (getr (oget lke))) =>  -[|p|p|p|p].
    + match KE_Init 1; [auto; smt() | done |].
      sp 1 => //; match Right 1; [auto; smt() | done |].
      by call (:true); auto => />. 
    + by match KE_Blocked1 1; auto; smt(ge0_cf ge0_cg ge0_cdt).
    + by match KE_Wait 1; auto; smt(ge0_cf ge0_cg ge0_cdt).
    + by match KE_Blocked2 1; auto; smt(ge0_cf ge0_cg ge0_cdt).
    by match KE_Available 1; auto; smt(ge0_cf ge0_cg ge0_cdt).
  + by match Ch_Wait 1; auto; smt(ge0_cf ge0_cg ge0_cdt).
  + match Ch_Blocked2 1; [auto; smt() | done |].
    by sp 1 => //; match Right 1; auto; smt(ge0_cf ge0_cg ge0_cdt).
  by match Ch_Available 1; auto; smt(ge0_cf ge0_cg ge0_cdt).
+ move=> kb ks FB *.                  
  proc.
  inline *.
  exlim (b) => -[bl|br].     
  + by match Left 2; auto; smt().
  match Right 2; [1: by auto; smt() | 2: done].
  exlim (b2) => -[b2l|b2r].     
  + by match Left 2; auto; smt().
  match Right 2; [1: by auto; smt() | 2: done].
  sp => //.
  seq 1 : true time [ N 22]; 1: done.
  call (:true); auto => * /=; by smt (). 
  by auto => /> /#.
+ move=> Z.
  have -> : 
    Pr[TRANS.H1.REAL.UC_emul(Z, CompRP(OTP, R.PPara(FChan.FAuth.FAuth, COMPOSITION.CompRF(DHKE, HybFChan.F2Auth.F2Auth)))).main() @ &m : res] = 
     Pr[RPi.REAL.UC_emul(Z, CompRP(OTP, R.PPara(FChan.FAuth.FAuth, COMPOSITION.CompRF(DHKE,HybFChan.F2Auth.PARA.PPara(HybFChan.F2Auth.FAuthLR.FAuth, HybFChan.F2Auth.FAuthRL.FAuth))))).main() @ &m : res].
  + by byequiv => //;sim.
  have -> : 
    Pr[TRANS.H1.REAL.UC_emul(Z, TRANS.H1.CompS(CompRF(OTP, FKEAuth), S)).main() @ &m : res] = 
    Pr[RPi.REAL.UC_emul(Z, RPi.CompS(CompRF(OTP, I.PPara(FChan.FAuth.FAuth, FKE)), S)).main() @ &m : res].
  + by byequiv => //; sim.
  by apply (hS Z).
+ move=> Z.
  have -> :
    Pr[TRANS.H2.REAL.UC_emul(Z, CompRF(OTP, FKEAuth)).main() @ &m : res] = 
    Pr[REAL.UC_emul(Z, CompRF(OTP, PARA.PPara(FChan.FAuth.FAuth, FKE))).main() @ &m : res].
  + by byequiv => //; sim.
  have -> : 
    Pr[TRANS.H2.REAL.UC_emul(Z, TRANS.H2.CompS(FSC, OTP_SIM)).main() @ &m : res] = 
    Pr[REAL.UC_emul(Z, CompS(FSC, OTP_SIM)).main() @ &m : res].
  + by byequiv => //; sim.
  by rewrite  -(OTPAdv Z &m).
exists S1 => Z.
have <- : 
  Pr[TRANS.GOAL.REAL.UC_emul(Z, CompRP(OTP, R.PPara(FChan.FAuth.FAuth, COMPOSITION.CompRF(DHKE, HybFChan.F2Auth.F2Auth)))).main() @ &m : res] = 
  Pr[RPi.REAL.UC_emul(Z, CompRP(OTP, R.PPara(FChan.FAuth.FAuth, COMPOSITION.CompRF(DHKE, HybFChan.F2Auth.F2Auth)))).main() @ &m : res].
+ by byequiv=> //; sim.
have <- :
  Pr[TRANS.GOAL.REAL.UC_emul(Z, TRANS.GOAL.CompS(FSC, S1)).main() @ &m : res] =
  Pr[RPi.REAL.UC_emul(Z, TRANS.GOAL.CompS(FSC, S1)).main () @ &m : res].
+ by byequiv => //;sim.
apply (hS1 Z).
qed.

end OTP.
back to top