require import AllCore List Int IntDiv Real SmtMap Distr DBool DList DProd FSet PROM SplitRO FelTactic. require import FinType. require (****) Subtype Ske RndProd Indistinguishability Monoid EventPartitioning. import StdOrder IntOrder RealOrder. (* TODO: this come from eclib/JUtil.ec *) op map2 ['a, 'b, 'c] (f:'a -> 'b -> 'c) (s:'a list) (t:'b list) = with s = "[]" , t = "[]" => [] with s = _ :: _ , t = "[]" => [] with s = "[]" , t = _ :: _ => [] with s = x :: s', t = y :: t' => f x y :: map2 f s' t'. lemma map2_zip (f:'a -> 'b -> 'c) s t : map2 f s t = map (fun (p:'a * 'b) => f p.`1 p.`2) (zip s t). proof. by elim: s t => [ | s1 s hrec] [ | t1 t] //=;rewrite hrec. qed. lemma size_map2 (f:'a -> 'b -> 'c) (l1:'a list) l2 : size (map2 f l1 l2) = min (size l1) (size l2). proof. by rewrite map2_zip size_map size_zip. qed. lemma nth_map2 dfla dflb dflc (f:'a -> 'b -> 'c) (l1:'a list) l2 i: 0 <= i < min (size l1) (size l2) => nth dflc (map2 f l1 l2) i = f (nth dfla l1 i) (nth dflb l2 i). proof. elim: l1 l2 i => [ | a l1 hrec] [ | b l2] i /=; 1..3:smt(size_ge0). case: (i=0) => [->> // | hi ?]. apply hrec;smt(). qed. (* -------------------------------------------------------------------------- *) theory Byte. type byte. clone include MFinite with type t <- byte rename "dunifin" as "dbyte". op zero : byte. op (+^) : byte -> byte -> byte. clone import Monoid as MB with type t <- byte, op idm <- zero, op (+) <- (+^). axiom addK b : b +^ b = zero. lemma xorK1 b1 b2 : b1 = b1 +^ b2 +^ b2. proof. by rewrite -addmA addK addm0. qed. end Byte. import Byte. type bytes = byte list. (* -------------------------------------------------------------------------- *) theory Key. type key. (* chacha key *) clone include MFinite with type t <- key rename "dunifin" as "dkey". end Key. import Key. theory Nonce. type nonce. (* chacha nonce *) clone MFinite with type t = nonce rename "dunifin" as "dnonce". end Nonce. import Nonce. (* -------------------------------------------------------------------------- *) theory C. op max_counter : int. axiom gt0_max_counter : 0 < max_counter. clone include Subtype with type T <- int, op P <- fun (i:int) => 0 <= i < max_counter + 1 rename (* [type] "sT" as "counter" *) (* Gives error. *) [op] "insubd" as "ofint" proof *. realize inhabited. exists 0. smt(gt0_max_counter). qed. type counter = sT. clone FinType with type t = counter, op enum = List.map ofint (iota_ 0 (max_counter + 1)) proof *. realize enum_spec. proof. move=> c; rewrite count_uniq_mem. + apply map_inj_in_uniq; last by apply iota_uniq. move=> x y /mema_iota hx /mema_iota hy heq. by rewrite -(insubdK x) 1:// -(insubdK y) 1:// heq. have -> // : c \in enum. rewrite /enum mapP; exists (val c). by rewrite mema_iota /= valP valKd. qed. end C. clone FinProdType as NonceCount with type t1 <- nonce, type t2 <- C.counter, theory FT1 <- Nonce.MFinite.Support, theory FT2 <- C.FinType. (* -------------------------------------------------------------------------- *) abstract theory GenBlock. op block_size : int. axiom ge0_block_size : 0 <= block_size. clone include Subtype with type T <- bytes, op P <- fun (l:bytes) => size l = block_size rename [op] "val" as "bytes_of_block" proof *. realize inhabited. exists (nseq block_size witness). smt(size_nseq ge0_block_size). qed. type block = sT. op dblock = dmap (dlist dbyte block_size) insubd. lemma dblock_ll : is_lossless dblock. proof. apply dmap_ll; apply dlist_ll; apply dbyte_ll. qed. lemma dblock_uni: is_uniform dblock. proof. apply dmap_uni_in_inj. + move=> x y hx hy heq. rewrite -(insubdK x) 1:(supp_dlist_size _ _ _ ge0_block_size hx) //. by rewrite -(insubdK y) 1:(supp_dlist_size _ _ _ ge0_block_size hy) // heq. by apply dlist_uni; apply dbyte_uni. qed. lemma dblock_fu: is_full dblock. proof. move=> x; rewrite supp_dmap; exists (bytes_of_block x). rewrite valKd /= -(valP x); apply dlist_fu => ??; apply dbyte_fu. qed. lemma dblock_funi: is_funiform dblock. proof. apply is_full_funiform; [apply dblock_fu | apply dblock_uni]. qed. op zero = insubd (mkseq (fun _ => Byte.zero) block_size). op (+^) (b1 b2:block) = insubd (map2 (+^) (bytes_of_block b1) (bytes_of_block b2)). lemma nth_xor x y i : 0 <= i < block_size => nth Byte.zero (bytes_of_block (x +^ y)) i = nth Byte.zero (bytes_of_block x) i +^ nth Byte.zero (bytes_of_block y) i. proof. move=> hi; rewrite /(+^) !insubdK ?size_map2 ?valP //. rewrite !(nth_map2 Byte.zero Byte.zero) ?valP //. qed. lemma nth_zero i : nth Byte.zero (bytes_of_block zero) i = Byte.zero. proof. rewrite /zero insubdK ?size_mkseq; 1:smt (ge0_block_size). smt (nth_mkseq nth_neg nth_default size_mkseq). qed. clone import Monoid as MB with type t <- block, op idm <- zero, op (+) <- (+^) proof *. realize Axioms.addmA. proof. move=> x y z; apply val_inj; apply (eq_from_nth Byte.zero); rewrite !valP //. by move=> i hi; rewrite !nth_xor // Byte.MB.addmA. qed. realize Axioms.addmC. proof. move=> x y; apply val_inj; apply (eq_from_nth Byte.zero); rewrite !valP //. by move=> i hi; rewrite !nth_xor // Byte.MB.addmC. qed. realize Axioms.add0m. proof. move=> x; apply val_inj; apply (eq_from_nth Byte.zero); rewrite !valP //. by move=> i hi; rewrite !nth_xor // nth_zero Byte.MB.add0m. qed. lemma addK b : b +^ b = zero. proof. apply val_inj; apply (eq_from_nth Byte.zero); rewrite !valP //. by move=> i hi; rewrite nth_xor // Byte.addK nth_zero. qed. lemma xorK1 b1 b2 : b1 = b1 +^ b2 +^ b2. proof. by rewrite -addmA addK addm0. qed. end GenBlock. (* block : [poly_tin; poly_tout; extra_block] *) clone import GenBlock as Poly_in rename "block" as "poly_in". hint solve 0 random : dpoly_in_ll dpoly_in_funi dpoly_in_fu. clone import GenBlock as Poly_out rename "block" as "poly_out". hint solve 0 random : dpoly_out_ll dpoly_out_funi dpoly_out_fu. clone import GenBlock as TPoly with op block_size = poly_in_size + poly_out_size proof ge0_block_size by smt (ge0_poly_in_size ge0_poly_out_size) rename "block" as "poly". hint solve 0 random : dpoly_ll dpoly_funi dpoly_fu. clone import GenBlock as Extra_block rename "block" as "extra_block". hint solve 0 random : dextra_block_ll dextra_block_funi dextra_block_fu. clone import GenBlock as Block with op block_size = poly_in_size + poly_out_size + extra_block_size proof ge0_block_size by smt (ge0_poly_in_size ge0_poly_out_size ge0_extra_block_size). hint solve 0 random : dblock_ll dblock_funi dblock_fu. axiom gt0_block_size : 0 < block_size. op extend (bs:bytes) : block = Block.insubd (take block_size bs ++ mkseq (fun _ => Byte.zero) (block_size - size bs)). lemma nth_extend m i : 0 <= i < block_size => nth Byte.zero (bytes_of_block (extend m)) i = if i < size m then nth Byte.zero m i else Byte.zero. proof. move=> [h0i hi]; rewrite /extend Block.insubdK. + by rewrite size_cat size_mkseq size_take 1:ge0_block_size /#. rewrite nth_cat size_take 1:ge0_block_size; smt (nth_take nth_mkseq). qed. (* -------------------------------------------------------------------------- *) op chacha20_block : key -> nonce -> C.counter -> block. (* -------------------------------------------------------------------------- *) (* Functional definition of chacha20 *) op gen_ctr_round (merge: bytes -> block -> bytes) genblock (k:key) (n:nonce) (round_st : bytes * bytes * int) = let (cph,m,c) = round_st in let stream = genblock k n (C.ofint c) in (cph ++ merge m stream, drop block_size m, c + 1). op gen_CTR_encrypt_bytes merge genblock key nonce counter m = let len = size m in let rounds = (len %/ block_size) + b2i (len %% block_size <> 0) in (iter rounds (gen_ctr_round merge genblock key nonce) ([], m, counter)).`1. op take_xor (m:bytes) (stream : block) = take (size m) (bytes_of_block (extend m +^ stream)). op map2_xor (m:bytes) (stream : block) = map2 Byte.(+^) m (bytes_of_block stream). (* This correspond exactly to the definition used in the functional correctness proof *) op chacha20_CTR_encrypt_bytes key nonce counter m = gen_CTR_encrypt_bytes map2_xor chacha20_block key nonce counter m. lemma take_xor_map2_xor m str: map2_xor m str = take_xor m str. proof. apply (eq_from_nth Byte.zero). + by rewrite size_map2 Block.valP size_take 1:size_ge0 Block.valP. rewrite size_map2 Block.valP => j hj. rewrite (nth_map2 Byte.zero Byte.zero) ?Block.valP 1://. rewrite nth_take 1:size_ge0 1:/# Block.nth_xor 1:/#; congr. smt (nth_extend). qed. lemma iter_gen_ctr_round_nil_cat merge genblock k n c m i j : let round = gen_ctr_round merge genblock k n in iter i round (c, m, j) = let (c', m', j') = iter i round ([], m, j) in (c ++ c', m', j'). proof. move=> round. elim /natind : i j m c => [i hi| i hi hrec] j m c; 1: by rewrite !iter0 //= cats0. rewrite !iterS //= {1 2}/round /gen_ctr_round hrec. by case : (iter _ _ _) => /= c' p' j'; rewrite catA. qed. lemma iter_gen_ctr_round_S merge genblock k n c m i j: let round = gen_ctr_round merge genblock k n in 0 <= i => iter (i + 1) round (c, m, j) = let (c', m', j') = iter i round ([], drop block_size m, j + 1) in (c ++ merge m (genblock k n (C.ofint j)) ++ c', m', j'). proof. by move=> round hi; rewrite iterSr // {2}/round /gen_ctr_round /= iter_gen_ctr_round_nil_cat. qed. lemma iter_gen_ctr_round_nil merge genblock k n i j: (forall str, merge [] str = []) => let round = gen_ctr_round merge genblock k n in iter i round ([], [],j) = ([], [], max j (j + i)). proof. move=> hm round; elim /natind: i j => [i hi| i hi hrec] j; 1: by rewrite iter0 // /#. rewrite iter_gen_ctr_round_S // drop_oversize 1:#smt:(gt0_block_size). rewrite hrec /= hm /#. qed. lemma gen_CTR_encrypt_bytes0 merge genblock k n c : (forall str, merge [] str = []) => gen_CTR_encrypt_bytes merge genblock k n c [] = []. proof. by move=> hm; rewrite /gen_CTR_encrypt_bytes /= iter_gen_ctr_round_nil. qed. lemma gen_CTR_encrypt_bytes_cons merge genblock k n c m: (forall str, merge [] str = []) => gen_CTR_encrypt_bytes merge genblock k n c m = merge m (genblock k n (C.ofint c)) ++ gen_CTR_encrypt_bytes merge genblock k n (c+1) (drop block_size m). proof. move=> hm; rewrite /gen_CTR_encrypt_bytes /=; have h0s := size_ge0 m. case : (size m < block_size) => hs. + have hb : 0 <= size m < `|block_size| by smt(). rewrite divz_small 1:// modz_small //= size_eq0. case: (m = []) => [->> | hne] @/b2i /=;1: by rewrite !iter0 // hm. rewrite (iter_gen_ctr_round_S _ _ _ _ _ _ 0) // iter0 //=. by rewrite drop_oversize 1:/# /= iter0. have -> : size m = block_size + size (drop block_size m). + by rewrite -{1}(cat_take_drop block_size m) size_cat size_take 1:ge0_block_size /#. have /= -> := divzMDl 1; 1: smt(gt0_block_size). rewrite modzDl -addzA addzC iter_gen_ctr_round_S; 1: smt (size_ge0 gt0_block_size). by case: (iter _ _ _) => />. qed. (* -------------------------------------------------------------------------- *) (* Functional definition of poly1305 *) type polynomial. op topol : bytes -> bytes -> polynomial. op max_ad_size : int. op max_cipher_size : int. axiom max_cipher_size_ok : max_cipher_size <= C.max_counter * block_size. op valid_topol (a:bytes) (c:bytes) = size a <= max_ad_size /\ size c <= max_cipher_size. axiom topol_inj a1 c1 a2 c2: valid_topol a1 c1 => valid_topol a2 c2 => topol a1 c1 = topol a2 c2 => a1 = a2 /\ c1 = c2. op poly1305_eval : poly_in -> polynomial -> poly_out. op (+) : poly_out -> poly_out -> poly_out. op (-) : poly_out -> poly_out -> poly_out. op poly1305 (r:poly_in) (s:poly_out) (p:polynomial) = s + poly1305_eval r p. op mk_rs (b:block) = let b = take (poly_in_size + poly_out_size) (bytes_of_block b) in (Poly_in.insubd (take poly_in_size b), Poly_out.insubd (drop poly_in_size b)). op genpoly1305 genblock (k:key) (n:nonce) (p:polynomial) = let (r,s) = mk_rs (genblock k n (C.ofint 0)) in poly1305 r s p. axiom poly_out_sub_add (p1 p2: poly_out) : p1 = p1 - p2 + p2. axiom poly_out_add_sub (p1 p2: poly_out) : p1 = p1 + p2 - p2. axiom poly_out_add_sub' (p1 p2: poly_out) : p1 = p1 + (p2 - p2). axiom poly_out_swap (t p1 p2:poly_out) : t - p1 + p2 = t + (p2 - p1). (* -------------------------------------------------------------------------- *) type message = bytes. type associated_data = bytes. type tag = poly_out. type plaintext = nonce * associated_data * message. type ciphertext = nonce * associated_data * message * tag. clone import Ske.SKE_RND with type key <- key, type plaintext <- plaintext, type ciphertext <- ciphertext. module ChaChaPoly = { proc init() = {} proc kg () = { var k; k <$ dkey; return k; } proc enc (k : key, nap : nonce * associated_data * message) : nonce * associated_data * message * tag = { var n, a, p, c, t; (n,a,p) <- nap; c <- gen_CTR_encrypt_bytes take_xor chacha20_block k n 1 p; t <- genpoly1305 chacha20_block k n (topol a c); return (n,a,c,t); } proc dec(k : key, nact : nonce * associated_data * message * tag) : (nonce * associated_data * message) option = { var n, a, c, p, t, t', result; result <- None; (n,a,c,t) <- nact; t' <- genpoly1305 chacha20_block k n (topol a c); if (t = t') { p <- gen_CTR_encrypt_bytes take_xor chacha20_block k n 1 c; result <- Some (n,a,p); } return result; } }. (* --------------------------------------------------------------------------- *) module type CC = { proc cc (k:key, n:nonce, c: C.counter) : block }. module type FCC = { proc init () : unit include CC }. module ChaCha(CC:CC) = { proc enc(k:key, n:nonce, p:message) : bytes = { var i, z, c; c <- []; i <- 1; while (p <> []) { z <@ CC.cc(k, n, C.ofint i); c <- c ++ take (size p) (bytes_of_block (extend p +^ z)); p <- drop block_size p; i <- i + 1; } return c; } }. module Poly(CC:CC) = { proc mac(k:key, n: nonce, a: associated_data, c: message) : tag = { var b, r, s; b <@ CC.cc(k, n, C.ofint 0); (r,s) <- mk_rs b; return poly1305 r s (topol a c); } }. module GenChaChaPoly(CC:FCC) : SKE = { include CC[init] include ChaChaPoly[kg] proc enc (k : key, nap : nonce * associated_data * message) : nonce * associated_data * message * tag = { var n, a, p, c, t; (n,a,p) <- nap; c <@ ChaCha(CC).enc(k,n,p); t <@ Poly(CC).mac(k,n,a,c); return (n,a,c,t); } proc dec(k : key, nact : nonce * associated_data * message * tag) : (nonce * associated_data * message) option = { var n, a, c, p, t, t', result; result <- None; (n,a,c,t) <- nact; t' <@ Poly(CC).mac(k,n,a,c); if (t = t') { p <@ ChaCha(CC).enc(k,n,c); result <- Some (n,a,p); } return result; } }. (* --------------------------------------------------------------------- *) (* We want to bound : `| Pr[CCA_game(A, Real_Oracles(ChaChaPoly(CCperm))).main() : res] - Pr[CCA_game(A, Ideal_Oracles).main() : res] | We process as follow: `| Pr[CCA_game(A, Real_Oracles(ChaChaPoly(CCperm))).main() : res] - Pr[CCA_game(A, Ideal_Oracles).main() : res] | <= Step 1 indistinguishability from a random oracle: `| Pr[CCA_game(A, Real_Oracles(ChaChaPoly(CCperm))).main() : res] - Pr[CCA_game(A, Real_Oracles(ChaChaPoly(RO))).main() : res] + `| Pr[CCA_game(A, Real_Oracles(ChaChaPoly(RO))).main() : res] - Pr[CCA_game(A, Ideal_Oracles).main() : res] | Step 2 : CCA <= CPA + UFCMA + extra stuff on RO Step 3 : enc return random => CPA ~ Ideal Step 4 : UFCMA with random enc *) abstract theory OpCC. type globS. op cc : globS -> key -> nonce -> C.counter -> block. module type Init = { proc init () : globS }. module OCC (I:Init) : FCC = { var gs : globS proc init () : unit = { gs <@ I.init(); } proc kg () : key = { var k; k <$ dkey; return k; } proc cc (k:key, n:nonce, c:C.counter) = { return cc gs k n c; } }. module OChaChaPoly (I:Init) = { include OCC(I) [init, kg] proc enc (k : key, nap : nonce * associated_data * message) : nonce * associated_data * message * tag = { var n, a, p, c, t; (n,a,p) <- nap; c <- gen_CTR_encrypt_bytes take_xor (cc OCC.gs) k n 1 p; t <- genpoly1305 (cc OCC.gs) k n (topol a c); return (n,a,c,t); } proc dec(k : key, nact : nonce * associated_data * message * tag) : (nonce * associated_data * message) option = { var n, a, c, p, t, t', result; result <- None; (n,a,c,t) <- nact; t' <- genpoly1305 (cc OCC.gs) k n (topol a c); if (t = t') { p <- gen_CTR_encrypt_bytes take_xor (cc OCC.gs) k n 1 c; result <- Some (n,a,p); } return result; } }. module type Adv (S:SKE) = { proc main () : bool }. section PROOFS. declare module I <: Init { -OCC }. declare module A <: Adv { -OCC, -I}. phoare chacha_spec k0 n0 p0 gs0 : [ChaCha(OCC(I)).enc : k = k0 /\ n = n0 /\ p = p0 /\ OCC.gs = gs0 ==> res = gen_CTR_encrypt_bytes take_xor (cc gs0) k0 n0 1 p0] = 1%r. proof. proc. rewrite /gen_CTR_encrypt_bytes /=. pose r := iter (size p0 %/ block_size + b2i (size p0 %% block_size <> 0)) (gen_ctr_round take_xor (cc gs0) k0 n0) ([], p0, 1). while (r = iter (size p %/ block_size + b2i (size p %% block_size <> 0)) (gen_ctr_round take_xor (cc OCC.gs) k n) (c, p, i)) (size p). + move=> z; inline *; auto => /> &m. move: (p{m}) => p + hp. have hd := StdOrder.IntOrder.lt0r_neq0 _ gt0_block_size. have -> : size p %/ block_size + b2i (size p %% block_size <> 0) = (size (drop block_size p) %/ block_size + b2i (size (drop block_size p) %% block_size <> 0)) + 1. + case (block_size <= size p) => h. + have -> : size p = block_size + size (drop block_size p). + by rewrite size_drop 1:ge0_block_size /#. by rewrite (divzMDl 1 _ _ hd) (modzMDl 1 _ block_size); ring. have hm : 0 <= size p < `|block_size| by smt (size_ge0). by rewrite modz_small 1:hm divz_small 1:hm drop_oversize 1:/# /= size_eq0 hp. rewrite iterSr 1:#smt:(size_ge0 gt0_block_size)=> /> _. smt(size_drop size_ge0 size_eq0 ge0_block_size). auto; rewrite /r=> {r} /> c i' p'; split;1: smt (size_eq0 size_ge0). by rewrite /b2i /= (iter0 0) 1:// => ->. qed. phoare poly_spec k0 n0 a0 c0 gs0 : [Poly(OCC(I)).mac : k = k0 /\ n = n0 /\ a = a0 /\ c = c0 /\ OCC.gs = gs0 ==> res = genpoly1305 (cc gs0) k0 n0 (topol a0 c0)] = 1%r. proof. proc; inline *; auto. qed. equiv CCP_OCCP : A(GenChaChaPoly(OCC(I))).main ~ A(OChaChaPoly(I)).main : ={glob A, glob I, glob OCC, arg} ==> ={res, glob A, glob I, glob OCC}. proof. proc (={glob I, glob OCC}) => //; 1,2: by sim. + proc. ecall{1} (poly_spec k{1} n{1} a{1} c{1} OCC.gs{1}). by ecall{1} (chacha_spec k{1} n{1} p{1} OCC.gs{1}); wp. proc. seq 3 3 : (={glob I, glob OCC, result, n, a, c, t, t',k}). + by ecall{1} (poly_spec k{1} n{1} a{1} c{1} OCC.gs{1});wp. by if;auto; ecall{1} (chacha_spec k{1} n{1} c{1} OCC.gs{1}). qed. lemma pr_CCP_OCCP &m : Pr[A(GenChaChaPoly(OCC(I))).main() @ &m : res] = Pr[A(OChaChaPoly(I)).main() @ &m : res]. proof. by byequiv CCP_OCCP. qed. end section PROOFS. end OpCC. clone import FullRO with type in_t <- (nonce*C.counter), type out_t <- block, type d_in_t <- unit, type d_out_t <- bool, op dout <- fun _ => dblock proof *. clone import FinEager as FiniteRO with theory FinFrom <- NonceCount proof *. module IndBlock = { var k : key proc init () = { k <$ dkey; } proc f (n:nonce, c:C.counter) = { return chacha20_block k n c; } }. module IndRO = { proc init = RO.init proc f = RO.get }. clone Indistinguishability as Indist with type t_in <- nonce * C.counter, type t_out <- block. module D(A:CCA_Adv, RO: Indist.Oracle) = { module O = { proc init () = {} module ChaCha = { proc enc(n:nonce, p:message) : bytes = { var i, z, c; c <- []; i <- 1; while (p <> []) { z <@ RO.f(n, C.ofint i); c <- c ++ take (size p) (bytes_of_block (extend p +^ z)); p <- drop block_size p; i <- i + 1; } return c; } } module Poly = { proc mac(n: nonce, a: associated_data, c: message) : tag = { var b, r, s; b <@ RO.f(n, C.ofint 0); (r,s) <- mk_rs b; return poly1305 r s (topol a c); } } proc enc (nap : nonce * associated_data * message) : nonce * associated_data * message * tag = { var n, a, p, c, t; (n,a,p) <- nap; c <@ ChaCha.enc(n,p); t <@ Poly.mac(n,a,c); return (n,a,c,t); } proc dec(nact : nonce * associated_data * message * tag) : (nonce * associated_data * message) option = { var n, a, c, p, t, t', result; result <- None; (n,a,c,t) <- nact; t' <@ Poly.mac(n,a,c); if (t = t') { p <@ ChaCha.enc(n,c); result <- Some (n,a,p); } return result; } } proc guess = CCA_game(A,O).main }. module CCRO(RO:RO) = { proc init = RO.init proc cc(k : key, n : nonce, c : C.counter) : block = { var result; result <@ RO.get (n,c); return result; } }. op test_poly (n:nonce) (lc:ciphertext list) r s = let pts = map (fun (c:ciphertext) => (topol c.`2 c.`3, c.`4)) (List.filter (fun (c:ciphertext) => c.`1 = n) lc) in List.has (fun (pt:polynomial*tag) => pt.`2 = poly1305 r s pt.`1) pts. module UFCMA_poly(A:CCA_Adv, RO:RO) = { proc main () = { var ns, forged, i, n, bl, r, s; CPA_game(CCA_CPA_Adv(A), RealOrcls(GenChaChaPoly(CCRO(RO)))).main(); ns <- undup (List.map (fun (p:ciphertext) => p.`1) Mem.lc); forged <- false; i <- 0; while (i < size ns) { n <- List.nth witness ns i; bl <@ RO.get(n,C.ofint 0); (r,s) <- mk_rs bl; forged <- forged || test_poly n Mem.lc r s; i <- i + 1; } return forged; } }. abstract theory Step1_2. clone import OpCC as OpCCinit with type globS <- unit, op cc <- fun _ => chacha20_block. module I_stateless = { proc init () = {} }. clone import OpCC as OpCCRO with type globS = (nonce * C.counter, block) fmap, op cc m k n c <- oget m.[(n,c)]. module IFinRO = { proc init () = { FinRO.init(); return RO.m; } }. op get (gs:OpCCRO.globS) (k:key) n c = oget gs.[(n,c)]. clone import CCA_CPA_UFCMA as CCA_UFCMA with type globS <- OpCCRO.globS, op enc gs k (nap:plaintext) = let (n,a,p) = nap in let c = gen_CTR_encrypt_bytes take_xor (get gs) k n 1 p in let t = genpoly1305 (get gs) k n (topol a c) in (n,a,c,t), op dec gs k (nact:ciphertext) = let (n,a,c,t) = nact in let t' = genpoly1305 (get gs) k n (topol a c) in if (t = t') then Some (n,a, gen_CTR_encrypt_bytes take_xor (get gs) k n 1 c) else None, op valid_key <- fun _ => true proof *. realize dec_enc. proof. move=> k _ gs [n a p]; rewrite /dec /enc /=. have htake_xor : forall str, take_xor [] str = []. + by move=> ?; rewrite /take_xor take0. have : forall j, 0 <= j => forall p c, j = size p => gen_CTR_encrypt_bytes take_xor (get gs) k n c (gen_CTR_encrypt_bytes take_xor (get gs) k n c p) = p; 2: by move=> /(_ (size p)) -> //;apply size_ge0. elim /sintind. move=> {p} i hi hrec p c ->>. case: (size p = 0). + by rewrite size_eq0 => ->>; rewrite !gen_CTR_encrypt_bytes0. move=> hs; rewrite (gen_CTR_encrypt_bytes_cons _ _ _ _ _ p) 1:// gen_CTR_encrypt_bytes_cons 1://. case: (size p < block_size) => hsz. + rewrite drop_oversize 1:/# gen_CTR_encrypt_bytes0 1:// cats0 drop_oversize. + by rewrite size_take // Block.valP /#. rewrite gen_CTR_encrypt_bytes0 1:// cats0. rewrite -!take_xor_map2_xor; apply (eq_from_nth Byte.zero). + by rewrite !size_map2 Block.valP /#. move=> j; rewrite !size_map2 Block.valP => hj. by rewrite !(nth_map2 Byte.zero Byte.zero) ?size_map2 ?Block.valP 1,2:/# -Byte.xorK1. rewrite drop_size_cat;1: by rewrite size_take 1:// Block.valP /#. rewrite (hrec (size (drop block_size p))) 2://; 1: smt(size_drop gt0_block_size). rewrite -{4}(cat_take_drop block_size p); congr. rewrite -!take_xor_map2_xor; apply (eq_from_nth Byte.zero). + smt (size_map2 Block.valP size_cat size_take gt0_block_size size_ge0). move=> j hj. have [hj1 hj2] : j < block_size /\ j < size p. + smt (size_map2 Block.valP size_cat size_take gt0_block_size size_ge0). rewrite (nth_map2 Byte.zero Byte.zero) ?(size_cat, size_map2, Block.valP) 1:#smt:(size_ge0). rewrite nth_cat ?(size_cat, size_map2, Block.valP) /min hsz /= hj1. by rewrite (nth_map2 Byte.zero Byte.zero) ?Block.valP 1:/# /= -Byte.xorK1 nth_take 1:ge0_block_size. qed. module St = { proc init () = { FinRO.init(); return RO.m; } proc kg = ChaChaPoly.kg }. clone Split as Split0 with type from <- nonce * C.counter, type to <- block, type input <- unit, type output <- bool, op sampleto <- fun _ => dblock proof *. clone import Split0.SplitDom as SplitD with op test = fun p:nonce * C.counter => C.val p.`2 = 0. clone import Split0.SplitCodom as SplitC1 with type to1 <- poly, type to2 <- extra_block, op topair = fun (b:block) => let bs = bytes_of_block b in (TPoly.insubd (take poly_size bs), Extra_block.insubd (drop poly_size bs)), op ofpair = fun (p:poly * extra_block) => Block.insubd (bytes_of_poly p.`1 ++ bytes_of_extra_block p.`2), op sampleto1 <- fun _ => dpoly, op sampleto2 <- fun _ => dextra_block proof *. realize topairK. proof. move=> x; rewrite /topair /ofpair /=. rewrite -{3}(Block.valKd x); congr. rewrite TPoly.insubdK. + rewrite size_take 1:ge0_poly_size Block.valP. smt (ge0_poly_in_size ge0_poly_out_size ge0_extra_block_size). rewrite Extra_block.insubdK 2:cat_take_drop 2://. rewrite size_drop 1:ge0_poly_size Block.valP. smt (ge0_poly_in_size ge0_poly_out_size ge0_extra_block_size). qed. realize ofpairK. proof. move=> [x1 x2]; rewrite /topair /ofpair /=. rewrite Block.insubdK. + by rewrite size_cat TPoly.valP Extra_block.valP. by rewrite take_size_cat 1:TPoly.valP 1:// drop_size_cat 1:TPoly.valP 1:// TPoly.valKd Extra_block.valKd. qed. realize sample_spec. proof. move=> _; rewrite /dblock; apply eq_distr => b. rewrite !dmap1E. apply (eq_trans _ (mu1 (dpoly `*` dextra_block) ((topair b).`1, (topair b).`2))); last first. + congr; apply fun_ext; smt (topairK ofpairK). rewrite dprod1E (_:block_size = poly_size + extra_block_size) //. rewrite dlist_add 1:ge0_poly_size 1:ge0_extra_block_size dmapE. rewrite !dmap1E /(\o) -dprodE &(mu_eq_support) => -[l1 l2] /supp_dprod /= [h1 h2]. have s1 := supp_dlist_size dbyte _ _ ge0_poly_size h1. have s2 := supp_dlist_size dbyte _ _ ge0_extra_block_size h2. rewrite eq_iff /pred1 /topair //=; split=> />. + rewrite insubdK 1:size_cat 1:s1 1:s2 //. by rewrite take_size_cat // drop_size_cat. move=> /(congr1 bytes_of_poly); rewrite insubdK=> // ->. move=> /(congr1 bytes_of_extra_block); rewrite insubdK=> // ->. rewrite insubdK. + rewrite size_drop 1:ge0_poly_size valP /block_size /poly_size. smt(ge0_poly_in_size ge0_poly_out_size ge0_extra_block_size). rewrite insubdK. + rewrite size_take 1:ge0_poly_size valP /poly_size /block_size. smt(ge0_poly_in_size ge0_poly_out_size ge0_extra_block_size). by rewrite cat_take_drop valKd. qed. clone Split as Split1 with type from <- nonce * C.counter, type to <- poly, type input <- unit, type output <- bool, op sampleto <- fun _ => dpoly proof *. (* TODO: share more stuff with the previous clone *) clone import Split1.SplitCodom as SplitC2 with type to1 <- poly_in, type to2 <- poly_out, op topair = fun (b:poly) => let bs = bytes_of_poly b in (Poly_in.insubd (take poly_in_size bs), Poly_out.insubd (drop poly_in_size bs)), op ofpair = fun (p:poly_in * poly_out) => TPoly.insubd (bytes_of_poly_in p.`1 ++ bytes_of_poly_out p.`2), op sampleto1 <- fun _ => dpoly_in, op sampleto2 <- fun _ => dpoly_out proof *. realize topairK. proof. move=> x; rewrite /topair /ofpair /=. rewrite -{3}(TPoly.valKd x); congr. rewrite Poly_in.insubdK. + rewrite size_take 1:ge0_poly_in_size TPoly.valP. smt (ge0_poly_in_size ge0_poly_out_size). rewrite Poly_out.insubdK 2:cat_take_drop 2://. rewrite size_drop 1:ge0_poly_in_size TPoly.valP. smt (ge0_poly_in_size ge0_poly_out_size). qed. realize ofpairK. proof. move=> [x1 x2]; rewrite /topair /ofpair /=. rewrite TPoly.insubdK. + by rewrite size_cat Poly_in.valP Poly_out.valP. by rewrite take_size_cat 1:Poly_in.valP 1:// drop_size_cat 1:Poly_in.valP 1:// Poly_in.valKd Poly_out.valKd. qed. realize sample_spec. proof. move=> _; rewrite /dpoly; apply eq_distr => b. rewrite !dmap1E. apply (eq_trans _ (mu1 (dpoly_in `*` dpoly_out) ((topair b).`1, (topair b).`2))); last first. + congr; apply fun_ext; smt (topairK ofpairK). rewrite dprod1E (_:poly_size = poly_in_size + poly_out_size) //. rewrite dlist_add 1:ge0_poly_in_size 1:ge0_poly_out_size dmapE. rewrite !dmap1E /(\o) -dprodE &(mu_eq_support) => -[l1 l2] /supp_dprod /= [h1 h2]. have s1 := supp_dlist_size dbyte _ _ ge0_poly_in_size h1. have s2 := supp_dlist_size dbyte _ _ ge0_poly_out_size h2. rewrite eq_iff /pred1 /topair //=; split=> />. + rewrite insubdK 1:size_cat 1:s1 1:s2 //. by rewrite take_size_cat // drop_size_cat. move=> /(congr1 bytes_of_poly_in); rewrite insubdK=> // ->. move=> /(congr1 bytes_of_poly_out); rewrite insubdK=> // ->. rewrite insubdK. + rewrite size_drop 1:ge0_poly_in_size valP /poly_size. smt(ge0_poly_in_size ge0_poly_out_size ge0_extra_block_size). rewrite insubdK. + rewrite size_take 1:ge0_poly_in_size valP /poly_size. smt(ge0_poly_in_size ge0_poly_out_size ge0_extra_block_size). by rewrite cat_take_drop valKd. qed. module G4 (A:CCA_Adv, RO:RO) = { proc distinguish () = { var b; Mem.k <@ GenChaChaPoly(CCRO(RO)).kg(); b <@ CCA_CPA_Adv(A, RealOrcls(GenChaChaPoly(CCRO(RO)))).main(); return b; } }. module G5_end(RO:RO) = { proc main() = { var ns, forged, i, n, bl, r,s ; ns <- undup (List.map (fun (p:ciphertext) => p.`1) Mem.lc); forged <- false; i <- 0; while (i < size ns) { n <- List.nth witness ns i; bl <@ RO.get(n,C.ofint 0); (r,s) <- mk_rs bl; forged <- forged || test_poly n Mem.lc r s; i <- i + 1; } return forged; } }. module G5 (A:CCA_Adv, RO:RO) = { proc distinguish () = { var b, forged; b <@ G4(A, RO).distinguish(); forged <@ G5_end(RO).main(); return forged; } }. module G6 (A:CCA_Adv, ROT:Split0.IdealAll.RO) = { proc distinguish () = { var b; ROF.RO.init(); b <@ G4(A, RO_DOM(ROT, ROF.RO)).distinguish(); return b; } }. module G7 (A:CCA_Adv, ROT:Split0.IdealAll.RO) = { proc distinguish () = { var b; ROF.RO.init(); b <@ G5(A, RO_DOM(ROT, ROF.RO)).distinguish(); return b; } }. module G8 (A:CCA_Adv, RO1:SplitC1.I1.RO) = { proc distinguish() = { var b; SplitC1.I2.RO.init(); b <@ G6(A, SplitC1.RO_Pair(RO1,SplitC1.I2.RO)).distinguish(); return b; } }. module G9 (A:CCA_Adv, RO1:SplitC1.I1.RO) = { proc distinguish() = { var b; SplitC1.I2.RO.init(); b <@ G7(A, SplitC1.RO_Pair(RO1,SplitC1.I2.RO)).distinguish(); return b; } }. section PROOFS. declare module A <: CCA_Adv { -RO, -FRO, -OpCCinit.OCC, -OpCCRO.OCC, -IndBlock, -Mem, -StLSke, -Split0.IdealAll.RO, -ROT.RO, -ROF.RO, -SplitC1.I1.RO, -SplitC1.I2.RO, -Split1.IdealAll.RO, -SplitC2.I1.RO, -SplitC2.I2.RO }. declare axiom A_ll : forall (O <: CCA_Oracles{-A}), islossless O.enc => islossless O.dec => islossless A(O).main. local module G1 (S:SKE) = CCA_game(A, RealOrcls(S)). local equiv poly_mac1 : Poly(OpCCinit.OCC(I_stateless)).mac ~ D(A, IndBlock).O.Poly.mac : k{1} = IndBlock.k{2} /\ ={n,a,c} ==> ={res}. proof. proc; inline *;auto. qed. local equiv chacha_enc1: ChaCha(OpCCinit.OCC(I_stateless)).enc ~ D(A, IndBlock).O.ChaCha.enc : k{1} = IndBlock.k{2} /\ ={n,p} ==> ={res}. proof. proc; inline *; wp; sim. qed. local module G2(RO:RO) = { module CCRO = { proc f = RO.get } proc distinguish = D(A,CCRO).guess }. local equiv poly_mac2 : Poly(OCC(IFinRO)).mac ~ D(A, G2(FinRO).CCRO).O.Poly.mac : OCC.gs{1} = RO.m{2} /\ ={n,a,c} ==> ={res}. proof. proc; inline *;auto. qed. local equiv chacha_enc2: ChaCha(OCC(IFinRO)).enc ~ D(A, G2(FinRO).CCRO).O.ChaCha.enc : OCC.gs{1} = RO.m{2} /\ ={n,p} ==> ={res}. proof. proc; inline *; wp. by while (={p, c, n, i} /\ OCC.gs{1} = RO.m{2}); auto. qed. local lemma step1 &m: Pr[CCA_game(A, RealOrcls(ChaChaPoly)).main() @ &m : res] - Pr[CCA_game(A,RealOrcls(OChaChaPoly(IFinRO))).main() @ &m : res] = Pr[Indist.Distinguish(D(A), IndBlock).game() @ &m : res] - Pr[Indist.Distinguish(D(A), IndRO).game() @ &m : res]. proof. congr. + have -> : Pr[CCA_game(A, RealOrcls(ChaChaPoly)).main() @ &m : res] = Pr[CCA_game(A, RealOrcls(OpCCinit.OChaChaPoly(I_stateless))).main() @ &m : res]. + by byequiv => //; proc; inline *; wp; call (_: ={Mem.k}); sim />. rewrite -(OpCCinit.pr_CCP_OCCP I_stateless G1 &m). byequiv => //; proc; inline *; wp; call (_: Mem.k{1} = IndBlock.k{2}); last by auto. + proc; inline GenChaChaPoly(OpCCinit.OCC(I_stateless)).enc; wp. by call poly_mac1; call chacha_enc1; auto. proc; inline GenChaChaPoly(OpCCinit.OCC(I_stateless)).dec; wp; conseq />. seq 5 3 : (={n,a,t,result,t'} /\ c0{1} = c{2} /\ k{1} = Mem.k{1} /\ Mem.k{1} = IndBlock.k{2}). + by call poly_mac1; auto. by if => //; wp; call chacha_enc1. congr. have -> : Pr[Indist.Distinguish(D(A), IndRO).game() @ &m : res] = Pr[MainD(G2,RO).distinguish() @ &m : res]. + by byequiv => //; proc; inline *; sim. rewrite (pr_RO_FinRO_D _ G2 &m () (fun x => x)) /=. + exact: dblock_ll. rewrite -(pr_CCP_OCCP IFinRO G1 &m). byequiv => //; proc; inline G2(FinRO).distinguish; wp. call (_: OCC.gs{1} = RO.m{2}). + proc; inline GenChaChaPoly(OCC(IFinRO)).enc; wp. by call poly_mac2; call chacha_enc2; auto. + proc; inline GenChaChaPoly(OCC(IFinRO)).dec; wp; conseq />. seq 5 3 : (={n,a,t,result,t'} /\ c0{1} = c{2} /\ OCC.gs{1} = RO.m{2}). + by call poly_mac2; auto. by if => //; wp; call chacha_enc2. inline D(A, G2(FinRO).CCRO).O.init RealOrcls(GenChaChaPoly(OCC(IFinRO))).init GenChaChaPoly(OCC(IFinRO)).kg; auto. conseq (_: ={glob A} /\ OCC.gs{1} = RO.m{2}) => />. by inline GenChaChaPoly(OCC(IFinRO)).init IFinRO.init;sim. qed. local lemma step2_1 &m : Pr[CCA_game(A,RealOrcls(OChaChaPoly(IFinRO))).main() @ &m : res] <= Pr[CPA_game(CCA_CPA_Adv(A), RealOrcls(StLSke(St))).main() @ &m : res] + Pr[UFCMA(A, St).main() @ &m : (exists c, c \in Mem.lc /\ dec StLSke.gs Mem.k c <> None)]. proof. have -> : Pr[CCA_game(A,RealOrcls(OChaChaPoly(IFinRO))).main() @ &m : res] = Pr[CCA_game(A,RealOrcls(StLSke(St))).main() @ &m : res]. + byequiv => //;proc; call (_: OCC.gs{1} = StLSke.gs{2} /\ ={Mem.k}); last by sim />. + by proc; inline *; auto => /> &2; case: (p{2}). proc; inline *; auto => /> &2; case: (c{2}) => /> n a c t. by rewrite /dec /get /= => ->. apply (CCA_CPA_UFCMA St _ _ A _ &m) => //. + by proc *; inline *; sim. by apply A_ll. qed. local module G3 (S:SKE) = CPA_game(CCA_CPA_Adv(A), RealOrcls(S)). local equiv UFCMA_genCC : UFCMA(A, St).main ~ CPA_game(CCA_CPA_Adv(A), RealOrcls(GenChaChaPoly(CCRO(FinRO)))).main : ={glob A} ==> ={res, Mem.lc} /\ StLSke.gs{1} = RO.m{2}. proof. proc *. transitivity*{1} { r <@ G3(StLSke(St)).main(); } => //; 1:smt(); 1: by sim. transitivity* {2} { r <@ G3(OChaChaPoly(IFinRO)).main(); } => //; 1:smt(). + inline *; wp. call (_: StLSke.gs{1} = OCC.gs{2} /\ ={Mem.k, Mem.log, Mem.lc}). + by proc; inline *; auto => /> &2; case: (p{2}). + by proc; auto. by auto; conseq (_: ={RO.m}) => //; sim. transitivity*{1} { r <@ G3(GenChaChaPoly(OCC(IFinRO))).main(); } => //;1:smt(). + by symmetry; call (CCP_OCCP IFinRO G3). inline *; sim (_: ={Mem.lc, Mem.log, Mem.k} /\ OCC.gs{1} = RO.m{2}). proc; inline *;auto. qed. local lemma step2_2 &m : Pr[CPA_game(CCA_CPA_Adv(A), RealOrcls(StLSke(St))).main() @ &m : res] + Pr[UFCMA(A, St).main() @ &m : (exists c, c \in Mem.lc /\ dec StLSke.gs Mem.k c <> None)] <= Pr[CPA_game(CCA_CPA_Adv(A), RealOrcls(GenChaChaPoly(CCRO(FinRO)))).main() @ &m : res] + Pr[UFCMA_poly(A, FinRO).main() @ &m : res]. proof. apply StdOrder.RealOrder.ler_add; 1: byequiv UFCMA_genCC => //. byequiv => //; proc *; inline UFCMA_poly(A, FinRO).main. seq 1 1: (={Mem.lc} /\ StLSke.gs{1} = RO.m{2}); 1 : by call UFCMA_genCC. wp;while{2} (0 <= i <= size ns /\ forged = exists j, 0 <= j < i /\ let n = nth witness ns j in let bl = oget RO.m.[(n,C.ofint 0)] in let (r,s) = mk_rs bl in test_poly n Mem.lc r s){2} (size ns - i){2}. + by move=> _ z; inline *; auto => /> /#. auto => /> &1 &2; rewrite size_ge0 /=; split; 1:smt(). move=> i; split; 1:smt(). move=> h1 h2 h3 [n a c t] hin hdec. have ->> {h1 h2 h3}: i = size (undup (map (fun (p : ciphertext) => p.`1) Mem.lc{2})) by smt(). pose ns := (undup (map (fun (p : ciphertext) => p.`1) Mem.lc{2})). exists (index n ns); rewrite index_ge0 index_mem. have in_ns : n \in ns. + by rewrite mem_undup; apply (map_f _ _ (n,a,c,t)). move: hdec; rewrite in_ns /dec /genpoly1305 /test_poly /= /get nth_index 1://. case: (mk_rs (oget RO.m{2}.[(n, C.ofint 0)])) => r s /=. case: (t = poly1305 r s (topol a c)) => // heq _. apply List.hasP; exists (topol a c, t) => /=;split; 2:by rewrite heq. by apply mapP; exists (n, a, c, t) => /=; apply List.mem_filter. qed. local lemma step2_3 &m : Pr[CPA_game(CCA_CPA_Adv(A), RealOrcls(GenChaChaPoly(CCRO(FinRO)))).main() @ &m : res] + Pr[UFCMA_poly(A, FinRO).main() @ &m : res] = Pr[Split1.IdealAll.MainD(G8(A), SplitC2.RO_Pair(SplitC2.I1.RO, SplitC2.I2.RO)).distinguish() @ &m : res] + Pr[Split1.IdealAll.MainD(G9(A), SplitC2.RO_Pair(SplitC2.I1.RO, SplitC2.I2.RO)).distinguish() @ &m : res]. proof. apply (eq_trans _ (Pr[MainD(G4(A), RO).distinguish() @ &m : res] + Pr[MainD(G5(A), RO).distinguish() @ &m : res])). + congr. + apply (eq_trans _ Pr[MainD(G4(A), FinRO).distinguish() @ &m : res]). + by byequiv => //; proc; inline *;sim. rewrite -(pr_RO_FinRO_D _ (G4(A)) &m () (fun x => x)) //. by move=> _; exact: dblock_ll. apply (eq_trans _ Pr[MainD(G5(A), FinRO).distinguish() @ &m : res]). + by byequiv => //; proc; inline *; sim. rewrite -(pr_RO_FinRO_D _ (G5(A)) &m () (fun x => x)) //. by move=> _; exact: dblock_ll. apply (eq_trans _ (Pr[Split0.IdealAll.MainD(G4(A), Split0.IdealAll.RO).distinguish() @ &m : res] + Pr[Split0.IdealAll.MainD(G5(A), Split0.IdealAll.RO).distinguish() @ &m : res])). + by congr; byequiv => //; sim. rewrite (SplitD.pr_RO_split (G4(A)) (fun _ r => r) &m ()) (SplitD.pr_RO_split (G5(A)) (fun _ r => r) &m ()) /=. apply (eq_trans _ (Pr[Split0.IdealAll.MainD(G6(A), Split0.IdealAll.RO).distinguish() @ &m : res] + Pr[Split0.IdealAll.MainD(G7(A), Split0.IdealAll.RO).distinguish() @ &m : res])). + by congr;byequiv => //; proc; inline *; sim. rewrite (SplitC1.pr_RO_split (G6(A)) (fun _ r => r) &m ()) (SplitC1.pr_RO_split (G7(A)) (fun _ r => r) &m ()) /=. rewrite -(SplitC2.pr_RO_split (G8(A)) (fun _ r => r) &m ()) -(SplitC2.pr_RO_split (G9(A)) (fun _ r => r) &m ()) /=. by congr; byequiv => //; proc; inline *; sim. qed. lemma step2 &m : Pr[CCA_game(A, RealOrcls(ChaChaPoly)).main() @ &m : res] <= Pr[Split1.IdealAll.MainD(G8(A), SplitC2.RO_Pair(SplitC2.I1.RO, SplitC2.I2.RO)).distinguish() @ &m : res] + Pr[Split1.IdealAll.MainD(G9(A), SplitC2.RO_Pair(SplitC2.I1.RO, SplitC2.I2.RO)).distinguish() @ &m : res] + (Pr[Indist.Distinguish(D(A), IndBlock).game() @ &m : res] - Pr[Indist.Distinguish(D(A), IndRO).game() @ &m : res]). proof. move: (step1 &m) (step2_1 &m) (step2_2 &m) (step2_3 &m) => /#. qed. end section PROOFS. end Step1_2. (* -------------------------------------------------------------------------- *) (* We now restrict the adversary to be : *) (* - nonce respective *) (* - for encryption end decryption : *) (* * the size of additionnal is bounded by [max_ad_size] *) (* * the size of message is bounded by max_cipher_size *) (* - number of call to *) (* * enc is bounded by qenc *) (* * dec is bounded by qdec *) op qenc : int. axiom ge0_qenc : 0 <= qenc. op qdec : int. axiom ge0_qdec : 0 <= qdec. op dec_bytes : int. axiom ge0_dec_bytes : 0 <= dec_bytes. op pr1_poly_out = mu1 dpoly_out witness. op pr_zeropol : real. axiom ge0_pr_zeropol : 0%r <= pr_zeropol. axiom pr_zeropol_spec ad1 ad2 m1 m2 t1 t2 : valid_topol ad1 m1 => valid_topol ad2 m2 => let p1 = topol ad1 m1 in let p2 = topol ad2 m2 in p2 <> p1 => mu dpoly_in (fun r => t2 = t1 + (poly1305_eval r p2 - poly1305_eval r p1)) <= pr_zeropol. op test_poly_in (n : nonce) (lc : ciphertext list) (r : poly_in) (amt: associated_data * message * tag) = let (a,m,t) = amt in let p = topol a m in let pts = map (fun (c : ciphertext) => (topol c.`2 c.`3, c.`4)) (filter (fun (c : ciphertext) => c.`1 = n /\ valid_topol c.`2 c.`3) lc) in valid_topol a m /\ has (fun (pt : polynomial * tag) => pt.`1 <> p /\ pt.`2 = t + (poly1305_eval r pt.`1 - poly1305_eval r p)) pts. lemma pr_TPI_ok n (lc:ciphertext list) (amt : associated_data * message * tag) (k : int) : size lc <= k => mu dpoly_in (fun r => test_poly_in n lc r amt) <= k%r * pr_zeropol. proof. move => hlc; rewrite /test_poly_in; case: amt => a m t /=. case: (valid_topol a m) => hv /=;last by rewrite mu0; smt (ge0_qdec size_ge0 ge0_pr_zeropol). pose lc' := List.map _ _; apply (ler_trans ((size lc')%r*pr_zeropol)); last by rewrite size_map size_filter //= ler_wpmul2r //= 1: ge0_pr_zeropol; smt (count_size size_ge0). apply mu_has_leM => /= ? /mapP [] [n' a' m' t'] /> /List.mem_filter |> ??. case: (topol a' m' <> topol a m) => ? /=; last by rewrite mu0; smt (ge0_pr_zeropol). by apply pr_zeropol_spec. qed. lemma filter_test_poly_in n lc r amt : test_poly_in n lc r amt = test_poly_in n (filter (fun c:ciphertext => c.`1 = n) lc) r amt. proof. move:amt=> [] a m t; rewrite/test_poly_in /=; case: (valid_topol a m) => //= *. smt(filter_predI). qed. lemma pr_TPI_ok_filter n (lc:ciphertext list) (amt : associated_data * message * tag) (k : int) : size (filter (fun (c:ciphertext) => c.`1 = n) lc) <= k => mu dpoly_in (fun r => test_poly_in n lc r amt) <= k%r * pr_zeropol. proof. have->:mu dpoly_in (fun r => test_poly_in n lc r amt) = mu dpoly_in (fun r => test_poly_in n (filter (fun (c : ciphertext) => c.`1 = n) lc) r amt). + by congr; apply fun_ext=> x; apply filter_test_poly_in. by move=> *; apply pr_TPI_ok=> //. qed. op check_plaintext (lenc:nonce list) (p:plaintext) = let (n, a, m) = p in ! n \in lenc /\ valid_topol a m /\ size lenc < qenc. op check_cipher (ndec:int) (c:ciphertext) = (let (n, a, m, t) = c in valid_topol a m) /\ ndec < qdec. (* Bounded and Nonce Respecting *) module BNR (O:CCA_Oracles) = { var lenc : nonce list var ndec : int proc init () = { lenc <- []; ndec <- 0; } proc enc (p:plaintext) = { var c; c <- witness; if (check_plaintext lenc p) { c <@ O.enc(p); lenc <- p.`1 :: lenc; } return c; } proc dec (c:ciphertext) = { var p; p <- None; if (check_cipher ndec c) { p <@ O.dec(c); ndec <- ndec + 1; } return p; } }. module BNR_Adv(A:CCA_Adv, O:CCA_Oracles) = { proc main() = { var b; BNR(O).init(); b <@ A(BNR(O)).main(); return b; } }. module EncRnd = { proc init () = {} proc cc(n:nonce, p:message) : bytes = { var i, z, c; p <- List.map (fun _ => witness<:byte>) p; c <- []; i <- 1; while (p <> []) { z <$ dblock; c <- c ++ take (size p) (bytes_of_block z); p <- drop block_size p; i <- i + 1; } return c; } proc enc (nap : nonce * associated_data * message) : nonce * associated_data * message * tag = { var n, a, p, c, t; (n,a,p) <- nap; c <@ cc(n,p); t <$ dpoly_out; return (n,a,c,t); } proc dec (nact: nonce * associated_data * message * tag) : (nonce * associated_data * message) option = { return None; } }. section PROOFS. declare module A <: CCA_Adv { -BNR, -Mem, -IndBlock, -RO, -FRO}. declare axiom A_ll : forall (O <: CCA_Oracles{-A}), islossless O.enc => islossless O.dec => islossless A(O).main. local clone import Step1_2 as Step1_2'. local module ROin = SplitC2.I1.RO. local module ROout = SplitC2.I2.RO. local module ROF = SplitD.ROF.RO. local lemma mk_rs_ofpair r s e : mk_rs (SplitC1.ofpair (SplitC2.ofpair (r, s), e)) = (r, s). proof. rewrite /mk_rs /SplitC1.ofpair /SplitC2.ofpair /= insubdK. + by rewrite size_cat TPoly.valP Extra_block.valP. have h1 : size (bytes_of_poly_in r ++ bytes_of_poly_out s) = poly_size. + by rewrite size_cat Poly_in.valP Poly_out.valP. rewrite TPoly.insubdK 1:// take_size_cat 1:// take_size_cat 2:drop_size_cat ?Poly_in.valP //. by rewrite Poly_in.valKd Poly_out.valKd. qed. op inv_cpa (mr1 : (nonce*C.counter, poly_in) fmap) (ms1 : (nonce*C.counter, poly_out) fmap) (log1 log2: (ciphertext, plaintext) fmap) (lc1 lc2 : ciphertext list) (lenc1 lenc2: nonce list) (ndec1 ndec2 :int) = log1 = log2 /\ lenc1 = lenc2 /\ lc1 = lc2 /\ ndec1 = ndec2 /\ (forall n c, (n,c) \in mr1 => n \in lenc1) /\ (forall n c, (n,c) \in ms1 => n \in lenc1). local equiv equ_cc n0 mr0 ms0: ChaCha(CCRO(SplitD.RO_DOM(SplitC1.RO_Pair(SplitC2.RO_Pair(ROin, ROout), SplitC1.I2.RO), ROF))).enc ~ EncRnd.cc : arg{2} = (arg.`2, arg.`3){1} /\ arg{2}.`1 = n0 /\ size arg{1}.`3 <= max_cipher_size /\ !n0 \in BNR.lenc{1} /\ (forall n c, (n,c) \in ROF.m => n \in BNR.lenc){1} /\ mr0 = ROin.m{1} /\ ms0 = ROout.m{1} ==> ={res} /\ size res{1} <= max_cipher_size /\ mr0 = ROin.m{1} /\ ms0 = ROout.m{1} /\ (forall n c, (n,c) \in ROF.m => n \in n0 :: BNR.lenc){1}. proof. proc; wp. exlim (size p{1}) => sz. while (={i, c, n} /\ size c{1} + size p{1} = sz /\ size p{1} = size p{2} /\ 1 <= i{1} /\ size p{1} <= (C.max_counter - (i{1} - 1)) * block_size /\ mr0 = ROin.m{1} /\ ms0 = ROout.m{1} /\ (forall n' c, (n',c) \in ROF.m => if n{2} = n' then 1 <= C.val c < i else n' \in BNR.lenc){1}). + inline{1} 1; inline{1} 4; wp. conseq (_ : (={i, c, n} /\ size c{1} + size p{1} = sz /\ size p{1} = size p{2} /\ 1 <= i{1} /\ size p{1} <= (C.max_counter - (i{1} - 1)) * block_size /\ mr0 = ROin.m{1} /\ ms0 = ROout.m{1} /\ (forall (n' : nonce) (c1 : C.counter), (n', c1) \in ROF.m{1} => if n{2} = n' then 1 <= C.val c1 < i{1} else n' \in BNR.lenc{1})) /\ p{1} <> [] /\ p{2} <> [] /\ i{1} <= C.max_counter ==> _) => //. + move=> /> &1 &2 -> *. have h1 : 0 < size p{2} by smt (size_ge0 size_eq0). have : 0 < (C.max_counter - (i{2} - 1)) * block_size by smt(). by rewrite (StdOrder.IntOrder.pmulr_lgt0 _ _ gt0_block_size) /#. rcondf{1} ^if; 1: by auto; smt (C.insubdK). inline{1} 5; rcondt{1} ^if; 1: by auto; smt (C.insubdK). wp; rnd (fun z => z +^ extend p{1}); auto => &1 &2 [#] 3!-> heq hs /= *;split. + move=> ??;apply xorK1. move=> h1 b ?; rewrite -h1 //= get_setE /= Block.MB.addmC /=. rewrite -!size_eq0 !size_drop 1,2:ge0_block_size hs /= size_cat size_take 1:size_ge0 -heq Block.valP; split;1: smt(); split; 1: smt(). split; 2: smt(mem_set C.insubdK). have := StdOrder.IntOrder.mulr_ge0 (C.max_counter - i{2}) block_size. smt (ge0_block_size). wp; skip=> &1 &2 [#] //= 4!->> h1 h2 h3 2!<<- //=. rewrite size_map //=; do!split; smt (max_cipher_size_ok). qed. local lemma step3 &m: Pr[Split1.IdealAll.MainD(G8(BNR_Adv(A)), SplitC2.RO_Pair(ROin, ROout)).distinguish() @ &m : res] = Pr[CPA_game(CCA_CPA_Adv(BNR_Adv(A)), EncRnd).main() @ &m : res]. proof. byequiv => //; proc; inline *; wp. call (_: (forall n c, (n,c) \in ROF.m => n \in BNR.lenc){1} /\ inv_cpa ROin.m{1} ROout.m{1} Mem.log{1} Mem.log{2} Mem.lc{1} Mem.lc{2} BNR.lenc{1} BNR.lenc{2} BNR.ndec{1} BNR.ndec{2}). + rewrite /inv_cpa;proc => /=; sp 1 1; if; 1:done; 2: by auto. wp; inline{1} 1; inline{2} 1; wp. inline{1} 2; inline{1} 3; inline{1} 7; inline{2} 2. seq 6 4: ( ={c, p, n, a, p0} /\ (p = p0 /\ p = (n, a, p2)){1} /\ p2{1} = p1{2} /\ c2{1} = c1{2} /\ (forall n' c, (n',c) \in ROF.m => n' \in n::BNR.lenc){1} /\ inv_cpa ROin.m{1} ROout.m{1} Mem.log{1} Mem.log{2} Mem.lc{1} Mem.lc{2} BNR.lenc{1} BNR.lenc{2} BNR.ndec{1} BNR.ndec{2} /\ check_plaintext BNR.lenc{1} p{1} ). + by ecall (equ_cc n{1} ROin.m{1} ROout.m{1}); rewrite /check_plaintext; auto => /> /#. move=> {&m};inline{1} 5; inline{1} 8. rcondt{1} ^if. + move=> &m; auto => />; rewrite /SplitD.test /= C.insubdK //=. smt(C.gt0_max_counter). inline{1} 9; wp. call{1} (_: true ==> true); 1: by islossless; apply dextra_block_ll. inline{1} 10; inline{1} 12; inline{1} 11. rcondt{1} ^if{1}; 1: by move=> &m; auto => /> /#. rcondt{1} ^if; 1: by move=> &m; auto => /> /#. swap{1} 12 -11; swap{1} 16 -14; wp. rnd (fun s => s + poly1305_eval r4{1} (topol a{2} c1{2})) (fun s => s - poly1305_eval r4{1} (topol a{2} c1{2})); rnd{1}; skip. smt (dpoly_in_ll poly_out_sub_add poly_out_add_sub get_setE mk_rs_ofpair mem_set). + by proc;inline *; sim /> => />. by auto => />; smt (dkey_ll mem_empty). qed. local module UFCMA (ROin:SplitC2.I1.RO) = { var log : (nonce, associated_data * message * tag)fmap var bad1 : bool var cbad1 : int var bad2 : bool var cbad2 : int proc set_bad1 (lt:tag list) : poly_out = { var t; t <$ dpoly_out; if (cbad1 < qenc /\ size lt <= qdec) { bad1 <- bad1 || t \in lt; cbad1 <- cbad1 + 1; } return t; } proc set_bad2 (lt:tag list) : poly_out = { var t; t <$ dpoly_out; (* if (cbad2 < qdec /\ size lt <= qdec) { *) bad2 <- bad2 || t \in lt; cbad2 <- cbad2 + 1; (* } *) return t; } module O = { proc init () = { log <- empty; ROin.init(); ROout.init(); ROF.init(); } proc enc (nap : nonce * associated_data * message) : nonce * associated_data * message * tag = { var n, a, p, c, t; (n,a,p) <- nap; c <@ EncRnd.cc(n,p); (* t <$ dp *) t <@ set_bad1(map (fun c:ciphertext => c.`4) (filter (fun (c:ciphertext) => c.`1 = n) Mem.lc)); ROin.sample(n,C.ofint 0); ROout.set((n,C.ofint 0), witness); log.[n] <- (a,c,t); return (n,a,c,t); } proc dec (nact: nonce * associated_data * message * tag) : (nonce * associated_data * message) option = { return None; } } proc distinguish () = { var b, ns, forged, i, n, r, t; bad1 <- false; cbad1 <- 0; bad2 <- false; cbad2 <- 0; b <@ CPA_game(CCA_CPA_Adv(BNR_Adv(A)), O).main(); forged <- false; if (size Mem.lc <= qdec) { ns <- undup (List.map (fun (p:ciphertext) => p.`1) Mem.lc); i <- 0; while (i < size ns) { n <- List.nth witness ns i; if ((n,C.ofint 0) \in ROout.m) { r <@ ROin.get(n,C.ofint 0); forged <- forged || test_poly_in n Mem.lc r (oget log.[n]); } else { r <@ ROin.get(n,C.ofint 0); t <@ set_bad2((map (fun c:ciphertext => c.`4 - poly1305_eval r (topol c.`2 c.`3)) (filter (fun c:ciphertext => c.`1 = n) Mem.lc))); ROout.set((n,C.ofint 0), witness); } i <- i + 1; } } return forged; } }. local clone import PROM.FullRO as ROIN with type in_t <- (nonce*C.counter), type out_t <- poly_in, type d_in_t <- unit, type d_out_t <- bool, op dout <- fun _ => dpoly_in proof *. op inv (mr1 mr2 : (nonce*C.counter, poly_in) fmap) (ms1 ms2 : (nonce*C.counter, poly_out) fmap) (log1 log2: (ciphertext, plaintext) fmap) (lc1 lc2 : ciphertext list) (lenc1 lenc2: nonce list) (ndec1 ndec2 :int) (nlog : (nonce, associated_data * message * tag) fmap) = inv_cpa mr1 ms1 log1 log2 lc1 lc2 lenc1 lenc2 ndec1 ndec2 /\ mr1 = mr2 /\ (forall s, s \in ms1 = s \in ms2) /\ (forall s, s \in ms1 = s \in mr1) /\ size lenc1 <= qenc /\ ndec1 <= qdec /\ (forall n, n \in nlog = n \in lenc1) /\ size lc1 <= ndec1 /\ (forall n, n \in lenc1 => let (a,c,t) = oget nlog.[n] in (n,a,c,t) \in log1) /\ (forall n a c t, (n,a,c,t) \in lc1 => valid_topol a c) /\ (forall n, n \in nlog => let (a,c,t) = oget nlog.[n] in valid_topol a c) /\ (forall n a c t, (n,a,c,t) \in lc1 => n \in nlog => nlog.[n] <> Some (a, c, t)) /\ (forall n, n \in lenc1 => let (a,c,t) = oget nlog.[n] in let r = oget mr1.[(n,C.ofint 0)] in let s = oget ms1.[(n,C.ofint 0)] in s = t - poly1305_eval r (topol a c)). local lemma step4_1 &m: Pr[Split1.IdealAll.MainD(G9(BNR_Adv(A)), SplitC2.RO_Pair(ROin, ROout)).distinguish() @ &m : res] <= Pr[UFCMA(ROIN.RO).distinguish() @ &m : res \/ UFCMA.bad2] + Pr[UFCMA(ROIN.RO).distinguish() @ &m : UFCMA.bad1]. proof. apply (ler_trans Pr[UFCMA(ROIN.RO).distinguish() @ &m : (res \/ UFCMA.bad2) \/ UFCMA.bad1]); last first. + by rewrite Pr [mu_or]; smt(mu_bounded). byequiv (_: ={glob A} ==> !(UFCMA.bad1 \/ UFCMA.bad2){2} => ={res}) => //; last by smt(). move=> {&m}; proc. inline{1} 2; inline{1} 4; inline{1} 5. seq 5 5: (!(UFCMA.bad1 \/ UFCMA.bad2){2} => b1{1} = b{2} /\ UFCMA.cbad1{2} <= size BNR.lenc{1} /\ UFCMA.cbad2{2} = 0 /\ inv ROin.m{1} ROIN.RO.m{2} ROout.m{1} ROout.m{2} Mem.log{1} Mem.log{2} Mem.lc{1} Mem.lc{2} BNR.lenc{1} BNR.lenc{2} BNR.ndec{1} BNR.ndec{2} UFCMA.log{2}). + inline*;wp; call (_: UFCMA.bad1 \/ UFCMA.bad2, UFCMA.cbad1{2} <= size BNR.lenc{1} /\ UFCMA.cbad2{2} = 0 /\ inv ROin.m{1} ROIN.RO.m{2} ROout.m{1} ROout.m{2} Mem.log{1} Mem.log{2} Mem.lc{1} Mem.lc{2} BNR.lenc{1} BNR.lenc{2} BNR.ndec{1} BNR.ndec{2} UFCMA.log{2} /\ (forall n c, (n,c) \in ROF.m => n \in BNR.lenc){1}). + by apply A_ll. + proc => /=. sp 1 1; if; 1: (by move=> />); 2: by auto. wp; inline{1} 1; inline{2} 1; wp. inline{1} 2; inline{1} 3; inline{1} 7; inline{2} 2. seq 6 4: ( !(UFCMA.bad1 \/ UFCMA.bad2){2} /\ ={c, p, n, a, p0} /\ (p = p0 /\ p = (n, a, p2)){1} /\ p2{1} = p1{2} /\ c2{1} = c1{2} /\ size c2{1} <= max_cipher_size /\ UFCMA.cbad1{2} <= size BNR.lenc{1} /\ UFCMA.cbad2{2} = 0 /\ (forall n' c, (n',c) \in ROF.m => n' \in n::BNR.lenc){1} /\ inv ROin.m{1} ROIN.RO.m{2} ROout.m{1} ROout.m{2} Mem.log{1} Mem.log{2} Mem.lc{1} Mem.lc{2} BNR.lenc{1} BNR.lenc{2} BNR.ndec{1} BNR.ndec{2} UFCMA.log{2} /\ check_plaintext BNR.lenc{1} p{1} ). + by ecall (equ_cc n{1} ROin.m{1} ROout.m{1}); rewrite /check_plaintext; auto => /> /#. inline{1} 5; inline{1} 8. rcondt{1} ^if. + by move=> &m; auto => />; rewrite /SplitD.test /= C.insubdK //=; smt(C.gt0_max_counter). inline{1} 9; wp. call{1} (_: true ==> true); 1: by islossless; apply dextra_block_ll. inline *. rcondt{1} ^if{1}; 1: by move=> &m; auto => /> /#. rcondt{1} ^if; 1: by move=> &m; auto => /> /#. rcondt{2} ^if{1}; 1: by auto => /> *; smt (size_map size_filter count_size ge0_qdec). rcondt{2} ^if; 1: by move=> &m; auto => /> /#. swap{1} 12 -11; swap{1} 16 -14; swap{2} 8 -7; swap{2} 3 -1 ;wp. rnd (fun s => s + poly1305_eval r{2} (topol a{2} c1{2})) (fun s => s - poly1305_eval r{2} (topol a{2} c1{2})); rnd; skip => />. move=> &1 &2 notbad size_c1 cbad nc_in_ROF nc_in_RO nc_in_I2 eq_in_I2 eq_in_I2_RO. move=> size_lenc ndec n_in_log size_lc n_in_lenc n_in_lc n_in_cmalog nact_in_lc n_split. move=> n_notin_lenc size_a size_p size_lenc2 r3 _. have hh : forall c1 c2 c3 c4, (c1, c2, c3, c4) \in Mem.lc{2} => c4 \in map (fun (c : ciphertext) => c.`4) (filter (fun (c : ciphertext) => c.`1 = c1) Mem.lc{2}). + by move=> c1 c2 c3 c4 h;rewrite mapP; exists (c1,c2,c3,c4); rewrite mem_filter. progress; 1..11: smt (size_map size_filter count_size mapP dpoly_in_ll poly_out_sub_add poly_out_add_sub get_setE mk_rs_ofpair mem_set). (* progress; 1..14, 16: *) (* smt (size_map size_filter count_size mapP dpoly_in_ll poly_out_sub_add *) (* poly_out_add_sub get_setE mk_rs_ofpair mem_set). *) + move: H3 H4; rewrite !get_setE //= !mem_set //= mk_rs_ofpair //=. by case: (n2 = n{2})=> //= *; smt(). + move: H3 H4; rewrite !get_setE/= !mem_set /=. by case: (n2 = n{2})=> //= *; smt(). + move: H3 H4; rewrite !get_setE/= !mem_set /=. by case: (n2 = n{2})=> //= *; smt(). + apply/negP; rewrite get_setE; case : (n2 = n{2}). + by move=> <<- [] 3!<<-; apply H2; rewrite (hh _ _ _ _ H3). smt (size_map size_filter count_size mapP get_setE mem_set). move: H3 H4; rewrite !get_setE/=. by case: (n2 = n{2})=> //= *; smt(). + move=> &2 _; islossless. while true (size p). + move=> z; wp; conseq (_: true) => />; 2: by islossless. move => &hr; elim (p{hr}) => //. smt (size_drop size_eq0 gt0_block_size). by auto; smt (size_ge0 size_eq0). + move=> _; proc; inline *; sp; if => //. swap 13 9; wp; conseq (_: true) => />; 1: smt(); islossless. while true (size p2). + move=> z; wp; conseq (_: true) => //=; 2: by islossless. move => &hr; elim (p2{hr}) => //. clear &hr. smt (size_drop size_eq0 gt0_block_size). by auto; smt (size_ge0 size_eq0 dpoly_out_ll). + by proc; inline *; sp 1 1; if; auto => /> *; smt(get_setE mem_set). + by move=> &2 _; islossless. + by move=> _; conseq />; islossless. auto=> /> k _; do ! split=> />. + smt (dkey_ll mem_empty ge0_qenc ge0_qdec). + smt (dkey_ll mem_empty ge0_qenc ge0_qdec). + smt (dkey_ll mem_empty ge0_qenc ge0_qdec). + smt (dkey_ll mem_empty ge0_qenc ge0_qdec). + smt (dkey_ll mem_empty ge0_qenc ge0_qdec). + smt (dkey_ll mem_empty ge0_qenc ge0_qdec). + smt (dkey_ll mem_empty ge0_qenc ge0_qdec). + smt (dkey_ll mem_empty ge0_qenc ge0_qdec). + smt(). inline{1} 1;wp; sp 0 1. case: ((UFCMA.bad1\/UFCMA.bad2){2}). + while{1} true (size ns - i){1}. + by move=> _ z; wp; conseq (_: true) => //; 1:smt(); islossless. if{2}. + while{2} ((UFCMA.bad1\/UFCMA.bad2){2}) (size ns - i){2}. + move=> _ z; sp; wp; if. + by conseq (_: true) => //; 1:smt(); islossless. by inline *; wp; conseq (_: true) => //; 1:smt(); islossless. by auto => /> /#. by auto => /> /#. rcondt{2} 1; 1: by auto => /> /#. while ( ={i, ns, Mem.lc} /\ uniq ns{1} /\ 0 <= i{1} /\ UFCMA.cbad1{2} <= qenc /\ UFCMA.cbad2{2} <= i{1} /\ size ns{1} <= qdec /\ ROin.m{1} = ROIN.RO.m{2} /\ size Mem.lc{1} <= qdec /\ forged0{1} = (forged{2} \/ UFCMA.bad1{2} \/ UFCMA.bad2{2}) /\ (forall n a c t, (n, a, c, t) \in Mem.lc{1} => valid_topol a c) /\ (forall s, s \in ROout.m{1} = s \in ROout.m{2}) /\ (forall s, s \in ROout.m{1} = s \in ROin.m{1}) /\ (forall j, i{1} <= j < size ns{1} => let n = nth witness ns{1} j in (n, C.ofint 0) \in ROout.m{1} => let (a, c, t) = oget UFCMA.log{2}.[n] in !(n,a,c,t) \in Mem.lc{1} /\ valid_topol a c /\ let r = oget ROin.m{1}.[(n, C.ofint 0)] in let s = oget ROout.m{1}.[(n, C.ofint 0)] in s = t - poly1305_eval r (topol a c))); last first. + auto => /> ; smt (undup_uniq size_undup size_map). (* + auto => /> *; have [#] * :=H H0. *) (* rewrite H3/=. *) (* move:H4=> [][]->>[#] 3->> [#]*. *) (* move:H6=> [#] ->> * /=. *) (* rewrite undup_uniq //=; do ! split=> //=. *) (* - smt (undup_uniq size_undup size_map). *) (* - smt (undup_uniq size_undup size_map). *) (* - smt (undup_uniq size_undup size_map). *) (* - move=> *. *) (* pose n1:= nth witness _ _. *) (* have* :=H5 _ _ H19. *) (* have:=H16 _ H21; rewrite H20 /= => -> //=. *) (* rewrite/n1/=. *) (* pose l1:= List.map _ _. *) (* pose l := undup _. *) (* have:=mem_nth witness l j; rewrite H17 H18 /= =>*. *) (* move:H22; rewrite mem_undup mapP => [][] /= [] n2 y1 y2 y3 /= [] *. *) (* have:=H15 n1 x1 x2 x3. *) (* have:=H11 n1 x1 x2 x3. *) (* have := H19; rewrite -H8 => *. *) (* have :=H13 n1 x1 x2 x3; rewrite H20 /= -H18/= get_some //= => -> //=. *) (* smt (undup_uniq size_undup size_map). *) inline{1} 2; rcondt{1} 3. + by move=> *;auto => />;rewrite /test /= C.insubdK; smt (C.gt0_max_counter). inline{1} 3; inline{1} 4; sp 0 1; wp. call{1} (_: true ==> true); 1: by islossless. wp; inline *; if{2}. + rcondf{2} ^if; 1: by auto => /#. do 2! (rcondf{1} ^if{1}; 1: by auto => /#). auto; rnd{1}; auto => /> &1 &2 h1 hi hcbad hcbad2 hns h2 h3 h4 h5 h6 h7 h8 r _ s _ e. rewrite mk_rs_ofpair /=. pose t1 := test_poly _ _ _ _; pose t2 := test_poly_in _ _ _ _. have /# : t1 = t2; rewrite /t1 /t2 /test_poly /test_poly_in => {t1 t2} /=. have := h6 i{2} h7; rewrite h4 => /(_ h8). case (oget UFCMA.log{2}.[_]) => a c t /= [# h9 hv ->]. rewrite hv /= !has_map !has_filter /predI /=. apply/eq_iff/eq_in_has => @/preim -[] /=. smt (poly_out_add_sub' topol_inj mem_filter poly_out_swap). rcondt{2} ^if; 1: by auto => /#. (* rcondt{2} ^if; 1: by auto=> /> *; smt (size_map size_filter count_size size_ge0). *) do 2! (rcondt{1} ^if{1}; 1: by auto => /#). swap{1} 6 -5; swap{1} 10 -8; swap{2} 2 -1; swap{2} 6 -4. auto=> /> &1 &2 H0 H1 H2 H3 H4 H5 H6 H7 H8 H9 H10 H11 r3 _ r4 _ r. rewrite mk_rs_ofpair /=; rewrite !get_setE /=. split; 1:smt(); split; 1:smt();split. + case: (forged{2}) => //; case: ((UFCMA.bad1\/UFCMA.bad2){2}) => //= H12 H13. - have[|]->//=:=H12. have:=H12; rewrite negb_or => /> *. rewrite /test_poly /= /poly1305 /=. pose f := fun (c : ciphertext) => c.`4 - poly1305_eval r3 (topol c.`2 c.`3). pose m := List.filter _ _. rewrite hasP /=. case: (r4 \in map f m); rewrite mapP. - rewrite eqT=> [][]c []. smt (poly_out_add_sub poly_out_sub_add hasP mapP). apply contraNF. smt (poly_out_add_sub poly_out_sub_add hasP mapP). smt(mem_set index_uniq get_setE). qed. require IterProc. clone import IterProc as IPNonce with type t <- nonce proof *. op sub_map (m1 : (nonce * C.counter, 'a) fmap) (m2 : (nonce * C.counter, 'a) fmap) i l = (forall n, (n, C.ofint 0) \in m2 => (n,C.ofint 0) \in m1) /\ (forall n, (n, C.ofint 0) \in m2 => m1.[(n,C.ofint 0)] = m2.[(n,C.ofint 0)]) /\ (forall j, 0 <= j < i => (nth witness l j, C.ofint 0) \in m1) /\ (forall n, (n, C.ofint 0) \in m1 => (n, C.ofint 0) \in m2 \/ exists j, 0 <= j < i /\ n = nth witness l j). local module UF = { var forged : bool }. local module Orcl : Orcl = { proc f (n : nonce) : unit = { var r, t; if ((n,C.ofint 0) \in ROout.m) { r <@ ROIN.RO.get(n,C.ofint 0); UF.forged <- UF.forged || test_poly_in n Mem.lc r (oget UFCMA.log.[n]); } else { r <@ ROIN.RO.get(n,C.ofint 0); t <@ UFCMA(ROIN.RO).set_bad2((map (fun c:ciphertext => c.`4 - poly1305_eval r (topol c.`2 c.`3)) (filter (fun c:ciphertext => c.`1 = n) Mem.lc))); ROout.set((n,C.ofint 0), witness); } } }. local module UFCMA2 (ROin:SplitC2.I1.RO) = { proc set_bad1 = UFCMA(ROin).set_bad1 proc set_bad2 = UFCMA(ROin).set_bad2 module O = UFCMA(ROin).O proc distinguish () = { var b, ns, ns1, ns2; UFCMA.bad1 <- false; UFCMA.cbad1 <- 0; UFCMA.bad2 <- false; UFCMA.cbad2 <- 0; b <@ CPA_game(CCA_CPA_Adv(BNR_Adv(A)), O).main(); UF.forged <- false; if (size Mem.lc <= qdec) { ns <- undup (List.map (fun (p:ciphertext) => p.`1) Mem.lc); ns1 <- filter (fun n => (n,C.ofint 0) \in ROout.m) ns; ns2 <- filter (fun n => (n,C.ofint 0) \notin ROout.m) ns; Iter(Orcl).iter(ns1++ns2); } return UF.forged; } }. local module UFCMA3 (ROin:SplitC2.I1.RO) = { proc set_bad1 = UFCMA(ROin).set_bad1 proc set_bad2 = UFCMA(ROin).set_bad2 module O = UFCMA(ROin).O proc distinguish () = { var b, ns, ns1, ns2, i, n, r, t; UFCMA.bad1 <- false; UFCMA.cbad1 <- 0; UFCMA.bad2 <- false; UFCMA.cbad2 <- 0; b <@ CPA_game(CCA_CPA_Adv(BNR_Adv(A)), O).main(); UF.forged <- false; if (size Mem.lc <= qdec) { ns <- undup (List.map (fun (p:ciphertext) => p.`1) Mem.lc); ns1 <- filter (fun n => (n,C.ofint 0) \in ROout.m) ns; ns2 <- filter (fun n => (n,C.ofint 0) \notin ROout.m) ns; i <- 0; while (i < size ns1) { n <- nth witness ns1 i; r <@ ROin.get(n,C.ofint 0); UF.forged <- UF.forged || test_poly_in n Mem.lc r (oget UFCMA.log.[n]); i <- i + 1; } i <- 0; while (i < size ns2) { n <- nth witness ns2 i; r <@ ROin.get(n,C.ofint 0); t <@ UFCMA(ROin).set_bad2((map (fun c:ciphertext => c.`4 - poly1305_eval r (topol c.`2 c.`3)) (filter (fun c:ciphertext => c.`1 = n) Mem.lc))); i <- i + 1; } } return UF.forged \/ UFCMA.bad2; } }. local equiv equiv_step4 : UFCMA(ROIN.RO).distinguish ~ UFCMA3(ROIN.RO).distinguish : ={glob A} ==> ={UFCMA.bad2} /\ res{1} = UF.forged{2} /\ res{2} = (UF.forged{2} \/ UFCMA.bad2{2}). proof. transitivity UFCMA2(RO).distinguish (={glob A} ==> ={res, UFCMA.bad2}) (={glob A} ==> ={UFCMA.bad2} /\ res{1} = UF.forged{2} /\ res{2} = (UF.forged{2} \/ UFCMA.bad2{2}))=> //=. + smt(). + proc; sim; sp. seq 2 2 : (={glob UFCMA, glob RO} /\ forged{1} = UF.forged{2})=> /=; 1: sim. if; 1, 3: by auto. transitivity{1} { UF.forged <- forged; ns <- undup (map (fun (p:ciphertext) => p.`1) Mem.lc); Iter(Orcl).iter(ns); forged <- UF.forged; } (={glob UFCMA, glob RO, forged} ==> ={UFCMA.bad2, forged}) (={glob UFCMA, glob RO} /\ forged{1} = UF.forged{2} ==> ={UFCMA.bad2} /\ forged{1} = UF.forged{2})=> //=; 1: smt(). + inline*. sp; wp. while(={ns, glob UFCMA, RO.m} /\ forged{1} = UF.forged{2} /\ l{2} = drop i{1} ns{1} /\ 0 <= i{1}). - sp; if; 1: smt(nth0_head nth_drop). + wp 5 5=> />. by conseq(:_==> ={RO.m, UFCMA.log,Mem.lc} /\ forged{1} = UF.forged{2})=> />; 2: sim; smt(nth0_head nth_drop size_drop size_eq0 drop_nth drop0). by wp 8 8=>/>; conseq(:_==> ={UFCMA.bad2, UFCMA.cbad2, RO.m}); 2: sim; smt(nth0_head nth_drop size_drop size_eq0 drop_nth drop0). by auto; smt(drop0 size_ge0 size_eq0). sp 1 0; wp=> />. call (iter_perm Orcl _); auto. - proc=> /=; inline*. case: (t1{1} = t2{1}). - rcondt{1} 4; 2: rcondt{2} 4; 1,2: auto=> />. + sp; if; auto=> />; smt(mem_set). + sp; if; auto=> />; smt(mem_set). by sim. sp; if{2}. + rcondt{1} 3=> />; 1: by auto=> />; if; auto=> />; smt(mem_set). if{1}. - rcondt{2} 7; 1: by auto=> />; auto. sp; swap 7 -6; swap{1} 1. wp; conseq(:_==> (r1,r3){1} = (r3,r1){2}); auto=> />. move=> &2 H *. rewrite !mem_set !get_setE/=. rewrite (eq_sym t2{2}) H /= => />; do ! split=> /> *; -1: smt(). by rewrite set_setE /= (eq_sym t2{2}) H /=; smt(). rcondf{2} 7; 1: by auto=> />; auto. sp; swap{1} 5 -3; swap{2} 11 -9; swap{1} 14 -12; swap{2} 8 -6; swap{1} 1. swap{1} [6..12] 5; sim. swap{2} 6 4; sim. wp; rnd; rnd; rnd; skip=> /> &2 *. rewrite !mem_set /= !get_setE /=. have h: t1{2} <> t2{2} by smt(). by rewrite (eq_sym t2{2}) h /= => /> *; rewrite set_setE //=; smt(). rcondf{1} 3=> />; 1: auto=> />. - if; auto; smt(mem_set). if{1}. - rcondt{2} 14; 1: by auto=> />; auto; smt(mem_set). sp. swap{1} 7 -5; swap{1} 11 -8; swap{2} 5 -3; swap{2} 14 -13. swap{1} 6 10; sim. swap{2} [13..17] -7; sim. wp; rnd; rnd; rnd; skip=> /> &2 *. rewrite !mem_set /= !get_setE /=. have h: t1{2} <> t2{2} by smt(). by rewrite (eq_sym t2{2}) h /= => /> *; rewrite set_setE //=; smt(). rcondf{2} 14; 1: by auto=> />; auto; smt(mem_set). sp. swap 5 -3; swap 14 -11; swap 18 -14. swap{1} 3; swap{1} 1 3. wp; auto=> /> &2 *. rewrite !mem_set /= !get_setE /=. have h: t1{2} <> t2{2} by smt(). rewrite (eq_sym t2{2}) h /= => /> *. rewrite set_setE //= (eq_sym t2{2}) h /= => /> *. by rewrite set_setE //= (eq_sym t2{2}) h /= => /> *; smt(). move=> /> &2 *; pose l := undup _. by have:=perm_filterC (fun n => (n,C.ofint 0) \in ROout.m{2}) l; rewrite /predC perm_eq_sym. proc; sp. seq 2 2 : (={glob UFCMA2,RO.m}); 1: by sim. inline*; if; auto; sp. splitwhile {1} 1 : size ns2 < size l. seq 1 1 : (={glob UFCMA2,RO.m,ns,ns1,ns2} /\ l{1} = ns2{2} /\ ns{2} = undup (map (fun (p : ciphertext) => p.`1) Mem.lc{2}) /\ ns1{2} = filter (fun (n0 : nonce) => (n0, C.ofint 0) \in ROout.m{2}) ns{2} /\ ns2{2} = filter (fun (n0 : nonce) => (n0, C.ofint 0) \notin ROout.m{2}) ns{2}). + move=> />. while(={glob UFCMA2, RO.m, ns, ns1, ns2} /\ ns{2} = undup (map (fun (p : ciphertext) => p.`1) Mem.lc{2}) /\ ns1{2} = filter (fun (n0 : nonce) => (n0, (C.ofint 0)%C) \in ROout.m{2}) ns{2} /\ ns2{2} = filter (fun (n0 : nonce) => (n0, (C.ofint 0)%C) \notin ROout.m{2}) ns{2} /\ l{1} = drop i{2} ns1{2} ++ ns2{2} /\ 0 <= i{2}); 2: by auto=>/>; smt(drop0 size_eq0 size_ge0 size_cat drop_oversize). sp=> />. conseq(: n{2} = nth witness ns1{2} i{2} /\ x{2} = (n{2}, (C.ofint 0)%C) /\ n{1} = head witness l{1} /\ (((UF.forged{1}, UFCMA.bad2{1}, UFCMA.cbad2{1}, UFCMA.bad1{1}, UFCMA.cbad1{1}, RO.m{1}, UFCMA.log{1}, SplitC2.I2.RO.m{1}, SplitD.ROF.RO.m{1}, BNR.ndec{1}, BNR.lenc{1}, Mem.lc{1}, Mem.log{1}, (glob A){1}) = (UF.forged{2}, UFCMA.bad2{2}, UFCMA.cbad2{2}, UFCMA.bad1{2}, UFCMA.cbad1{2}, RO.m{2}, UFCMA.log{2}, SplitC2.I2.RO.m{2}, SplitD.ROF.RO.m{2}, BNR.ndec{2}, BNR.lenc{2}, Mem.lc{2}, Mem.log{2}, (glob A){2}) /\ ={RO.m, ns, ns1, ns2}) /\ ns{2} = undup (map (fun (p : ciphertext) => p.`1) Mem.lc{2}) /\ ns1{2} = filter (fun (n0 : nonce) => (n0, (C.ofint 0)%C) \in ROout.m{2}) ns{2} /\ ns2{2} = filter (fun (n0 : nonce) => (n0, (C.ofint 0)%C) \notin ROout.m{2}) ns{2} /\ l{1} = drop i{2} ns1{2} ++ ns2{2} /\ 0 <= i{2}) /\ (l{1} <> [] /\ size ns2{1} < size l{1}) /\ i{2} < size ns1{2} /\ ={n} /\ (forall j, 0 <= j < size ns1{2} => (nth witness ns1{2} j, C.ofint 0) \in ROout.m{2}) ==> _). - move=> /> &2. pose l := undup _; pose l1 := List.filter _ _; pose l2 := List.filter _ _. move=> H H0 H1 *. have:= H1; rewrite size_cat ltz_addr => h. rewrite -nth0_head nth_cat //= h /= nth_drop //= => *. have:=allP (fun (n0 : nonce) => (n0, (C.ofint 0)%C) \in SplitC2.I2.RO.m{2}) l1. have->//=->//=:=filter_all (fun (n0 : nonce) => (n0, (C.ofint 0)%C) \in SplitC2.I2.RO.m{2}) l. by apply mem_nth. rcondt{1} 1; 1: by auto=> /#. wp 5 4. conseq(:_==> ={UF.forged, RO.m}) => />; 2: by sim; auto. move=> /> &2. pose l := undup _; pose l1 := List.filter _ _; pose l2 := List.filter _ _. rewrite size_cat //= ltz_addr => *. by rewrite (drop_nth witness i{2}) //= drop0 //=; smt(size_drop size_eq0 size_ge0 size_cat). sp. while(={UF.forged,UFCMA.bad2,UFCMA.cbad2,UFCMA.log,Mem.lc,Mem.log,RO.m,ns,ns1,ns2} /\ l{1} = drop i{2} ns2{2} /\ 0 <= i{2} /\ ns{2} = undup (map (fun (p : ciphertext) => p.`1) Mem.lc{2}) /\ ns1{2} = filter (fun (n0 : nonce) => (n0, C.ofint 0) \in ROout.m{2}) ns{2} /\ ns2{2} = filter (fun (n0 : nonce) => (n0, C.ofint 0) \notin ROout.m{2}) ns{2} /\ sub_map ROout.m{1} ROout.m{2} i{2} ns2{2}); 2: by auto; smt(drop0 size_eq0 size_ge0). sp. conseq(: n{2} = nth witness ns2{2} i{2} /\ x0{2} = (n{2}, C.ofint 0) /\ n{1} = head witness l{1} /\ (={UF.forged, UFCMA.bad2, UFCMA.cbad2, UFCMA.log, Mem.lc, Mem.log, RO.m, ns, ns1, ns2} /\ l{1} = drop i{2} ns2{2} /\ 0 <= i{2} /\ ns{2} = undup (map (fun (p : ciphertext) => p.`1) Mem.lc{2}) /\ ns1{2} = filter (fun (n0 : nonce) => (n0, (C.ofint 0)%C) \in ROout.m{2}) ns{2} /\ ns2{2} = filter (fun (n0 : nonce) => (n0, (C.ofint 0)%C) \notin ROout.m{2}) ns{2} /\ sub_map ROout.m{1} ROout.m{2} i{2} ns2{2}) /\ l{1} <> [] /\ i{2} < size ns2{2} /\ ={n} ==> _). + by move=> /> *; rewrite (drop_nth witness) //=. rcondf{1} 1; 1: auto=> />. + move=> &hr H H0 H1 H2 H3 *. rewrite -nth0_head nth_drop //=. pose l:= undup _. pose l2 := List.filter _ _ . have/=:=H3 (nth witness l2 i{m}). apply absurd=> /= -> /=. rewrite negb_or negb_exists/=; split=> />. - have:=allP (fun (n0 : nonce) => (n0, C.ofint 0) \notin SplitC2.I2.RO.m{m}) l2. have-> /= -> //=:=filter_all (fun (n0 : nonce) => (n0, C.ofint 0) \notin SplitC2.I2.RO.m{m}) l. by rewrite mem_nth //=. move=> a; case: (0 <= a < i{m})=> //= [#]*. have := before_index witness (nth witness l2 i{m}) l2 a. have huniq:uniq l2 by rewrite filter_uniq undup_uniq. by rewrite index_uniq //=; smt(). wp 9 8; conseq(:_==> ={UFCMA.bad2, UFCMA.cbad2, t, RO.m})=> />; 2: by sim; auto. move=> /> &1 &2. pose l := undup _. pose l2 := List.filter _ _. move=> *. have h1: forall j, 0 <= j < size l2 => (nth witness l2 j, C.ofint 0) \notin ROout.m{2}. + move=> j [#] *. - have:=allP (fun (n0 : nonce) => (n0, C.ofint 0) \notin ROout.m{2}) l2. have-> /= -> //=:=filter_all (fun (n0 : nonce) => (n0, C.ofint 0) \notin SplitC2.I2.RO.m{2}) l. by rewrite mem_nth //=. rewrite (drop_nth witness i{2} l2) //= drop0 //=; do ! split=> /> *. + smt(). + smt(mem_set). + rewrite get_set_neqE /=; smt(mem_nth). + smt(mem_set). + smt(mem_set). + smt(mem_set size_drop size_ge0 size_eq0). + smt(mem_set size_drop size_ge0 size_eq0). qed. local module UFCMA4 (ROin:SplitC2.I1.RO) = { var cforged : int proc set_bad1 = UFCMA(ROin).set_bad1 module O = UFCMA(ROin).O proc set_forged () = { var n, r, ns, ns1; ns <- undup (List.map (fun (p:ciphertext) => p.`1) Mem.lc); ns1 <- filter (fun n => (n,C.ofint 0) \in ROout.m) ns; if (cforged < size ns1) { n <- nth witness ns1 cforged; r <$ dpoly_in; ROin.set((n,C.ofint 0), r); UF.forged <- UF.forged || test_poly_in n Mem.lc r (oget UFCMA.log.[n]); cforged <- cforged + 1; } } proc set_bad2 () = { var n, r, ns, ns2, t; ns <- undup (List.map (fun (p:ciphertext) => p.`1) Mem.lc); ns2 <- filter (fun n => (n,C.ofint 0) \notin ROout.m) ns; if (UFCMA.cbad2 < size ns2) { n <- nth witness ns2 UFCMA.cbad2; r <@ ROin.get(n,C.ofint 0); t <$ dpoly_out; UFCMA.bad2 <- UFCMA.bad2 || t \in (map (fun c:ciphertext => c.`4 - poly1305_eval r (topol c.`2 c.`3)) (filter (fun c:ciphertext => c.`1 = n) Mem.lc)); UFCMA.cbad2 <- UFCMA.cbad2 + 1; } } proc test_forged () : unit = { var ns, ns1; UF.forged <- false; cforged <- 0; ns <- undup (List.map (fun (p:ciphertext) => p.`1) Mem.lc); ns1 <- filter (fun n => (n,C.ofint 0) \in ROout.m) ns; while (cforged < size ns1) { set_forged(); } } proc test_bad2 () : unit = { var ns, ns2; UFCMA.bad2 <- false; UFCMA.cbad2 <- 0; ns <- undup (List.map (fun (p:ciphertext) => p.`1) Mem.lc); ns2 <- filter (fun n => (n,C.ofint 0) \notin ROout.m) ns; while (UFCMA.cbad2 < size ns2) { set_bad2(); } } proc distinguish () = { var b; UFCMA.bad1 <- false; UFCMA.cbad1 <- 0; UFCMA.bad2 <- false; UFCMA.cbad2 <- 0; b <@ CPA_game(CCA_CPA_Adv(BNR_Adv(A)), O).main(); UF.forged <- false; if (size Mem.lc <= qdec) { test_forged(); test_bad2(); } return UF.forged \/ UFCMA.bad2; } }. import StdBigop.Bigreal. import StdBigop.Bigint. local lemma step4_bad2 &m : Pr[UFCMA(ROIN.RO).distinguish() @ &m : res \/ UFCMA.bad2] <= qdec%r * (maxr pr_zeropol pr1_poly_out). proof. have->:Pr[UFCMA(ROIN.RO).distinguish() @ &m : res \/ UFCMA.bad2] = Pr[UFCMA3(ROIN.RO).distinguish() @ &m : res] by byequiv equiv_step4. have->:Pr[UFCMA3(ROIN.RO).distinguish() @ &m : res] = Pr[UFCMA3(ROIN.LRO).distinguish() @ &m : res]. + byequiv (ROIN.FullEager.RO_LRO_D UFCMA3 _)=> //=; exact: dpoly_in_ll. have->:Pr[UFCMA3(ROIN.LRO).distinguish() @ &m : res] = Pr[UFCMA4(ROIN.LRO).distinguish() @ &m : UF.forged \/ UFCMA.bad2]. + byequiv=> //=; proc; inline*; sim; sp. seq 5 5 : (={glob UFCMA3, RO.m} /\ (UF.forged,UFCMA.bad2,UFCMA.cbad2, RO.m){2} = (false,false,0, empty)). + by wp; conseq/>; sim. if; auto; sp. while(={ns2, Mem.lc, RO.m, UFCMA.bad2, UF.forged, ROout.m} /\ i{1} = UFCMA.cbad2{2} /\ ns{1} = undup (map (fun (p : ciphertext) => p.`1) Mem.lc{1}) /\ ns2{1} = filter (fun (n1 : nonce) => (n1, (C.ofint 0)%C) \notin ROout.m{1}) ns{1} /\ size Mem.lc{2} <= qdec). + sp; rcondt{2} 1; 1: auto=> />. sp; conseq(:_==> ={RO.m, UFCMA.bad2} /\ i{1} = UFCMA.cbad2{2})=>/>. by swap{1} 4 1; wp; conseq(:_==> ={RO.m} /\ t0{1} = t{2} /\ r{1} = r0{2})=> />; sim. wp=> />. conseq(:_==> ={Mem.lc,RO.m,UF.forged,ROout.m})=> />. while(={Mem.lc,RO.m,UF.forged,ROout.m, ns1, UFCMA.log} /\ i{1} = UFCMA4.cforged{2} /\ size Mem.lc{2} <= qdec /\ 0 <= i{1} /\ (forall j, i{1} <= j < size ns1{1} => (nth witness ns1{1} j, C.ofint 0) \notin RO.m{1}) /\ ns{1} = undup (map (fun (p : ciphertext) => p.`1) Mem.lc{1}) /\ ns1{1} = filter (fun (n1 : nonce) => (n1, (C.ofint 0)%C) \in ROout.m{1}) ns{1}). + sp; rcondt{2} 1; 1: by auto. rcondt{1} 2; 1: by auto=> /#. wp; auto=> /> &h. pose lc := Mem.lc{h}; pose l := undup _; pose l1 := List.filter _ _. move=> ? ? H1 *; rewrite !get_setE /=; split=> /> *; 1: smt(). rewrite mem_set negb_or /= H1 //= 1:/#. by rewrite eq_sym before_index //= index_uniq //= 1:/# 1:filter_uniq //= 1:undup_uniq /#. by auto=> />; smt(mem_empty). byphoare=> //=; proc. sp; seq 1 : true 1%r (qdec%r * (maxr pr_zeropol pr1_poly_out)) 0%r _ (!UFCMA.bad2); auto=> />. + by inline*; auto. sp; if; last by hoare; auto; smt(ge0_qdec ge0_pr_zeropol). case: (0 < size Mem.lc); last first. + inline*; sp. rcondf 1; 1: by auto; smt(size_eq0 size_ge0). sp; rcondf 1; 1: by auto; smt(size_eq0 size_ge0). by hoare; auto; smt(ge0_qdec ge0_pr_zeropol). exists * Mem.lc, ROout.m; elim * => l roout. pose undupl := undup (map (fun (c:ciphertext) => c.`1) l). pose l1 := filter (fun n => (n, C.ofint 0) \in roout) undupl. pose l2 := filter (fun n => (n, C.ofint 0) \notin roout) undupl. pose n1 := size (filter (fun (c:ciphertext) => (c.`1, C.ofint 0) \in roout) l). pose n2 := size (filter (fun (c:ciphertext) => (c.`1, C.ofint 0) \notin roout) l). seq 1 : UF.forged (n1%r * pr_zeropol) 1%r 1%r (n2%r * pr1_poly_out) (l = Mem.lc /\ roout = ROout.m /\ 0 < size Mem.lc <= qdec); auto. + by inline*; auto=> />; while(true); auto. + case: (0 < size l1); last first. - inline*; sp. rcondf 1; 1: by auto; smt(size_eq0 size_ge0). by hoare; auto; smt(size_ge0 ge0_pr_zeropol). call(: Mem.lc = l /\ ROout.m = roout /\ 0 < size l <= qdec /\ 0 < size l1 ==> UF.forged); auto. bypr=> {&m} &m [#] *. fel 4 UFCMA4.cforged (* the query counter *) (fun i => (size (filter (fun (c:ciphertext) => c.`1 = nth witness l1 i) l))%r * pr_zeropol) (* the probability of bad occuring during ith query *) (size l1) (* the bound on the counter (after which we stop caring) *) UF.forged (* the bad event *) [UFCMA4(LRO).set_forged : (UFCMA4.cforged < size l1) ] (* condition(s) under which the oracle(s) do not respond *) (ROout.m = roout /\ Mem.lc = l /\ UFCMA4.cforged <= size l1) (* general unconditional invariants *); first last. + by move=> &h />. + auto=> /> /#. + proc; sp 2. if; last by hoare; auto; smt(size_ge0 ge0_pr_zeropol). inline*; wp; rnd; auto=> &h /> *. by apply pr_TPI_ok_filter=> //=. + by move=> c; proc; sp; inline*; sp; if; auto=> /#. + by move=> b c; proc; inline*; sp; rcondf 1; auto. + (* compute sum of probabilities *) have hn1: n1 = size (filter (fun n => (n, C.ofint 0) \in roout) (map (fun (c:ciphertext) => c.`1) l)). + by rewrite /n1 !size_filter count_map /preim. rewrite -BRA.mulr_suml ler_wpmul2r 1:ge0_pr_zeropol -sumr_ofint le_fromint IntOrder.lerr_eq. rewrite hn1 eq_sym -big_count -BIA.big_filter /= /l1 //= (BIA.big_nth witness); apply BIA.congr_big_seq=> />. move=> x; rewrite /(\o) /predT /= mem_range !size_filter /pred1 //==> [#] * /=. by rewrite count_map. case: (0 < size l2)=> * />; last first. + inline*; sp; rcondf 1; 1: by auto=> &h />; smt(size_ge0 size_eq0). by hoare; auto; smt(size_ge0 mu_bounded). call(: Mem.lc = l /\ ROout.m = roout /\ 0 < size l <= qdec /\ 0 < size l2 ==> UFCMA.bad2); auto. bypr=> {&m} &m [#] *. fel 4 UFCMA.cbad2 (* the query counter *) (fun i => (size (filter (fun (c:ciphertext) => c.`1 = nth witness l2 i) l))%r * pr1_poly_out) (* the probability of bad occuring during ith query *) (size l2) (* the bound on the counter (after which we stop caring) *) UFCMA.bad2 (* the bad event *) [UFCMA4(LRO).set_bad2 : (UFCMA.cbad2 < size l2) ] (* condition(s) under which the oracle(s) do not respond *) (ROout.m = roout /\ Mem.lc = l /\ UFCMA.cbad2 <= size l2) (* general unconditional invariants *); first last. + by move=> />. + by auto=> /> /#. + proc; inline*; sp 2; if; 2: auto=> />. wp; rnd=> />; sp; conseq(:_==> true); 2: by auto. move=> &h /> *. pose lc := List.map _ _. have h := mu_mem_le_mu1 dpoly_out lc pr1_poly_out _; 1: smt(dpoly_out_funi). rewrite (StdOrder.RealOrder.ler_trans _ _ _ h) //= ler_wpmul2r; 1: smt(mu_bounded). by rewrite le_fromint IntOrder.lerr_eq //= size_map. + move=> c; proc; inline*; sp; rcondt 1; 1: auto=> />. by wp -1=> />; conseq(:_==> true); auto; smt(). + by move=> b c; proc; inline*; sp; rcondf 1; auto=> />. + have hn2: n2 = size (filter (fun n => (n, C.ofint 0) \notin roout) (map (fun (c:ciphertext) => c.`1) l)). + by rewrite /n2 !size_filter count_map /preim. rewrite -BRA.mulr_suml ler_wpmul2r; 1:smt(mu_bounded). rewrite -sumr_ofint le_fromint IntOrder.lerr_eq. rewrite hn2 eq_sym -big_count -BIA.big_filter /= //= (BIA.big_nth witness); apply BIA.congr_big_seq=> />. move=> x; rewrite /(\o) /predT /= mem_range !size_filter /pred1 //==> [#] * /=. by rewrite count_map. move=> &h /> ? H0 *. apply (RealOrder.ler_trans ((n1+n2)%r * maxr pr_zeropol pr1_poly_out)). + rewrite fromintD RField.mulrDl. by apply ler_add; rewrite ler_wpmul2l; smt(mu_bounded ge0_pr_zeropol size_ge0). apply ler_wpmul2r; 1: smt(mu_bounded ge0_pr_zeropol size_ge0). rewrite le_fromint (IntOrder.ler_trans _ _ _ _ H0) //=. by rewrite/n1/n2 2!size_filter count_predC. qed. local module UFCMA_l = { var lbad1 : (tag * tag) list proc set_bad1 (lt:tag list) : poly_out = { var t; t <$ dpoly_out; if (UFCMA.cbad1 < qenc /\ size lt <= qdec) { lbad1 <- lbad1 ++ (List.map (fun t' => (t,t')) lt); UFCMA.cbad1 <- UFCMA.cbad1 + 1; } return t; } module O = { proc init () = { UFCMA.log <- empty; ROIN.RO.init(); ROout.init(); ROF.init(); } proc enc (nap : nonce * associated_data * message) : nonce * associated_data * message * tag = { var n, a, p, c, t; (n,a,p) <- nap; c <@ EncRnd.cc(n,p); (* t <$ dp *) t <@ set_bad1(map (fun c:ciphertext => c.`4) (filter (fun (c:ciphertext) => c.`1 = n) Mem.lc)); ROIN.RO.sample(n,C.ofint 0); ROout.set((n,C.ofint 0), witness); UFCMA.log.[n] <- (a,c,t); return (n,a,c,t); } proc dec (nact: nonce * associated_data * message * tag) : (nonce * associated_data * message) option = { return None; } } proc f () = { var b; lbad1 <- []; UFCMA.cbad1 <- 0; b <@ CPA_game(CCA_CPA_Adv(BNR_Adv(A)), O).main(); } }. op make_lbad1 (log : (nonce, associated_data * message * tag) fmap) (lc : ciphertext list) (lenc : nonce list) = flatten (map (fun n:nonce => map (fun c:ciphertext => ((oget log.[n]).`3, c.`4)) (filter (fun c:ciphertext => c.`1 = n) lc)) lenc). lemma make_lbad1_size_cons2 log lc lenc c : uniq lenc => size (make_lbad1 log (c::lc) lenc) = if c.`1 \in lenc then size (make_lbad1 log lc lenc) + 1 else size (make_lbad1 log lc lenc). proof. move:log lc c; elim:lenc=> //=. move=> n lenc Hrec [#] log lc c [] H1 H2. have/=:=Hrec log lc c H2. rewrite !size_flatten /= !sumzE /= !BIA.big_cons /=. pose p1 := predT _; have->/={p1}:p1=true by done. case: (c.`1 \in lenc) => ? H0 * /=. + rewrite H0 //=. have-> /= :c.`1 <> n by smt(). by rewrite addzA; congr=> /=. rewrite H0. case: (c.`1 = n)=> //= <<-. by pose x1 := size _; pose x2 := BIA.big _ _ _; smt(). qed. lemma leq_make_lbad1 log lc lenc : uniq lenc => size (make_lbad1 log lc lenc) <= size lc. proof. rewrite size_flatten sumzE=> *. rewrite!BIA.big_map /= !predTofV/(\o) /=. pose x:=BIA.big _ _ _. have->:x = BIA.big predT (fun n => count (pred1 n) (map (fun c:ciphertext => c.`1) lc)) lenc. + apply BIA.congr_big_seq=> //=; rewrite {1}/predT {1}/predT /==> *. by rewrite size_map size_filter count_map /pred1 /preim; congr; apply fun_ext=> y *; rewrite (eq_sym y.`1). have<-:= size_map (fun c:ciphertext => c.`1). rewrite -(filter_predT (map (fun c:ciphertext => c.`1) lc)). rewrite -big_count. pose nlc := map _ lc. have H0 :=perm_filterC (mem lenc) (undup nlc). rewrite -(BIA.eq_big_perm _ _ _ _ H0) BIA.big_cat. rewrite filter_predT. have H1 :perm_eq (filter (mem lenc) (undup nlc)) (filter (mem nlc) lenc). + have->:=uniq_perm_eq (filter (mem lenc) (undup nlc)) (filter (mem nlc) lenc); rewrite ?filter_uniq ?undup_uniq /= //= ?filter_uniq //=. by move=> *; rewrite !mem_filter mem_undup. rewrite (BIA.eq_big_perm _ _ _ _ H1). rewrite BIA.big_filter (BIA.big_mkcond (mem nlc)) /= IntOrder.ler_paddr. + by apply sumr_ge0=> //=; smt(count_ge0). apply ler_sum_seq=> //=; rewrite /predT/= => *. smt(count_eq0 has_pred1). qed. lemma make_lbad1_size_cons3 (log : (_, _) fmap) lc lenc n a c t: uniq lenc => ! n \in lenc => size (make_lbad1 log.[n <- (a,c,t)] lc (n::lenc)) = size (filter (fun (c0 : ciphertext) => c0.`1 = n) lc) + size (make_lbad1 log lc lenc). proof. move=> huniq hnin. rewrite !size_flatten /= !sumzE /= !BIA.big_cons /=. pose p1 := predT _; have->/={p1}:p1=true by done. rewrite !size_map; congr. rewrite !BIA.big_map /= !predTofV/(\o) /=. apply BIA.congr_big_seq => />; rewrite {1}/predT /= => *. by rewrite !size_map. qed. op inv_lbad1 (lbad1 : (tag * tag) list) (lenc : nonce list) (ufcmalog : (nonce, associated_data * message * tag) fmap) (log : (ciphertext, plaintext) fmap) (lc : ciphertext list) (cbad1 : int) (ndec : int) = uniq lenc /\ cbad1 <= qenc /\ size lenc <= qenc /\ size lbad1 <= size (make_lbad1 ufcmalog lc lenc) <= qdec /\ size lc <= ndec <= qdec /\ uniq lenc /\ (forall n, n \in lenc => let (a,c,t) = oget ufcmalog.[n] in (n,a,c,t) \in log) /\ (forall n, n \in lenc = n \in ufcmalog) /\ (* (forall n a c t, (n,a,c,t) \in lc1 => n \in nlog => nlog.[n] <> Some (a, c, t)) /\ *) (forall t t', (t,t') \in lbad1 => (exists n, n \in lenc /\ (oget ufcmalog.[n]).`3 = t /\ exists a c, (n, a, c, t') \in lc)). local lemma step4_bad1_lbad1 &m: Pr[UFCMA(ROIN.RO).distinguish() @ &m : UFCMA.bad1] <= Pr[UFCMA_l.f() @ &m : size UFCMA_l.lbad1 <= qdec /\ exists tt, tt \in UFCMA_l.lbad1 /\ tt.`1 = tt.`2]. proof. byequiv=> //=; proc; sp. seq 1 1 : (size UFCMA_l.lbad1{2} <= qdec /\ (UFCMA.bad1{1} => exists (tt : tag * tag), (tt \in UFCMA_l.lbad1{2}) /\ tt.`1 = tt.`2)); last first. + sp; if{1}; auto=> />; sp; wp=> />; while{1}(true)(size ns{1}-i{1}); auto=> />; 2: smt(). by inline*; sp; if; auto; smt(dpoly_in_ll dpoly_out_ll). inline*; sp; wp. call(: ={glob BNR, UFCMA.cbad1, UFCMA.cbad1, glob RO, Mem.log, Mem.lc} /\ inv_lbad1 UFCMA_l.lbad1{2} BNR.lenc{2} UFCMA.log{2} Mem.log{2} Mem.lc{2} UFCMA.cbad1{2} BNR.ndec{2} /\ (UFCMA.bad1{1} => exists (tt : tag * tag), (tt \in UFCMA_l.lbad1{2}) /\ tt.`1 = tt.`2)); first last. + by proc; inline*; sp 1 1; if; auto; smt(in_cons make_lbad1_size_cons2 leq_make_lbad1). + by skip; smt( mem_empty ge0_qenc ge0_qdec). proc; sp; if; 1, 3: auto=> />. sp; wp=> /=. case: (UFCMA.cbad1{1} < qenc); last first. + inline*; sp. rcondf{1} 5; 1: auto=> />. - by conseq(:_==> true)=> />; while(true); auto. rcondf{2} 5; 1: auto=> />. - by conseq(:_==> true)=> />; while(true); auto. wp -7 -7=> />. move => />; smt (get_setE). conseq (: ={c1, t, RO.m, Mem.log}); [2:sim=> /> /#]. move => />. smt(get_setE leq_make_lbad1 make_lbad1_size_cons3 size_ge0). inline*; sp. rcondt{1} 5; 1: auto=> />. - conseq(:_==> true)=> />; 1: smt(size_map size_filter count_size). by while(true); auto. rcondt{2} 5; 1: auto=> />. - conseq(:_==> true)=> />; 1: smt(size_map size_filter count_size). by while(true); auto. swap 3 1; swap [4..6] 12; wp -10 -10=> /=. swap 4 4; wp -1 -1. conseq(:_==> ={c1, t0, RO.m, Mem.log, Mem.lc}); [2:sim=> /> /#]. move=> /> &1 &2 *; do ! split => />. - smt(). - smt(). - rewrite size_cat !size_map make_lbad1_size_cons3 //= /#. - smt(leq_make_lbad1). - smt(get_setE). - smt(get_setE). - move=> ? ? H15; have:=H15; rewrite mem_cat=> [#][] H16 *. + smt(get_setE). have:= H16; rewrite mapP /= => [#][] t2 [#] h <<- <<-; have:=h. rewrite mapP /==> [#] [][] x1 x2 x3 x4 /=; rewrite mem_filter /= => [#] <<- ? ->>. smt(get_setE). smt(List.mem_filter mem_cat mapP). qed. local clone EventPartitioning as EP with type input <- unit, type output <- unit. local clone EP.ListPartitioning as LP with type partition <- int. op w1 : poly_out. op w2 : poly_out. declare axiom neq_w1_w2 : w1 <> w2. local lemma step4_lbad1_sum &m : Pr[UFCMA_l.f() @ &m : size UFCMA_l.lbad1 <= qdec /\ exists tt, tt \in UFCMA_l.lbad1 /\ tt.`1 = tt.`2] <= BRA.big predT (fun (i : int) => Pr[UFCMA_l.f() @ &m : let tt = nth (w1,w2) UFCMA_l.lbad1 i in tt.`1 = tt.`2]) (iota_ 0 qdec). proof. pose E := fun (_:unit) (g:glob UFCMA_l) (_:unit) => size g.`1 <= qdec /\ exists tt, tt \in g.`1 /\ tt.`1 = tt.`2. pose phi := fun (_:unit) (g:glob UFCMA_l) (_:unit) => find (fun (tt:tag * tag) => tt.`1 = tt.`2) g.`1. have -> := LP.list_partitioning UFCMA_l () E phi (iota_ 0 qdec) &m (iota_uniq 0 qdec). have -> /= : Pr[UFCMA_l.f() @ &m : E tt (glob UFCMA_l) res /\ ! (phi tt (glob UFCMA_l) res \in iota_ 0 qdec)] = 0%r. + byphoare => //. by hoare; conseq (:true) => // /> *; smt (List.has_find List.hasP mem_iota find_ge0). apply StdBigop.Bigreal.ler_sum_seq => i /mem_iota hi _ /=. byequiv (:_ ==> ={UFCMA_l.lbad1})=> //; first by sim. smt (nth_find List.hasP). qed. local module UFCMA_li = { var i : int var badi : bool var cbadi : int proc set_bad1i (ti:tag) = { var t; t <$ dpoly_out; if (cbadi < 1) { badi <- badi || t = ti; cbadi <- cbadi + 1; } return t; } proc set_bad1 (lt:tag list) : poly_out = { var t; t <$ dpoly_out; if (UFCMA.cbad1 < qenc /\ size lt <= qdec) { if (size UFCMA_l.lbad1 <= i < size UFCMA_l.lbad1 + size lt) { t <@ set_bad1i (nth witness lt (i - size UFCMA_l.lbad1)); } UFCMA_l.lbad1 <- UFCMA_l.lbad1 ++ (List.map (fun t' => (t,t')) lt); UFCMA.cbad1 <- UFCMA.cbad1 + 1; } return t; } module O = { proc init () = { UFCMA.log <- empty; ROIN.RO.init(); ROout.init(); ROF.init(); } proc enc (nap : nonce * associated_data * message) : nonce * associated_data * message * tag = { var n, a, p, c, t; (n,a,p) <- nap; c <@ EncRnd.cc(n,p); (* t <$ dp *) t <@ set_bad1(map (fun c:ciphertext => c.`4) (filter (fun (c:ciphertext) => c.`1 = n) Mem.lc)); ROIN.RO.sample(n,C.ofint 0); ROout.set((n,C.ofint 0), witness); UFCMA.log.[n] <- (a,c,t); return (n,a,c,t); } proc dec (nact: nonce * associated_data * message * tag) : (nonce * associated_data * message) option = { return None; } } proc f (i0:int) = { var b; cbadi <- 0; badi <- false; i <- i0; UFCMA_l.lbad1 <- []; UFCMA.cbad1 <- 0; b <@ CPA_game(CCA_CPA_Adv(BNR_Adv(A)), O).main(); } }. op inv_lbad1_i (lbad1 : (tag * tag) list) (lenc : nonce list) (ufcmalog : (nonce, associated_data * message * tag) fmap) (log : (ciphertext, plaintext) fmap) (lc : ciphertext list) (cbad1 : int) (ndec : int) = uniq lenc /\ cbad1 <= qenc /\ size lenc <= qenc /\ size lbad1 <= size (make_lbad1 ufcmalog lc lenc) <= qdec /\ size lc <= ndec <= qdec /\ uniq lenc. local lemma step4_badi &m nth0 : 0 <= nth0 < qdec => Pr[UFCMA_l.f() @ &m : let tt = nth (w1,w2) UFCMA_l.lbad1 nth0 in tt.`1 = tt.`2] <= Pr[UFCMA_li.f(nth0) @ &m : UFCMA_li.badi]. proof. move=> [#] Hi0 Hiq. byequiv=> //=; proc; inline*; sp; wp=> />. call(: ={RO.m, BNR.ndec, Mem.log, BNR.lenc, UFCMA.cbad1, Mem.lc, UFCMA.log, UFCMA_l.lbad1} /\ UFCMA_li.i{2} = nth0 /\ (size UFCMA_l.lbad1{2} <= nth0 => 0 = UFCMA_li.cbadi{2}) /\ inv_lbad1_i UFCMA_l.lbad1{1} BNR.lenc{1} UFCMA.log{1} Mem.log{1} Mem.lc{1} UFCMA.cbad1{1} BNR.ndec{1} /\ (((nth (w1,w2) UFCMA_l.lbad1{1} nth0).`1 = (nth (w1,w2) UFCMA_l.lbad1{1} nth0).`2) => UFCMA_li.badi{2})); first last. + proc; sp; if; auto; inline*; auto; smt(make_lbad1_size_cons2 size_ge0 leq_make_lbad1). + by auto; smt(neq_w1_w2 size_ge0 ge0_qdec size_flatten ge0_qenc). + proc; inline*; sp; if; 1, 3: auto; sp. swap [5..6] 7. swap 3 -2; sp. swap [4..5] -3; sp. case: (UFCMA.cbad1{1} < qenc /\ size lt{1} <= qdec); last first. - rcondf{1} 9; 1: by auto=> />; while(true); auto. rcondf{2} 9; 1: by auto=> />; while(true); auto. wp -9 -9=> /> /=. by conseq(:_==> ={c1, t0, RO.m}); [2:sim=> /> /#]; 1: smt(make_lbad1_size_cons3 leq_make_lbad1 size_ge0). rcondt{1} 9; 1: by auto=> />; while(true); auto. rcondt{2} 9; 1: by auto=> />; while(true); auto. case: (size UFCMA_l.lbad1{2} + size lt{2} <= UFCMA_li.i{2}). + rcondf{2} 9; 1: by auto=> />; while(true); auto; smt(). wp -11 -11=> /> /=. conseq(:_==> ={c1, t0, RO.m}); [2:sim=> /> /#]. move=> &1 &2 /> *. rewrite !size_cat !size_map //= => {&1}; split; 1: smt(size_map size_ge0). rewrite nth_cat; split. - pose ns := size (make_lbad1 _ _ _). pose ks := size (List.filter _ _). split; 1: smt(). split; 1: smt(). split; 2: smt(make_lbad1_size_cons3 leq_make_lbad1). by rewrite /ns make_lbad1_size_cons3 //= /#. case: (size UFCMA_l.lbad1{2} <= UFCMA_li.i{2})=> //= h. - have->/=: ! UFCMA_li.i{2} < size UFCMA_l.lbad1{2} by smt(). rewrite nth_default //= ?neq_w1_w2 /=; smt(size_map). by have/=: UFCMA_li.i{2} < size UFCMA_l.lbad1{2}; smt(). case: (size UFCMA_l.lbad1{2} <= UFCMA_li.i{2}); last first. + rcondf{2} 9; 1: by auto=> />; while(true); auto; smt(). wp -11 -11=> /> /=. conseq(:_==> ={c1, t0, RO.m}); [2:sim=> /> /#]. move=> &1 &2 /> *. rewrite !size_cat !size_map //= => {&1}; split; 1: smt(size_map size_ge0). rewrite nth_cat; split. - pose ns := size (make_lbad1 _ _ _). pose ks := size (List.filter _ _). split; 1: smt(). split; 1: smt(). split; 2: smt(make_lbad1_size_cons3 leq_make_lbad1). by rewrite /ns make_lbad1_size_cons3 //= /#. by have->/=: UFCMA_li.i{2} < size UFCMA_l.lbad1{2} by smt(). rcondt{2} 9; 1: by auto=> />; while(true); auto; smt(). rcondt{2} 11; 1: by auto=> />; while(true); auto; smt(). swap {2} 10 -6; wp 5 6=> />. conseq(:_==> ={c1, RO.m} /\ t0{1} = t1{2}); last first. + by sim; auto=> />; sim=> /#. move=> /> &2 ????????????? H12 H13 *. move:H12 H13; rewrite !size_map !size_cat !size_map => *; do ! split. + smt(size_map). + smt(). + smt(). + by rewrite make_lbad1_size_cons3 //=; smt(). + smt(leq_make_lbad1). + rewrite nth_cat=> []. have h :! (nth0 < size UFCMA_l.lbad1{2}) by smt(nth_default neq_w1_w2). rewrite h /=. by rewrite (nth_map witness) //=; smt(size_map). qed. local lemma pr_step4_badi &m nth0 : 0 <= nth0 < qdec => Pr[UFCMA_li.f(nth0) @ &m : UFCMA_li.badi] <= pr1_poly_out. proof. move=> [#] *. fel 2 UFCMA_li.cbadi (fun _ => pr1_poly_out) 1 UFCMA_li.badi [UFCMA_li.set_bad1i :(UFCMA_li.cbadi < 1)] (UFCMA_li.cbadi <= 1) => />. + by rewrite /= BRA.big_int1 /=. + by auto=> />. + proc. rcondt 2; 1: auto; wp=> />; rnd=> />; skip=> /> &hr *. by have <- //=:=dpoly_out_funi witness ti{hr}. + move=> c; proc; auto=> />; smt(). by move=> b c; proc; auto. qed. local lemma step4_bad1 &m : Pr[UFCMA(ROIN.RO).distinguish() @ &m : UFCMA.bad1] <= qdec%r * pr1_poly_out. proof. apply (RealOrder.ler_trans _ _ _ (step4_bad1_lbad1 &m)). apply (RealOrder.ler_trans _ _ _ (step4_lbad1_sum &m)). apply (RealOrder.ler_trans _ _ _ (StdBigop.Bigreal.ler_sum_seq _ _ (fun _ => pr1_poly_out) _ _)); last by rewrite sumr_const count_predT size_iota; smt(ge0_qdec). move=> nth0; rewrite mem_iota /predT /= => [#] *. apply (RealOrder.ler_trans _ _ _ (step4_badi &m nth0 _))=> //. exact (pr_step4_badi &m). qed. lemma conclusion &m : Pr[CCA_game(BNR_Adv(A), RealOrcls(ChaChaPoly)).main() @ &m : res] <= Pr[CCA_game(CCA_CPA_Adv(BNR_Adv(A)), EncRnd).main() @ &m : res] + (Pr[Indist.Distinguish(D(BNR_Adv(A)), IndBlock).game() @ &m : res] - Pr[Indist.Distinguish(D(BNR_Adv(A)), IndRO).game() @ &m : res]) + qdec%r * (maxr pr_zeropol pr1_poly_out) + qdec%r * pr1_poly_out. proof. have := step2 (BNR_Adv(A)) _ &m. + move=> O enc_ll dec_ll. by islossless; apply: (A_ll (<: BNR(O)) _ _); islossless. have := step3 &m. have := step4_1 &m. have := step4_bad1 &m. by have /# := step4_bad2 &m. qed. end section PROOFS.