cost.ec
require import Distr DBool Int List CHoareTactic StdBigop.
import Bigint IntExtra BIA.
require BitWord ROM.
(*****************)
(* Example BR93: *)
(*****************)
(* Plaintexts *)
op k : { int | 0 < k } as gt0_k.
clone import BitWord as Plaintext with
op n <- k
proof gt0_n by exact/gt0_k
rename
"word" as "ptxt"
"dunifin" as "dptxt".
import DWord.
(* Nonces *)
op l : { int | 0 < l } as gt0_l.
clone import BitWord as Randomness with
op n <- l
proof gt0_n by exact/gt0_l
rename
"word" as "rand"
"dunifin" as "drand".
import DWord.
(* Ciphertexts *)
op n = l + k.
lemma gt0_n: 0 < n by smt(gt0_k gt0_l).
clone import BitWord as Ciphertext with
op n <- n
proof gt0_n by exact/Self.gt0_n
rename "word" as "ctxt".
(* Parsing and Formatting *)
op (||) (r:rand) (p:ptxt) : ctxt = mkctxt ((ofrand r) ++ (ofptxt p)).
op parse (c:ctxt): rand * ptxt =
(mkrand (take l (ofctxt c)),mkptxt (drop l (ofctxt c))).
lemma parseK r p: parse (r || p) = (r,p).
proof.
rewrite /parse /(||) ofctxtK 1:size_cat 1:size_rand 1:size_ptxt //=.
(* by rewrite take_cat drop_cat size_rand take0 drop0 cats0 mkrandK mkptxtK. *)
admit.
qed.
lemma formatI (r : rand) (p : ptxt) r' p':
(r || p) = (r' || p') => (r,p) = (r',p').
proof. by move=> h; rewrite -(@parseK r p) -(@parseK r' p') h. qed.
(* A set `pkey * skey` of keypairs, equipped with *)
(* a lossless, full, uniform distribution dkeys *)
type pkey, skey.
op dkeys: { (pkey * skey) distr | is_lossless dkeys
/\ is_funiform dkeys } as dkeys_llfuni.
(* A family `f` of trapdoor permutations over `rand`, *)
(* indexed by `pkey`, with inverse family `fi` indexed by `skey` *)
op f : pkey -> rand -> rand.
op fi: skey -> rand -> rand.
axiom fK pk sk x: (pk,sk) \in dkeys => fi sk (f pk x) = x.
(* Random Oracle *)
clone import ROM.Lazy as H with
type from <- rand,
type to <- ptxt,
op dsample _ <- dptxt.
(************************************************************************)
(* Costs of various operators. *)
schema cost_witness ['a] `{P} : cost[P: witness<:'a>] = 1.
schema cost_eq ['a] `{P} {a b : 'a} :
cost[P : a = b] = cost[P : a] + cost[P : b] + 1.
schema cost_deq ['a] `{P} {a b : 'a} :
cost[P : a <> b] = cost[P : a] + cost[P : b] + 1.
schema cost_cons ['a] `{P} {e : 'a} {l : 'a list} :
cost[P : e :: l] =
cost[P : e] + cost[P : l] + 1.
schema cost_nil ['a] `{P} : cost[P : [<:'a>]] = 1.
schema cost_head ['a] `{P} {l : 'a list} :
cost[P : head witness l] =
cost[P : l] + 1.
schema cost_drop ['a] `{P} {l : 'a list} :
cost[P: drop 1 l] = cost[P: l] + 1.
schema cost_pp `{P} {r : rand} {p : ptxt} :
cost[P : r || p] =
cost[P : r] + cost[P : p] + 1.
schema cost_dptxt `{P} : cost[P : dptxt] = 1.
schema cost_pos ['a] `{P} {e : 'a} : 0 <= cost[P : e].
op kf : int.
axiom kfp : 0 <= kf.
schema cost_f `{P} {pk : pkey} {r : rand} : cost[P : f pk r] = kf.
hint simplify
cost_witness, cost_eq, cost_deq, cost_cons, cost_nil,
cost_head, cost_drop, cost_pp, cost_dptxt, cost_f.
(************************************************************************)
module type Oracle = {
proc init () : unit
proc o (x : rand) : ptxt
}.
module type AOracle = {
proc o (x : rand) : ptxt
}.
module type Adv (H : AOracle) = {
proc a1(p:pkey): (ptxt * ptxt)
proc a2(c:ctxt): bool
}.
(*****************************************)
(* Other example, of the accepted syntax *)
op k1 : int.
op k2 : int.
(* We have two possibility to give module restrictions. *)
(* We can give the restrictions function by function, e.g.: *)
op ka1, ka2 : int.
module type OAdv (H1 : Oracle) (H : AOracle) = {
proc a1(x : pkey) : ptxt * ptxt `{ka1, H.o : k1, H1.o : 1}
proc a2(x : ctxt) : bool `{ka2, H.o : k2, H1.o : 3}
}.
(* Or we can give the restrictions at the top-level, e.g.: *)
module type OAdvBis (H1 : Oracle, H : AOracle)
[a1 : {H.o, H1.o} `{ka1, H.o : k1, H1.o : 1 },
a2 : {H.o, H1.o} `{ka2, H.o : k2, H1.o : 3 }]
= {
proc a1(p:pkey): (ptxt * ptxt)
proc a2(c:ctxt): bool
}.
(*****************************************)
(* Inverter *)
module I (A : Adv) (H : Oracle) = {
var qs : rand list
module QRO = {
proc o (x : rand) = {
var r;
qs <- x :: qs;
r <- H.o(x);
return r;
}
}
module A0 = A(QRO)
proc invert(pk : pkey, y : rand) : rand = {
var x, m0, m1, h, b;
qs <- [];
H.init();
(m0,m1) <- A0.a1(pk);
h <$ dptxt;
b <- A0.a2(y || h);
x <- nth witness qs (find (fun p => f pk p = y) qs);
return x;
}
}.
(* Same as I, but using a while-loop to find the element in the list. *)
module IW (A : Adv) (H : Oracle) = {
var qs : rand list
module QRO = {
proc o (x : rand) = {
var r;
qs <- x :: qs;
r <- H.o(x);
return r;
}
}
module A0 = A(QRO)
proc invert(pk : pkey, y : rand) : rand = {
var r, x, m0, m1, h, b;
var qs0;
qs <- [];
H.init();
(m0,m1) <- A0.a1(pk);
h <$ dptxt;
b <- A0.a2(y || h);
r <- witness;
qs0 <- qs;
while (qs0 <> []){
x <- head witness qs0;
if (f pk x = y) {
r <- x; qs0 <- [];
} else {
qs0 <- drop 1 qs0;
}
}
return r;
}
}.
equiv eq_I_IW (H <: Oracle{-I, -IW}) (A <: Adv{-I, -IW, -H}):
I(A,H).invert ~ IW(A,H).invert:
={arg} /\ ={glob H, glob A} ==> ={res}.
proof.
proc.
seq 5 5 : (={glob H, glob A, b,h, pk, y} /\ I.qs{1} = IW.qs{2}); first by sim.
while {2} (
if qs0 = [] then
r = nth witness IW.qs (find (fun (p0 : rand) => f pk p0 = y) IW.qs)
else
exists i, r = witness /\ 0 <= i < size IW.qs /\ qs0 = drop i IW.qs /\ !has (fun x => f pk x = y) (take i IW.qs)){2}
(size qs0{2}).
+ move=> &1 z; auto => &2 />.
case: (qs0{2}) => //= hd qs0 [i />] h0i hi hdr hhas.
have heq : IW.qs{2} = take i IW.qs{2} ++ hd :: qs0.
+ by rewrite hdr cat_take_drop.
rewrite heq; move: hhas; (pose tk := take i IW.qs{2}) => hhas.
have heq1 :
nth witness (tk ++ hd :: qs0) (find (fun (p0 : rand) => f pk{2} p0 = y{2}) (tk ++ hd :: qs0)) =
nth witness (hd :: qs0) (find (fun (p0 : rand) => f pk{2} p0 = y{2}) (hd :: qs0)).
+ by rewrite find_cat hhas /= nth_cat; smt (find_ge0).
split.
+ by move=> heq2;rewrite heq2 heq1 /= heq2 /=; smt (size_ge0).
rewrite heq1 drop0 => hy; split;2:smt().
split.
+ by move=> />;rewrite hy.
move=> hqs; exists (i + 1).
rewrite size_cat /= -cat_rcons.
have -> : i + 1 = size (rcons tk hd).
+ by rewrite size_rcons size_take // hi.
rewrite drop_size_cat // take_size_cat // size_rcons -cats1 has_cat /=.
smt (size_ge0).
auto => /> &2;split.
+ move=> ?;exists 0;rewrite drop0 take0 /=.
smt (size_eq0 size_ge0).
smt (size_eq0 size_ge0).
qed.
op kloop k1 k2 = (4 + kf) * (k1 + k2) + 2 * (k1 + k2 + 1) + 1.
lemma bound_i
(kinit kh ka1 k1 ka2 k2 : int)
(H <: Oracle {-I, -IW} [init : `{kinit}, o : `{kh}])
(A <: Adv {-I, -IW, -H}
[a1 : `{ka1, #H.o : k1}, a2 : `{ka2, #H.o : k2}]) :
0 <= k1 => 0 <= k2 =>
choare[IW(A,H).invert: true ==> true]
time [4 + 2 * (k1 + k2) + kloop k1 k2;
A.a1 : 1;
A.a2 : 1;
H.o : k1 + k2;
H.init : 1].
proof.
move => hk1 hk2; proc.
seq 5 : (size IW.qs <= k1 + k2) time [kloop k1 k2].
call (_: true ;
(IW(A, H).QRO.o : size IW.qs - k1)
time
[(IW(A, H).QRO.o : [fun _ => 1; H.o : fun _ => 1])])
=> * /=.
(* The invariant is preserved by calls to the oracle QRO. *)
proc; call (_: true); auto => * /=; first by smt ().
rnd.
call (_: true;
(IW(A, H).QRO.o : size IW.qs)
time
[(IW(A, H).QRO.o : [fun _ => 1; H.o : fun _ => 1])])
=> * /=.
(* The invariant is preserved by calls to the oracle QRO. *)
proc; call (_: true); auto => * => /=; [1: by smt ()].
call (_: true); auto ; move => * /=.
split; move => * /=; first by smt (dptxt_ll).
rewrite !big_constz !count_predT !size_range; by smt (kfp).
(* we bound the list lookup time. *)
(* wp : (size I.qs <= k1 + k2). *)
while (true) (size qs0) (k1 + k2) time [fun _ => 4 + kf].
+ move => z /=; auto => * /=; split; [2 : by smt (kfp)].
move : H0; case: (qs0{hr}); first by smt ().
move => x0 l; have := size_ge0 l; by smt ().
+ move => &hr; case: (qs0{hr}) => * //; have := size_ge0 l; by smt ().
auto; move => &hr /=; rewrite !big_constz !count_predT !size_range.
move: hk1 hk2. smt (kfp).
qed.