diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 8e1d090b..00000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: Hakyber - -on: - workflow_dispatch: - push: - branches: - - master - -env: - OPAMROOT: /home/charlie/.opam - OPAMYES: true - OPAMJOBS: 2 - ECRJOBS: 1 - -jobs: - ec: - name: Check Hakyber EasyCrypt Project - runs-on: ubuntu-20.04 - container: - image: easycryptpa/ec-build-box - strategy: - fail-fast: false - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - uses: actions/checkout@v4 - name: Checkout EasyCrypt - with: - repository: EasyCrypt/easycrypt - ref: refs/heads/main - path: easycrypt - - name: Update OPAM & EasyCrypt dependencies - run: | - opam update - opam pin alt-ergo 2.5.2 - opam pin add -n easycrypt easycrypt - opam install --deps-only easycrypt - - name: Compile & Install EasyCrypt - run: opam install easycrypt - - name: Detect SMT provers - run: | - rm -f ~/.why3.conf - opam config exec -- easycrypt why3config -why3 ~/.why3.conf - - name: Compile Project - run: opam config exec -- make checkec diff --git a/.github/workflows/prover-ec-dev-version.yml b/.github/workflows/prover-ec-dev-version.yml new file mode 100644 index 00000000..610b1e07 --- /dev/null +++ b/.github/workflows/prover-ec-dev-version.yml @@ -0,0 +1,36 @@ +name: Hakyber on EasyCrypt/dev + +on: + workflow_dispatch: + push: + branches: + - master + +env: + OPAMROOT: /home/charlie/.opam + OPAMYES: true + OPAMJOBS: 2 + ECRJOBS: 1 + +jobs: + ec: + name: Hakyber on EasyCrypt/dev + runs-on: ubuntu-20.04 + container: + image: ghcr.io/easycrypt/ec-build-box + strategy: + fail-fast: false + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - name: Compile & Install EasyCrypt + run: | + opam pin --dev-repo add -n easycrypt https://github.com/EasyCrypt/easycrypt.git + opam install -v easycrypt + - name: Detect SMT provers + run: | + opam exec -- easycrypt why3config + - name: Compile Project + run: | + opam exec -- make checkec diff --git a/proof/spec/log b/proof/spec/log new file mode 100644 index 00000000..87ae28c8 --- /dev/null +++ b/proof/spec/log @@ -0,0 +1,47359 @@ +[-] [0002] 0.1% (-1.0B / [frag -1.0B]) [\] [0003] 0.2% (-1.0B / [frag -1.0B]) [|] [0004] 0.2% (-1.0B / [frag -1.0B]) [/] [0005] 0.3% (-1.0B / [frag -1.0B]) [-] [0007] 0.3% (-1.0B / [frag -1.0B]) [\] [0008] 0.4% (-1.0B / [frag -1.0B]) [|] [0009] 0.5% (-1.0B / [frag -1.0B]) * In [theories]: + +abstract theory Matrix. + theory ZR. + type t. + + op zeror : t. + + op (+) : t -> t -> t. + + op [-] : t -> t. + + axiom nosmt addrA: associative ZR.(+). + + axiom nosmt addrC: commutative ZR.(+). + + axiom nosmt add0r: left_id zeror ZR.(+). + + axiom nosmt addNr: left_inverse zeror ZR.[-] ZR.(+). + + theory AddMonoid. + lemma addmA: associative ZR.(+). + + lemma addmC: commutative ZR.(+). + + lemma add0m: left_id zeror ZR.(+). + + lemma addm0: right_id zeror ZR.(+). + + lemma addmCA: left_commutative ZR.(+). + + lemma addmAC: right_commutative ZR.(+). + + lemma addmACA: interchange ZR.(+) ZR.(+). + + lemma iteropE: forall (n : int) (x : t), iterop n ZR.(+) x zeror = iter n ((+) x) zeror. + end AddMonoid. + + abbrev (-) (x y : t) : t = x + -y. + + lemma nosmt addr0: right_id zeror ZR.(+). + + lemma nosmt addrN: right_inverse zeror ZR.[-] ZR.(+). + + lemma nosmt addrCA: left_commutative ZR.(+). + + lemma nosmt addrAC: right_commutative ZR.(+). + + lemma nosmt addrACA: interchange ZR.(+) ZR.(+). + + lemma nosmt subrr: forall (x : t), x - x = zeror. + + lemma nosmt addKr: left_loop ZR.[-] ZR.(+). + + lemma nosmt addNKr: rev_left_loop ZR.[-] ZR.(+). + + lemma nosmt addrK: right_loop ZR.[-] ZR.(+). + + lemma nosmt addrNK: rev_right_loop ZR.[-] ZR.(+). + + lemma nosmt subrK: forall (x y : t), x - y + y = x. + + lemma nosmt addrI: right_injective ZR.(+). + + lemma nosmt addIr: left_injective ZR.(+). + + lemma nosmt opprK: involutive ZR.[-]. + + lemma oppr_inj: injective ZR.[-]. + + lemma nosmt oppr0: -zeror = zeror. + + lemma oppr_eq0: forall (x : t), -x = zeror <=> x = zeror. + + lemma nosmt subr0: forall (x : t), x - zeror = x. + + lemma nosmt sub0r: forall (x : t), zeror - x = -x. + + lemma nosmt opprD: forall (x y : t), - (x + y) = (-x) - y. + + lemma nosmt opprB: forall (x y : t), - (x - y) = y - x. + + lemma nosmt subrACA: interchange (fun (x y : t) => x - y) ZR.(+). + + lemma nosmt subr_eq: forall (x y z : t), x - z = y <=> x = y + z. + + lemma nosmt subr_eq0: forall (x y : t), x - y = zeror <=> x = y. + + lemma nosmt addr_eq0: forall (x y : t), x + y = zeror <=> x = -y. + + lemma nosmt eqr_opp: forall (x y : t), -x = -y <=> x = y. + + lemma eqr_oppLR: forall (x y : t), -x = y <=> x = -y. + + lemma nosmt eqr_sub: forall (x y z t : t), x - y = z - t <=> x + t = z + y. + + lemma subr_add2r: forall (z x y : t), x + z - (y + z) = x - y. + + op intmul (x : t) (n : int) : t = if n < 0 then - iterop (-n) ZR.(+) x zeror else iterop n ZR.(+) x zeror. + + lemma intmulpE: forall (z : t) (c : int), 0 <= c => intmul z c = iterop c ZR.(+) z zeror. + + lemma mulr0z: forall (x : t), intmul x 0 = zeror. + + lemma mulr1z: forall (x : t), intmul x 1 = x. + + lemma mulr2z: forall (x : t), intmul x 2 = x + x. + + lemma mulrNz: forall (x : t) (n : int), intmul x (-n) = - intmul x n. + + lemma mulrS: forall (x : t) (n : int), 0 <= n => intmul x (n + 1) = x + intmul x n. + + lemma mulNrz: forall (x : t) (n : int), intmul (-x) n = - intmul x n. + + lemma mulNrNz: forall (x : t) (n : int), intmul (-x) (-n) = intmul x n. + + lemma mulrSz: forall (x : t) (n : int), intmul x (n + 1) = x + intmul x n. + + lemma mulrDz: forall (x : t) (n m : int), intmul x (n + m) = intmul x n + intmul x m. + + op oner : t. + + op ( * ) : t -> t -> t. + + op invr : t -> t. + + pred unit : t. + + abbrev (/) (x y : t) : t = x * invr y. + + axiom nosmt oner_neq0: oner <> zeror. + + axiom nosmt mulrA: associative ZR.( * ). + + axiom nosmt mulrC: commutative ZR.( * ). + + axiom nosmt mul1r: left_id oner ZR.( * ). + + axiom nosmt mulrDl: left_distributive ZR.( * ) ZR.(+). + + axiom nosmt mulVr: left_inverse_in unit oner invr ZR.( * ). + + axiom nosmt unitP: forall (x y : t), y * x = oner => unit x. + + axiom nosmt unitout: forall (x : t), ! unit x => invr x = x. + + theory MulMonoid. + lemma addmA: associative ZR.( * ). + + lemma addmC: commutative ZR.( * ). + + lemma add0m: left_id oner ZR.( * ). + + lemma addm0: right_id oner ZR.( * ). + + lemma addmCA: left_commutative ZR.( * ). + + lemma addmAC: right_commutative ZR.( * ). + + lemma addmACA: interchange ZR.( * ) ZR.( * ). + + lemma iteropE: forall (n : int) (x : t), iterop n ZR.( * ) x oner = iter n (( * ) x) oner. + end MulMonoid. + + lemma nosmt mulr1: right_id oner ZR.( * ). + + lemma nosmt mulrCA: left_commutative ZR.( * ). + + lemma nosmt mulrAC: right_commutative ZR.( * ). + + lemma nosmt mulrACA: interchange ZR.( * ) ZR.( * ). + + lemma nosmt mulrSl: forall (x y : t), (x + oner) * y = x * y + y. + + lemma nosmt mulrDr: right_distributive ZR.( * ) ZR.(+). + + lemma nosmt mul0r: left_zero zeror ZR.( * ). + + lemma nosmt mulr0: right_zero zeror ZR.( * ). + + lemma nosmt mulrN: forall (x y : t), x * -y = - x * y. + + lemma nosmt mulNr: forall (x y : t), (-x) * y = - x * y. + + lemma nosmt mulrNN: forall (x y : t), (-x) * -y = x * y. + + lemma nosmt mulN1r: forall (x : t), (-oner) * x = -x. + + lemma nosmt mulrN1: forall (x : t), x * -oner = -x. + + lemma nosmt mulrBl: left_distributive ZR.( * ) (fun (x y : t) => x - y). + + lemma nosmt mulrBr: right_distributive ZR.( * ) (fun (x y : t) => x - y). + + lemma mulrnAl: forall (x y : t) (n : int), 0 <= n => intmul x n * y = intmul (x * y) n. + + lemma mulrnAr: forall (x y : t) (n : int), 0 <= n => x * intmul y n = intmul (x * y) n. + + lemma mulrzAl: forall (x y : t) (z : int), intmul x z * y = intmul (x * y) z. + + lemma mulrzAr: forall (x y : t) (z : int), x * intmul y z = intmul (x * y) z. + + lemma nosmt mulrV: right_inverse_in unit oner invr ZR.( * ). + + lemma nosmt divrr: forall (x : t), unit x => x / x = oner. + + lemma nosmt invr_out: forall (x : t), ! unit x => invr x = x. + + lemma nosmt unitrP: forall (x : t), unit x <=> exists (y : t), y * x = oner. + + lemma nosmt mulKr: left_loop_in unit invr ZR.( * ). + + lemma nosmt mulrK: right_loop_in unit invr ZR.( * ). + + lemma nosmt mulVKr: rev_left_loop_in unit invr ZR.( * ). + + lemma nosmt mulrVK: rev_right_loop_in unit invr ZR.( * ). + + lemma nosmt mulrI: right_injective_in unit ZR.( * ). + + lemma nosmt mulIr: left_injective_in unit ZR.( * ). + + lemma nosmt unitrE: forall (x : t), unit x <=> x / x = oner. + + lemma nosmt invrK: involutive invr. + + lemma nosmt invr_inj: injective invr. + + lemma nosmt unitrV: forall (x : t), unit (invr x) <=> unit x. + + lemma nosmt unitr1: unit oner. + + lemma nosmt invr1: invr oner = oner. + + lemma nosmt div1r: forall (x : t), oner / x = invr x. + + lemma nosmt divr1: forall (x : t), x / oner = x. + + lemma nosmt unitr0: ! unit zeror. + + lemma nosmt invr0: invr zeror = zeror. + + lemma nosmt unitrN1: unit (-oner). + + lemma nosmt invrN1: invr (-oner) = -oner. + + lemma nosmt unitrMl: forall (x y : t), unit y => unit (x * y) <=> unit x. + + lemma nosmt unitrMr: forall (x y : t), unit x => unit (x * y) <=> unit y. + + lemma nosmt unitrM: forall (x y : t), unit (x * y) <=> unit x /\ unit y. + + lemma nosmt unitrN: forall (x : t), unit (-x) <=> unit x. + + lemma nosmt invrM: forall (x y : t), unit x => unit y => invr (x * y) = invr y / x. + + lemma nosmt invrN: forall (x : t), invr (-x) = - invr x. + + lemma nosmt invr_neq0: forall (x : t), x <> zeror => invr x <> zeror. + + lemma nosmt invr_eq0: forall (x : t), invr x = zeror <=> x = zeror. + + lemma nosmt invr_eq1: forall (x : t), invr x = oner <=> x = oner. + + op ofint (n : int) : t = intmul oner n. + + lemma ofint0: ofint 0 = zeror. + + lemma ofint1: ofint 1 = oner. + + lemma ofintS: forall (i : int), 0 <= i => ofint (i + 1) = oner + ofint i. + + lemma ofintN: forall (i : int), ofint (-i) = - ofint i. + + lemma mul1r0z: forall (x : t), x * ofint 0 = zeror. + + lemma mul1r1z: forall (x : t), x * ofint 1 = x. + + lemma mul1r2z: forall (x : t), x * ofint 2 = x + x. + + lemma mulr_intl: forall (x : t) (z : int), ofint z * x = intmul x z. + + lemma mulr_intr: forall (x : t) (z : int), x * ofint z = intmul x z. + + lemma fracrDE: forall (n1 n2 d1 d2 : t), unit d1 => unit d2 => n1 / d1 + n2 / d2 = (n1 * d2 + n2 * d1) / (d1 * d2). + + op exp (x : t) (n : int) : t = if n < 0 then invr (iterop (-n) ZR.( * ) x oner) else iterop n ZR.( * ) x oner. + + lemma expr0: forall (x : t), exp x 0 = oner. + + lemma expr1: forall (x : t), exp x 1 = x. + + lemma exprS: forall (x : t) (i : int), 0 <= i => exp x (i + 1) = x * exp x i. + + lemma expr_pred: forall (x : t) (i : int), 0 < i => exp x i = x * exp x (i - 1). + + lemma exprSr: forall (x : t) (i : int), 0 <= i => exp x (i + 1) = exp x i * x. + + lemma expr2: forall (x : t), exp x 2 = x * x. + + lemma exprN: forall (x : t) (i : int), exp x (-i) = invr (exp x i). + + lemma exprN1: forall (x : t), exp x (-1) = invr x. + + lemma unitrX: forall (x : t) (m : int), unit x => unit (exp x m). + + lemma unitrX_neq0: forall (x : t) (m : int), m <> 0 => unit (exp x m) => unit x. + + lemma exprV: forall (x : t) (i : int), exp (invr x) i = exp x (-i). + + lemma exprVn: forall (x : t) (n : int), 0 <= n => exp (invr x) n = invr (exp x n). + + lemma exprMn: forall (x y : t) (n : int), 0 <= n => exp (x * y) n = exp x n * exp y n. + + lemma exprD_nneg: forall (x : t) (m n : int), 0 <= m => 0 <= n => exp x (m + n) = exp x m * exp x n. + + lemma exprD: forall (x : t) (m n : int), unit x => exp x (m + n) = exp x m * exp x n. + + lemma exprM: forall (x : t) (m n : int), exp x (m * n) = exp (exp x m) n. + + lemma expr0n: forall (n : int), 0 <= n => exp zeror n = if n = 0 then oner else zeror. + + lemma expr0z: forall (z : int), exp zeror z = if z = 0 then oner else zeror. + + lemma expr1z: forall (z : int), exp oner z = oner. + + lemma sqrrD: forall (x y : t), exp (x + y) 2 = exp x 2 + intmul (x * y) 2 + exp y 2. + + lemma sqrrN: forall (x : t), exp (-x) 2 = exp x 2. + + lemma sqrrB: forall (x y : t), exp (x - y) 2 = exp x 2 - intmul (x * y) 2 + exp y 2. + + lemma signr_odd: forall (n : int), 0 <= n => exp (-oner) (b2i (odd n)) = exp (-oner) n. + + lemma subr_sqr_1: forall (x : t), exp x 2 - oner = (x - oner) * (x + oner). + + op lreg (x : t) : bool = injective (fun (y : t) => x * y). + + lemma mulrI_eq0: forall (x y : t), lreg x => x * y = zeror <=> y = zeror. + + lemma lreg_neq0: forall (x : t), lreg x => x <> zeror. + + lemma mulrI0_lreg: forall (x : t), (forall (y : t), x * y = zeror => y = zeror) => lreg x. + + lemma lregN: forall (x : t), lreg x => lreg (-x). + + lemma lreg1: lreg oner. + + lemma lregM: forall (x y : t), lreg x => lreg y => lreg (x * y). + + lemma lregXn: forall (x : t) (n : int), 0 <= n => lreg x => lreg (exp x n). + + instance ring with [()] t + op rzero = Top.Matrix.ZR.zeror + op rone = Top.Matrix.ZR.oner + op add = Top.Matrix.ZR.+ + op opp = Top.Matrix.ZR.[-] + op mul = Top.Matrix.ZR.* + op expr = Top.Matrix.ZR.exp + op ofint = Top.Matrix.ZR.ofint + + instance t with Top.Ring.ZModule.zmodule. + + instance t with Top.Matrix.ZR.ring. + + instance t with Top.Ring.IDomain.idomain. + end ZR. + + theory Big. + theory CR. + instance ring with [()] t + op rzero = Top.Matrix.ZR.zeror + op rone = Top.Matrix.ZR.oner + op add = Top.Matrix.ZR.+ + op opp = Top.Matrix.ZR.[-] + op mul = Top.Matrix.ZR.* + op expr = Top.Matrix.ZR.exp + op ofint = Top.Matrix.ZR.ofint + + instance t with Top.Ring.ZModule.zmodule. + + instance t with Top.Matrix.Big.CR.ring. + + instance t with Top.Ring.IDomain.idomain. + end CR. + + theory BAdd. + op big ['a] (P : 'a -> bool) (F : 'a -> t) (r : 'a list) : t = foldr ZR.(+) zeror (map F (filter P r)). + + abbrev bigi (P : int -> bool) (F : int -> t) (i j : int) : t = big P F (range i j). + + lemma big_nil ['a]: forall (P : 'a -> bool) (F : 'a -> t), big P F [] = zeror. + + lemma big_cons ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (x : 'a) (s : 'a list), + big P F (x :: s) = if P x then F x + big P F s else big P F s. + + lemma big_consT ['a]: forall (F : 'a -> t) (x : 'a) (s : 'a list), big predT F (x :: s) = F x + big predT F s. + + lemma nosmt big_rec ['a]: + forall (K : t -> bool) (r : 'a list) (P : 'a -> bool) (F : 'a -> t), + K zeror => (forall (i : 'a) (x : t), P i => K x => K (F i + x)) => K (big P F r). + + lemma nosmt big_ind ['a]: + forall (K : t -> bool) (r : 'a list) (P : 'a -> bool) (F : 'a -> t), + (forall (x y : t), K x => K y => K (x + y)) => K zeror => (forall (i : 'a), P i => K (F i)) => K (big P F r). + + lemma nosmt big_rec2 ['a]: + forall (K : t -> t -> bool) (r : 'a list) (P : 'a -> bool) (F1 F2 : + 'a -> t), + K zeror zeror => + (forall (i : 'a) (y1 y2 : t), P i => K y1 y2 => K (F1 i + y1) (F2 i + y2)) => K (big P F1 r) (big P F2 r). + + lemma nosmt big_ind2 ['a]: + forall (K : t -> t -> bool) (r : 'a list) (P : 'a -> bool) (F1 F2 : + 'a -> t), + (forall (x1 x2 y1 y2 : t), K x1 x2 => K y1 y2 => K (x1 + y1) (x2 + y2)) => + K zeror zeror => (forall (i : 'a), P i => K (F1 i) (F2 i)) => K (big P F1 r) (big P F2 r). + + lemma nosmt big_endo ['a]: + forall (f : t -> t), + f zeror = zeror => + (forall (x y : t), f (x + y) = f x + f y) => + forall (r : 'a list) (P : 'a -> bool) (F : 'a -> t), f (big P F r) = big P (f \o F) r. + + lemma nosmt big_map ['a, 'b]: + forall (h : 'b -> 'a) (P : 'a -> bool) (F : 'a -> t) (s : 'b list), big P F (map h s) = big (P \o h) (F \o h) s. + + lemma nosmt big_mapT ['a, 'b]: + forall (h : 'b -> 'a) (F : 'a -> t) (s : 'b list), big predT F (map h s) = big predT (F \o h) s. + + lemma nosmt big_comp ['a]: + forall (h : t -> t) (P : 'a -> bool) (F : 'a -> t) (s : 'a list), + h zeror = zeror => morphism_2 h ZR.(+) ZR.(+) => h (big P F s) = big P (h \o F) s. + + lemma nosmt big_nth ['a]: + forall (x0 : 'a) (P : 'a -> bool) (F : 'a -> t) (s : 'a list), + big P F s = bigi (P \o nth x0 s) (F \o nth x0 s) 0 (size s). + + lemma nosmt big_const ['a]: + forall (P : 'a -> bool) (x : t) (s : 'a list), big P (fun (_ : 'a) => x) s = iter (count P s) ((+) x) zeror. + + lemma nosmt big_seq1 ['a]: forall (F : 'a -> t) (x : 'a), big predT F [x] = F x. + + lemma nosmt big_mkcond ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list), + big P F s = big predT (fun (i : 'a) => if P i then F i else zeror) s. + + lemma nosmt big_filter ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list), big predT F (filter P s) = big P F s. + + lemma big_filter_cond ['a]: + forall (P1 P2 : 'a -> bool) (F : 'a -> t) (s : 'a list), big P2 F (filter P1 s) = big (predI P1 P2) F s. + + lemma nosmt eq_bigl ['a]: + forall (P1 P2 : 'a -> bool) (F : 'a -> t) (s : 'a list), + (forall (i : 'a), P1 i <=> P2 i) => big P1 F s = big P2 F s. + + lemma nosmt eq_bigr ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> t) (s : 'a list), + (forall (i : 'a), P i => F1 i = F2 i) => big P F1 s = big P F2 s. + + lemma nosmt big_distrl ['a]: + forall (op_ : t -> t -> t) (P : 'a -> bool) (F : 'a -> t) (s : 'a list) (t : t), + left_zero zeror op_ => + left_distributive op_ ZR.(+) => op_ (big P F s) t = big P (fun (a : 'a) => op_ (F a) t) s. + + lemma nosmt big_distrr ['a]: + forall (op_ : t -> t -> t) (P : 'a -> bool) (F : 'a -> t) (s : 'a list) (t : t), + right_zero zeror op_ => + right_distributive op_ ZR.(+) => op_ t (big P F s) = big P (fun (a : 'a) => op_ t (F a)) s. + + lemma nosmt big_distr ['a, 'b]: + forall (op_ : t -> t -> t) (P1 : 'a -> bool) (P2 : 'b -> bool) (F1 : + 'a -> t) (s1 : 'a list) (F2 : 'b -> t) (s2 : 'b list), + commutative op_ => + left_zero zeror op_ => + left_distributive op_ ZR.(+) => + op_ (big P1 F1 s1) (big P2 F2 s2) = + big P1 (fun (a1 : 'a) => big P2 (fun (a2 : 'b) => op_ (F1 a1) (F2 a2)) s2) s1. + + lemma big_andbC ['a]: + forall (P Q : 'a -> bool) (F : 'a -> t) (s : 'a list), + big (fun (x : 'a) => P x /\ Q x) F s = big (fun (x : 'a) => Q x /\ P x) F s. + + lemma nosmt eq_big ['a]: + forall (P1 P2 : 'a -> bool) (F1 F2 : 'a -> t) (s : 'a list), + (forall (i : 'a), P1 i <=> P2 i) => (forall (i : 'a), P1 i => F1 i = F2 i) => big P1 F1 s = big P2 F2 s. + + lemma nosmt congr_big ['a]: + forall (r1 r2 : 'a list) (P1 P2 : 'a -> bool) (F1 F2 : 'a -> t), + r1 = r2 => + (forall (x : 'a), P1 x <=> P2 x) => (forall (i : 'a), P1 i => F1 i = F2 i) => big P1 F1 r1 = big P2 F2 r2. + + lemma big_hasC ['a]: forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list), ! has P s => big P F s = zeror. + + lemma big_pred0_eq ['a]: forall (F : 'a -> t) (s : 'a list), big pred0 F s = zeror. + + lemma big_pred0 ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list), (forall (i : 'a), P i <=> false) => big P F s = zeror. + + lemma big_cat ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s1 s2 : 'a list), big P F (s1 ++ s2) = big P F s1 + big P F s2. + + lemma nosmt big_catl ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s1 s2 : 'a list), ! has P s2 => big P F (s1 ++ s2) = big P F s1. + + lemma nosmt big_catr ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s1 s2 : 'a list), ! has P s1 => big P F (s1 ++ s2) = big P F s2. + + lemma nosmt big_rcons ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list) (x : 'a), + big P F (rcons s x) = if P x then big P F s + F x else big P F s. + + lemma nosmt eq_big_perm ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s1 s2 : 'a list), perm_eq s1 s2 => big P F s1 = big P F s2. + + lemma nosmt eq_big_perm_map ['a]: + forall (F : 'a -> t) (s1 s2 : 'a list), perm_eq (map F s1) (map F s2) => big predT F s1 = big predT F s2. + + lemma nosmt big_seq_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list), big P F s = big (fun (i : 'a) => (i \in s) /\ P i) F s. + + lemma nosmt big_seq ['a]: forall (F : 'a -> t) (s : 'a list), big predT F s = big (fun (i : 'a) => i \in s) F s. + + lemma nosmt big_rem ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list) (x : 'a), + x \in s => big P F s = (if P x then F x else zeror) + big P F (rem x s). + + lemma nosmt bigD1 ['a]: + forall (F : 'a -> t) (s : 'a list) (x : 'a), x \in s => uniq s => big predT F s = F x + big (predC1 x) F s. + + lemma nosmt bigD1_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list) (x : 'a), + P x => x \in s => uniq s => big P F s = F x + big (predI P (predC1 x)) F s. + + lemma nosmt bigD1_cond_if ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list) (x : 'a), + uniq s => big P F s = (if (x \in s) /\ P x then F x else zeror) + big (predI P (predC1 x)) F s. + + lemma big_split ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> t) (s : 'a list), + big P (fun (i : 'a) => F1 i + F2 i) s = big P F1 s + big P F2 s. + + lemma nosmt bigID ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (a : 'a -> bool) (s : 'a list), + big P F s = big (predI P a) F s + big (predI P (predC a)) F s. + + lemma bigU ['a]: + forall (P Q : 'a -> bool) (F : 'a -> t) (s : 'a list), + (forall (x : 'a), ! (P x /\ Q x)) => big (predU P Q) F s = big P F s + big Q F s. + + lemma bigEM ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list), big predT F s = big P F s + big (predC P) F s. + + lemma nosmt big_reindex ['a, 'b]: + forall (P : 'a -> bool) (F : 'a -> t) (f : 'b -> 'a) (f' : 'a -> 'b) (s : 'a list), + (forall (x : 'a), x \in s => f (f' x) = x) => big P F s = big (P \o f) (F \o f) (map f' s). + + lemma nosmt big_pair_pswap ['a, 'b]: + forall (p : 'a * 'b -> bool) (f : 'a * 'b -> t) (s : ('a * 'b) list), + big p f s = big (p \o pswap) (f \o pswap) (map pswap s). + + lemma nosmt eq_big_seq ['a]: + forall (F1 F2 : 'a -> t) (s : 'a list), + (forall (x : 'a), x \in s => F1 x = F2 x) => big predT F1 s = big predT F2 s. + + lemma nosmt congr_big_seq ['a]: + forall (P1 P2 : 'a -> bool) (F1 F2 : 'a -> t) (s : 'a list), + (forall (x : 'a), x \in s => P1 x = P2 x) => + (forall (x : 'a), x \in s => P1 x => P2 x => F1 x = F2 x) => big P1 F1 s = big P2 F2 s. + + lemma big1_eq ['a]: forall (P : 'a -> bool) (s : 'a list), big P (fun (_ : 'a) => zeror) s = zeror. + + lemma big1 ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list), (forall (i : 'a), P i => F i = zeror) => big P F s = zeror. + + lemma big1_seq ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list), + (forall (i : 'a), P i /\ (i \in s) => F i = zeror) => big P F s = zeror. + + lemma big_eq_idm_filter ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list), + (forall (x : 'a), ! P x => F x = zeror) => big predT F s = big P F s. + + lemma big_flatten ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (rr : 'a list list), + big P F (flatten rr) = big predT (fun (s : 'a list) => big P F s) rr. + + lemma nosmt big_pair ['a, 'b]: + forall (F : 'a * 'b -> t) (s : ('a * 'b) list), + uniq s => + big predT F s = + big predT (fun (a : 'a) => big predT F (filter (fun (xy : 'a * 'b) => xy.`1 = a) s)) (undup (unzip1 s)). + + lemma big_nseq_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (n : int) (x : 'a), + big P F (nseq n x) = if P x then iter n ((+) (F x)) zeror else zeror. + + lemma big_nseq ['a]: forall (F : 'a -> t) (n : int) (x : 'a), big predT F (nseq n x) = iter n ((+) (F x)) zeror. + + lemma big_undup ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list), + big P F s = big P (fun (a : 'a) => iter (count (pred1 a) s) ((+) (F a)) zeror) (undup s). + + lemma nosmt exchange_big ['a, 'b]: + forall (P1 : 'a -> bool) (P2 : 'b -> bool) (F : 'a -> 'b -> t) (s1 : 'a list) (s2 : 'b list), + big P1 (fun (a : 'a) => big P2 (F a) s2) s1 = big P2 (fun (b : 'b) => big P1 (fun (a : 'a) => F a b) s1) s2. + + lemma partition_big ['a, 'b]: + forall (px : 'a -> 'b) (P : 'a -> bool) (Q : 'b -> bool) (F : + 'a -> t) (s : 'a list) (s' : 'b list), + uniq s' => + (forall (x : 'a), x \in s => P x => (px x \in s') /\ Q (px x)) => + big P F s = big Q (fun (x : 'b) => big (fun (y : 'a) => P y /\ px y = x) F s) s'. + + lemma nosmt big_allpairs ['a, 'b, 'c]: + forall (f : 'a -> 'b -> 'c) (F : 'c -> t) (s : 'a list) (t : 'b list), + big predT F (allpairs f s t) = big predT (fun (x : 'a) => big predT (fun (y : 'b) => F (f x y)) t) s. + + lemma nosmt big_int_cond: + forall (m n : int) (P : int -> bool) (F : int -> t), + bigi P F m n = bigi (fun (i : int) => (m <= i && i < n) /\ P i) F m n. + + lemma nosmt big_int: + forall (m n : int) (F : int -> t), bigi predT F m n = bigi (fun (i : int) => m <= i && i < n) F m n. + + lemma nosmt congr_big_int: + forall (m1 n1 m2 n2 : int) (P1 P2 : int -> bool) (F1 F2 : int -> t), + m1 = m2 => + n1 = n2 => + (forall (i : int), m1 <= i && i < n2 => P1 i = P2 i) => + (forall (i : int), P1 i /\ m1 <= i && i < n2 => F1 i = F2 i) => bigi P1 F1 m1 n1 = bigi P2 F2 m2 n2. + + lemma nosmt eq_big_int: + forall (m n : int) (F1 F2 : int -> t), + (forall (i : int), m <= i && i < n => F1 i = F2 i) => bigi predT F1 m n = bigi predT F2 m n. + + lemma nosmt big_ltn_cond: + forall (m n : int) (P : int -> bool) (F : int -> t), + m < n => let x = bigi P F (m + 1) n in bigi P F m n = if P m then F m + x else x. + + lemma nosmt big_ltn: forall (m n : int) (F : int -> t), m < n => bigi predT F m n = F m + bigi predT F (m + 1) n. + + lemma nosmt big_geq: forall (m n : int) (P : int -> bool) (F : int -> t), n <= m => bigi P F m n = zeror. + + lemma nosmt big_addn: + forall (m n a : int) (P : int -> bool) (F : int -> t), + bigi P F (m + a) n = bigi (fun (i : int) => P (i + a)) (fun (i : int) => F (i + a)) m (n - a). + + lemma nosmt big_int1: forall (n : int) (F : int -> t), bigi predT F n (n + 1) = F n. + + lemma nosmt big_cat_int: + forall (n m p : int) (P : int -> bool) (F : int -> t), + m <= n => n <= p => bigi P F m p = bigi P F m n + bigi P F n p. + + lemma nosmt big_int_recl: + forall (n m : int) (F : int -> t), + m <= n => bigi predT F m (n + 1) = F m + bigi predT (fun (i : int) => F (i + 1)) m n. + + lemma nosmt big_int_recr: + forall (n m : int) (F : int -> t), m <= n => bigi predT F m (n + 1) = bigi predT F m n + F n. + + lemma nosmt big_int_recl_cond: + forall (n m : int) (P : int -> bool) (F : int -> t), + m <= n => + bigi P F m (n + 1) = + (if P m then F m else zeror) + bigi (fun (i : int) => P (i + 1)) (fun (i : int) => F (i + 1)) m n. + + lemma nosmt big_int_recr_cond: + forall (n m : int) (P : int -> bool) (F : int -> t), + m <= n => bigi P F m (n + 1) = bigi P F m n + if P n then F n else zeror. + + lemma nosmt bigi_split_odd_even: + forall (n : int) (F : int -> t), + 0 <= n => bigi predT (fun (i : int) => F (2 * i) + F (2 * i + 1)) 0 n = bigi predT F 0 (2 * n). + + lemma sumrD ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> t) (r : 'a list), + big P F1 r + big P F2 r = big P (fun (x : 'a) => F1 x + F2 x) r. + + lemma sumrN ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (r : 'a list), - big P F r = big P (fun (x : 'a) => - F x) r. + + lemma sumrB ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> t) (r : 'a list), + big P F1 r - big P F2 r = big P (fun (x : 'a) => F1 x - F2 x) r. + + lemma nosmt sumr_const ['a]: + forall (P : 'a -> bool) (x : t) (s : 'a list), big P (fun (_ : 'a) => x) s = intmul x (count P s). + + lemma sumri_const: forall (k : t) (n m : int), n <= m => bigi predT (fun (_ : int) => k) n m = intmul k (m - n). + + lemma sumr_undup ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list), + big P F s = big P (fun (a : 'a) => intmul (F a) (count (pred1 a) s)) (undup s). + + lemma telescoping_sum: + forall (F : int -> t) (m n : int), m <= n => F m - F n = bigi predT (fun (i : int) => F i - F (i + 1)) m n. + + lemma telescoping_sum_down: + forall (F : int -> t) (m n : int), m <= n => F n - F m = bigi predT (fun (i : int) => F (i + 1) - F i) m n. + + lemma nosmt sumr_1 ['a]: + forall (P : 'a -> bool) (s : 'a list), big P (fun (_ : 'a) => oner) s = ofint (count P s). + + lemma mulr_suml ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list) (x : t), big P F s * x = big P (fun (i : 'a) => F i * x) s. + + lemma mulr_sumr ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list) (x : t), x * big P F s = big P (fun (i : 'a) => x * F i) s. + + lemma divr_suml ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list) (x : t), big P F s / x = big P (fun (i : 'a) => F i / x) s. + + lemma nosmt sum_pair_dep ['a, 'b]: + forall (u : 'a -> t) (v : 'a -> 'b -> t) (J : ('a * 'b) list), + uniq J => + big predT (fun (ij : 'a * 'b) => u ij.`1 * v ij.`1 ij.`2) J = + big predT + (fun (i : 'a) => + u i * big predT (fun (ij : 'a * 'b) => v ij.`1 ij.`2) (filter (fun (ij : 'a * 'b) => ij.`1 = i) J)) + (undup (unzip1 J)). + + lemma nosmt sum_pair ['a, 'b]: + forall (u : 'a -> t) (v : 'b -> t) (J : ('a * 'b) list), + uniq J => + big predT (fun (ij : 'a * 'b) => u ij.`1 * v ij.`2) J = + big predT (fun (i : 'a) => u i * big predT v (unzip2 (filter (fun (ij : 'a * 'b) => ij.`1 = i) J))) + (undup (unzip1 J)). + end BAdd. + + theory BMul. + op big ['a] (P : 'a -> bool) (F : 'a -> t) (r : 'a list) : t = foldr ZR.( * ) oner (map F (filter P r)). + + abbrev bigi (P : int -> bool) (F : int -> t) (i j : int) : t = big P F (range i j). + + lemma big_nil ['a]: forall (P : 'a -> bool) (F : 'a -> t), big P F [] = oner. + + lemma big_cons ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (x : 'a) (s : 'a list), + big P F (x :: s) = if P x then F x * big P F s else big P F s. + + lemma big_consT ['a]: forall (F : 'a -> t) (x : 'a) (s : 'a list), big predT F (x :: s) = F x * big predT F s. + + lemma nosmt big_rec ['a]: + forall (K : t -> bool) (r : 'a list) (P : 'a -> bool) (F : 'a -> t), + K oner => (forall (i : 'a) (x : t), P i => K x => K (F i * x)) => K (big P F r). + + lemma nosmt big_ind ['a]: + forall (K : t -> bool) (r : 'a list) (P : 'a -> bool) (F : 'a -> t), + (forall (x y : t), K x => K y => K (x * y)) => K oner => (forall (i : 'a), P i => K (F i)) => K (big P F r). + + lemma nosmt big_rec2 ['a]: + forall (K : t -> t -> bool) (r : 'a list) (P : 'a -> bool) (F1 F2 : + 'a -> t), + K oner oner => + (forall (i : 'a) (y1 y2 : t), P i => K y1 y2 => K (F1 i * y1) (F2 i * y2)) => K (big P F1 r) (big P F2 r). + + lemma nosmt big_ind2 ['a]: + forall (K : t -> t -> bool) (r : 'a list) (P : 'a -> bool) (F1 F2 : + 'a -> t), + (forall (x1 x2 y1 y2 : t), K x1 x2 => K y1 y2 => K (x1 * y1) (x2 * y2)) => + K oner oner => (forall (i : 'a), P i => K (F1 i) (F2 i)) => K (big P F1 r) (big P F2 r). + + lemma nosmt big_endo ['a]: + forall (f : t -> t), + f oner = oner => + (forall (x y : t), f (x * y) = f x * f y) => + forall (r : 'a list) (P : 'a -> bool) (F : 'a -> t), f (big P F r) = big P (f \o F) r. + + lemma nosmt big_map ['a, 'b]: + forall (h : 'b -> 'a) (P : 'a -> bool) (F : 'a -> t) (s : 'b list), big P F (map h s) = big (P \o h) (F \o h) s. + + lemma nosmt big_mapT ['a, 'b]: + forall (h : 'b -> 'a) (F : 'a -> t) (s : 'b list), big predT F (map h s) = big predT (F \o h) s. + + lemma nosmt big_comp ['a]: + forall (h : t -> t) (P : 'a -> bool) (F : 'a -> t) (s : 'a list), + h oner = oner => morphism_2 h ZR.( * ) ZR.( * ) => h (big P F s) = big P (h \o F) s. + + lemma nosmt big_nth ['a]: + forall (x0 : 'a) (P : 'a -> bool) (F : 'a -> t) (s : 'a list), + big P F s = bigi (P \o nth x0 s) (F \o nth x0 s) 0 (size s). + + lemma nosmt big_const ['a]: + forall (P : 'a -> bool) (x : t) (s : 'a list), big P (fun (_ : 'a) => x) s = iter (count P s) (( * ) x) oner. + + lemma nosmt big_seq1 ['a]: forall (F : 'a -> t) (x : 'a), big predT F [x] = F x. + + lemma nosmt big_mkcond ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list), + big P F s = big predT (fun (i : 'a) => if P i then F i else oner) s. + + lemma nosmt big_filter ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list), big predT F (filter P s) = big P F s. + + lemma big_filter_cond ['a]: + forall (P1 P2 : 'a -> bool) (F : 'a -> t) (s : 'a list), big P2 F (filter P1 s) = big (predI P1 P2) F s. + + lemma nosmt eq_bigl ['a]: + forall (P1 P2 : 'a -> bool) (F : 'a -> t) (s : 'a list), + (forall (i : 'a), P1 i <=> P2 i) => big P1 F s = big P2 F s. + + lemma nosmt eq_bigr ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> t) (s : 'a list), + (forall (i : 'a), P i => F1 i = F2 i) => big P F1 s = big P F2 s. + + lemma nosmt big_distrl ['a]: + forall (op_ : t -> t -> t) (P : 'a -> bool) (F : 'a -> t) (s : 'a list) (t : t), + left_zero oner op_ => + left_distributive op_ ZR.( * ) => op_ (big P F s) t = big P (fun (a : 'a) => op_ (F a) t) s. + + lemma nosmt big_distrr ['a]: + forall (op_ : t -> t -> t) (P : 'a -> bool) (F : 'a -> t) (s : 'a list) (t : t), + right_zero oner op_ => + right_distributive op_ ZR.( * ) => op_ t (big P F s) = big P (fun (a : 'a) => op_ t (F a)) s. + + lemma nosmt big_distr ['a, 'b]: + forall (op_ : t -> t -> t) (P1 : 'a -> bool) (P2 : 'b -> bool) (F1 : + 'a -> t) (s1 : 'a list) (F2 : 'b -> t) (s2 : 'b list), + commutative op_ => + left_zero oner op_ => + left_distributive op_ ZR.( * ) => + op_ (big P1 F1 s1) (big P2 F2 s2) = + big P1 (fun (a1 : 'a) => big P2 (fun (a2 : 'b) => op_ (F1 a1) (F2 a2)) s2) s1. + + lemma big_andbC ['a]: + forall (P Q : 'a -> bool) (F : 'a -> t) (s : 'a list), + big (fun (x : 'a) => P x /\ Q x) F s = big (fun (x : 'a) => Q x /\ P x) F s. + + lemma nosmt eq_big ['a]: + forall (P1 P2 : 'a -> bool) (F1 F2 : 'a -> t) (s : 'a list), + (forall (i : 'a), P1 i <=> P2 i) => (forall (i : 'a), P1 i => F1 i = F2 i) => big P1 F1 s = big P2 F2 s. + + lemma nosmt congr_big ['a]: + forall (r1 r2 : 'a list) (P1 P2 : 'a -> bool) (F1 F2 : 'a -> t), + r1 = r2 => + (forall (x : 'a), P1 x <=> P2 x) => (forall (i : 'a), P1 i => F1 i = F2 i) => big P1 F1 r1 = big P2 F2 r2. + + lemma big_hasC ['a]: forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list), ! has P s => big P F s = oner. + + lemma big_pred0_eq ['a]: forall (F : 'a -> t) (s : 'a list), big pred0 F s = oner. + + lemma big_pred0 ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list), (forall (i : 'a), P i <=> false) => big P F s = oner. + + lemma big_cat ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s1 s2 : 'a list), big P F (s1 ++ s2) = big P F s1 * big P F s2. + + lemma nosmt big_catl ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s1 s2 : 'a list), ! has P s2 => big P F (s1 ++ s2) = big P F s1. + + lemma nosmt big_catr ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s1 s2 : 'a list), ! has P s1 => big P F (s1 ++ s2) = big P F s2. + + lemma nosmt big_rcons ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list) (x : 'a), + big P F (rcons s x) = if P x then big P F s * F x else big P F s. + + lemma nosmt eq_big_perm ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s1 s2 : 'a list), perm_eq s1 s2 => big P F s1 = big P F s2. + + lemma nosmt eq_big_perm_map ['a]: + forall (F : 'a -> t) (s1 s2 : 'a list), perm_eq (map F s1) (map F s2) => big predT F s1 = big predT F s2. + + lemma nosmt big_seq_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list), big P F s = big (fun (i : 'a) => (i \in s) /\ P i) F s. + + lemma nosmt big_seq ['a]: forall (F : 'a -> t) (s : 'a list), big predT F s = big (fun (i : 'a) => i \in s) F s. + + lemma nosmt big_rem ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list) (x : 'a), + x \in s => big P F s = (if P x then F x else oner) * big P F (rem x s). + + lemma nosmt bigD1 ['a]: + forall (F : 'a -> t) (s : 'a list) (x : 'a), x \in s => uniq s => big predT F s = F x * big (predC1 x) F s. + + lemma nosmt bigD1_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list) (x : 'a), + P x => x \in s => uniq s => big P F s = F x * big (predI P (predC1 x)) F s. + + lemma nosmt bigD1_cond_if ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list) (x : 'a), + uniq s => big P F s = (if (x \in s) /\ P x then F x else oner) * big (predI P (predC1 x)) F s. + + lemma big_split ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> t) (s : 'a list), + big P (fun (i : 'a) => F1 i * F2 i) s = big P F1 s * big P F2 s. + + lemma nosmt bigID ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (a : 'a -> bool) (s : 'a list), + big P F s = big (predI P a) F s * big (predI P (predC a)) F s. + + lemma bigU ['a]: + forall (P Q : 'a -> bool) (F : 'a -> t) (s : 'a list), + (forall (x : 'a), ! (P x /\ Q x)) => big (predU P Q) F s = big P F s * big Q F s. + + lemma bigEM ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list), big predT F s = big P F s * big (predC P) F s. + + lemma nosmt big_reindex ['a, 'b]: + forall (P : 'a -> bool) (F : 'a -> t) (f : 'b -> 'a) (f' : 'a -> 'b) (s : 'a list), + (forall (x : 'a), x \in s => f (f' x) = x) => big P F s = big (P \o f) (F \o f) (map f' s). + + lemma nosmt big_pair_pswap ['a, 'b]: + forall (p : 'a * 'b -> bool) (f : 'a * 'b -> t) (s : ('a * 'b) list), + big p f s = big (p \o pswap) (f \o pswap) (map pswap s). + + lemma nosmt eq_big_seq ['a]: + forall (F1 F2 : 'a -> t) (s : 'a list), + (forall (x : 'a), x \in s => F1 x = F2 x) => big predT F1 s = big predT F2 s. + + lemma nosmt congr_big_seq ['a]: + forall (P1 P2 : 'a -> bool) (F1 F2 : 'a -> t) (s : 'a list), + (forall (x : 'a), x \in s => P1 x = P2 x) => + (forall (x : 'a), x \in s => P1 x => P2 x => F1 x = F2 x) => big P1 F1 s = big P2 F2 s. + + lemma big1_eq ['a]: forall (P : 'a -> bool) (s : 'a list), big P (fun (_ : 'a) => oner) s = oner. + + lemma big1 ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list), (forall (i : 'a), P i => F i = oner) => big P F s = oner. + + lemma big1_seq ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list), + (forall (i : 'a), P i /\ (i \in s) => F i = oner) => big P F s = oner. + + lemma big_eq_idm_filter ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list), + (forall (x : 'a), ! P x => F x = oner) => big predT F s = big P F s. + + lemma big_flatten ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (rr : 'a list list), + big P F (flatten rr) = big predT (fun (s : 'a list) => big P F s) rr. + + lemma nosmt big_pair ['a, 'b]: + forall (F : 'a * 'b -> t) (s : ('a * 'b) list), + uniq s => + big predT F s = + big predT (fun (a : 'a) => big predT F (filter (fun (xy : 'a * 'b) => xy.`1 = a) s)) (undup (unzip1 s)). + + lemma big_nseq_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (n : int) (x : 'a), + big P F (nseq n x) = if P x then iter n (( * ) (F x)) oner else oner. + + lemma big_nseq ['a]: forall (F : 'a -> t) (n : int) (x : 'a), big predT F (nseq n x) = iter n (( * ) (F x)) oner. + + lemma big_undup ['a]: + forall (P : 'a -> bool) (F : 'a -> t) (s : 'a list), + big P F s = big P (fun (a : 'a) => iter (count (pred1 a) s) (( * ) (F a)) oner) (undup s). + + lemma nosmt exchange_big ['a, 'b]: + forall (P1 : 'a -> bool) (P2 : 'b -> bool) (F : 'a -> 'b -> t) (s1 : 'a list) (s2 : 'b list), + big P1 (fun (a : 'a) => big P2 (F a) s2) s1 = big P2 (fun (b : 'b) => big P1 (fun (a : 'a) => F a b) s1) s2. + + lemma partition_big ['a, 'b]: + forall (px : 'a -> 'b) (P : 'a -> bool) (Q : 'b -> bool) (F : + 'a -> t) (s : 'a list) (s' : 'b list), + uniq s' => + (forall (x : 'a), x \in s => P x => (px x \in s') /\ Q (px x)) => + big P F s = big Q (fun (x : 'b) => big (fun (y : 'a) => P y /\ px y = x) F s) s'. + + lemma nosmt big_allpairs ['a, 'b, 'c]: + forall (f : 'a -> 'b -> 'c) (F : 'c -> t) (s : 'a list) (t : 'b list), + big predT F (allpairs f s t) = big predT (fun (x : 'a) => big predT (fun (y : 'b) => F (f x y)) t) s. + + lemma nosmt big_int_cond: + forall (m n : int) (P : int -> bool) (F : int -> t), + bigi P F m n = bigi (fun (i : int) => (m <= i && i < n) /\ P i) F m n. + + lemma nosmt big_int: + forall (m n : int) (F : int -> t), bigi predT F m n = bigi (fun (i : int) => m <= i && i < n) F m n. + + lemma nosmt congr_big_int: + forall (m1 n1 m2 n2 : int) (P1 P2 : int -> bool) (F1 F2 : int -> t), + m1 = m2 => + n1 = n2 => + (forall (i : int), m1 <= i && i < n2 => P1 i = P2 i) => + (forall (i : int), P1 i /\ m1 <= i && i < n2 => F1 i = F2 i) => bigi P1 F1 m1 n1 = bigi P2 F2 m2 n2. + + lemma nosmt eq_big_int: + forall (m n : int) (F1 F2 : int -> t), + (forall (i : int), m <= i && i < n => F1 i = F2 i) => bigi predT F1 m n = bigi predT F2 m n. + + lemma nosmt big_ltn_cond: + forall (m n : int) (P : int -> bool) (F : int -> t), + m < n => let x = bigi P F (m + 1) n in bigi P F m n = if P m then F m * x else x. + + lemma nosmt big_ltn: forall (m n : int) (F : int -> t), m < n => bigi predT F m n = F m * bigi predT F (m + 1) n. + + lemma nosmt big_geq: forall (m n : int) (P : int -> bool) (F : int -> t), n <= m => bigi P F m n = oner. + + lemma nosmt big_addn: + forall (m n a : int) (P : int -> bool) (F : int -> t), + bigi P F (m + a) n = bigi (fun (i : int) => P (i + a)) (fun (i : int) => F (i + a)) m (n - a). + + lemma nosmt big_int1: forall (n : int) (F : int -> t), bigi predT F n (n + 1) = F n. + + lemma nosmt big_cat_int: + forall (n m p : int) (P : int -> bool) (F : int -> t), + m <= n => n <= p => bigi P F m p = bigi P F m n * bigi P F n p. + + lemma nosmt big_int_recl: + forall (n m : int) (F : int -> t), + m <= n => bigi predT F m (n + 1) = F m * bigi predT (fun (i : int) => F (i + 1)) m n. + + lemma nosmt big_int_recr: + forall (n m : int) (F : int -> t), m <= n => bigi predT F m (n + 1) = bigi predT F m n * F n. + + lemma nosmt big_int_recl_cond: + forall (n m : int) (P : int -> bool) (F : int -> t), + m <= n => + bigi P F m (n + 1) = + (if P m then F m else oner) * bigi (fun (i : int) => P (i + 1)) (fun (i : int) => F (i + 1)) m n. + + lemma nosmt big_int_recr_cond: + forall (n m : int) (P : int -> bool) (F : int -> t), + m <= n => bigi P F m (n + 1) = bigi P F m n * if P n then F n else oner. + + lemma nosmt bigi_split_odd_even: + forall (n : int) (F : int -> t), + 0 <= n => bigi predT (fun (i : int) => F (2 * i) * F (2 * i + 1)) 0 n = bigi predT F 0 (2 * n). + end BMul. + + lemma mulr_big ['a]: + forall (P Q : 'a -> bool) (f g : 'a -> t) (r s : 'a list), + (big P f r)%BAdd * (big Q g s)%BAdd = + (big P (fun (x : 'a) => (big Q (fun (y : 'a) => f x * g y) s)%BAdd) r)%BAdd. + + lemma subrXX: + forall (x y : t) (n : int), + 0 <= n => exp x n - exp y n = (x - y) * (bigi predT (fun (i : int) => exp x (n - 1 - i) * exp y i) 0 n)%BAdd. + + lemma nosmt mulr_const_cond ['a]: + forall (p : 'a -> bool) (s : 'a list) (c : t), (big p (fun (_ : 'a) => c) s)%BMul = exp c (count p s). + + lemma nosmt mulr_const ['a]: forall (s : 'a list) (c : t), (big predT (fun (_ : 'a) => c) s)%BMul = exp c (size s). + end Big. + + type R = t. + + op size : int. + + axiom ge0_size: 0 <= size. + + hint solve 0 : ge0_size. + + type vector. + + theory Vector. + op tofunv : vector -> int -> R. + + op offunv : (int -> R) -> vector. + + op prevector (f : int -> R) : bool = forall (i : int), ! (0 <= i && i < size) => f i = zeror. + + op vclamp (v : int -> R) (i : int) : R = if 0 <= i && i < size then v i else zeror. + + axiom tofunv_prevector: forall (v : vector), prevector (tofunv v). + + axiom tofunvK: cancel tofunv offunv. + + axiom offunvK: forall (v : int -> R), tofunv (offunv v) = vclamp v. + + op "_.[_]" (v : vector) (i : int) : R = tofunv v i. + + lemma offunvE: forall (v : int -> R) (i : int), 0 <= i && i < size => (offunv v).[i] = v i. + + lemma getv_out: forall (v : vector) (i : int), ! (0 <= i && i < size) => v.[i] = zeror. + + lemma eq_vectorP: forall (v1 v2 : vector), v1 = v2 <=> forall (i : int), 0 <= i && i < size => v1.[i] = v2.[i]. + + lemma vectorW: + forall (P : vector -> bool), (forall (f : int -> R), prevector f => P (offunv f)) => forall (v : vector), P v. + + op vectc (c : R) : vector = offunv (fun (_ : int) => c). + + abbrev zerov : vector = vectc zeror. + + lemma offunCE: forall (c : R) (i : int), 0 <= i && i < size => (vectc c).[i] = c. + + lemma offun0E: forall (i : int), zerov.[i] = zeror. + + hint simplify. + + op (+) (v1 v2 : vector) : vector = offunv (fun (i : int) => v1.[i] + v2.[i]). + + op [-] (v : vector) : vector = offunv (fun (i : int) => - v.[i]). + + lemma offunD: forall (v1 v2 : vector) (i : int), (v1 + v2).[i] = v1.[i] + v2.[i]. + + hint simplify. + + lemma offunN: forall (v : vector) (i : int), (-v).[i] = - v.[i]. + + hint simplify. + + theory ZModule. + lemma nosmt addrA: associative Vector.(+). + + lemma nosmt addrC: commutative Vector.(+). + + lemma nosmt add0r: left_id zerov Vector.(+). + + lemma nosmt addNr: left_inverse zerov Vector.[-] Vector.(+). + + theory AddMonoid. + lemma addmA: associative Vector.(+). + + lemma addmC: commutative Vector.(+). + + lemma add0m: left_id zerov Vector.(+). + + lemma addm0: right_id zerov Vector.(+). + + lemma addmCA: left_commutative Vector.(+). + + lemma addmAC: right_commutative Vector.(+). + + lemma addmACA: interchange Vector.(+) Vector.(+). + + lemma iteropE: forall (n : int) (x : vector), iterop n Vector.(+) x zerov = iter n ((+) x) zerov. + end AddMonoid. + + abbrev (-) (x y : vector) : vector = x + -y. + + lemma nosmt addr0: right_id zerov Vector.(+). + + lemma nosmt addrN: right_inverse zerov Vector.[-] Vector.(+). + + lemma nosmt addrCA: left_commutative Vector.(+). + + lemma nosmt addrAC: right_commutative Vector.(+). + + lemma nosmt addrACA: interchange Vector.(+) Vector.(+). + + lemma nosmt subrr: forall (x : vector), x - x = zerov. + + lemma nosmt addKr: left_loop Vector.[-] Vector.(+). + + lemma nosmt addNKr: rev_left_loop Vector.[-] Vector.(+). + + lemma nosmt addrK: right_loop Vector.[-] Vector.(+). + + lemma nosmt addrNK: rev_right_loop Vector.[-] Vector.(+). + + lemma nosmt subrK: forall (x y : vector), x - y + y = x. + + lemma nosmt addrI: right_injective Vector.(+). + + lemma nosmt addIr: left_injective Vector.(+). + + lemma nosmt opprK: involutive Vector.[-]. + + lemma oppr_inj: injective Vector.[-]. + + lemma nosmt oppr0: - zerov = zerov. + + lemma oppr_eq0: forall (x : vector), -x = zerov <=> x = zerov. + + lemma nosmt subr0: forall (x : vector), x - zerov = x. + + lemma nosmt sub0r: forall (x : vector), zerov - x = -x. + + lemma nosmt opprD: forall (x y : vector), - (x + y) = (-x) - y. + + lemma nosmt opprB: forall (x y : vector), - (x - y) = y - x. + + lemma nosmt subrACA: interchange (fun (x y : vector) => x - y) Vector.(+). + + lemma nosmt subr_eq: forall (x y z : vector), x - z = y <=> x = y + z. + + lemma nosmt subr_eq0: forall (x y : vector), x - y = zerov <=> x = y. + + lemma nosmt addr_eq0: forall (x y : vector), x + y = zerov <=> x = -y. + + lemma nosmt eqr_opp: forall (x y : vector), -x = -y <=> x = y. + + lemma eqr_oppLR: forall (x y : vector), -x = y <=> x = -y. + + lemma nosmt eqr_sub: forall (x y z t : vector), x - y = z - t <=> x + t = z + y. + + lemma subr_add2r: forall (z x y : vector), x + z - (y + z) = x - y. + + op intmul (x : vector) (n : int) : vector = + if n < 0 then - iterop (-n) Vector.(+) x zerov else iterop n Vector.(+) x zerov. + + lemma intmulpE: forall (z : vector) (c : int), 0 <= c => intmul z c = iterop c Vector.(+) z zerov. + + lemma mulr0z: forall (x : vector), intmul x 0 = zerov. + + lemma mulr1z: forall (x : vector), intmul x 1 = x. + + lemma mulr2z: forall (x : vector), intmul x 2 = x + x. + + lemma mulrNz: forall (x : vector) (n : int), intmul x (-n) = - intmul x n. + + lemma mulrS: forall (x : vector) (n : int), 0 <= n => intmul x (n + 1) = x + intmul x n. + + lemma mulNrz: forall (x : vector) (n : int), intmul (-x) n = - intmul x n. + + lemma mulNrNz: forall (x : vector) (n : int), intmul (-x) (-n) = intmul x n. + + lemma mulrSz: forall (x : vector) (n : int), intmul x (n + 1) = x + intmul x n. + + lemma mulrDz: forall (x : vector) (n m : int), intmul x (n + m) = intmul x n + intmul x m. + end ZModule. + + lemma offunB: forall (v1 v2 : vector) (i : int), (v1 - v2).[i] = v1.[i] - v2.[i]. + + op dotp (v1 v2 : vector) : t = bigi predT (fun (i : int) => v1.[i] * v2.[i]) 0 size. + + lemma dotpC: commutative dotp. + + lemma dotpDr: forall (v1 v2 v3 : vector), dotp v1 (v2 + v3) = dotp v1 v2 + dotp v1 v3. + + lemma dotpDl: forall (v1 v2 v3 : vector), dotp (v1 + v2) v3 = dotp v1 v3 + dotp v2 v3. + + op scalev (a : t) (v : vector) : vector = offunv (fun (i : int) => a * v.[i]). + + abbrev ( ** ) : t -> vector -> vector = scalev. + + lemma scalevE: forall (a : t) (v : vector) (i : int), (a ** v).[i] = a * v.[i]. + + lemma scalevDl: forall (a b : t) (v : vector), (a + b) ** v = a ** v + b ** v. + + lemma scalevDr: forall (a : t) (v w : vector), a ** (v + w) = a ** v + a ** w. + + lemma scalevA: forall (a b : t) (v : vector), a * b ** v = a ** (b ** v). + + lemma scalevAC: forall (a b : t) (v : vector), a ** (b ** v) = b ** (a ** v). + end Vector. + + export Vector. + + theory Matrix. + type matrix. + + op tofunm : matrix -> int -> int -> R. + + op offunm : (int -> int -> R) -> matrix. + + abbrev mrange (i j : int) : bool = (0 <= i && i < size) /\ 0 <= j && j < size. + + lemma nosmt mrangeL: forall (i j : int), mrange i j => 0 <= i && i < size. + + lemma nosmt mrangeR: forall (i j : int), mrange i j => 0 <= j && j < size. + + lemma nosmt mrangeC: forall (i j : int), mrange i j = mrange j i. + + op prematrix (f : int -> int -> R) : bool = forall (i j : int), ! mrange i j => f i j = zeror. + + op mclamp (m : int -> int -> R) (i j : int) : R = if mrange i j then m i j else zeror. + + axiom tofunm_prematrix: forall (m : matrix), prematrix (tofunm m). + + axiom tofunmK: cancel tofunm offunm. + + axiom offunmK: forall (m : int -> int -> R), tofunm (offunm m) = mclamp m. + + op "_.[_]" (m : matrix) (ij : int * int) : R = tofunm m ij.`1 ij.`2. + + lemma offunmE: forall (m : int -> int -> R) (i j : int), mrange i j => (offunm m).[i, j] = m i j. + + lemma getm_out: forall (m : matrix) (i j : int), ! mrange i j => m.[i, j] = zeror. + + lemma getm_outL: forall (m : matrix) (i j : int), ! (0 <= i && i < size) => m.[i, j] = zeror. + + lemma getm_outR: forall (m : matrix) (i j : int), ! (0 <= j && j < size) => m.[i, j] = zeror. + + lemma eq_matrixP: forall (m1 m2 : matrix), m1 = m2 <=> forall (i j : int), mrange i j => m1.[i, j] = m2.[i, j]. + + lemma matrixW: + forall (P : matrix -> bool), + (forall (f : int -> int -> R), prematrix f => P (offunm f)) => forall (v : matrix), P v. + + op matrixc (c : R) : matrix = offunm (fun (_ _ : int) => c). + + op diagmx (v : vector) : matrix = offunm (fun (i j : int) => if i = j then v.[i] else zeror). + + abbrev diagc (c : R) : matrix = diagmx (vectc c). + + abbrev zerom : matrix = matrixc zeror. + + abbrev onem : matrix = diagc oner. + + lemma offunCE: forall (c : R) (i j : int), mrange i j => (matrixc c).[i, j] = c. + + lemma diagmxE: forall (v : vector) (i j : int), (diagmx v).[i, j] = if i = j then v.[i] else zeror. + + lemma offun0E: forall (i j : int), zerom.[i, j] = zeror. + + lemma offun1E: forall (i j : int), mrange i j => onem.[i, j] = if i = j then oner else zeror. + + lemma offun1_neqE: forall (i j : int), i <> j => onem.[i, j] = zeror. + + hint simplify. + + op (+) (m1 m2 : matrix) : matrix = offunm (fun (i j : int) => m1.[i, j] + m2.[i, j]). + + op [-] (m : matrix) : matrix = offunm (fun (i j : int) => - m.[i, j]). + + lemma offunD: forall (m1 m2 : matrix) (i j : int), (m1 + m2).[i, j] = m1.[i, j] + m2.[i, j]. + + hint simplify. + + lemma offunN: forall (m : matrix) (i j : int), (-m).[i, j] = - m.[i, j]. + + hint simplify. + + theory ZModule. + lemma nosmt addrA: associative Matrix.(+). + + lemma nosmt addrC: commutative Matrix.(+). + + lemma nosmt add0r: left_id zerom Matrix.(+). + + lemma nosmt addNr: left_inverse zerom Matrix.[-] Matrix.(+). + + theory AddMonoid. + lemma addmA: associative Matrix.(+). + + lemma addmC: commutative Matrix.(+). + + lemma add0m: left_id zerom Matrix.(+). + + lemma addm0: right_id zerom Matrix.(+). + + lemma addmCA: left_commutative Matrix.(+). + + lemma addmAC: right_commutative Matrix.(+). + + lemma addmACA: interchange Matrix.(+) Matrix.(+). + + lemma iteropE: forall (n : int) (x : matrix), iterop n Matrix.(+) x zerom = iter n ((+) x) zerom. + end AddMonoid. + + abbrev (-) (x y : matrix) : matrix = x + -y. + + lemma nosmt addr0: right_id zerom Matrix.(+). + + lemma nosmt addrN: right_inverse zerom Matrix.[-] Matrix.(+). + + lemma nosmt addrCA: left_commutative Matrix.(+). + + lemma nosmt addrAC: right_commutative Matrix.(+). + + lemma nosmt addrACA: interchange Matrix.(+) Matrix.(+). + + lemma nosmt subrr: forall (x : matrix), x - x = zerom. + + lemma nosmt addKr: left_loop Matrix.[-] Matrix.(+). + + lemma nosmt addNKr: rev_left_loop Matrix.[-] Matrix.(+). + + lemma nosmt addrK: right_loop Matrix.[-] Matrix.(+). + + lemma nosmt addrNK: rev_right_loop Matrix.[-] Matrix.(+). + + lemma nosmt subrK: forall (x y : matrix), x - y + y = x. + + lemma nosmt addrI: right_injective Matrix.(+). + + lemma nosmt addIr: left_injective Matrix.(+). + + lemma nosmt opprK: involutive Matrix.[-]. + + lemma oppr_inj: injective Matrix.[-]. + + lemma nosmt oppr0: - zerom = zerom. + + lemma oppr_eq0: forall (x : matrix), -x = zerom <=> x = zerom. + + lemma nosmt subr0: forall (x : matrix), x - zerom = x. + + lemma nosmt sub0r: forall (x : matrix), zerom - x = -x. + + lemma nosmt opprD: forall (x y : matrix), - (x + y) = (-x) - y. + + lemma nosmt opprB: forall (x y : matrix), - (x - y) = y - x. + + lemma nosmt subrACA: interchange (fun (x y : matrix) => x - y) Matrix.(+). + + lemma nosmt subr_eq: forall (x y z : matrix), x - z = y <=> x = y + z. + + lemma nosmt subr_eq0: forall (x y : matrix), x - y = zerom <=> x = y. + + lemma nosmt addr_eq0: forall (x y : matrix), x + y = zerom <=> x = -y. + + lemma nosmt eqr_opp: forall (x y : matrix), -x = -y <=> x = y. + + lemma eqr_oppLR: forall (x y : matrix), -x = y <=> x = -y. + + lemma nosmt eqr_sub: forall (x y z t : matrix), x - y = z - t <=> x + t = z + y. + + lemma subr_add2r: forall (z x y : matrix), x + z - (y + z) = x - y. + + op intmul (x : matrix) (n : int) : matrix = + if n < 0 then - iterop (-n) Matrix.(+) x zerom else iterop n Matrix.(+) x zerom. + + lemma intmulpE: forall (z : matrix) (c : int), 0 <= c => intmul z c = iterop c Matrix.(+) z zerom. + + lemma mulr0z: forall (x : matrix), intmul x 0 = zerom. + + lemma mulr1z: forall (x : matrix), intmul x 1 = x. + + lemma mulr2z: forall (x : matrix), intmul x 2 = x + x. + + lemma mulrNz: forall (x : matrix) (n : int), intmul x (-n) = - intmul x n. + + lemma mulrS: forall (x : matrix) (n : int), 0 <= n => intmul x (n + 1) = x + intmul x n. + + lemma mulNrz: forall (x : matrix) (n : int), intmul (-x) n = - intmul x n. + + lemma mulNrNz: forall (x : matrix) (n : int), intmul (-x) (-n) = intmul x n. + + lemma mulrSz: forall (x : matrix) (n : int), intmul x (n + 1) = x + intmul x n. + + lemma mulrDz: forall (x : matrix) (n m : int), intmul x (n + m) = intmul x n + intmul x m. + end ZModule. + + lemma offunB: forall (m1 m2 : matrix) (i j : int), (m1 - m2).[i, j] = m1.[i, j] - m2.[i, j]. + + op trace (m : matrix) : t = bigi predT (fun (i : int) => m.[i, i]) 0 size. + + op trmx (m : matrix) : matrix = offunm (fun (i j : int) => m.[j, i]). + + lemma trmxE: forall (m : matrix) (i j : int), (trmx m).[i, j] = m.[j, i]. + + lemma trmxK: forall (m : matrix), trmx (trmx m) = m. + + lemma trmx1: trmx onem = onem. + + lemma trmxD: forall (m1 m2 : matrix), trmx (m1 + m2) = trmx m1 + trmx m2. + + lemma trace_trmx: forall (m : matrix), trace (trmx m) = trace m. + + op ( * ) (m1 m2 : matrix) : matrix = + offunm (fun (i j : int) => bigi predT (fun (k : int) => m1.[i, k] * m2.[k, j]) 0 size). + + lemma offunM: + forall (m1 m2 : matrix) (i j : int), + (m1 * m2).[i, j] = bigi predT (fun (k : int) => m1.[i, k] * m2.[k, j]) 0 size. + + hint simplify. + + lemma mulmx1: right_id onem Matrix.( * ). + + lemma mul1mx: left_id onem Matrix.( * ). + + lemma mulmxDl: forall (m1 m2 m : matrix), (m1 + m2) * m = m1 * m + m2 * m. + + lemma mulmxDr: forall (m1 m2 m : matrix), m * (m1 + m2) = m * m1 + m * m2. + + lemma mulmxA: associative Matrix.( * ). + + lemma trmxM: forall (m1 m2 : matrix), trmx (m1 * m2) = trmx m2 * trmx m1. + + op ( *^ ) (m : matrix) (v : vector) : vector = + offunv (fun (i : int) => bigi predT (fun (j : int) => m.[i, j] * v.[j]) 0 size). + + op ( ^* ) (v : vector) (m : matrix) : vector = + offunv (fun (j : int) => bigi predT (fun (i : int) => v.[i] * m.[i, j]) 0 size). + + lemma mulmxTv: forall (m : matrix) (v : vector), trmx m *^ v = v ^* m. + + lemma mulmxv0: forall (m : matrix), m *^ zerov = zerov. + + lemma mulmx1v: forall (v : vector), onem *^ v = v. + + lemma mulmxvDl: forall (m1 m2 : matrix) (v : vector), (m1 + m2) *^ v = m1 *^ v + m2 *^ v. + + lemma mulmxvDr: forall (m : matrix) (v1 v2 : vector), m *^ (v1 + v2) = m *^ v1 + m *^ v2. + + lemma mulmxvA: forall (m1 m2 : matrix) (v : vector), m1 * m2 *^ v = m1 *^ (m2 *^ v). + + lemma mulvmxT: forall (v : vector) (m : matrix), v ^* trmx m = m *^ v. + + lemma mulv0mx: forall (m : matrix), zerov ^* m = zerov. + + lemma mulvmx1: forall (v : vector), v ^* onem = v. + + lemma mulvmxDr: forall (v : vector) (m1 m2 : matrix), v ^* (m1 + m2) = v ^* m1 + v ^* m2. + + lemma mulvmxDl: forall (v1 v2 : vector) (m : matrix), (v1 + v2) ^* m = v1 ^* m + v2 ^* m. + + lemma mulvmxA: forall (v : vector) (m1 m2 : matrix), v ^* m1 ^* m2 = v ^* (m1 * m2). + + lemma mulmxvE: + forall (m : matrix) (v : vector) (i : int), (m *^ v).[i] = bigi predT (fun (j : int) => m.[i, j] * v.[j]) 0 size. + + lemma mulvmxE: + forall (m : matrix) (v : vector) (j : int), (v ^* m).[j] = bigi predT (fun (i : int) => v.[i] * m.[i, j]) 0 size. + + op colmx (v : vector) : matrix = offunm (fun (i _ : int) => v.[i]). + + op rowmx (v : vector) : matrix = offunm (fun (_ j : int) => v.[j]). + + lemma colmxT: forall (v : vector), trmx (colmx v) = rowmx v. + + lemma rowmxT: forall (v : vector), trmx (rowmx v) = colmx v. + + lemma colmxE: forall (v : vector) (i j : int), 0 <= j && j < size => (colmx v).[i, j] = v.[i]. + + lemma rowmxE: forall (v : vector) (i j : int), 0 <= i && i < size => (rowmx v).[i, j] = v.[j]. + + lemma colmx_mulmxv: forall (m : matrix) (v : vector), colmx (m *^ v) = m * colmx v. + + lemma rowmx_mulvmx: forall (v : vector) (m : matrix), rowmx (v ^* m) = rowmx v * m. + + lemma mulmx_diag: + forall (v1 v2 : vector), diagmx v1 * diagmx v2 = diagmx (offunv (fun (i : int) => v1.[i] * v2.[i])). + + lemma dotp_tr: forall (v1 v2 : vector), dotp v1 v2 = trace (diagmx v1 * diagmx v2). + + lemma dotp_mulmxv: forall (m : matrix) (v1 v2 : vector), dotp (m *^ v1) v2 = dotp v1 (v2 ^* m). + + op dvector (d : R distr) : vector distr = + dmap (djoin (nseq size d)) (fun (xs : R list) => offunv (nth witness xs)). + + lemma dvector1E: + forall (d : R distr) (v : vector), mu1 (dvector d) v = (bigi predT (fun (i : int) => mu1 d v.[i]) 0 size)%BRM. + + lemma dvector_uni: forall (d : R distr), is_uniform d => is_uniform (dvector d). + + lemma dvector_ll: forall (d : R distr), is_lossless d => is_lossless (dvector d). + + lemma dvector_fu: forall (d : R distr), is_full d => is_full (dvector d). + + lemma dvector_funi: forall (d : R distr), is_full d => is_uniform d => is_funiform (dvector d). + + op dmatrix (d : R distr) : matrix distr = + dmap (djoin (nseq size (djoin (nseq size d)))) + (fun (xs : R list list) => offunm (fun (i j : int) => nth witness (nth witness xs j) i)). + + lemma dmatrix1E: + forall (d : R distr) (m : matrix), + mu1 (dmatrix d) m = + (bigi predT (fun (i : int) => (bigi predT (fun (j : int) => mu1 d m.[i, j]) 0 size)%BRM) 0 size)%BRM. + + lemma dmatrix_dvector: + forall (d : R distr), + dmatrix d = + dmap (djoin (nseq size (dvector d))) + (fun (vs : vector list) => offunm (fun (i j : int) => (nth witness vs j).[i])). + + lemma dmatrix_uni: forall (d : R distr), is_uniform d => is_uniform (dmatrix d). + + lemma dmatrix_ll: forall (d : R distr), is_lossless d => is_lossless (dmatrix d). + + lemma dmatrix_fu: forall (d : R distr), is_full d => is_full (dmatrix d). + + lemma dmatrix_funi: forall (d : R distr), is_full d => is_uniform d => is_funiform (dmatrix d). + end Matrix. + + export Matrix. +end Matrix. + +[/] [0011] 0.5% (-1.0B / [frag -1.0B]) [-] [0026] 1.2% (-1.0B / [frag -1.0B]) [\] [0028] 1.3% (-1.0B / [frag -1.0B]) [|] [0030] 1.3% (-1.0B / [frag -1.0B]) [/] [0030] 1.4% (-1.0B / [frag -1.0B]) [-] [0032] 1.4% (-1.0B / [frag -1.0B]) [\] [0033] 1.4% (-1.0B / [frag -1.0B]) [|] [0034] 1.4% (-1.0B / [frag -1.0B]) [/] [0034] 1.5% (-1.0B / [frag -1.0B]) [-] [0036] 1.5% (-1.0B / [frag -1.0B]) [\] [0037] 1.5% (-1.0B / [frag -1.0B]) [|] [0038] 1.6% (-1.0B / [frag -1.0B]) [/] [0040] 1.6% (-1.0B / [frag -1.0B]) [-] [0042] 1.7% (-1.0B / [frag -1.0B]) [\] [0043] 1.8% (-1.0B / [frag -1.0B]) [|] [0044] 1.8% (-1.0B / [frag -1.0B]) [/] [0045] 1.9% (-1.0B / [frag -1.0B]) [-] [0046] 2.0% (-1.0B / [frag -1.0B]) [\] [0047] 2.0% (-1.0B / [frag -1.0B]) [|] [0048] 2.0% (-1.0B / [frag -1.0B]) [/] [0048] 2.1% (-1.0B / [frag -1.0B]) [-] [0050] 2.1% (-1.0B / [frag -1.0B]) [\] [0052] 2.2% (-1.0B / [frag -1.0B]) [|] [0055] 2.3% (-1.0B / [frag -1.0B]) [/] [0057] 2.4% (-1.0B / [frag -1.0B]) [-] [0059] 2.4% (-1.0B / [frag -1.0B]) [\] [0060] 2.4% (-1.0B / [frag -1.0B]) [|] [0061] 2.5% (-1.0B / [frag -1.0B]) [/] [0062] 2.5% (-1.0B / [frag -1.0B]) [-] [0063] 2.5% (-1.0B / [frag -1.0B]) [\] [0067] 2.5% (-1.0B / [frag -1.0B]) [|] [0074] 2.8% (-1.0B / [frag -1.0B]) [/] [0082] 2.9% (-1.0B / [frag -1.0B]) [-] [0089] 3.1% (-1.0B / [frag -1.0B]) [\] [0094] 3.3% (-1.0B / [frag -1.0B]) [|] [0142] 4.8% (-1.0B / [frag -1.0B]) [/] [0147] 5.0% (-1.0B / [frag -1.0B]) [-] [0150] 5.2% (-1.0B / [frag -1.0B]) [\] [0150] 5.2% (-1.0B / [frag -1.0B]) [|] [0152] 5.3% (-1.0B / [frag -1.0B]) [/] [0153] 5.3% (-1.0B / [frag -1.0B]) [-] [0158] 5.6% (-1.0B / [frag -1.0B]) [\] [0158] 5.6% (-1.0B / [frag -1.0B]) [|] [0160] 5.7% (-1.0B / [frag -1.0B]) [/] [0161] 5.7% (-1.0B / [frag -1.0B]) [-] [0162] 5.8% (-1.0B / [frag -1.0B]) [\] [0162] 5.8% (-1.0B / [frag -1.0B]) [|] [0164] 5.9% (-1.0B / [frag -1.0B]) [/] [0165] 6.0% (-1.0B / [frag -1.0B]) [-] [0166] 6.0% (-1.0B / [frag -1.0B]) [\] [0170] 6.2% (-1.0B / [frag -1.0B]) [|] [0171] 6.2% (-1.0B / [frag -1.0B]) [/] [0172] 6.4% (-1.0B / [frag -1.0B]) [-] [0173] 6.4% (-1.0B / [frag -1.0B]) [\] [0174] 6.4% (-1.0B / [frag -1.0B]) [|] [0176] 6.4% (-1.0B / [frag -1.0B]) [/] [0232] 8.9% (-1.0B / [frag -1.0B]) [-] [0234] 9.0% (-1.0B / [frag -1.0B]) [\] [0238] 9.2% (-1.0B / [frag -1.0B]) [|] [0238] 9.2% (-1.0B / [frag -1.0B]) [/] [0240] 9.3% (-1.0B / [frag -1.0B]) [-] [0241] 9.3% (-1.0B / [frag -1.0B]) [\] [0242] 9.3% (-1.0B / [frag -1.0B]) [|] [0247] 9.7% (-1.0B / [frag -1.0B]) [/] [0252] 10.1% (-1.0B / [frag -1.0B]) [-] [0255] 10.2% (-1.0B / [frag -1.0B]) [\] [0256] 10.2% (-1.0B / [frag -1.0B]) [|] [0257] 10.3% (-1.0B / [frag -1.0B]) [/] [0257] 10.3% (-1.0B / [frag -1.0B]) [-] [0258] 10.4% (-1.0B / [frag -1.0B]) [\] [0259] 10.4% (-1.0B / [frag -1.0B]) [|] [0260] 10.5% (-1.0B / [frag -1.0B]) [/] [0261] 10.6% (-1.0B / [frag -1.0B]) [-] [0262] 10.6% (-1.0B / [frag -1.0B]) [\] [0266] 10.9% (-1.0B / [frag -1.0B]) [|] [0267] 10.9% (-1.0B / [frag -1.0B]) [/] [0270] 11.0% (-1.0B / [frag -1.0B]) [-] [0271] 11.0% (-1.0B / [frag -1.0B]) [\] [0272] 11.1% (-1.0B / [frag -1.0B]) [|] [0272] 11.1% (-1.0B / [frag -1.0B]) [/] [0273] 11.2% (-1.0B / [frag -1.0B]) [-] [0274] 11.2% (-1.0B / [frag -1.0B]) [\] [0275] 11.2% (-1.0B / [frag -1.0B]) [|] [0276] 11.3% (-1.0B / [frag -1.0B]) [/] [0277] 11.3% (-1.0B / [frag -1.0B]) [-] [0278] 11.4% (-1.0B / [frag -1.0B]) [\] [0282] 11.6% (-1.0B / [frag -1.0B]) [|] [0283] 11.6% (-1.0B / [frag -1.0B]) [/] [0285] 11.6% (-1.0B / [frag -1.0B]) [-] [0287] 11.8% (-1.0B / [frag -1.0B]) [\] [0288] 11.8% (-1.0B / [frag -1.0B]) [|] [0289] 11.8% (-1.0B / [frag -1.0B]) [/] [0290] 12.0% (-1.0B / [frag -1.0B]) [-] [0291] 12.0% (-1.0B / [frag -1.0B]) [\] [0292] 12.0% (-1.0B / [frag -1.0B]) [|] [0293] 12.0% (-1.0B / [frag -1.0B]) [/] [0297] 12.2% (-1.0B / [frag -1.0B]) [-] [0299] 12.4% (-1.0B / [frag -1.0B]) [\] [0301] 12.6% (-1.0B / [frag -1.0B]) [|] [0303] 12.7% (-1.0B / [frag -1.0B]) [/] [0305] 12.9% (-1.0B / [frag -1.0B]) [-] [0305] 12.9% (-1.0B / [frag -1.0B]) [\] [0307] 13.0% (-1.0B / [frag -1.0B]) [|] [0308] 13.0% (-1.0B / [frag -1.0B]) [/] [0309] 13.0% (-1.0B / [frag -1.0B]) [-] [0311] 13.2% (-1.0B / [frag -1.0B]) [\] [0311] 13.2% (-1.0B / [frag -1.0B]) [|] [0313] 13.3% (-1.0B / [frag -1.0B]) [/] [0314] 13.3% (-1.0B / [frag -1.0B]) [-] [0315] 13.3% (-1.0B / [frag -1.0B]) [\] [0317] 13.5% (-1.0B / [frag -1.0B]) [|] [0317] 13.5% (-1.0B / [frag -1.0B]) [/] [0319] 13.6% (-1.0B / [frag -1.0B]) [-] [0320] 13.6% (-1.0B / [frag -1.0B]) [\] [0321] 13.6% (-1.0B / [frag -1.0B]) [|] [0323] 13.7% (-1.0B / [frag -1.0B]) [/] [0323] 13.8% (-1.0B / [frag -1.0B]) [-] [0325] 13.9% (-1.0B / [frag -1.0B]) [\] [0326] 13.9% (-1.0B / [frag -1.0B]) [|] [0327] 13.9% (-1.0B / [frag -1.0B]) [/] [0393] 16.3% (-1.0B / [frag -1.0B]) [-] [0395] 16.4% (-1.0B / [frag -1.0B]) [\] [0400] 16.6% (-1.0B / [frag -1.0B]) [|] [0403] 16.7% (-1.0B / [frag -1.0B]) [/] [0408] 16.8% (-1.0B / [frag -1.0B]) [-] [0408] 16.9% (-1.0B / [frag -1.0B]) [\] [0410] 16.9% (-1.0B / [frag -1.0B]) [|] [0411] 17.0% (-1.0B / [frag -1.0B]) [/] [0411] 17.0% (-1.0B / [frag -1.0B]) [-] [0412] 17.0% (-1.0B / [frag -1.0B]) [\] [0413] 17.0% (-1.0B / [frag -1.0B]) [|] [0416] 17.3% (-1.0B / [frag -1.0B]) [/] [0418] 17.3% (-1.0B / [frag -1.0B]) [-] [0418] 17.4% (-1.0B / [frag -1.0B]) [\] [0419] 17.4% (-1.0B / [frag -1.0B]) [|] [0420] 17.4% (-1.0B / [frag -1.0B]) [/] [0421] 17.4% (-1.0B / [frag -1.0B]) [-] [0421] 17.5% (-1.0B / [frag -1.0B]) [\] [0422] 17.5% (-1.0B / [frag -1.0B]) [|] [0423] 17.5% (-1.0B / [frag -1.0B]) [/] [0424] 17.5% (-1.0B / [frag -1.0B]) [-] [0425] 17.6% (-1.0B / [frag -1.0B]) [\] [0429] 17.8% (-1.0B / [frag -1.0B]) [|] [0429] 17.9% (-1.0B / [frag -1.0B]) [/] [0430] 17.9% (-1.0B / [frag -1.0B]) [-] [0436] 18.2% (-1.0B / [frag -1.0B]) [\] [0441] 18.4% (-1.0B / [frag -1.0B]) [|] [0441] 18.5% (-1.0B / [frag -1.0B]) [/] [0442] 18.5% (-1.0B / [frag -1.0B]) [-] [0444] 18.6% (-1.0B / [frag -1.0B]) [\] [0447] 18.7% (-1.0B / [frag -1.0B]) [|] [0448] 18.8% (-1.0B / [frag -1.0B]) [/] [0449] 18.9% (-1.0B / [frag -1.0B]) [-] [0451] 19.0% (-1.0B / [frag -1.0B]) [\] [0452] 19.0% (-1.0B / [frag -1.0B]) [|] [0453] 19.1% (-1.0B / [frag -1.0B]) [/] [0454] 19.1% (-1.0B / [frag -1.0B]) [-] [0455] 19.2% (-1.0B / [frag -1.0B]) [\] [0455] 19.2% (-1.0B / [frag -1.0B]) [|] [0457] 19.3% (-1.0B / [frag -1.0B]) [/] [0458] 19.3% (-1.0B / [frag -1.0B]) [-] [0463] 19.4% (-1.0B / [frag -1.0B]) [\] [0463] 19.4% (-1.0B / [frag -1.0B]) [|] [0465] 19.5% (-1.0B / [frag -1.0B]) [/] [0466] 19.6% (-1.0B / [frag -1.0B]) [-] [0467] 19.6% (-1.0B / [frag -1.0B]) [\] [0467] 19.6% (-1.0B / [frag -1.0B]) [|] [0468] 19.6% (-1.0B / [frag -1.0B]) [/] [0468] 19.7% (-1.0B / [frag -1.0B]) [-] [0469] 19.7% (-1.0B / [frag -1.0B]) [\] [0471] 19.8% (-1.0B / [frag -1.0B]) [|] [0475] 20.2% (-1.0B / [frag -1.0B]) [/] [0477] 20.3% (-1.0B / [frag -1.0B]) [-] [0478] 20.3% (-1.0B / [frag -1.0B]) [\] [0479] 20.4% (-1.0B / [frag -1.0B]) [|] [0479] 20.4% (-1.0B / [frag -1.0B]) [/] [0479] 20.4% (-1.0B / [frag -1.0B]) [-] [0479] 20.4% (-1.0B / [frag -1.0B]) [\] [0480] 20.4% (-1.0B / [frag -1.0B]) [|] [0485] 20.6% (-1.0B / [frag -1.0B]) [/] [0488] 20.9% (-1.0B / [frag -1.0B]) [-] [0488] 21.0% (-1.0B / [frag -1.0B]) [\] [0488] 21.0% (-1.0B / [frag -1.0B]) [|] [0492] 21.2% (-1.0B / [frag -1.0B]) [/] [0495] 21.4% (-1.0B / [frag -1.0B]) [-] [0495] 21.5% (-1.0B / [frag -1.0B]) [\] [0496] 21.5% (-1.0B / [frag -1.0B]) [|] [0498] 21.5% (-1.0B / [frag -1.0B]) [/] [0499] 21.6% (-1.0B / [frag -1.0B]) [-] [0499] 21.8% (-1.0B / [frag -1.0B]) [\] [0503] 21.9% (-1.0B / [frag -1.0B]) [|] [0504] 21.9% (-1.0B / [frag -1.0B]) [/] [0505] 22.0% (-1.0B / [frag -1.0B]) [-] [0506] 22.0% (-1.0B / [frag -1.0B]) [\] [0507] 22.1% (-1.0B / [frag -1.0B]) [|] [0508] 22.2% (-1.0B / [frag -1.0B]) [/] [0508] 22.2% (-1.0B / [frag -1.0B]) [-] [0510] 22.2% (-1.0B / [frag -1.0B]) [\] [0511] 22.2% (-1.0B / [frag -1.0B]) [|] [0515] 22.4% (-1.0B / [frag -1.0B]) [/] [0515] 22.4% (-1.0B / [frag -1.0B]) [-] [0517] 22.4% (-1.0B / [frag -1.0B]) [\] [0518] 22.5% (-1.0B / [frag -1.0B]) [|] [0519] 22.5% (-1.0B / [frag -1.0B]) [/] [0520] 22.6% (-1.0B / [frag -1.0B]) [-] [0521] 22.6% (-1.0B / [frag -1.0B]) [\] [0522] 22.6% (-1.0B / [frag -1.0B]) [|] [0539] 23.2% (-1.0B / [frag -1.0B]) [/] [0540] 23.2% (-1.0B / [frag -1.0B]) [-] [0619] 25.5% (-1.0B / [frag -1.0B]) [\] [0629] 25.9% (-1.0B / [frag -1.0B]) [|] [0632] 26.1% (-1.0B / [frag -1.0B]) [/] [0636] 26.3% (-1.0B / [frag -1.0B]) [-] [0640] 26.6% (-1.0B / [frag -1.0B]) [\] [0652] 27.1% (-1.0B / [frag -1.0B]) [|] [0655] 27.2% (-1.0B / [frag -1.0B]) [/] [0656] 27.4% (-1.0B / [frag -1.0B]) [-] [0657] 27.5% (-1.0B / [frag -1.0B]) [\] [0658] 27.5% (-1.0B / [frag -1.0B]) [|] [0659] 27.6% (-1.0B / [frag -1.0B]) [/] [0659] 27.7% (-1.0B / [frag -1.0B]) [-] [0662] 27.9% (-1.0B / [frag -1.0B]) [\] [0664] 28.0% (-1.0B / [frag -1.0B]) [|] [0666] 28.1% (-1.0B / [frag -1.0B]) [/] [0667] 28.2% (-1.0B / [frag -1.0B]) [-] [0668] 28.3% (-1.0B / [frag -1.0B]) [\] [0669] 28.3% (-1.0B / [frag -1.0B]) [|] [0670] 28.4% (-1.0B / [frag -1.0B]) [/] [0671] 28.4% (-1.0B / [frag -1.0B]) [-] [0673] 28.4% (-1.0B / [frag -1.0B]) [\] [0674] 28.5% (-1.0B / [frag -1.0B]) [|] [0675] 28.6% (-1.0B / [frag -1.0B]) [/] [0676] 28.7% (-1.0B / [frag -1.0B]) [-] [0677] 28.7% (-1.0B / [frag -1.0B]) [\] [0678] 28.7% (-1.0B / [frag -1.0B]) [|] [0679] 28.7% (-1.0B / [frag -1.0B]) [/] [0680] 28.8% (-1.0B / [frag -1.0B]) [-] [0683] 28.8% (-1.0B / [frag -1.0B]) [\] [0685] 28.9% (-1.0B / [frag -1.0B]) [|] [0686] 29.0% (-1.0B / [frag -1.0B]) [/] [0792] 34.5% (-1.0B / [frag -1.0B]) [-] [0805] 35.0% (-1.0B / [frag -1.0B]) [\] [0806] 35.1% (-1.0B / [frag -1.0B]) [|] [0807] 35.2% (-1.0B / [frag -1.0B]) [/] [0808] 35.2% (-1.0B / [frag -1.0B]) [-] [0809] 35.3% (-1.0B / [frag -1.0B]) [\] [0810] 35.3% (-1.0B / [frag -1.0B]) [|] [0812] 35.3% (-1.0B / [frag -1.0B]) [/] [0813] 35.4% (-1.0B / [frag -1.0B]) [-] [0814] 35.5% (-1.0B / [frag -1.0B]) [\] [0815] 35.5% (-1.0B / [frag -1.0B]) [|] [0816] 35.6% (-1.0B / [frag -1.0B]) [/] [0817] 35.6% (-1.0B / [frag -1.0B]) [-] [0819] 35.6% (-1.0B / [frag -1.0B]) [\] [0820] 35.6% (-1.0B / [frag -1.0B]) [|] [0821] 35.7% (-1.0B / [frag -1.0B]) [/] [0822] 35.8% (-1.0B / [frag -1.0B]) [-] [0823] 35.9% (-1.0B / [frag -1.0B]) [\] [0824] 36.0% (-1.0B / [frag -1.0B]) [|] [0825] 36.0% (-1.0B / [frag -1.0B]) [/] [0826] 36.1% (-1.0B / [frag -1.0B]) [-] [0826] 36.1% (-1.0B / [frag -1.0B]) [\] [0826] 36.1% (-1.0B / [frag -1.0B]) [|] [0828] 36.2% (-1.0B / [frag -1.0B]) [/] [0829] 36.2% (-1.0B / [frag -1.0B]) [-] [0830] 36.4% (-1.0B / [frag -1.0B]) [\] [0831] 36.4% (-1.0B / [frag -1.0B]) [|] [0832] 36.5% (-1.0B / [frag -1.0B]) [/] [0834] 36.6% (-1.0B / [frag -1.0B]) [-] [0835] 36.6% (-1.0B / [frag -1.0B]) [\] [0836] 36.6% (-1.0B / [frag -1.0B]) [|] [0837] 36.6% (-1.0B / [frag -1.0B]) [/] [0837] 36.8% (-1.0B / [frag -1.0B]) [-] [0839] 36.8% (-1.0B / [frag -1.0B]) [\] [0840] 36.9% (-1.0B / [frag -1.0B]) [|] [0841] 37.0% (-1.0B / [frag -1.0B]) [/] [0841] 37.1% (-1.0B / [frag -1.0B]) [-] [0843] 37.1% (-1.0B / [frag -1.0B]) [\] [0844] 37.2% (-1.0B / [frag -1.0B]) [|] [0845] 37.2% (-1.0B / [frag -1.0B]) [/] [0847] 37.2% (-1.0B / [frag -1.0B]) [-] [0849] 37.3% (-1.0B / [frag -1.0B]) [\] [0850] 37.4% (-1.0B / [frag -1.0B]) [|] [0851] 37.4% (-1.0B / [frag -1.0B]) [/] [0853] 37.6% (-1.0B / [frag -1.0B]) [-] [0853] 37.6% (-1.0B / [frag -1.0B]) [\] [0854] 37.6% (-1.0B / [frag -1.0B]) [|] [0856] 37.7% (-1.0B / [frag -1.0B]) [/] [0857] 37.8% (-1.0B / [frag -1.0B]) [-] [0858] 37.8% (-1.0B / [frag -1.0B]) [\] [0860] 37.9% (-1.0B / [frag -1.0B]) [|] [0861] 38.0% (-1.0B / [frag -1.0B]) [/] [0862] 38.0% (-1.0B / [frag -1.0B]) [-] [0863] 38.1% (-1.0B / [frag -1.0B]) [\] [0864] 38.1% (-1.0B / [frag -1.0B]) [|] [0865] 38.1% (-1.0B / [frag -1.0B]) [/] [0867] 38.2% (-1.0B / [frag -1.0B]) [-] [0868] 38.3% (-1.0B / [frag -1.0B]) [\] [0869] 38.3% (-1.0B / [frag -1.0B]) [|] [0870] 38.4% (-1.0B / [frag -1.0B]) [/] [0870] 38.4% (-1.0B / [frag -1.0B]) [-] [0871] 38.4% (-1.0B / [frag -1.0B]) [\] [0873] 38.5% (-1.0B / [frag -1.0B]) [|] [0874] 38.5% (-1.0B / [frag -1.0B]) [/] [0875] 38.5% (-1.0B / [frag -1.0B]) [-] [0875] 38.5% (-1.0B / [frag -1.0B]) [\] [0877] 38.6% (-1.0B / [frag -1.0B]) [|] [0878] 38.6% (-1.0B / [frag -1.0B]) [/] [0879] 38.6% (-1.0B / [frag -1.0B]) [-] [0880] 38.7% (-1.0B / [frag -1.0B]) [\] [0881] 38.7% (-1.0B / [frag -1.0B]) [|] [0883] 38.7% (-1.0B / [frag -1.0B]) [/] [0884] 38.7% (-1.0B / [frag -1.0B]) [-] [0885] 38.8% (-1.0B / [frag -1.0B]) [\] [0886] 38.9% (-1.0B / [frag -1.0B]) [|] [0887] 38.9% (-1.0B / [frag -1.0B]) [/] [0888] 39.0% (-1.0B / [frag -1.0B]) [-] [0889] 39.0% (-1.0B / [frag -1.0B]) [\] [0891] 39.0% (-1.0B / [frag -1.0B]) [|] [0892] 39.0% (-1.0B / [frag -1.0B]) [/] [0893] 39.1% (-1.0B / [frag -1.0B]) [-] [0894] 39.1% (-1.0B / [frag -1.0B]) [\] [0895] 39.1% (-1.0B / [frag -1.0B]) [|] [0895] 39.2% (-1.0B / [frag -1.0B]) [/] [0896] 39.2% (-1.0B / [frag -1.0B]) [-] [0898] 39.3% (-1.0B / [frag -1.0B]) [\] [0899] 39.3% (-1.0B / [frag -1.0B]) [|] [0900] 39.3% (-1.0B / [frag -1.0B]) [/] [0902] 39.4% (-1.0B / [frag -1.0B]) [-] [0902] 39.4% (-1.0B / [frag -1.0B]) [\] [0904] 39.4% (-1.0B / [frag -1.0B]) [|] [0904] 39.5% (-1.0B / [frag -1.0B]) [/] [0905] 39.5% (-1.0B / [frag -1.0B]) [-] [0907] 39.5% (-1.0B / [frag -1.0B]) [\] [0908] 39.6% (-1.0B / [frag -1.0B]) [|] [0908] 39.6% (-1.0B / [frag -1.0B]) [/] [0910] 39.6% (-1.0B / [frag -1.0B]) [-] [0911] 39.7% (-1.0B / [frag -1.0B]) [\] [0911] 39.7% (-1.0B / [frag -1.0B]) [|] [0913] 39.7% (-1.0B / [frag -1.0B]) [/] [0915] 39.7% (-1.0B / [frag -1.0B]) * In [theories]: + +theory MLWEPKEHash. + theory MLWE_. + theory Matrix_. + theory ZR. + lemma nosmt addrA: associative (&+). + + lemma nosmt addrC: commutative (&+). + + lemma nosmt add0r: left_id Rq.zero (&+). + + lemma nosmt addNr: left_inverse Rq.zero (&-) (&+). + + theory AddMonoid. + lemma addmA: associative (&+). + + lemma addmC: commutative (&+). + + lemma add0m: left_id Rq.zero (&+). + + lemma addm0: right_id Rq.zero (&+). + + lemma addmCA: left_commutative (&+). + + lemma addmAC: right_commutative (&+). + + lemma addmACA: interchange (&+) (&+). + + lemma iteropE: forall (n : int) (x : poly), iterop n (&+) x Rq.zero = iter n ((&+) x) Rq.zero. + end AddMonoid. + + abbrev (-) (x y : poly) : poly = (x - y)%KMatrix.ZR. + + lemma nosmt addr0: right_id Rq.zero (&+). + + lemma nosmt addrN: right_inverse Rq.zero (&-) (&+). + + lemma nosmt addrCA: left_commutative (&+). + + lemma nosmt addrAC: right_commutative (&+). + + lemma nosmt addrACA: interchange (&+) (&+). + + lemma nosmt subrr: forall (x : poly), x - x = Rq.zero. + + lemma nosmt addKr: left_loop (&-) (&+). + + lemma nosmt addNKr: rev_left_loop (&-) (&+). + + lemma nosmt addrK: right_loop (&-) (&+). + + lemma nosmt addrNK: rev_right_loop (&-) (&+). + + lemma nosmt subrK: forall (x y : poly), (x - y) &+ y = x. + + lemma nosmt addrI: right_injective (&+). + + lemma nosmt addIr: left_injective (&+). + + lemma nosmt opprK: involutive (&-). + + lemma oppr_inj: injective (&-). + + lemma nosmt oppr0: (&-) Rq.zero = Rq.zero. + + lemma oppr_eq0: forall (x : poly), (&-) x = Rq.zero <=> x = Rq.zero. + + lemma nosmt subr0: forall (x : poly), x - Rq.zero = x. + + lemma nosmt sub0r: forall (x : poly), Rq.zero - x = (&-) x. + + lemma nosmt opprD: forall (x y : poly), (&-) (x &+ y) = (&-) x - y. + + lemma nosmt opprB: forall (x y : poly), (&-) (x - y) = y - x. + + lemma nosmt subrACA: interchange (fun (x y : poly) => x - y) (&+). + + lemma nosmt subr_eq: forall (x y z : poly), x - z = y <=> x = y &+ z. + + lemma nosmt subr_eq0: forall (x y : poly), x - y = Rq.zero <=> x = y. + + lemma nosmt addr_eq0: forall (x y : poly), x &+ y = Rq.zero <=> x = (&-) y. + + lemma nosmt eqr_opp: forall (x y : poly), (&-) x = (&-) y <=> x = y. + + lemma eqr_oppLR: forall (x y : poly), (&-) x = y <=> x = (&-) y. + + lemma nosmt eqr_sub: forall (x y z t : poly), x - y = z - t <=> x &+ t = z &+ y. + + lemma subr_add2r: forall (z x y : poly), x &+ z - y &+ z = x - y. + + op intmul (x : poly) (n : int) : poly = + if n < 0 then (&-) (iterop (-n) (&+) x Rq.zero) else iterop n (&+) x Rq.zero. + + lemma intmulpE: forall (z : poly) (c : int), 0 <= c => intmul z c = iterop c (&+) z Rq.zero. + + lemma mulr0z: forall (x : poly), intmul x 0 = Rq.zero. + + lemma mulr1z: forall (x : poly), intmul x 1 = x. + + lemma mulr2z: forall (x : poly), intmul x 2 = x &+ x. + + lemma mulrNz: forall (x : poly) (n : int), intmul x (-n) = (&-) (intmul x n). + + lemma mulrS: forall (x : poly) (n : int), 0 <= n => intmul x (n + 1) = x &+ intmul x n. + + lemma mulNrz: forall (x : poly) (n : int), intmul ((&-) x) n = (&-) (intmul x n). + + lemma mulNrNz: forall (x : poly) (n : int), intmul ((&-) x) (-n) = intmul x n. + + lemma mulrSz: forall (x : poly) (n : int), intmul x (n + 1) = x &+ intmul x n. + + lemma mulrDz: forall (x : poly) (n m : int), intmul x (n + m) = intmul x n &+ intmul x m. + + abbrev (/) (x y : poly) : poly = (x / y)%KMatrix.ZR. + + lemma nosmt oner_neq0: Rq.one <> Rq.zero. + + lemma nosmt mulrA: associative ( &* ). + + lemma nosmt mulrC: commutative ( &* ). + + lemma nosmt mul1r: left_id Rq.one ( &* ). + + lemma nosmt mulrDl: left_distributive ( &* ) (&+). + + lemma nosmt mulVr: left_inverse_in KMatrix.ZR.unit Rq.one Top.Correctness.invr ( &* ). + + lemma nosmt unitP: forall (x y : poly), y &* x = Rq.one => (unit x)%KMatrix.ZR. + + lemma nosmt unitout: forall (x : poly), ! (unit x)%KMatrix.ZR => invr x = x. + + theory MulMonoid. + lemma addmA: associative ( &* ). + + lemma addmC: commutative ( &* ). + + lemma add0m: left_id Rq.one ( &* ). + + lemma addm0: right_id Rq.one ( &* ). + + lemma addmCA: left_commutative ( &* ). + + lemma addmAC: right_commutative ( &* ). + + lemma addmACA: interchange ( &* ) ( &* ). + + lemma iteropE: forall (n : int) (x : poly), iterop n ( &* ) x Rq.one = iter n (( &* ) x) Rq.one. + end MulMonoid. + + lemma nosmt mulr1: right_id Rq.one ( &* ). + + lemma nosmt mulrCA: left_commutative ( &* ). + + lemma nosmt mulrAC: right_commutative ( &* ). + + lemma nosmt mulrACA: interchange ( &* ) ( &* ). + + lemma nosmt mulrSl: forall (x y : poly), x &+ Rq.one &* y = x &* y &+ y. + + lemma nosmt mulrDr: right_distributive ( &* ) (&+). + + lemma nosmt mul0r: left_zero Rq.zero ( &* ). + + lemma nosmt mulr0: right_zero Rq.zero ( &* ). + + lemma nosmt mulrN: forall (x y : poly), x &* (&-) y = (&-) (x &* y). + + lemma nosmt mulNr: forall (x y : poly), (&-) x &* y = (&-) (x &* y). + + lemma nosmt mulrNN: forall (x y : poly), (&-) x &* (&-) y = x &* y. + + lemma nosmt mulN1r: forall (x : poly), (&-) Rq.one &* x = (&-) x. + + lemma nosmt mulrN1: forall (x : poly), x &* (&-) Rq.one = (&-) x. + + lemma nosmt mulrBl: left_distributive ( &* ) (fun (x y : poly) => x - y). + + lemma nosmt mulrBr: right_distributive ( &* ) (fun (x y : poly) => x - y). + + lemma mulrnAl: forall (x y : poly) (n : int), 0 <= n => intmul x n &* y = intmul (x &* y) n. + + lemma mulrnAr: forall (x y : poly) (n : int), 0 <= n => x &* intmul y n = intmul (x &* y) n. + + lemma mulrzAl: forall (x y : poly) (z : int), intmul x z &* y = intmul (x &* y) z. + + lemma mulrzAr: forall (x y : poly) (z : int), x &* intmul y z = intmul (x &* y) z. + + lemma nosmt mulrV: right_inverse_in KMatrix.ZR.unit Rq.one Top.Correctness.invr ( &* ). + + lemma nosmt divrr: forall (x : poly), (unit x)%KMatrix.ZR => x / x = Rq.one. + + lemma nosmt invr_out: forall (x : poly), ! (unit x)%KMatrix.ZR => invr x = x. + + lemma nosmt unitrP: forall (x : poly), (unit x)%KMatrix.ZR <=> exists (y : poly), y &* x = Rq.one. + + lemma nosmt mulKr: left_loop_in KMatrix.ZR.unit Top.Correctness.invr ( &* ). + + lemma nosmt mulrK: right_loop_in KMatrix.ZR.unit Top.Correctness.invr ( &* ). + + lemma nosmt mulVKr: rev_left_loop_in KMatrix.ZR.unit Top.Correctness.invr ( &* ). + + lemma nosmt mulrVK: rev_right_loop_in KMatrix.ZR.unit Top.Correctness.invr ( &* ). + + lemma nosmt mulrI: right_injective_in KMatrix.ZR.unit ( &* ). + + lemma nosmt mulIr: left_injective_in KMatrix.ZR.unit ( &* ). + + lemma nosmt unitrE: forall (x : poly), (unit x)%KMatrix.ZR <=> x / x = Rq.one. + + lemma nosmt invrK: involutive Top.Correctness.invr. + + lemma nosmt invr_inj: injective Top.Correctness.invr. + + lemma nosmt unitrV: forall (x : poly), (unit (invr x))%KMatrix.ZR <=> (unit x)%KMatrix.ZR. + + lemma nosmt unitr1: (unit Rq.one)%KMatrix.ZR. + + lemma nosmt invr1: invr Rq.one = Rq.one. + + lemma nosmt div1r: forall (x : poly), Rq.one / x = invr x. + + lemma nosmt divr1: forall (x : poly), x / Rq.one = x. + + lemma nosmt unitr0: ! (unit Rq.zero)%KMatrix.ZR. + + lemma nosmt invr0: invr Rq.zero = Rq.zero. + + lemma nosmt unitrN1: (unit ((&-) Rq.one))%KMatrix.ZR. + + lemma nosmt invrN1: invr ((&-) Rq.one) = (&-) Rq.one. + + lemma nosmt unitrMl: + forall (x y : poly), (unit y)%KMatrix.ZR => (unit (x &* y))%KMatrix.ZR <=> (unit x)%KMatrix.ZR. + + lemma nosmt unitrMr: + forall (x y : poly), (unit x)%KMatrix.ZR => (unit (x &* y))%KMatrix.ZR <=> (unit y)%KMatrix.ZR. + + lemma nosmt unitrM: + forall (x y : poly), (unit (x &* y))%KMatrix.ZR <=> (unit x)%KMatrix.ZR /\ (unit y)%KMatrix.ZR. + + lemma nosmt unitrN: forall (x : poly), (unit ((&-) x))%KMatrix.ZR <=> (unit x)%KMatrix.ZR. + + lemma nosmt invrM: + forall (x y : poly), (unit x)%KMatrix.ZR => (unit y)%KMatrix.ZR => invr (x &* y) = invr y / x. + + lemma nosmt invrN: forall (x : poly), invr ((&-) x) = (&-) (invr x). + + lemma nosmt invr_neq0: forall (x : poly), x <> Rq.zero => invr x <> Rq.zero. + + lemma nosmt invr_eq0: forall (x : poly), invr x = Rq.zero <=> x = Rq.zero. + + lemma nosmt invr_eq1: forall (x : poly), invr x = Rq.one <=> x = Rq.one. + + op ofint (n : int) : poly = intmul Rq.one n. + + lemma ofint0: (ofint 0)%ZR = Rq.zero. + + lemma ofint1: (ofint 1)%ZR = Rq.one. + + lemma ofintS: forall (i : int), 0 <= i => (ofint (i + 1))%ZR = Rq.one &+ (ofint i)%ZR. + + lemma ofintN: forall (i : int), (ofint (-i))%ZR = (&-) ((ofint i))%ZR. + + lemma mul1r0z: forall (x : poly), x &* (ofint 0)%ZR = Rq.zero. + + lemma mul1r1z: forall (x : poly), x &* (ofint 1)%ZR = x. + + lemma mul1r2z: forall (x : poly), x &* (ofint 2)%ZR = x &+ x. + + lemma mulr_intl: forall (x : poly) (z : int), (ofint z)%ZR &* x = intmul x z. + + lemma mulr_intr: forall (x : poly) (z : int), x &* (ofint z)%ZR = intmul x z. + + lemma fracrDE: + forall (n1 n2 d1 d2 : poly), + (unit d1)%KMatrix.ZR => (unit d2)%KMatrix.ZR => n1 / d1 &+ (n2 / d2) = n1 &* d2 &+ (n2 &* d1) / (d1 &* d2). + + op exp (x : poly) (n : int) : poly = + if n < 0 then invr (iterop (-n) ( &* ) x Rq.one) else iterop n ( &* ) x Rq.one. + + lemma expr0: forall (x : poly), exp x 0 = Rq.one. + + lemma expr1: forall (x : poly), exp x 1 = x. + + lemma exprS: forall (x : poly) (i : int), 0 <= i => exp x (i + 1) = x &* exp x i. + + lemma expr_pred: forall (x : poly) (i : int), 0 < i => exp x i = x &* exp x (i - 1). + + lemma exprSr: forall (x : poly) (i : int), 0 <= i => exp x (i + 1) = exp x i &* x. + + lemma expr2: forall (x : poly), exp x 2 = x &* x. + + lemma exprN: forall (x : poly) (i : int), exp x (-i) = invr (exp x i). + + lemma exprN1: forall (x : poly), exp x (-1) = invr x. + + lemma unitrX: forall (x : poly) (m : int), (unit x)%KMatrix.ZR => (unit (exp x m))%KMatrix.ZR. + + lemma unitrX_neq0: forall (x : poly) (m : int), m <> 0 => (unit (exp x m))%KMatrix.ZR => (unit x)%KMatrix.ZR. + + lemma exprV: forall (x : poly) (i : int), exp (invr x) i = exp x (-i). + + lemma exprVn: forall (x : poly) (n : int), 0 <= n => exp (invr x) n = invr (exp x n). + + lemma exprMn: forall (x y : poly) (n : int), 0 <= n => exp (x &* y) n = exp x n &* exp y n. + + lemma exprD_nneg: forall (x : poly) (m n : int), 0 <= m => 0 <= n => exp x (m + n) = exp x m &* exp x n. + + lemma exprD: forall (x : poly) (m n : int), (unit x)%KMatrix.ZR => exp x (m + n) = exp x m &* exp x n. + + lemma exprM: forall (x : poly) (m n : int), exp x (m * n) = exp (exp x m) n. + + lemma expr0n: forall (n : int), 0 <= n => exp Rq.zero n = if n = 0 then Rq.one else Rq.zero. + + lemma expr0z: forall (z : int), exp Rq.zero z = if z = 0 then Rq.one else Rq.zero. + + lemma expr1z: forall (z : int), exp Rq.one z = Rq.one. + + lemma sqrrD: forall (x y : poly), exp (x &+ y) 2 = exp x 2 &+ intmul (x &* y) 2 &+ exp y 2. + + lemma sqrrN: forall (x : poly), exp ((&-) x) 2 = exp x 2. + + lemma sqrrB: forall (x y : poly), exp (x - y) 2 = (exp x 2 - intmul (x &* y) 2) &+ exp y 2. + + lemma signr_odd: forall (n : int), 0 <= n => exp ((&-) Rq.one) (b2i (odd n)) = exp ((&-) Rq.one) n. + + lemma subr_sqr_1: forall (x : poly), exp x 2 - Rq.one = (x - Rq.one) &* (x &+ Rq.one). + + op lreg (x : poly) : bool = injective (fun (y : poly) => x &* y). + + lemma mulrI_eq0: forall (x y : poly), lreg x => x &* y = Rq.zero <=> y = Rq.zero. + + lemma lreg_neq0: forall (x : poly), lreg x => x <> Rq.zero. + + lemma mulrI0_lreg: forall (x : poly), (forall (y : poly), x &* y = Rq.zero => y = Rq.zero) => lreg x. + + lemma lregN: forall (x : poly), lreg x => lreg ((&-) x). + + lemma lreg1: lreg Rq.one. + + lemma lregM: forall (x y : poly), lreg x => lreg y => lreg (x &* y). + + lemma lregXn: forall (x : poly) (n : int), 0 <= n => lreg x => lreg (exp x n). + + instance ring with [()] poly + op rzero = Top.Rq.zero + op rone = Top.Rq.one + op add = Top.Rq.&+ + op opp = Top.Rq.&- + op mul = Top.Rq.&* + op expr = Top.MLWEPKEHash.MLWE_.Matrix_.ZR.exp + op ofint = Top.MLWEPKEHash.MLWE_.Matrix_.ZR.ofint + + instance poly with Top.Ring.ZModule.zmodule. + + instance poly with Top.MLWEPKEHash.MLWE_.Matrix_.ZR.ring. + + instance poly with Top.Ring.IDomain.idomain. + end ZR. + + theory Big. + theory CR. + instance ring with [()] poly + op rzero = Top.Rq.zero + op rone = Top.Rq.one + op add = Top.Rq.&+ + op opp = Top.Rq.&- + op mul = Top.Rq.&* + op expr = Top.MLWEPKEHash.MLWE_.Matrix_.ZR.exp + op ofint = Top.MLWEPKEHash.MLWE_.Matrix_.ZR.ofint + + instance poly with Top.Ring.ZModule.zmodule. + + instance poly with Top.MLWEPKEHash.MLWE_.Matrix_.Big.CR.ring. + + instance poly with Top.Ring.IDomain.idomain. + end CR. + + theory BAdd. + op big ['a] (P : 'a -> bool) (F : 'a -> poly) (r : 'a list) : poly = foldr (&+) Rq.zero (map F (filter P r)). + + abbrev bigi (P : int -> bool) (F : int -> poly) (i j : int) : poly = big P F (range i j). + + lemma big_nil ['a]: forall (P : 'a -> bool) (F : 'a -> poly), big P F [] = Rq.zero. + + lemma big_cons ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (x : 'a) (s : 'a list), + big P F (x :: s) = if P x then F x &+ big P F s else big P F s. + + lemma big_consT ['a]: + forall (F : 'a -> poly) (x : 'a) (s : 'a list), big predT F (x :: s) = F x &+ big predT F s. + + lemma nosmt big_rec ['a]: + forall (K : poly -> bool) (r : 'a list) (P : 'a -> bool) (F : + 'a -> poly), K Rq.zero => (forall (i : 'a) (x : poly), P i => K x => K (F i &+ x)) => K (big P F r). + + lemma nosmt big_ind ['a]: + forall (K : poly -> bool) (r : 'a list) (P : 'a -> bool) (F : + 'a -> poly), + (forall (x y : poly), K x => K y => K (x &+ y)) => + K Rq.zero => (forall (i : 'a), P i => K (F i)) => K (big P F r). + + lemma nosmt big_rec2 ['a]: + forall (K : poly -> poly -> bool) (r : 'a list) (P : 'a -> bool) (F1 F2 : + 'a -> poly), + K Rq.zero Rq.zero => + (forall (i : 'a) (y1 y2 : poly), P i => K y1 y2 => K (F1 i &+ y1) (F2 i &+ y2)) => + K (big P F1 r) (big P F2 r). + + lemma nosmt big_ind2 ['a]: + forall (K : poly -> poly -> bool) (r : 'a list) (P : 'a -> bool) (F1 F2 : + 'a -> poly), + (forall (x1 x2 y1 y2 : poly), K x1 x2 => K y1 y2 => K (x1 &+ y1) (x2 &+ y2)) => + K Rq.zero Rq.zero => (forall (i : 'a), P i => K (F1 i) (F2 i)) => K (big P F1 r) (big P F2 r). + + lemma nosmt big_endo ['a]: + forall (f : poly -> poly), + f Rq.zero = Rq.zero => + (forall (x y : poly), f (x &+ y) = f x &+ f y) => + forall (r : 'a list) (P : 'a -> bool) (F : 'a -> poly), f (big P F r) = big P (f \o F) r. + + lemma nosmt big_map ['a, 'b]: + forall (h : 'b -> 'a) (P : 'a -> bool) (F : 'a -> poly) (s : 'b list), + big P F (map h s) = big (P \o h) (F \o h) s. + + lemma nosmt big_mapT ['a, 'b]: + forall (h : 'b -> 'a) (F : 'a -> poly) (s : 'b list), big predT F (map h s) = big predT (F \o h) s. + + lemma nosmt big_comp ['a]: + forall (h : poly -> poly) (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + h Rq.zero = Rq.zero => morphism_2 h (&+) (&+) => h (big P F s) = big P (h \o F) s. + + lemma nosmt big_nth ['a]: + forall (x0 : 'a) (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = bigi (P \o nth x0 s) (F \o nth x0 s) 0 (size s). + + lemma nosmt big_const ['a]: + forall (P : 'a -> bool) (x : poly) (s : 'a list), + big P (fun (_ : 'a) => x) s = iter (count P s) ((&+) x) Rq.zero. + + lemma nosmt big_seq1 ['a]: forall (F : 'a -> poly) (x : 'a), big predT F [x] = F x. + + lemma nosmt big_mkcond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = big predT (fun (i : 'a) => if P i then F i else Rq.zero) s. + + lemma nosmt big_filter ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), big predT F (filter P s) = big P F s. + + lemma big_filter_cond ['a]: + forall (P1 P2 : 'a -> bool) (F : 'a -> poly) (s : 'a list), big P2 F (filter P1 s) = big (predI P1 P2) F s. + + lemma nosmt eq_bigl ['a]: + forall (P1 P2 : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P1 i <=> P2 i) => big P1 F s = big P2 F s. + + lemma nosmt eq_bigr ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i => F1 i = F2 i) => big P F1 s = big P F2 s. + + lemma nosmt big_distrl ['a]: + forall (op_ : poly -> poly -> poly) (P : 'a -> bool) (F : + 'a -> poly) (s : 'a list) (t : poly), + left_zero Rq.zero op_ => + left_distributive op_ (&+) => op_ (big P F s) t = big P (fun (a : 'a) => op_ (F a) t) s. + + lemma nosmt big_distrr ['a]: + forall (op_ : poly -> poly -> poly) (P : 'a -> bool) (F : + 'a -> poly) (s : 'a list) (t : poly), + right_zero Rq.zero op_ => + right_distributive op_ (&+) => op_ t (big P F s) = big P (fun (a : 'a) => op_ t (F a)) s. + + lemma nosmt big_distr ['a, 'b]: + forall (op_ : poly -> poly -> poly) (P1 : 'a -> bool) (P2 : + 'b -> bool) (F1 : 'a -> poly) (s1 : 'a list) (F2 : 'b -> poly) (s2 : 'b list), + commutative op_ => + left_zero Rq.zero op_ => + left_distributive op_ (&+) => + op_ (big P1 F1 s1) (big P2 F2 s2) = + big P1 (fun (a1 : 'a) => big P2 (fun (a2 : 'b) => op_ (F1 a1) (F2 a2)) s2) s1. + + lemma big_andbC ['a]: + forall (P Q : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big (fun (x : 'a) => P x /\ Q x) F s = big (fun (x : 'a) => Q x /\ P x) F s. + + lemma nosmt eq_big ['a]: + forall (P1 P2 : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + (forall (i : 'a), P1 i <=> P2 i) => (forall (i : 'a), P1 i => F1 i = F2 i) => big P1 F1 s = big P2 F2 s. + + lemma nosmt congr_big ['a]: + forall (r1 r2 : 'a list) (P1 P2 : 'a -> bool) (F1 F2 : 'a -> poly), + r1 = r2 => + (forall (x : 'a), P1 x <=> P2 x) => (forall (i : 'a), P1 i => F1 i = F2 i) => big P1 F1 r1 = big P2 F2 r2. + + lemma big_hasC ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), ! has P s => big P F s = Rq.zero. + + lemma big_pred0_eq ['a]: forall (F : 'a -> poly) (s : 'a list), big pred0 F s = Rq.zero. + + lemma big_pred0 ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i <=> false) => big P F s = Rq.zero. + + lemma big_cat ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), big P F (s1 ++ s2) = big P F s1 &+ big P F s2. + + lemma nosmt big_catl ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), ! has P s2 => big P F (s1 ++ s2) = big P F s1. + + lemma nosmt big_catr ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), ! has P s1 => big P F (s1 ++ s2) = big P F s2. + + lemma nosmt big_rcons ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + big P F (rcons s x) = if P x then big P F s &+ F x else big P F s. + + lemma nosmt eq_big_perm ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), perm_eq s1 s2 => big P F s1 = big P F s2. + + lemma nosmt eq_big_perm_map ['a]: + forall (F : 'a -> poly) (s1 s2 : 'a list), perm_eq (map F s1) (map F s2) => big predT F s1 = big predT F s2. + + lemma nosmt big_seq_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = big (fun (i : 'a) => (i \in s) /\ P i) F s. + + lemma nosmt big_seq ['a]: + forall (F : 'a -> poly) (s : 'a list), big predT F s = big (fun (i : 'a) => i \in s) F s. + + lemma nosmt big_rem ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + x \in s => big P F s = (if P x then F x else Rq.zero) &+ big P F (rem x s). + + lemma nosmt bigD1 ['a]: + forall (F : 'a -> poly) (s : 'a list) (x : 'a), + x \in s => uniq s => big predT F s = F x &+ big (predC1 x) F s. + + lemma nosmt bigD1_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + P x => x \in s => uniq s => big P F s = F x &+ big (predI P (predC1 x)) F s. + + lemma nosmt bigD1_cond_if ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + uniq s => big P F s = (if (x \in s) /\ P x then F x else Rq.zero) &+ big (predI P (predC1 x)) F s. + + lemma big_split ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + big P (fun (i : 'a) => F1 i &+ F2 i) s = big P F1 s &+ big P F2 s. + + lemma nosmt bigID ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (a : 'a -> bool) (s : 'a list), + big P F s = big (predI P a) F s &+ big (predI P (predC a)) F s. + + lemma bigU ['a]: + forall (P Q : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (x : 'a), ! (P x /\ Q x)) => big (predU P Q) F s = big P F s &+ big Q F s. + + lemma bigEM ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), big predT F s = big P F s &+ big (predC P) F s. + + lemma nosmt big_reindex ['a, 'b]: + forall (P : 'a -> bool) (F : 'a -> poly) (f : 'b -> 'a) (f' : + 'a -> 'b) (s : 'a list), + (forall (x : 'a), x \in s => f (f' x) = x) => big P F s = big (P \o f) (F \o f) (map f' s). + + lemma nosmt big_pair_pswap ['a, 'b]: + forall (p : 'a * 'b -> bool) (f : 'a * 'b -> poly) (s : ('a * 'b) list), + big p f s = big (p \o pswap) (f \o pswap) (map pswap s). + + lemma nosmt eq_big_seq ['a]: + forall (F1 F2 : 'a -> poly) (s : 'a list), + (forall (x : 'a), x \in s => F1 x = F2 x) => big predT F1 s = big predT F2 s. + + lemma nosmt congr_big_seq ['a]: + forall (P1 P2 : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + (forall (x : 'a), x \in s => P1 x = P2 x) => + (forall (x : 'a), x \in s => P1 x => P2 x => F1 x = F2 x) => big P1 F1 s = big P2 F2 s. + + lemma big1_eq ['a]: forall (P : 'a -> bool) (s : 'a list), big P (fun (_ : 'a) => Rq.zero) s = Rq.zero. + + lemma big1 ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i => F i = Rq.zero) => big P F s = Rq.zero. + + lemma big1_seq ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i /\ (i \in s) => F i = Rq.zero) => big P F s = Rq.zero. + + lemma big_eq_idm_filter ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (x : 'a), ! P x => F x = Rq.zero) => big predT F s = big P F s. + + lemma big_flatten ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (rr : 'a list list), + big P F (flatten rr) = big predT (fun (s : 'a list) => big P F s) rr. + + lemma nosmt big_pair ['a, 'b]: + forall (F : 'a * 'b -> poly) (s : ('a * 'b) list), + uniq s => + big predT F s = + big predT (fun (a : 'a) => big predT F (filter (fun (xy : 'a * 'b) => xy.`1 = a) s)) (undup (unzip1 s)). + + lemma big_nseq_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (n : int) (x : 'a), + big P F (nseq n x) = if P x then iter n ((&+) (F x)) Rq.zero else Rq.zero. + + lemma big_nseq ['a]: + forall (F : 'a -> poly) (n : int) (x : 'a), big predT F (nseq n x) = iter n ((&+) (F x)) Rq.zero. + + lemma big_undup ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = big P (fun (a : 'a) => iter (count (pred1 a) s) ((&+) (F a)) Rq.zero) (undup s). + + lemma nosmt exchange_big ['a, 'b]: + forall (P1 : 'a -> bool) (P2 : 'b -> bool) (F : 'a -> 'b -> poly) (s1 : 'a list) (s2 : 'b list), + big P1 (fun (a : 'a) => big P2 (F a) s2) s1 = + big P2 (fun (b : 'b) => big P1 (fun (a : 'a) => F a b) s1) s2. + + lemma partition_big ['a, 'b]: + forall (px : 'a -> 'b) (P : 'a -> bool) (Q : 'b -> bool) (F : + 'a -> poly) (s : 'a list) (s' : 'b list), + uniq s' => + (forall (x : 'a), x \in s => P x => (px x \in s') /\ Q (px x)) => + big P F s = big Q (fun (x : 'b) => big (fun (y : 'a) => P y /\ px y = x) F s) s'. + + lemma nosmt big_allpairs ['a, 'b, 'c]: + forall (f : 'a -> 'b -> 'c) (F : 'c -> poly) (s : 'a list) (t : 'b list), + big predT F (allpairs f s t) = big predT (fun (x : 'a) => big predT (fun (y : 'b) => F (f x y)) t) s. + + lemma nosmt big_int_cond: + forall (m n : int) (P : int -> bool) (F : int -> poly), + bigi P F m n = bigi (fun (i : int) => (m <= i && i < n) /\ P i) F m n. + + lemma nosmt big_int: + forall (m n : int) (F : int -> poly), bigi predT F m n = bigi (fun (i : int) => m <= i && i < n) F m n. + + lemma nosmt congr_big_int: + forall (m1 n1 m2 n2 : int) (P1 P2 : int -> bool) (F1 F2 : + int -> poly), + m1 = m2 => + n1 = n2 => + (forall (i : int), m1 <= i && i < n2 => P1 i = P2 i) => + (forall (i : int), P1 i /\ m1 <= i && i < n2 => F1 i = F2 i) => bigi P1 F1 m1 n1 = bigi P2 F2 m2 n2. + + lemma nosmt eq_big_int: + forall (m n : int) (F1 F2 : int -> poly), + (forall (i : int), m <= i && i < n => F1 i = F2 i) => bigi predT F1 m n = bigi predT F2 m n. + + lemma nosmt big_ltn_cond: + forall (m n : int) (P : int -> bool) (F : int -> poly), + m < n => let x = bigi P F (m + 1) n in bigi P F m n = if P m then F m &+ x else x. + + lemma nosmt big_ltn: + forall (m n : int) (F : int -> poly), m < n => bigi predT F m n = F m &+ bigi predT F (m + 1) n. + + lemma nosmt big_geq: + forall (m n : int) (P : int -> bool) (F : int -> poly), n <= m => bigi P F m n = Rq.zero. + + lemma nosmt big_addn: + forall (m n a : int) (P : int -> bool) (F : int -> poly), + bigi P F (m + a) n = bigi (fun (i : int) => P (i + a)) (fun (i : int) => F (i + a)) m (n - a). + + lemma nosmt big_int1: forall (n : int) (F : int -> poly), bigi predT F n (n + 1) = F n. + + lemma nosmt big_cat_int: + forall (n m p : int) (P : int -> bool) (F : int -> poly), + m <= n => n <= p => bigi P F m p = bigi P F m n &+ bigi P F n p. + + lemma nosmt big_int_recl: + forall (n m : int) (F : int -> poly), + m <= n => bigi predT F m (n + 1) = F m &+ bigi predT (fun (i : int) => F (i + 1)) m n. + + lemma nosmt big_int_recr: + forall (n m : int) (F : int -> poly), m <= n => bigi predT F m (n + 1) = bigi predT F m n &+ F n. + + lemma nosmt big_int_recl_cond: + forall (n m : int) (P : int -> bool) (F : int -> poly), + m <= n => + bigi P F m (n + 1) = + (if P m then F m else Rq.zero) &+ bigi (fun (i : int) => P (i + 1)) (fun (i : int) => F (i + 1)) m n. + + lemma nosmt big_int_recr_cond: + forall (n m : int) (P : int -> bool) (F : int -> poly), + m <= n => bigi P F m (n + 1) = bigi P F m n &+ if P n then F n else Rq.zero. + + lemma nosmt bigi_split_odd_even: + forall (n : int) (F : int -> poly), + 0 <= n => bigi predT (fun (i : int) => F (2 * i) &+ F (2 * i + 1)) 0 n = bigi predT F 0 (2 * n). + + lemma sumrD ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> poly) (r : 'a list), + big P F1 r &+ big P F2 r = big P (fun (x : 'a) => F1 x &+ F2 x) r. + + lemma sumrN ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (r : 'a list), + (&-) (big P F r) = big P (fun (x : 'a) => (&-) (F x)) r. + + lemma sumrB ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> poly) (r : 'a list), + (big P F1 r - big P F2 r)%ZR = big P (fun (x : 'a) => (F1 x - F2 x)%ZR) r. + + lemma nosmt sumr_const ['a]: + forall (P : 'a -> bool) (x : poly) (s : 'a list), big P (fun (_ : 'a) => x) s = (intmul x (count P s))%ZR. + + lemma sumri_const: + forall (k : poly) (n m : int), n <= m => bigi predT (fun (_ : int) => k) n m = (intmul k (m - n))%ZR. + + lemma sumr_undup ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = big P (fun (a : 'a) => (intmul (F a) (count (pred1 a) s))%ZR) (undup s). + + lemma telescoping_sum: + forall (F : int -> poly) (m n : int), + m <= n => (F m - F n)%ZR = bigi predT (fun (i : int) => (F i - F (i + 1))%ZR) m n. + + lemma telescoping_sum_down: + forall (F : int -> poly) (m n : int), + m <= n => (F n - F m)%ZR = bigi predT (fun (i : int) => (F (i + 1) - F i)%ZR) m n. + + lemma nosmt sumr_1 ['a]: + forall (P : 'a -> bool) (s : 'a list), big P (fun (_ : 'a) => Rq.one) s = (ofint (count P s))%ZR. + + lemma mulr_suml ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : poly), + big P F s &* x = big P (fun (i : 'a) => F i &* x) s. + + lemma mulr_sumr ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : poly), + x &* big P F s = big P (fun (i : 'a) => x &* F i) s. + + lemma divr_suml ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : poly), + (big P F s / x)%ZR = big P (fun (i : 'a) => (F i / x)%ZR) s. + + lemma nosmt sum_pair_dep ['a, 'b]: + forall (u : 'a -> poly) (v : 'a -> 'b -> poly) (J : ('a * 'b) list), + uniq J => + big predT (fun (ij : 'a * 'b) => u ij.`1 &* v ij.`1 ij.`2) J = + big predT + (fun (i : 'a) => + u i &* big predT (fun (ij : 'a * 'b) => v ij.`1 ij.`2) (filter (fun (ij : 'a * 'b) => ij.`1 = i) J)) + (undup (unzip1 J)). + + lemma nosmt sum_pair ['a, 'b]: + forall (u : 'a -> poly) (v : 'b -> poly) (J : ('a * 'b) list), + uniq J => + big predT (fun (ij : 'a * 'b) => u ij.`1 &* v ij.`2) J = + big predT (fun (i : 'a) => u i &* big predT v (unzip2 (filter (fun (ij : 'a * 'b) => ij.`1 = i) J))) + (undup (unzip1 J)). + end BAdd. + + theory BMul. + op big ['a] (P : 'a -> bool) (F : 'a -> poly) (r : 'a list) : poly = + foldr ( &* ) Rq.one (map F (filter P r)). + + abbrev bigi (P : int -> bool) (F : int -> poly) (i j : int) : poly = big P F (range i j). + + lemma big_nil ['a]: forall (P : 'a -> bool) (F : 'a -> poly), big P F [] = Rq.one. + + lemma big_cons ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (x : 'a) (s : 'a list), + big P F (x :: s) = if P x then F x &* big P F s else big P F s. + + lemma big_consT ['a]: + forall (F : 'a -> poly) (x : 'a) (s : 'a list), big predT F (x :: s) = F x &* big predT F s. + + lemma nosmt big_rec ['a]: + forall (K : poly -> bool) (r : 'a list) (P : 'a -> bool) (F : + 'a -> poly), K Rq.one => (forall (i : 'a) (x : poly), P i => K x => K (F i &* x)) => K (big P F r). + + lemma nosmt big_ind ['a]: + forall (K : poly -> bool) (r : 'a list) (P : 'a -> bool) (F : + 'a -> poly), + (forall (x y : poly), K x => K y => K (x &* y)) => + K Rq.one => (forall (i : 'a), P i => K (F i)) => K (big P F r). + + lemma nosmt big_rec2 ['a]: + forall (K : poly -> poly -> bool) (r : 'a list) (P : 'a -> bool) (F1 F2 : + 'a -> poly), + K Rq.one Rq.one => + (forall (i : 'a) (y1 y2 : poly), P i => K y1 y2 => K (F1 i &* y1) (F2 i &* y2)) => + K (big P F1 r) (big P F2 r). + + lemma nosmt big_ind2 ['a]: + forall (K : poly -> poly -> bool) (r : 'a list) (P : 'a -> bool) (F1 F2 : + 'a -> poly), + (forall (x1 x2 y1 y2 : poly), K x1 x2 => K y1 y2 => K (x1 &* y1) (x2 &* y2)) => + K Rq.one Rq.one => (forall (i : 'a), P i => K (F1 i) (F2 i)) => K (big P F1 r) (big P F2 r). + + lemma nosmt big_endo ['a]: + forall (f : poly -> poly), + f Rq.one = Rq.one => + (forall (x y : poly), f (x &* y) = f x &* f y) => + forall (r : 'a list) (P : 'a -> bool) (F : 'a -> poly), f (big P F r) = big P (f \o F) r. + + lemma nosmt big_map ['a, 'b]: + forall (h : 'b -> 'a) (P : 'a -> bool) (F : 'a -> poly) (s : 'b list), + big P F (map h s) = big (P \o h) (F \o h) s. + + lemma nosmt big_mapT ['a, 'b]: + forall (h : 'b -> 'a) (F : 'a -> poly) (s : 'b list), big predT F (map h s) = big predT (F \o h) s. + + lemma nosmt big_comp ['a]: + forall (h : poly -> poly) (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + h Rq.one = Rq.one => morphism_2 h ( &* ) ( &* ) => h (big P F s) = big P (h \o F) s. + + lemma nosmt big_nth ['a]: + forall (x0 : 'a) (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = bigi (P \o nth x0 s) (F \o nth x0 s) 0 (size s). + + lemma nosmt big_const ['a]: + forall (P : 'a -> bool) (x : poly) (s : 'a list), + big P (fun (_ : 'a) => x) s = iter (count P s) (( &* ) x) Rq.one. + + lemma nosmt big_seq1 ['a]: forall (F : 'a -> poly) (x : 'a), big predT F [x] = F x. + + lemma nosmt big_mkcond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = big predT (fun (i : 'a) => if P i then F i else Rq.one) s. + + lemma nosmt big_filter ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), big predT F (filter P s) = big P F s. + + lemma big_filter_cond ['a]: + forall (P1 P2 : 'a -> bool) (F : 'a -> poly) (s : 'a list), big P2 F (filter P1 s) = big (predI P1 P2) F s. + + lemma nosmt eq_bigl ['a]: + forall (P1 P2 : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P1 i <=> P2 i) => big P1 F s = big P2 F s. + + lemma nosmt eq_bigr ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i => F1 i = F2 i) => big P F1 s = big P F2 s. + + lemma nosmt big_distrl ['a]: + forall (op_ : poly -> poly -> poly) (P : 'a -> bool) (F : + 'a -> poly) (s : 'a list) (t : poly), + left_zero Rq.one op_ => + left_distributive op_ ( &* ) => op_ (big P F s) t = big P (fun (a : 'a) => op_ (F a) t) s. + + lemma nosmt big_distrr ['a]: + forall (op_ : poly -> poly -> poly) (P : 'a -> bool) (F : + 'a -> poly) (s : 'a list) (t : poly), + right_zero Rq.one op_ => + right_distributive op_ ( &* ) => op_ t (big P F s) = big P (fun (a : 'a) => op_ t (F a)) s. + + lemma nosmt big_distr ['a, 'b]: + forall (op_ : poly -> poly -> poly) (P1 : 'a -> bool) (P2 : + 'b -> bool) (F1 : 'a -> poly) (s1 : 'a list) (F2 : 'b -> poly) (s2 : 'b list), + commutative op_ => + left_zero Rq.one op_ => + left_distributive op_ ( &* ) => + op_ (big P1 F1 s1) (big P2 F2 s2) = + big P1 (fun (a1 : 'a) => big P2 (fun (a2 : 'b) => op_ (F1 a1) (F2 a2)) s2) s1. + + lemma big_andbC ['a]: + forall (P Q : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big (fun (x : 'a) => P x /\ Q x) F s = big (fun (x : 'a) => Q x /\ P x) F s. + + lemma nosmt eq_big ['a]: + forall (P1 P2 : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + (forall (i : 'a), P1 i <=> P2 i) => (forall (i : 'a), P1 i => F1 i = F2 i) => big P1 F1 s = big P2 F2 s. + + lemma nosmt congr_big ['a]: + forall (r1 r2 : 'a list) (P1 P2 : 'a -> bool) (F1 F2 : 'a -> poly), + r1 = r2 => + (forall (x : 'a), P1 x <=> P2 x) => (forall (i : 'a), P1 i => F1 i = F2 i) => big P1 F1 r1 = big P2 F2 r2. + + lemma big_hasC ['a]: forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), ! has P s => big P F s = Rq.one. + + lemma big_pred0_eq ['a]: forall (F : 'a -> poly) (s : 'a list), big pred0 F s = Rq.one. + + lemma big_pred0 ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i <=> false) => big P F s = Rq.one. + + lemma big_cat ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), big P F (s1 ++ s2) = big P F s1 &* big P F s2. + + lemma nosmt big_catl ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), ! has P s2 => big P F (s1 ++ s2) = big P F s1. + + lemma nosmt big_catr ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), ! has P s1 => big P F (s1 ++ s2) = big P F s2. + + lemma nosmt big_rcons ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + big P F (rcons s x) = if P x then big P F s &* F x else big P F s. + + lemma nosmt eq_big_perm ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), perm_eq s1 s2 => big P F s1 = big P F s2. + + lemma nosmt eq_big_perm_map ['a]: + forall (F : 'a -> poly) (s1 s2 : 'a list), perm_eq (map F s1) (map F s2) => big predT F s1 = big predT F s2. + + lemma nosmt big_seq_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = big (fun (i : 'a) => (i \in s) /\ P i) F s. + + lemma nosmt big_seq ['a]: + forall (F : 'a -> poly) (s : 'a list), big predT F s = big (fun (i : 'a) => i \in s) F s. + + lemma nosmt big_rem ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + x \in s => big P F s = (if P x then F x else Rq.one) &* big P F (rem x s). + + lemma nosmt bigD1 ['a]: + forall (F : 'a -> poly) (s : 'a list) (x : 'a), + x \in s => uniq s => big predT F s = F x &* big (predC1 x) F s. + + lemma nosmt bigD1_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + P x => x \in s => uniq s => big P F s = F x &* big (predI P (predC1 x)) F s. + + lemma nosmt bigD1_cond_if ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + uniq s => big P F s = (if (x \in s) /\ P x then F x else Rq.one) &* big (predI P (predC1 x)) F s. + + lemma big_split ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + big P (fun (i : 'a) => F1 i &* F2 i) s = big P F1 s &* big P F2 s. + + lemma nosmt bigID ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (a : 'a -> bool) (s : 'a list), + big P F s = big (predI P a) F s &* big (predI P (predC a)) F s. + + lemma bigU ['a]: + forall (P Q : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (x : 'a), ! (P x /\ Q x)) => big (predU P Q) F s = big P F s &* big Q F s. + + lemma bigEM ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), big predT F s = big P F s &* big (predC P) F s. + + lemma nosmt big_reindex ['a, 'b]: + forall (P : 'a -> bool) (F : 'a -> poly) (f : 'b -> 'a) (f' : + 'a -> 'b) (s : 'a list), + (forall (x : 'a), x \in s => f (f' x) = x) => big P F s = big (P \o f) (F \o f) (map f' s). + + lemma nosmt big_pair_pswap ['a, 'b]: + forall (p : 'a * 'b -> bool) (f : 'a * 'b -> poly) (s : ('a * 'b) list), + big p f s = big (p \o pswap) (f \o pswap) (map pswap s). + + lemma nosmt eq_big_seq ['a]: + forall (F1 F2 : 'a -> poly) (s : 'a list), + (forall (x : 'a), x \in s => F1 x = F2 x) => big predT F1 s = big predT F2 s. + + lemma nosmt congr_big_seq ['a]: + forall (P1 P2 : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + (forall (x : 'a), x \in s => P1 x = P2 x) => + (forall (x : 'a), x \in s => P1 x => P2 x => F1 x = F2 x) => big P1 F1 s = big P2 F2 s. + + lemma big1_eq ['a]: forall (P : 'a -> bool) (s : 'a list), big P (fun (_ : 'a) => Rq.one) s = Rq.one. + + lemma big1 ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i => F i = Rq.one) => big P F s = Rq.one. + + lemma big1_seq ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i /\ (i \in s) => F i = Rq.one) => big P F s = Rq.one. + + lemma big_eq_idm_filter ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (x : 'a), ! P x => F x = Rq.one) => big predT F s = big P F s. + + lemma big_flatten ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (rr : 'a list list), + big P F (flatten rr) = big predT (fun (s : 'a list) => big P F s) rr. + + lemma nosmt big_pair ['a, 'b]: + forall (F : 'a * 'b -> poly) (s : ('a * 'b) list), + uniq s => + big predT F s = + big predT (fun (a : 'a) => big predT F (filter (fun (xy : 'a * 'b) => xy.`1 = a) s)) (undup (unzip1 s)). + + lemma big_nseq_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (n : int) (x : 'a), + big P F (nseq n x) = if P x then iter n (( &* ) (F x)) Rq.one else Rq.one. + + lemma big_nseq ['a]: + forall (F : 'a -> poly) (n : int) (x : 'a), big predT F (nseq n x) = iter n (( &* ) (F x)) Rq.one. + + lemma big_undup ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = big P (fun (a : 'a) => iter (count (pred1 a) s) (( &* ) (F a)) Rq.one) (undup s). + + lemma nosmt exchange_big ['a, 'b]: + forall (P1 : 'a -> bool) (P2 : 'b -> bool) (F : 'a -> 'b -> poly) (s1 : 'a list) (s2 : 'b list), + big P1 (fun (a : 'a) => big P2 (F a) s2) s1 = + big P2 (fun (b : 'b) => big P1 (fun (a : 'a) => F a b) s1) s2. + + lemma partition_big ['a, 'b]: + forall (px : 'a -> 'b) (P : 'a -> bool) (Q : 'b -> bool) (F : + 'a -> poly) (s : 'a list) (s' : 'b list), + uniq s' => + (forall (x : 'a), x \in s => P x => (px x \in s') /\ Q (px x)) => + big P F s = big Q (fun (x : 'b) => big (fun (y : 'a) => P y /\ px y = x) F s) s'. + + lemma nosmt big_allpairs ['a, 'b, 'c]: + forall (f : 'a -> 'b -> 'c) (F : 'c -> poly) (s : 'a list) (t : 'b list), + big predT F (allpairs f s t) = big predT (fun (x : 'a) => big predT (fun (y : 'b) => F (f x y)) t) s. + + lemma nosmt big_int_cond: + forall (m n : int) (P : int -> bool) (F : int -> poly), + bigi P F m n = bigi (fun (i : int) => (m <= i && i < n) /\ P i) F m n. + + lemma nosmt big_int: + forall (m n : int) (F : int -> poly), bigi predT F m n = bigi (fun (i : int) => m <= i && i < n) F m n. + + lemma nosmt congr_big_int: + forall (m1 n1 m2 n2 : int) (P1 P2 : int -> bool) (F1 F2 : + int -> poly), + m1 = m2 => + n1 = n2 => + (forall (i : int), m1 <= i && i < n2 => P1 i = P2 i) => + (forall (i : int), P1 i /\ m1 <= i && i < n2 => F1 i = F2 i) => bigi P1 F1 m1 n1 = bigi P2 F2 m2 n2. + + lemma nosmt eq_big_int: + forall (m n : int) (F1 F2 : int -> poly), + (forall (i : int), m <= i && i < n => F1 i = F2 i) => bigi predT F1 m n = bigi predT F2 m n. + + lemma nosmt big_ltn_cond: + forall (m n : int) (P : int -> bool) (F : int -> poly), + m < n => let x = bigi P F (m + 1) n in bigi P F m n = if P m then F m &* x else x. + + lemma nosmt big_ltn: + forall (m n : int) (F : int -> poly), m < n => bigi predT F m n = F m &* bigi predT F (m + 1) n. + + lemma nosmt big_geq: forall (m n : int) (P : int -> bool) (F : int -> poly), n <= m => bigi P F m n = Rq.one. + + lemma nosmt big_addn: + forall (m n a : int) (P : int -> bool) (F : int -> poly), + bigi P F (m + a) n = bigi (fun (i : int) => P (i + a)) (fun (i : int) => F (i + a)) m (n - a). + + lemma nosmt big_int1: forall (n : int) (F : int -> poly), bigi predT F n (n + 1) = F n. + + lemma nosmt big_cat_int: + forall (n m p : int) (P : int -> bool) (F : int -> poly), + m <= n => n <= p => bigi P F m p = bigi P F m n &* bigi P F n p. + + lemma nosmt big_int_recl: + forall (n m : int) (F : int -> poly), + m <= n => bigi predT F m (n + 1) = F m &* bigi predT (fun (i : int) => F (i + 1)) m n. + + lemma nosmt big_int_recr: + forall (n m : int) (F : int -> poly), m <= n => bigi predT F m (n + 1) = bigi predT F m n &* F n. + + lemma nosmt big_int_recl_cond: + forall (n m : int) (P : int -> bool) (F : int -> poly), + m <= n => + bigi P F m (n + 1) = + (if P m then F m else Rq.one) &* bigi (fun (i : int) => P (i + 1)) (fun (i : int) => F (i + 1)) m n. + + lemma nosmt big_int_recr_cond: + forall (n m : int) (P : int -> bool) (F : int -> poly), + m <= n => bigi P F m (n + 1) = bigi P F m n &* if P n then F n else Rq.one. + + lemma nosmt bigi_split_odd_even: + forall (n : int) (F : int -> poly), + 0 <= n => bigi predT (fun (i : int) => F (2 * i) &* F (2 * i + 1)) 0 n = bigi predT F 0 (2 * n). + end BMul. + + lemma mulr_big ['a]: + forall (P Q : 'a -> bool) (f g : 'a -> poly) (r s : 'a list), + (big P f r)%BAdd &* (big Q g s)%BAdd = + (big P (fun (x : 'a) => (big Q (fun (y : 'a) => f x &* g y) s)%BAdd) r)%BAdd. + + lemma subrXX: + forall (x y : poly) (n : int), + 0 <= n => + (exp x n - exp y n)%ZR = + (x - y)%ZR &* (bigi predT (fun (i : int) => (exp x (n - 1 - i))%ZR &* (exp y i)%ZR) 0 n)%BAdd. + + lemma nosmt mulr_const_cond ['a]: + forall (p : 'a -> bool) (s : 'a list) (c : poly), (big p (fun (_ : 'a) => c) s)%BMul = (exp c (count p s))%ZR. + + lemma nosmt mulr_const ['a]: + forall (s : 'a list) (c : poly), (big predT (fun (_ : 'a) => c) s)%BMul = (exp c (size s))%ZR. + end Big. + + lemma ge0_size: 0 <= kvec. + + hint solve 0 : ge0_size. + + theory Vector. + lemma tofunv_prevector: forall (v : polyvec), prevector (tofunv v). + + lemma tofunvK: cancel tofunv offunv. + + lemma offunvK: forall (v : int -> poly), tofunv (offunv v) = vclamp v. + + op "_.[_]" (v : polyvec) (i : int) : poly = tofunv v i. + + lemma offunvE: forall (v : int -> poly) (i : int), 0 <= i && i < kvec => ((offunv v).[i])%Vector = v i. + + lemma getv_out: forall (v : polyvec) (i : int), ! (0 <= i && i < kvec) => (v.[i])%Vector = Rq.zero. + + lemma eq_vectorP: + forall (v1 v2 : polyvec), + v1 = v2 <=> forall (i : int), 0 <= i && i < kvec => (v1.[i])%Vector = (v2.[i])%Vector. + + lemma vectorW: + forall (P : polyvec -> bool), + (forall (f : int -> poly), prevector f => P (offunv f)) => forall (v : polyvec), P v. + + op vectc (c : poly) : polyvec = offunv (fun (_ : int) => c). + + abbrev zerov : polyvec = (vectc Rq.zero)%Vector. + + lemma offunCE: forall (c : poly) (i : int), 0 <= i && i < kvec => ((vectc c).[i])%Vector = c. + + lemma offun0E: forall (i : int), (Vector.zerov.[i])%Vector = Rq.zero. + + hint simplify. + + op [-] (v : polyvec) : polyvec = offunv (fun (i : int) => (&-) (v.[i])%Vector). + + lemma offunD: + forall (v1 v2 : polyvec) (i : int), + ((v1 + v2)%KMatrix.Vector.[i])%Vector = (v1.[i])%Vector &+ (v2.[i])%Vector. + + hint simplify. + + lemma offunN: forall (v : polyvec) (i : int), ((-v).[i])%Vector = (&-) (v.[i])%Vector. + + hint simplify. + + theory ZModule. + lemma nosmt addrA: associative KMatrix.Vector.(+). + + lemma nosmt addrC: commutative KMatrix.Vector.(+). + + lemma nosmt add0r: left_id Vector.zerov KMatrix.Vector.(+). + + lemma nosmt addNr: left_inverse Vector.zerov Vector.[-] KMatrix.Vector.(+). + + theory AddMonoid. + lemma addmA: associative KMatrix.Vector.(+). + + lemma addmC: commutative KMatrix.Vector.(+). + + lemma add0m: left_id Vector.zerov KMatrix.Vector.(+). + + lemma addm0: right_id Vector.zerov KMatrix.Vector.(+). + + lemma addmCA: left_commutative KMatrix.Vector.(+). + + lemma addmAC: right_commutative KMatrix.Vector.(+). + + lemma addmACA: interchange KMatrix.Vector.(+) KMatrix.Vector.(+). + + lemma iteropE: + forall (n : int) (x : polyvec), + iterop n KMatrix.Vector.(+) x Vector.zerov = iter n (((+) x))%KMatrix.Vector Vector.zerov. + end AddMonoid. + + abbrev (-) (x y : polyvec) : polyvec = (x + (-y)%Vector)%KMatrix.Vector. + + lemma nosmt addr0: right_id Vector.zerov KMatrix.Vector.(+). + + lemma nosmt addrN: right_inverse Vector.zerov Vector.[-] KMatrix.Vector.(+). + + lemma nosmt addrCA: left_commutative KMatrix.Vector.(+). + + lemma nosmt addrAC: right_commutative KMatrix.Vector.(+). + + lemma nosmt addrACA: interchange KMatrix.Vector.(+) KMatrix.Vector.(+). + + lemma nosmt subrr: forall (x : polyvec), x - x = Vector.zerov. + + lemma nosmt addKr: left_loop Vector.[-] KMatrix.Vector.(+). + + lemma nosmt addNKr: rev_left_loop Vector.[-] KMatrix.Vector.(+). + + lemma nosmt addrK: right_loop Vector.[-] KMatrix.Vector.(+). + + lemma nosmt addrNK: rev_right_loop Vector.[-] KMatrix.Vector.(+). + + lemma nosmt subrK: forall (x y : polyvec), (x - y + y)%KMatrix.Vector = x. + + lemma nosmt addrI: right_injective KMatrix.Vector.(+). + + lemma nosmt addIr: left_injective KMatrix.Vector.(+). + + lemma nosmt opprK: involutive Vector.[-]. + + lemma oppr_inj: injective Vector.[-]. + + lemma nosmt oppr0: (- Vector.zerov)%Vector = Vector.zerov. + + lemma oppr_eq0: forall (x : polyvec), (-x)%Vector = Vector.zerov <=> x = Vector.zerov. + + lemma nosmt subr0: forall (x : polyvec), x - Vector.zerov = x. + + lemma nosmt sub0r: forall (x : polyvec), Vector.zerov - x = (-x)%Vector. + + lemma nosmt opprD: forall (x y : polyvec), (- (x + y)%KMatrix.Vector)%Vector = (-x)%Vector - y. + + lemma nosmt opprB: forall (x y : polyvec), (- (x - y))%Vector = y - x. + + lemma nosmt subrACA: interchange (fun (x y : polyvec) => x - y) KMatrix.Vector.(+). + + lemma nosmt subr_eq: forall (x y z : polyvec), x - z = y <=> x = (y + z)%KMatrix.Vector. + + lemma nosmt subr_eq0: forall (x y : polyvec), x - y = Vector.zerov <=> x = y. + + lemma nosmt addr_eq0: forall (x y : polyvec), (x + y)%KMatrix.Vector = Vector.zerov <=> x = (-y)%Vector. + + lemma nosmt eqr_opp: forall (x y : polyvec), (-x)%Vector = (-y)%Vector <=> x = y. + + lemma eqr_oppLR: forall (x y : polyvec), (-x)%Vector = y <=> x = (-y)%Vector. + + lemma nosmt eqr_sub: + forall (x y z t : polyvec), x - y = z - t <=> (x + t)%KMatrix.Vector = (z + y)%KMatrix.Vector. + + lemma subr_add2r: forall (z x y : polyvec), (x + z)%KMatrix.Vector - (y + z)%KMatrix.Vector = x - y. + + op intmul (x : polyvec) (n : int) : polyvec = + if n < 0 then (- iterop (-n) KMatrix.Vector.(+) x Vector.zerov)%Vector + else iterop n KMatrix.Vector.(+) x Vector.zerov. + + lemma intmulpE: + forall (z : polyvec) (c : int), 0 <= c => intmul z c = iterop c KMatrix.Vector.(+) z Vector.zerov. + + lemma mulr0z: forall (x : polyvec), intmul x 0 = Vector.zerov. + + lemma mulr1z: forall (x : polyvec), intmul x 1 = x. + + lemma mulr2z: forall (x : polyvec), intmul x 2 = (x + x)%KMatrix.Vector. + + lemma mulrNz: forall (x : polyvec) (n : int), intmul x (-n) = (- intmul x n)%Vector. + + lemma mulrS: forall (x : polyvec) (n : int), 0 <= n => intmul x (n + 1) = (x + intmul x n)%KMatrix.Vector. + + lemma mulNrz: forall (x : polyvec) (n : int), intmul (-x)%Vector n = (- intmul x n)%Vector. + + lemma mulNrNz: forall (x : polyvec) (n : int), intmul (-x)%Vector (-n) = intmul x n. + + lemma mulrSz: forall (x : polyvec) (n : int), intmul x (n + 1) = (x + intmul x n)%KMatrix.Vector. + + lemma mulrDz: forall (x : polyvec) (n m : int), intmul x (n + m) = (intmul x n + intmul x m)%KMatrix.Vector. + end ZModule. + + lemma offunB: + forall (v1 v2 : polyvec) (i : int), ((v1 - v2)%ZModule.[i])%Vector = ((v1.[i])%Vector - (v2.[i])%Vector)%ZR. + + lemma dotpC: commutative dotp. + + lemma dotpDr: forall (v1 v2 v3 : polyvec), dotp v1 (v2 + v3)%KMatrix.Vector = dotp v1 v2 &+ dotp v1 v3. + + lemma dotpDl: forall (v1 v2 v3 : polyvec), dotp (v1 + v2)%KMatrix.Vector v3 = dotp v1 v3 &+ dotp v2 v3. + + op scalev (a : poly) (v : polyvec) : polyvec = offunv (fun (i : int) => a &* (v.[i])%Vector). + + abbrev ( ** ) : poly -> polyvec -> polyvec = Vector.scalev. + + lemma scalevE: forall (a : poly) (v : polyvec) (i : int), ((a ** v).[i])%Vector = a &* (v.[i])%Vector. + + lemma scalevDl: + forall (a b : poly) (v : polyvec), (a &+ b ** v)%Vector = ((a ** v)%Vector + (b ** v)%Vector)%KMatrix.Vector. + + lemma scalevDr: + forall (a : poly) (v w : polyvec), + (a ** (v + w)%KMatrix.Vector)%Vector = ((a ** v)%Vector + (a ** w)%Vector)%KMatrix.Vector. + + lemma scalevA: forall (a b : poly) (v : polyvec), (a &* b ** v)%Vector = (a ** (b ** v))%Vector. + + lemma scalevAC: forall (a b : poly) (v : polyvec), (a ** (b ** v))%Vector = (b ** (a ** v))%Vector. + end Vector. + + export Vector. + + theory Matrix. + abbrev mrange (i j : int) : bool = (mrange i j)%KMatrix.Matrix. + + lemma nosmt mrangeL: forall (i j : int), (mrange i j)%Matrix => 0 <= i && i < kvec. + + lemma nosmt mrangeR: forall (i j : int), (mrange i j)%Matrix => 0 <= j && j < kvec. + + lemma nosmt mrangeC: forall (i j : int), (mrange i j)%Matrix = (mrange j i)%Matrix. + + lemma tofunm_prematrix: forall (m : polymat), prematrix (tofunm m). + + lemma tofunmK: cancel tofunm offunm. + + lemma offunmK: forall (m : int -> int -> poly), tofunm (offunm m) = mclamp m. + + op "_.[_]" (m : polymat) (ij : int * int) : poly = tofunm m ij.`1 ij.`2. + + lemma offunmE: + forall (m : int -> int -> poly) (i j : int), (mrange i j)%Matrix => ((offunm m).[i, j])%Matrix = m i j. + + lemma getm_out: forall (m : polymat) (i j : int), ! (mrange i j)%Matrix => (m.[i, j])%Matrix = Rq.zero. + + lemma getm_outL: forall (m : polymat) (i j : int), ! (0 <= i && i < kvec) => (m.[i, j])%Matrix = Rq.zero. + + lemma getm_outR: forall (m : polymat) (i j : int), ! (0 <= j && j < kvec) => (m.[i, j])%Matrix = Rq.zero. + + lemma eq_matrixP: + forall (m1 m2 : polymat), + m1 = m2 <=> forall (i j : int), (mrange i j)%Matrix => (m1.[i, j])%Matrix = (m2.[i, j])%Matrix. + + lemma matrixW: + forall (P : polymat -> bool), + (forall (f : int -> int -> poly), prematrix f => P (offunm f)) => forall (v : polymat), P v. + + op matrixc (c : poly) : polymat = offunm (fun (_ _ : int) => c). + + op diagmx (v : polyvec) : polymat = offunm (fun (i j : int) => if i = j then (v.[i])%Vector else Rq.zero). + + abbrev diagc (c : poly) : polymat = (diagmx ((vectc c))%Vector)%Matrix. + + abbrev zerom : polymat = (matrixc Rq.zero)%Matrix. + + abbrev onem : polymat = (diagc Rq.one)%Matrix. + + lemma offunCE: forall (c : poly) (i j : int), (mrange i j)%Matrix => ((matrixc c).[i, j])%Matrix = c. + + lemma diagmxE: + forall (v : polyvec) (i j : int), ((diagmx v).[i, j])%Matrix = if i = j then (v.[i])%Vector else Rq.zero. + + lemma offun0E: forall (i j : int), (Matrix.zerom.[i, j])%Matrix = Rq.zero. + + lemma offun1E: + forall (i j : int), (mrange i j)%Matrix => (Matrix.onem.[i, j])%Matrix = if i = j then Rq.one else Rq.zero. + + lemma offun1_neqE: forall (i j : int), i <> j => (Matrix.onem.[i, j])%Matrix = Rq.zero. + + hint simplify. + + op (+) (m1 m2 : polymat) : polymat = offunm (fun (i j : int) => (m1.[i, j])%Matrix &+ (m2.[i, j])%Matrix). + + op [-] (m : polymat) : polymat = offunm (fun (i j : int) => (&-) (m.[i, j])%Matrix). + + lemma offunD: + forall (m1 m2 : polymat) (i j : int), ((m1 + m2).[i, j])%Matrix = (m1.[i, j])%Matrix &+ (m2.[i, j])%Matrix. + + hint simplify. + + lemma offunN: forall (m : polymat) (i j : int), ((-m).[i, j])%Matrix = (&-) (m.[i, j])%Matrix. + + hint simplify. + + theory ZModule. + lemma nosmt addrA: associative Matrix.(+). + + lemma nosmt addrC: commutative Matrix.(+). + + lemma nosmt add0r: left_id Matrix.zerom Matrix.(+). + + lemma nosmt addNr: left_inverse Matrix.zerom Matrix.[-] Matrix.(+). + + theory AddMonoid. + lemma addmA: associative Matrix.(+). + + lemma addmC: commutative Matrix.(+). + + lemma add0m: left_id Matrix.zerom Matrix.(+). + + lemma addm0: right_id Matrix.zerom Matrix.(+). + + lemma addmCA: left_commutative Matrix.(+). + + lemma addmAC: right_commutative Matrix.(+). + + lemma addmACA: interchange Matrix.(+) Matrix.(+). + + lemma iteropE: + forall (n : int) (x : polymat), iterop n Matrix.(+) x Matrix.zerom = iter n (((+) x))%Matrix Matrix.zerom. + end AddMonoid. + + abbrev (-) (x y : polymat) : polymat = (x + -y)%Matrix. + + lemma nosmt addr0: right_id Matrix.zerom Matrix.(+). + + lemma nosmt addrN: right_inverse Matrix.zerom Matrix.[-] Matrix.(+). + + lemma nosmt addrCA: left_commutative Matrix.(+). + + lemma nosmt addrAC: right_commutative Matrix.(+). + + lemma nosmt addrACA: interchange Matrix.(+) Matrix.(+). + + lemma nosmt subrr: forall (x : polymat), x - x = Matrix.zerom. + + lemma nosmt addKr: left_loop Matrix.[-] Matrix.(+). + + lemma nosmt addNKr: rev_left_loop Matrix.[-] Matrix.(+). + + lemma nosmt addrK: right_loop Matrix.[-] Matrix.(+). + + lemma nosmt addrNK: rev_right_loop Matrix.[-] Matrix.(+). + + lemma nosmt subrK: forall (x y : polymat), (x - y + y)%Matrix = x. + + lemma nosmt addrI: right_injective Matrix.(+). + + lemma nosmt addIr: left_injective Matrix.(+). + + lemma nosmt opprK: involutive Matrix.[-]. + + lemma oppr_inj: injective Matrix.[-]. + + lemma nosmt oppr0: (- Matrix.zerom)%Matrix = Matrix.zerom. + + lemma oppr_eq0: forall (x : polymat), (-x)%Matrix = Matrix.zerom <=> x = Matrix.zerom. + + lemma nosmt subr0: forall (x : polymat), x - Matrix.zerom = x. + + lemma nosmt sub0r: forall (x : polymat), Matrix.zerom - x = (-x)%Matrix. + + lemma nosmt opprD: forall (x y : polymat), (- (x + y))%Matrix = (-x)%Matrix - y. + + lemma nosmt opprB: forall (x y : polymat), (- (x - y))%Matrix = y - x. + + lemma nosmt subrACA: interchange (fun (x y : polymat) => x - y) Matrix.(+). + + lemma nosmt subr_eq: forall (x y z : polymat), x - z = y <=> x = (y + z)%Matrix. + + lemma nosmt subr_eq0: forall (x y : polymat), x - y = Matrix.zerom <=> x = y. + + lemma nosmt addr_eq0: forall (x y : polymat), (x + y)%Matrix = Matrix.zerom <=> x = (-y)%Matrix. + + lemma nosmt eqr_opp: forall (x y : polymat), (-x)%Matrix = (-y)%Matrix <=> x = y. + + lemma eqr_oppLR: forall (x y : polymat), (-x)%Matrix = y <=> x = (-y)%Matrix. + + lemma nosmt eqr_sub: forall (x y z t : polymat), x - y = z - t <=> (x + t)%Matrix = (z + y)%Matrix. + + lemma subr_add2r: forall (z x y : polymat), (x + z)%Matrix - (y + z)%Matrix = x - y. + + op intmul (x : polymat) (n : int) : polymat = + if n < 0 then (- iterop (-n) Matrix.(+) x Matrix.zerom)%Matrix else iterop n Matrix.(+) x Matrix.zerom. + + lemma intmulpE: forall (z : polymat) (c : int), 0 <= c => intmul z c = iterop c Matrix.(+) z Matrix.zerom. + + lemma mulr0z: forall (x : polymat), intmul x 0 = Matrix.zerom. + + lemma mulr1z: forall (x : polymat), intmul x 1 = x. + + lemma mulr2z: forall (x : polymat), intmul x 2 = (x + x)%Matrix. + + lemma mulrNz: forall (x : polymat) (n : int), intmul x (-n) = (- intmul x n)%Matrix. + + lemma mulrS: forall (x : polymat) (n : int), 0 <= n => intmul x (n + 1) = (x + intmul x n)%Matrix. + + lemma mulNrz: forall (x : polymat) (n : int), intmul (-x)%Matrix n = (- intmul x n)%Matrix. + + lemma mulNrNz: forall (x : polymat) (n : int), intmul (-x)%Matrix (-n) = intmul x n. + + lemma mulrSz: forall (x : polymat) (n : int), intmul x (n + 1) = (x + intmul x n)%Matrix. + + lemma mulrDz: forall (x : polymat) (n m : int), intmul x (n + m) = (intmul x n + intmul x m)%Matrix. + end ZModule. + + lemma offunB: + forall (m1 m2 : polymat) (i j : int), + ((m1 - m2)%ZModule.[i, j])%Matrix = ((m1.[i, j])%Matrix - (m2.[i, j])%Matrix)%ZR. + + op trace (m : polymat) : poly = (bigi predT (fun (i : int) => (m.[i, i])%Matrix) 0 kvec)%Big.BAdd. + + op trmx (m : polymat) : polymat = offunm (fun (i j : int) => (m.[j, i])%Matrix). + + lemma trmxE: forall (m : polymat) (i j : int), ((trmx m).[i, j])%Matrix = (m.[j, i])%Matrix. + + lemma trmxK: forall (m : polymat), (trmx ((trmx m))%Matrix)%Matrix = m. + + lemma trmx1: (trmx Matrix.onem)%Matrix = Matrix.onem. + + lemma trmxD: forall (m1 m2 : polymat), (trmx (m1 + m2)%Matrix)%Matrix = (trmx m1 + trmx m2)%Matrix. + + lemma trace_trmx: forall (m : polymat), (trace ((trmx m))%Matrix)%Matrix = (trace m)%Matrix. + + op ( * ) (m1 m2 : polymat) : polymat = + offunm + (fun (i j : int) => + (bigi predT (fun (k : int) => (m1.[i, k])%Matrix &* (m2.[k, j])%Matrix) 0 kvec)%Big.BAdd). + + lemma offunM: + forall (m1 m2 : polymat) (i j : int), + ((m1 * m2).[i, j])%Matrix = + (bigi predT (fun (k : int) => (m1.[i, k])%Matrix &* (m2.[k, j])%Matrix) 0 kvec)%Big.BAdd. + + hint simplify. + + lemma mulmx1: right_id Matrix.onem Matrix.( * ). + + lemma mul1mx: left_id Matrix.onem Matrix.( * ). + + lemma mulmxDl: forall (m1 m2 m : polymat), ((m1 + m2) * m)%Matrix = (m1 * m + m2 * m)%Matrix. + + lemma mulmxDr: forall (m1 m2 m : polymat), (m * (m1 + m2))%Matrix = (m * m1 + m * m2)%Matrix. + + lemma mulmxA: associative Matrix.( * ). + + lemma trmxM: forall (m1 m2 : polymat), (trmx (m1 * m2)%Matrix)%Matrix = (trmx m2 * trmx m1)%Matrix. + + op ( *^ ) (m : polymat) (v : polyvec) : polyvec = + offunv (fun (i : int) => (bigi predT (fun (j : int) => (m.[i, j])%Matrix &* (v.[j])%Vector) 0 kvec)%Big.BAdd). + + op ( ^* ) (v : polyvec) (m : polymat) : polyvec = + offunv (fun (j : int) => (bigi predT (fun (i : int) => (v.[i])%Vector &* (m.[i, j])%Matrix) 0 kvec)%Big.BAdd). + + lemma mulmxTv: forall (m : polymat) (v : polyvec), (trmx m *^ v)%Matrix = (v ^* m)%Matrix. + + lemma mulmxv0: forall (m : polymat), (m *^ Vector.zerov)%Matrix = Vector.zerov. + + lemma mulmx1v: forall (v : polyvec), (Matrix.onem *^ v)%Matrix = v. + + lemma mulmxvDl: + forall (m1 m2 : polymat) (v : polyvec), + ((m1 + m2) *^ v)%Matrix = ((m1 *^ v)%Matrix + (m2 *^ v)%Matrix)%KMatrix.Vector. + + lemma mulmxvDr: + forall (m : polymat) (v1 v2 : polyvec), + (m *^ (v1 + v2)%KMatrix.Vector)%Matrix = ((m *^ v1)%Matrix + (m *^ v2)%Matrix)%KMatrix.Vector. + + lemma mulmxvA: forall (m1 m2 : polymat) (v : polyvec), (m1 * m2 *^ v)%Matrix = (m1 *^ (m2 *^ v))%Matrix. + + lemma mulvmxT: forall (v : polyvec) (m : polymat), (v ^* trmx m)%Matrix = (m *^ v)%Matrix. + + lemma mulv0mx: forall (m : polymat), (Vector.zerov ^* m)%Matrix = Vector.zerov. + + lemma mulvmx1: forall (v : polyvec), (v ^* Matrix.onem)%Matrix = v. + + lemma mulvmxDr: + forall (v : polyvec) (m1 m2 : polymat), + (v ^* (m1 + m2))%Matrix = ((v ^* m1)%Matrix + (v ^* m2)%Matrix)%KMatrix.Vector. + + lemma mulvmxDl: + forall (v1 v2 : polyvec) (m : polymat), + ((v1 + v2)%KMatrix.Vector ^* m)%Matrix = ((v1 ^* m)%Matrix + (v2 ^* m)%Matrix)%KMatrix.Vector. + + lemma mulvmxA: forall (v : polyvec) (m1 m2 : polymat), (v ^* m1 ^* m2)%Matrix = (v ^* (m1 * m2))%Matrix. + + lemma mulmxvE: + forall (m : polymat) (v : polyvec) (i : int), + ((m *^ v)%Matrix.[i])%Vector = + (bigi predT (fun (j : int) => (m.[i, j])%Matrix &* (v.[j])%Vector) 0 kvec)%Big.BAdd. + + lemma mulvmxE: + forall (m : polymat) (v : polyvec) (j : int), + ((v ^* m)%Matrix.[j])%Vector = + (bigi predT (fun (i : int) => (v.[i])%Vector &* (m.[i, j])%Matrix) 0 kvec)%Big.BAdd. + + op colmx (v : polyvec) : polymat = offunm (fun (i _ : int) => (v.[i])%Vector). + + op rowmx (v : polyvec) : polymat = offunm (fun (_ j : int) => (v.[j])%Vector). + + lemma colmxT: forall (v : polyvec), (trmx ((colmx v))%Matrix)%Matrix = (rowmx v)%Matrix. + + lemma rowmxT: forall (v : polyvec), (trmx ((rowmx v))%Matrix)%Matrix = (colmx v)%Matrix. + + lemma colmxE: + forall (v : polyvec) (i j : int), 0 <= j && j < kvec => ((colmx v).[i, j])%Matrix = (v.[i])%Vector. + + lemma rowmxE: + forall (v : polyvec) (i j : int), 0 <= i && i < kvec => ((rowmx v).[i, j])%Matrix = (v.[j])%Vector. + + lemma colmx_mulmxv: forall (m : polymat) (v : polyvec), (colmx (m *^ v)%Matrix)%Matrix = (m * colmx v)%Matrix. + + lemma rowmx_mulvmx: forall (v : polyvec) (m : polymat), (rowmx (v ^* m)%Matrix)%Matrix = (rowmx v * m)%Matrix. + + lemma mulmx_diag: + forall (v1 v2 : polyvec), + (diagmx v1 * diagmx v2)%Matrix = + (diagmx (offunv (fun (i : int) => (v1.[i])%Vector &* (v2.[i])%Vector)))%Matrix. + + lemma dotp_tr: forall (v1 v2 : polyvec), dotp v1 v2 = (trace (diagmx v1 * diagmx v2)%Matrix)%Matrix. + + lemma dotp_mulmxv: forall (m : polymat) (v1 v2 : polyvec), dotp (m *^ v1)%Matrix v2 = dotp v1 (v2 ^* m)%Matrix. + + op dvector (d : poly distr) : polyvec distr = + dmap (djoin (nseq kvec d)) (fun (xs : poly list) => offunv (nth witness xs)). + + lemma dvector1E: + forall (d : poly distr) (v : polyvec), + mu1 ((dvector d))%Matrix v = + (bigi predT (fun (i : int) => mu1 d (v.[i])%Vector) 0 kvec)%StdBigop.Bigreal.BRM. + + lemma dvector_uni: forall (d : poly distr), is_uniform d => is_uniform ((dvector d))%Matrix. + + lemma dvector_ll: forall (d : poly distr), is_lossless d => is_lossless ((dvector d))%Matrix. + + lemma dvector_fu: forall (d : poly distr), is_full d => is_full ((dvector d))%Matrix. + + lemma dvector_funi: forall (d : poly distr), is_full d => is_uniform d => is_funiform ((dvector d))%Matrix. + + op dmatrix (d : poly distr) : polymat distr = + dmap (djoin (nseq kvec (djoin (nseq kvec d)))) + (fun (xs : poly list list) => offunm (fun (i j : int) => nth witness (nth witness xs j) i)). + + lemma dmatrix1E: + forall (d : poly distr) (m : polymat), + mu1 ((dmatrix d))%Matrix m = + (bigi predT + (fun (i : int) => (bigi predT (fun (j : int) => mu1 d (m.[i, j])%Matrix) 0 kvec)%StdBigop.Bigreal.BRM) 0 + kvec)%StdBigop.Bigreal.BRM. + + lemma dmatrix_dvector: + forall (d : poly distr), + (dmatrix d)%Matrix = + dmap (djoin (nseq kvec ((dvector d))%Matrix)) + (fun (vs : polyvec list) => offunm (fun (i j : int) => ((nth witness vs j).[i])%Vector)). + + lemma dmatrix_uni: forall (d : poly distr), is_uniform d => is_uniform ((dmatrix d))%Matrix. + + lemma dmatrix_ll: forall (d : poly distr), is_lossless d => is_lossless ((dmatrix d))%Matrix. + + lemma dmatrix_fu: forall (d : poly distr), is_full d => is_full ((dmatrix d))%Matrix. + + lemma dmatrix_funi: forall (d : poly distr), is_full d => is_uniform d => is_funiform ((dmatrix d))%Matrix. + end Matrix. + + export Matrix. + end Matrix_. + + instance ring with [()] poly + op rzero = Top.Rq.zero + op rone = Top.Rq.one + op add = Top.Rq.&+ + op opp = Top.Rq.&- + op mul = Top.Rq.&* + op expr = Top.MLWEPKEHash.MLWE_.Matrix_.ZR.exp + op ofint = Top.MLWEPKEHash.MLWE_.Matrix_.ZR.ofint + + instance poly with Top.Ring.ZModule.zmodule. + + instance poly with Top.Ring.ComRing.ring. + + instance poly with Top.Ring.IDomain.idomain. + + lemma duni_R_ll: is_lossless duni_R. + + hint solve 0 lossless : duni_R_ll. + + hint solve 0 random : duni_R_ll. + + lemma duni_R_uni: is_uniform duni_R. + + hint solve 0 random : duni_R_uni. + + lemma duni_R_fu: is_full duni_R. + + hint solve 0 random : duni_R_fu. + + lemma duni_R_funi: is_funiform duni_R. + + lemma dshort_R_ll: is_lossless dshort_R. + + hint solve 0 lossless : dshort_R_ll. + + hint solve 0 random : dshort_R_ll. + + op duni : polyvec distr = (dvector duni_R)%Matrix_.Matrix. + + op dshort : polyvec distr = (dvector dshort_R)%Matrix_.Matrix. + + lemma duni_ll: is_lossless duni. + + lemma duni_fu: is_full duni. + + lemma duni_uni: is_uniform duni. + + lemma duni_funi: is_funiform duni. + + lemma dshort_ll: is_lossless dshort. + + op duni_matrix : polymat distr = (dmatrix duni_R)%Matrix_.Matrix. + + lemma duni_matrix_ll: is_lossless duni_matrix. + + lemma duni_matrix_fu: is_full duni_matrix. + + lemma duni_matrix_uni: is_uniform duni_matrix. + + lemma duni_matrix_funi: is_funiform duni_matrix. + + module type Adv_T = { + proc guess(A : polymat, t : polyvec, uv : polyvec * poly) : bool {} + } + + abbrev m_transpose : polymat -> polymat = Matrix_.Matrix.trmx. + + abbrev (`<*>`) : polyvec -> polyvec -> poly = dotp. + + abbrev (&+) : poly -> poly -> poly = Rq.(&+). + + abbrev (&-) : poly -> poly -> poly = fun (x y : poly) => (x &+ ((&-) y)%Rq)%MLWE_. + + module MLWE(Adv : Adv_T) = { + proc main(b : bool) : bool = { + var s : polyvec; + var e : polyvec; + var _A : polymat; + var u0 : polyvec; + var u1 : polyvec; + var t : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + _A <$ duni_matrix; + s <$ dshort; + e <$ dshort; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + t <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ Adv.guess(_A, t, if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + lemma dseed_ll: is_lossless srand. + + hint solve 0 lossless : dseed_ll. + + hint solve 0 random : dseed_ll. + + module type HAdv_T = { + proc guess(sd : W8.t Array32.t, t : polyvec, uv : polyvec * poly) : bool {} + } + + module MLWE_H(Adv : HAdv_T) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var _A : polymat; + var u0 : polyvec; + var u1 : polyvec; + var t : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + sd <$ srand; + s <$ dshort; + e <$ dshort; + _A <- if tr then (trmx (H sd))%Matrix_.Matrix else H sd; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + t <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ Adv.guess(sd, t, if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + theory MLWE_ROM. + theory RO_H. + type in_t = W8.t Array32.t. + + type out_t = polymat. + + op dout (_ : W8.t Array32.t) : polymat distr = duni_matrix. + + type d_in_t = bool. + + type d_out_t = bool. + + module type RO = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + } + + module type RO_Distinguisher(G : RO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.rem, G.sample} + } + + module MainD(D : RO_Distinguisher, RO0 : RO) = { + proc distinguish(x : d_in_t) : d_out_t = { + var r : d_out_t; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + module type ROmap = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + + proc restrK() : (in_t, out_t) SmtMap.fmap {} + } + + module type FRO = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + + proc queried(x : in_t, f : PROM.flag) : bool {} + + proc allKnown() : (in_t, out_t) SmtMap.fmap {} + } + + module type FRO_Distinguisher(G : FRO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.rem, G.sample, G.queried, G.allKnown} + } + + abstract theory MkRO. + module RO = { + var m : (in_t, out_t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : in_t, y : out_t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : in_t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + RO.get(x); + } + + proc restrK() : (in_t, out_t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : in_t) : out_t = RO.get + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc rem(x : in_t) : unit = RO.rem + + proc sample(x : in_t) : unit = {} + }. + end MkRO. + + module RO = { + var m : (in_t, out_t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : in_t, y : out_t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : in_t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + RO.get(x); + } + + proc restrK() : (in_t, out_t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : in_t) : out_t = RO.get + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc rem(x : in_t) : unit = RO.rem + + proc sample(x : in_t) : unit = {} + }. + + module FRO = { + var m : (in_t, out_t * PROM.flag) SmtMap.fmap + + proc init() : unit = { + FRO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \in FRO.m)%SmtMap) + r <- (oget (FRO.m.[x])%SmtMap).`1; + FRO.m.[x] <- (r, PROM.Known); + + return r; + } + + proc set(x : in_t, y : out_t) : unit = { + FRO.m.[x] <- (y, PROM.Known); + } + + proc rem(x : in_t) : unit = { + FRO.m <- (rem FRO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + if ((x \notin FRO.m)%SmtMap) + FRO.m.[x] <- (c, PROM.Unknown); + } + + proc queried(x : in_t, f : PROM.flag) : bool = { + return (x \in FRO.m)%SmtMap /\ ((FRO.m.[x])%SmtMap \is f)%PROM; + } + + proc allKnown() : (in_t, out_t) SmtMap.fmap = { + return (restr PROM.Known FRO.m)%SmtMap; + } + }. + + lemma RO_FRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_get: + equiv[ RO.get ~ FRO.get : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> ={res} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_sample: + equiv[ RO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(RO).distinguish ~ D(FRO).distinguish : + ={arg, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_init_ll: islossless RO.init. + + lemma FRO_init_ll: islossless FRO.init. + + lemma FRO_in_dom_ll: islossless FRO.queried. + + lemma FRO_restrK_ll: islossless FRO.allKnown. + + lemma RO_set_ll: islossless RO.set. + + lemma FRO_set_ll: islossless FRO.set. + + lemma RO_get_ll: (forall (x : in_t), is_lossless (dout x)) => islossless RO.get. + + lemma FRO_get_ll: (forall (x : in_t), is_lossless (dout x)) => islossless FRO.get. + + lemma RO_sample_ll: (forall (x : in_t), is_lossless (dout x)) => islossless RO.sample. + + lemma FRO_sample_ll: (forall (x : in_t), is_lossless (dout x)) => islossless FRO.sample. + + module type RM_Distinguisher(G : ROmap) = { + proc distinguish(_ : d_in_t) : d_out_t {G.get, G.sample, G.restrK} + } + + module MainRM(D : RM_Distinguisher, RO0 : ROmap) = { + proc distinguish(x : d_in_t) : d_out_t = { + var r : d_out_t; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + lemma fcoll_bound ['rT]: + forall (f : out_t -> 'rT) (Pc : real), + 0%r <= Pc => + (forall (x1 x2 : in_t) (y : 'rT), y \in dmap (dout x1) f => mu1 (dmap (dout x2) f) y <= Pc) => + forall (q : int), + 0 <= q => + forall (D(G : ROmap) <: RM_Distinguisher{+all mem, -RO} ) &m (z : d_in_t), + Pr[MainRM(D, RO).distinguish(z) @ &m : (fcoll f RO.m)%SmtMap /\ (fsize RO.m)%SmtMap <= q] <= + (q * (q - 1))%r / 2%r * Pc. + + theory FullEager. + abstract theory EagerCore. + axiom dout_ll: forall (x : in_t), is_lossless (dout x). + + module type Orcl = { + proc f(x : in_t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : in_t list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : in_t list, l2 : in_t list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : in_t, l : in_t list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : in_t, t2 : in_t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : in_t, t2 : in_t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : in_t list) (i : in_t) (s2 : in_t list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : in_t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : in_t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : in_t, y : out_t) : unit = FRO.set + + proc rem(x : in_t) : unit = FRO.rem + + proc sample(x : in_t) : unit = FRO.sample + + proc queried(x : in_t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (in_t, out_t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : in_t) (mx1 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : in_t) (mx1 mx2 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : in_t) (r1 : out_t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + end FullEager. + + abstract theory FinEager. + abstract theory EagerCore. + axiom dout_ll: forall (x : in_t), is_lossless (dout x). + + module type Orcl = { + proc f(x : in_t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : in_t list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : in_t list, l2 : in_t list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : in_t, l : in_t list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : in_t, t2 : in_t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : in_t, t2 : in_t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : in_t list) (i : in_t) (s2 : in_t list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : in_t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : in_t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : in_t, y : out_t) : unit = FRO.set + + proc rem(x : in_t) : unit = FRO.rem + + proc sample(x : in_t) : unit = FRO.sample + + proc queried(x : in_t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (in_t, out_t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : in_t) (mx1 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : in_t) (mx1 mx2 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : in_t) (r1 : out_t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + theory FinFrom. + type t = in_t. + + op enum : t list. + + op card : int = size enum. + + axiom enum_spec: forall (x : t), count (pred1 x) enum = 1. + + lemma enumP: forall (x : t), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : t list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinFrom. + + module FinRO = { + proc rem(x : in_t) : unit = RO.rem + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc init() : unit = { + var l : FinFrom.t list; + + l <- FinFrom.enum; + RO.init(); + while (l <> []){ + RO.sample(head witness l); + l <- behead l; + } + } + + proc get(x : in_t) : out_t = { + return oget (RO.m.[x])%SmtMap; + } + + proc sample(x : in_t) : unit = {} + }. + + module type FinRO_Distinguisher(G : RO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.sample} + } + + lemma RO_FinRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ MainD(D, RO).distinguish ~ MainD(D, FinRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_RO_FinRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ) &m (x : d_in_t) (p : + d_out_t -> bool), + Pr[MainD(D, RO).distinguish(x) @ &m : p res] = Pr[MainD(D, FinRO).distinguish(x) @ &m : p res]. + + theory MUniFinFun. + op mfun ['u] (d : in_t -> 'u distr) (f : in_t -> 'u) : real = + (big predT (fun (x : in_t) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma mfunE ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mfun d f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + lemma isdistr_dfun ['u]: forall (d : in_t -> 'u distr), isdistr (mfun d). + + op dfun ['u] (d : in_t -> 'u distr) : (in_t -> 'u) distr = mk (mfun d). + + lemma dfun1E ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mu1 (dfun d) f = (big predT (fun (x : in_t) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun1E_djoin ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mu1 (dfun d) f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + op tofun ['u] (us : 'u list) (x : in_t) : 'u = nth witness us (index x FinFrom.enum). + + op offun ['u] (f : in_t -> 'u) : 'u list = map f FinFrom.enum. + + lemma offunK ['u]: cancel offun tofun. + + lemma tofunK ['u]: forall (us : 'u list), size us = FinFrom.card => offun (tofun us) = us. + + lemma dfun_dmap ['u]: forall (d : in_t -> 'u distr), dfun d = dmap (djoinmap d FinFrom.enum) tofun. + + lemma dfun_supp ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), f \in dfun d <=> forall (x : in_t), f x \in d x. + + lemma dfun_fu ['u]: forall (d : in_t -> 'u distr), (forall (x : in_t), is_full (d x)) => is_full (dfun d). + + lemma dfun_uni ['u]: + forall (d : in_t -> 'u distr), (forall (x : in_t), is_uniform (d x)) => is_uniform (dfun d). + + lemma dfun_funi ['u]: + forall (d : in_t -> 'u distr), + (forall (x : in_t), is_uniform (d x)) => (forall (x : in_t), is_full (d x)) => is_funiform (dfun d). + + lemma dfunE_djoin ['u]: + forall (du : in_t -> 'u distr) (E : (in_t -> 'u) -> bool), + mu (dfun du) E = mu (djoinmap du FinFrom.enum) (E \o tofun). + + lemma dfunE ['u]: + forall (du : in_t -> 'u distr) (p : in_t -> 'u -> bool), + mu (dfun du) (fun (f : in_t -> 'u) => forall (x : in_t), p x (f x)) = + (big predT (fun (x : in_t) => mu (du x) (p x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma weight_dfun ['u]: + forall (df : in_t -> 'u distr), + weight (dfun df) = (big predT (fun (x : in_t) => weight (df x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun_ll ['u]: + forall (d : in_t -> 'u distr), (forall (x : in_t), is_lossless (d x)) => is_lossless (dfun d). + + lemma dlet_dfun ['u, 'v]: + forall (d1 : in_t -> 'u distr) (d2 : in_t -> 'u -> 'v distr), + dlet (dfun d1) (fun (f1 : in_t -> 'u) => dfun (fun (x : in_t) => d2 x (f1 x))) = + dfun (fun (x : in_t) => dlet (d1 x) (fun (x1 : 'u) => d2 x x1)). + + lemma dfun_unit ['u]: forall (f : in_t -> 'u), dfun (fun (x : in_t) => dunit (f x)) = dunit f. + + lemma dmap_dfun ['u, 'v]: + forall (d : in_t -> 'u distr) (F : in_t -> 'u -> 'v), + dmap (dfun d) (fun (f : in_t -> 'u) (x : in_t) => F x (f x)) = + dfun (fun (x : in_t) => dmap (d x) (fun (fx : 'u) => F x fx)). + + lemma dfun1E_fix1 ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t) (f : in_t -> 'u), + mu1 (dfun d) f = mu1 (d x0) (f x0) * mu1 (dfun d.[x0 <- dunit (f x0)]) f. + + lemma dlet_dfun_fupdate_ll ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t) (v : 'u), + dlet (dfun d.[x0 <- dunit v]) (fun (f : in_t -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + dfun d. + + lemma dfunE_dlet_fix1 ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t), dfun d = dlet (d x0) (fun (v : 'u) => dfun d.[x0 <- dunit v]). + + lemma dlet_dfun_update ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t), + dlet (dfun d) (fun (f : in_t -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + (weight (d x0) \cdot dfun d). + + lemma dfun_projE ['u]: + forall (df : in_t -> 'u distr) (x : in_t), + dmap (dfun df) (fun (f : in_t -> 'u) => f x) = (weight (dfun df) / weight (df x) \cdot df x). + + lemma eq_from_dfun_proj_eq ['u]: + forall (df1 df2 : in_t -> 'u distr), + (forall (x : in_t), is_lossless (df1 x)) => + (forall (x : in_t), is_lossless (df2 x)) => + (forall (x : in_t), + dmap (dfun df1) (fun (f : in_t -> 'u) => f x) = dmap (dfun df2) (fun (f : in_t -> 'u) => f x)) => + df1 == df2. + + lemma dfun_prod1E ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr) (f : + in_t -> 'u) (g : in_t -> 'v), + mu1 (dfun (fun (x : in_t) => df x `*` dg x)) (fun (x : in_t) => (f x, g x)) = + mu1 (dfun df) f * mu1 (dfun dg) g. + + lemma dprod_funE ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr), + dfun df `*` dfun dg = + dmap (dfun (fun (x : in_t) => df x `*` dg x)) + (fun (fg : in_t -> 'u * 'v) => ((fun (p : 'u * 'v) => p.`1) \o fg, (fun (p : 'u * 'v) => p.`2) \o fg)). + + lemma dfun_prodE ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr), + dfun (fun (x : in_t) => df x `*` dg x) = + dmap (dfun df `*` dfun dg) (fun (fg : (in_t -> 'u) * (in_t -> 'v)) (x : in_t) => (fg.`1 x, fg.`2 x)). + + lemma dfun_condE ['u]: + forall (X : in_t -> bool) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dmap (dfun dt `*` dfun df) + (fun (tf : (in_t -> 'u) * (in_t -> 'u)) (x : in_t) => if X x then tf.`1 x else tf.`2 x) = + dfun (fun (x : in_t) => if X x then dt x else df x). + + lemma dlet_dfun_cond ['u]: + forall (dX : in_t -> bool distr) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dX x)) => + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dlet (dfun dX) (fun (X : in_t -> bool) => dfun (fun (x : in_t) => if X x then dt x else df x)) = + dfun (fun (x : in_t) => dlet (dX x) (fun (b : bool) => if b then dt x else df x)). + + lemma dfun_dcondE ['u]: + forall (dX : in_t -> bool distr) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dX x)) => + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : in_t -> bool) => + dmap (dfun dt `*` dfun df) + (fun (tf : (in_t -> 'u) * (in_t -> 'u)) (x : in_t) => if X x then tf.`1 x else tf.`2 x)) = + dfun (fun (x : in_t) => (mu1 (dX x) true \cdot dt x) + (mu1 (dX x) false \cdot df x)). + end MUniFinFun. + + module FunRO = { + var f : in_t -> out_t + + proc init() : unit = { + FunRO.f <$ (dfun dout)%MUniFinFun; + } + + proc get(x : in_t) : out_t = { + return FunRO.f x; + } + + proc set(x : in_t, y : out_t) : unit = { + FunRO.f <- fun (z : in_t) => if z = x then y else FunRO.f z; + } + + proc sample(x : in_t) : unit = {} + + proc rem(x : in_t) : unit = {} + }. + + lemma FinRO_FunRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ), + equiv[ MainD(D, FinRO).distinguish ~ MainD(D, FunRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_FinRO_FunRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ) &m (x : d_in_t) + (p : d_out_t -> bool), + Pr[MainD(D, FinRO).distinguish(x) @ &m : p res] = Pr[MainD(D, FunRO).distinguish(x) @ &m : p res]. + end FinEager. + end RO_H. + + module type Ideal_RO = { + proc get(x : RO_H.in_t) : RO_H.out_t {} + } + + module type ROAdv_T(O : Ideal_RO) = { + proc guess(sd : W8.t Array32.t, t : polyvec, uv : polyvec * poly) : bool {O.get} + } + + module MLWE_RO(Adv : ROAdv_T, O : RO_H.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var _A : RO_H.out_t; + var u0 : polyvec; + var u1 : polyvec; + var t : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + O.init(); + sd <$ srand; + s <$ dshort; + e <$ dshort; + _A <@ O.get(sd); + _A <- if tr then (trmx _A)%Matrix_.Matrix else _A; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + t <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ Adv(O).guess(sd, t, if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + theory MLWE_vs_MLWE_ROM. + module B(A : ROAdv_T, O : RO_H.RO) = { + var _sd : W8.t Array32.t + + var __A : polymat + + module FakeRO = { + proc get(sd : W8.t Array32.t) : polymat = { + var _Ares : polymat; + + _Ares <- B.__A; + if (sd <> B._sd) { + O.sample(sd); + _Ares <@ O.get(sd); + } + + return _Ares; + } + } + + proc guess(_A : polymat, t : polyvec, uv : polyvec * poly) : bool = { + var sd : W8.t Array32.t; + var b : bool; + + sd <$ srand; + B._sd <- sd; + B.__A <- _A; + O.init(); + b <@ A(B(A, O).FakeRO).guess(sd, t, uv); + + return b; + } + }. + + module Bt(A : ROAdv_T, O : RO_H.RO) = { + var _sd : W8.t Array32.t + + var __A : polymat + + module FakeRO = { + proc get(sd : W8.t Array32.t) : polymat = { + var _Ares : polymat; + + _Ares <- Bt.__A; + if (sd <> Bt._sd) { + O.sample(sd); + _Ares <@ O.get(sd); + } + + return _Ares; + } + } + + proc guess(_A : polymat, t : polyvec, uv : polyvec * poly) : bool = { + var sd : W8.t Array32.t; + var b : bool; + + sd <$ srand; + Bt._sd <- sd; + Bt.__A <- (trmx _A)%Matrix_.Matrix; + O.init(); + b <@ A(Bt(A, O).FakeRO).guess(sd, t, uv); + + return b; + } + }. + + lemma MLWE_RO_equiv: + forall (b : bool) &m (A(O : Ideal_RO) <: ROAdv_T{+all mem, -RO_H.LRO, -B} ), + Pr[MLWE_RO(A, RO_H.LRO).main(false, b) @ &m : res] = Pr[MLWE(B(A, RO_H.LRO)).main(b) @ &m : res]. + + lemma MLWE_RO_equiv_t: + forall (b : bool) &m (A(O : Ideal_RO) <: ROAdv_T{+all mem, -RO_H.LRO, -Bt} ), + Pr[MLWE_RO(A, RO_H.LRO).main(true, b) @ &m : res] = Pr[MLWE(Bt(A, RO_H.LRO)).main(b) @ &m : res]. + end MLWE_vs_MLWE_ROM. + end MLWE_ROM. + + theory MLWE_SMP. + theory RO_SMP. + type in_t. + + type out_t. + + op dout : in_t -> out_t distr. + + type d_in_t. + + type d_out_t. + + module type RO = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + } + + module type RO_Distinguisher(G : RO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.rem, G.sample} + } + + module MainD(D : RO_Distinguisher, RO0 : RO) = { + proc distinguish(x : d_in_t) : d_out_t = { + var r : d_out_t; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + module type ROmap = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + + proc restrK() : (in_t, out_t) SmtMap.fmap {} + } + + module type FRO = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + + proc queried(x : in_t, f : PROM.flag) : bool {} + + proc allKnown() : (in_t, out_t) SmtMap.fmap {} + } + + module type FRO_Distinguisher(G : FRO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.rem, G.sample, G.queried, G.allKnown} + } + + abstract theory MkRO. + module RO = { + var m : (in_t, out_t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : in_t, y : out_t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : in_t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + RO.get(x); + } + + proc restrK() : (in_t, out_t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : in_t) : out_t = RO.get + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc rem(x : in_t) : unit = RO.rem + + proc sample(x : in_t) : unit = {} + }. + end MkRO. + + module RO = { + var m : (in_t, out_t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : in_t, y : out_t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : in_t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + RO.get(x); + } + + proc restrK() : (in_t, out_t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : in_t) : out_t = RO.get + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc rem(x : in_t) : unit = RO.rem + + proc sample(x : in_t) : unit = {} + }. + + module FRO = { + var m : (in_t, out_t * PROM.flag) SmtMap.fmap + + proc init() : unit = { + FRO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \in FRO.m)%SmtMap) + r <- (oget (FRO.m.[x])%SmtMap).`1; + FRO.m.[x] <- (r, PROM.Known); + + return r; + } + + proc set(x : in_t, y : out_t) : unit = { + FRO.m.[x] <- (y, PROM.Known); + } + + proc rem(x : in_t) : unit = { + FRO.m <- (rem FRO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + if ((x \notin FRO.m)%SmtMap) + FRO.m.[x] <- (c, PROM.Unknown); + } + + proc queried(x : in_t, f : PROM.flag) : bool = { + return (x \in FRO.m)%SmtMap /\ ((FRO.m.[x])%SmtMap \is f)%PROM; + } + + proc allKnown() : (in_t, out_t) SmtMap.fmap = { + return (restr PROM.Known FRO.m)%SmtMap; + } + }. + + lemma RO_FRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_get: + equiv[ RO.get ~ FRO.get : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> ={res} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_sample: + equiv[ RO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(RO).distinguish ~ D(FRO).distinguish : + ={arg, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_init_ll: islossless RO.init. + + lemma FRO_init_ll: islossless FRO.init. + + lemma FRO_in_dom_ll: islossless FRO.queried. + + lemma FRO_restrK_ll: islossless FRO.allKnown. + + lemma RO_set_ll: islossless RO.set. + + lemma FRO_set_ll: islossless FRO.set. + + lemma RO_get_ll: (forall (x : in_t), is_lossless (dout x)) => islossless RO.get. + + lemma FRO_get_ll: (forall (x : in_t), is_lossless (dout x)) => islossless FRO.get. + + lemma RO_sample_ll: (forall (x : in_t), is_lossless (dout x)) => islossless RO.sample. + + lemma FRO_sample_ll: (forall (x : in_t), is_lossless (dout x)) => islossless FRO.sample. + + module type RM_Distinguisher(G : ROmap) = { + proc distinguish(_ : d_in_t) : d_out_t {G.get, G.sample, G.restrK} + } + + module MainRM(D : RM_Distinguisher, RO0 : ROmap) = { + proc distinguish(x : d_in_t) : d_out_t = { + var r : d_out_t; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + lemma fcoll_bound ['rT]: + forall (f : out_t -> 'rT) (Pc : real), + 0%r <= Pc => + (forall (x1 x2 : in_t) (y : 'rT), y \in dmap (dout x1) f => mu1 (dmap (dout x2) f) y <= Pc) => + forall (q : int), + 0 <= q => + forall (D(G : ROmap) <: RM_Distinguisher{+all mem, -RO} ) &m (z : d_in_t), + Pr[MainRM(D, RO).distinguish(z) @ &m : (fcoll f RO.m)%SmtMap /\ (fsize RO.m)%SmtMap <= q] <= + (q * (q - 1))%r / 2%r * Pc. + + theory FullEager. + abstract theory EagerCore. + axiom dout_ll: forall (x : in_t), is_lossless (dout x). + + module type Orcl = { + proc f(x : in_t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : in_t list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : in_t list, l2 : in_t list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : in_t, l : in_t list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : in_t, t2 : in_t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : in_t, t2 : in_t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : in_t list) (i : in_t) (s2 : in_t list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : in_t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : in_t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : in_t, y : out_t) : unit = FRO.set + + proc rem(x : in_t) : unit = FRO.rem + + proc sample(x : in_t) : unit = FRO.sample + + proc queried(x : in_t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (in_t, out_t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : in_t) (mx1 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : in_t) (mx1 mx2 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : in_t) (r1 : out_t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + end FullEager. + + abstract theory FinEager. + abstract theory EagerCore. + axiom dout_ll: forall (x : in_t), is_lossless (dout x). + + module type Orcl = { + proc f(x : in_t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : in_t list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : in_t list, l2 : in_t list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : in_t, l : in_t list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : in_t, t2 : in_t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : in_t, t2 : in_t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : in_t list) (i : in_t) (s2 : in_t list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : in_t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : in_t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : in_t, y : out_t) : unit = FRO.set + + proc rem(x : in_t) : unit = FRO.rem + + proc sample(x : in_t) : unit = FRO.sample + + proc queried(x : in_t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (in_t, out_t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : in_t) (mx1 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : in_t) (mx1 mx2 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : in_t) (r1 : out_t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + theory FinFrom. + type t = in_t. + + op enum : t list. + + op card : int = size enum. + + axiom enum_spec: forall (x : t), count (pred1 x) enum = 1. + + lemma enumP: forall (x : t), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : t list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinFrom. + + module FinRO = { + proc rem(x : in_t) : unit = RO.rem + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc init() : unit = { + var l : FinFrom.t list; + + l <- FinFrom.enum; + RO.init(); + while (l <> []){ + RO.sample(head witness l); + l <- behead l; + } + } + + proc get(x : in_t) : out_t = { + return oget (RO.m.[x])%SmtMap; + } + + proc sample(x : in_t) : unit = {} + }. + + module type FinRO_Distinguisher(G : RO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.sample} + } + + lemma RO_FinRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ MainD(D, RO).distinguish ~ MainD(D, FinRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_RO_FinRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ) &m (x : d_in_t) (p : + d_out_t -> bool), + Pr[MainD(D, RO).distinguish(x) @ &m : p res] = Pr[MainD(D, FinRO).distinguish(x) @ &m : p res]. + + theory MUniFinFun. + op mfun ['u] (d : in_t -> 'u distr) (f : in_t -> 'u) : real = + (big predT (fun (x : in_t) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma mfunE ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mfun d f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + lemma isdistr_dfun ['u]: forall (d : in_t -> 'u distr), isdistr (mfun d). + + op dfun ['u] (d : in_t -> 'u distr) : (in_t -> 'u) distr = mk (mfun d). + + lemma dfun1E ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mu1 (dfun d) f = (big predT (fun (x : in_t) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun1E_djoin ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mu1 (dfun d) f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + op tofun ['u] (us : 'u list) (x : in_t) : 'u = nth witness us (index x FinFrom.enum). + + op offun ['u] (f : in_t -> 'u) : 'u list = map f FinFrom.enum. + + lemma offunK ['u]: cancel offun tofun. + + lemma tofunK ['u]: forall (us : 'u list), size us = FinFrom.card => offun (tofun us) = us. + + lemma dfun_dmap ['u]: forall (d : in_t -> 'u distr), dfun d = dmap (djoinmap d FinFrom.enum) tofun. + + lemma dfun_supp ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), f \in dfun d <=> forall (x : in_t), f x \in d x. + + lemma dfun_fu ['u]: forall (d : in_t -> 'u distr), (forall (x : in_t), is_full (d x)) => is_full (dfun d). + + lemma dfun_uni ['u]: + forall (d : in_t -> 'u distr), (forall (x : in_t), is_uniform (d x)) => is_uniform (dfun d). + + lemma dfun_funi ['u]: + forall (d : in_t -> 'u distr), + (forall (x : in_t), is_uniform (d x)) => (forall (x : in_t), is_full (d x)) => is_funiform (dfun d). + + lemma dfunE_djoin ['u]: + forall (du : in_t -> 'u distr) (E : (in_t -> 'u) -> bool), + mu (dfun du) E = mu (djoinmap du FinFrom.enum) (E \o tofun). + + lemma dfunE ['u]: + forall (du : in_t -> 'u distr) (p : in_t -> 'u -> bool), + mu (dfun du) (fun (f : in_t -> 'u) => forall (x : in_t), p x (f x)) = + (big predT (fun (x : in_t) => mu (du x) (p x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma weight_dfun ['u]: + forall (df : in_t -> 'u distr), + weight (dfun df) = (big predT (fun (x : in_t) => weight (df x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun_ll ['u]: + forall (d : in_t -> 'u distr), (forall (x : in_t), is_lossless (d x)) => is_lossless (dfun d). + + lemma dlet_dfun ['u, 'v]: + forall (d1 : in_t -> 'u distr) (d2 : in_t -> 'u -> 'v distr), + dlet (dfun d1) (fun (f1 : in_t -> 'u) => dfun (fun (x : in_t) => d2 x (f1 x))) = + dfun (fun (x : in_t) => dlet (d1 x) (fun (x1 : 'u) => d2 x x1)). + + lemma dfun_unit ['u]: forall (f : in_t -> 'u), dfun (fun (x : in_t) => dunit (f x)) = dunit f. + + lemma dmap_dfun ['u, 'v]: + forall (d : in_t -> 'u distr) (F : in_t -> 'u -> 'v), + dmap (dfun d) (fun (f : in_t -> 'u) (x : in_t) => F x (f x)) = + dfun (fun (x : in_t) => dmap (d x) (fun (fx : 'u) => F x fx)). + + lemma dfun1E_fix1 ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t) (f : in_t -> 'u), + mu1 (dfun d) f = mu1 (d x0) (f x0) * mu1 (dfun d.[x0 <- dunit (f x0)]) f. + + lemma dlet_dfun_fupdate_ll ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t) (v : 'u), + dlet (dfun d.[x0 <- dunit v]) (fun (f : in_t -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + dfun d. + + lemma dfunE_dlet_fix1 ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t), dfun d = dlet (d x0) (fun (v : 'u) => dfun d.[x0 <- dunit v]). + + lemma dlet_dfun_update ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t), + dlet (dfun d) (fun (f : in_t -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + (weight (d x0) \cdot dfun d). + + lemma dfun_projE ['u]: + forall (df : in_t -> 'u distr) (x : in_t), + dmap (dfun df) (fun (f : in_t -> 'u) => f x) = (weight (dfun df) / weight (df x) \cdot df x). + + lemma eq_from_dfun_proj_eq ['u]: + forall (df1 df2 : in_t -> 'u distr), + (forall (x : in_t), is_lossless (df1 x)) => + (forall (x : in_t), is_lossless (df2 x)) => + (forall (x : in_t), + dmap (dfun df1) (fun (f : in_t -> 'u) => f x) = dmap (dfun df2) (fun (f : in_t -> 'u) => f x)) => + df1 == df2. + + lemma dfun_prod1E ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr) (f : + in_t -> 'u) (g : in_t -> 'v), + mu1 (dfun (fun (x : in_t) => df x `*` dg x)) (fun (x : in_t) => (f x, g x)) = + mu1 (dfun df) f * mu1 (dfun dg) g. + + lemma dprod_funE ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr), + dfun df `*` dfun dg = + dmap (dfun (fun (x : in_t) => df x `*` dg x)) + (fun (fg : in_t -> 'u * 'v) => ((fun (p : 'u * 'v) => p.`1) \o fg, (fun (p : 'u * 'v) => p.`2) \o fg)). + + lemma dfun_prodE ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr), + dfun (fun (x : in_t) => df x `*` dg x) = + dmap (dfun df `*` dfun dg) (fun (fg : (in_t -> 'u) * (in_t -> 'v)) (x : in_t) => (fg.`1 x, fg.`2 x)). + + lemma dfun_condE ['u]: + forall (X : in_t -> bool) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dmap (dfun dt `*` dfun df) + (fun (tf : (in_t -> 'u) * (in_t -> 'u)) (x : in_t) => if X x then tf.`1 x else tf.`2 x) = + dfun (fun (x : in_t) => if X x then dt x else df x). + + lemma dlet_dfun_cond ['u]: + forall (dX : in_t -> bool distr) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dX x)) => + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dlet (dfun dX) (fun (X : in_t -> bool) => dfun (fun (x : in_t) => if X x then dt x else df x)) = + dfun (fun (x : in_t) => dlet (dX x) (fun (b : bool) => if b then dt x else df x)). + + lemma dfun_dcondE ['u]: + forall (dX : in_t -> bool distr) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dX x)) => + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : in_t -> bool) => + dmap (dfun dt `*` dfun df) + (fun (tf : (in_t -> 'u) * (in_t -> 'u)) (x : in_t) => if X x then tf.`1 x else tf.`2 x)) = + dfun (fun (x : in_t) => (mu1 (dX x) true \cdot dt x) + (mu1 (dX x) false \cdot df x)). + end MUniFinFun. + + module FunRO = { + var f : in_t -> out_t + + proc init() : unit = { + FunRO.f <$ (dfun dout)%MUniFinFun; + } + + proc get(x : in_t) : out_t = { + return FunRO.f x; + } + + proc set(x : in_t, y : out_t) : unit = { + FunRO.f <- fun (z : in_t) => if z = x then y else FunRO.f z; + } + + proc sample(x : in_t) : unit = {} + + proc rem(x : in_t) : unit = {} + }. + + lemma FinRO_FunRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ), + equiv[ MainD(D, FinRO).distinguish ~ MainD(D, FunRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_FinRO_FunRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ) &m (x : d_in_t) + (p : d_out_t -> bool), + Pr[MainD(D, FinRO).distinguish(x) @ &m : p res] = Pr[MainD(D, FunRO).distinguish(x) @ &m : p res]. + end FinEager. + end RO_SMP. + + module type SMP_RO = { + proc get(x : RO_SMP.in_t) : RO_SMP.out_t {} + } + + module type Sampler(O : SMP_RO) = { + proc sampleA(sd : W8.t Array32.t) : polymat {O.get} + + proc sampleAT(sd : W8.t Array32.t) : polymat {O.get} + } + + module type PSampler = { + proc sampleA(sd : W8.t Array32.t) : polymat {} + + proc sampleAT(sd : W8.t Array32.t) : polymat {} + } + + module type SAdv_T(O : SMP_RO) = { + proc interact(sd : W8.t Array32.t, t : polyvec) : unit {O.get} + + proc guess(uv : polyvec * poly) : bool {O.get} + } + + module MLWE_SMP(Adv : SAdv_T, S : Sampler, O : RO_SMP.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var _A : polymat; + var u0 : polyvec; + var u1 : polyvec; + var t : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + O.init(); + sd <$ srand; + t <$ duni; + Adv(O).interact(sd, t); + if (tr) + _A <@ S(O).sampleAT(sd); + else + _A <@ S(O).sampleA(sd); + s <$ dshort; + e <$ dshort; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ Adv(O).guess(if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + end MLWE_SMP. + + theory SMP_vs_ROM. + theory MLWE_SMP. + theory RO_SMP. + type in_t = W8.t Array32.t. + + type out_t = polymat. + + op dout (_ : W8.t Array32.t) : polymat distr = duni_matrix. + + type d_in_t = bool. + + type d_out_t = bool. + + module type RO = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + } + + module type RO_Distinguisher(G : RO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.rem, G.sample} + } + + module MainD(D : RO_Distinguisher, RO0 : RO) = { + proc distinguish(x : d_in_t) : d_out_t = { + var r : d_out_t; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + module type ROmap = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + + proc restrK() : (in_t, out_t) SmtMap.fmap {} + } + + module type FRO = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + + proc queried(x : in_t, f : PROM.flag) : bool {} + + proc allKnown() : (in_t, out_t) SmtMap.fmap {} + } + + module type FRO_Distinguisher(G : FRO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.rem, G.sample, G.queried, G.allKnown} + } + + abstract theory MkRO. + module RO = { + var m : (in_t, out_t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : in_t, y : out_t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : in_t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + RO.get(x); + } + + proc restrK() : (in_t, out_t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : in_t) : out_t = RO.get + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc rem(x : in_t) : unit = RO.rem + + proc sample(x : in_t) : unit = {} + }. + end MkRO. + + module RO = { + var m : (in_t, out_t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : in_t, y : out_t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : in_t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + RO.get(x); + } + + proc restrK() : (in_t, out_t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : in_t) : out_t = RO.get + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc rem(x : in_t) : unit = RO.rem + + proc sample(x : in_t) : unit = {} + }. + + module FRO = { + var m : (in_t, out_t * PROM.flag) SmtMap.fmap + + proc init() : unit = { + FRO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \in FRO.m)%SmtMap) + r <- (oget (FRO.m.[x])%SmtMap).`1; + FRO.m.[x] <- (r, PROM.Known); + + return r; + } + + proc set(x : in_t, y : out_t) : unit = { + FRO.m.[x] <- (y, PROM.Known); + } + + proc rem(x : in_t) : unit = { + FRO.m <- (rem FRO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + if ((x \notin FRO.m)%SmtMap) + FRO.m.[x] <- (c, PROM.Unknown); + } + + proc queried(x : in_t, f : PROM.flag) : bool = { + return (x \in FRO.m)%SmtMap /\ ((FRO.m.[x])%SmtMap \is f)%PROM; + } + + proc allKnown() : (in_t, out_t) SmtMap.fmap = { + return (restr PROM.Known FRO.m)%SmtMap; + } + }. + + lemma RO_FRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_get: + equiv[ RO.get ~ FRO.get : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> ={res} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_sample: + equiv[ RO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(RO).distinguish ~ D(FRO).distinguish : + ={arg, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_init_ll: islossless RO.init. + + lemma FRO_init_ll: islossless FRO.init. + + lemma FRO_in_dom_ll: islossless FRO.queried. + + lemma FRO_restrK_ll: islossless FRO.allKnown. + + lemma RO_set_ll: islossless RO.set. + + lemma FRO_set_ll: islossless FRO.set. + + lemma RO_get_ll: (forall (x : in_t), is_lossless (dout x)) => islossless RO.get. + + lemma FRO_get_ll: (forall (x : in_t), is_lossless (dout x)) => islossless FRO.get. + + lemma RO_sample_ll: (forall (x : in_t), is_lossless (dout x)) => islossless RO.sample. + + lemma FRO_sample_ll: (forall (x : in_t), is_lossless (dout x)) => islossless FRO.sample. + + module type RM_Distinguisher(G : ROmap) = { + proc distinguish(_ : d_in_t) : d_out_t {G.get, G.sample, G.restrK} + } + + module MainRM(D : RM_Distinguisher, RO0 : ROmap) = { + proc distinguish(x : d_in_t) : d_out_t = { + var r : d_out_t; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + lemma fcoll_bound ['rT]: + forall (f : out_t -> 'rT) (Pc : real), + 0%r <= Pc => + (forall (x1 x2 : in_t) (y : 'rT), y \in dmap (dout x1) f => mu1 (dmap (dout x2) f) y <= Pc) => + forall (q : int), + 0 <= q => + forall (D(G : ROmap) <: RM_Distinguisher{+all mem, -RO} ) &m (z : d_in_t), + Pr[MainRM(D, RO).distinguish(z) @ &m : (fcoll f RO.m)%SmtMap /\ (fsize RO.m)%SmtMap <= q] <= + (q * (q - 1))%r / 2%r * Pc. + + theory FullEager. + abstract theory EagerCore. + axiom dout_ll: forall (x : in_t), is_lossless (dout x). + + module type Orcl = { + proc f(x : in_t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : in_t list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : in_t list, l2 : in_t list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : in_t, l : in_t list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : in_t, t2 : in_t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : in_t, t2 : in_t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : in_t list) (i : in_t) (s2 : in_t list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : in_t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : in_t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : in_t, y : out_t) : unit = FRO.set + + proc rem(x : in_t) : unit = FRO.rem + + proc sample(x : in_t) : unit = FRO.sample + + proc queried(x : in_t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (in_t, out_t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : in_t) (mx1 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : in_t) (mx1 mx2 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : in_t) (r1 : out_t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + end FullEager. + + abstract theory FinEager. + abstract theory EagerCore. + axiom dout_ll: forall (x : in_t), is_lossless (dout x). + + module type Orcl = { + proc f(x : in_t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : in_t list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : in_t list, l2 : in_t list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : in_t, l : in_t list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : in_t, t2 : in_t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : in_t, t2 : in_t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : in_t list) (i : in_t) (s2 : in_t list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : in_t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : in_t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : in_t, y : out_t) : unit = FRO.set + + proc rem(x : in_t) : unit = FRO.rem + + proc sample(x : in_t) : unit = FRO.sample + + proc queried(x : in_t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (in_t, out_t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : in_t) (mx1 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : in_t) (mx1 mx2 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : in_t) (r1 : out_t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + theory FinFrom. + type t = in_t. + + op enum : t list. + + op card : int = size enum. + + axiom enum_spec: forall (x : t), count (pred1 x) enum = 1. + + lemma enumP: forall (x : t), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : t list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinFrom. + + module FinRO = { + proc rem(x : in_t) : unit = RO.rem + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc init() : unit = { + var l : FinFrom.t list; + + l <- FinFrom.enum; + RO.init(); + while (l <> []){ + RO.sample(head witness l); + l <- behead l; + } + } + + proc get(x : in_t) : out_t = { + return oget (RO.m.[x])%SmtMap; + } + + proc sample(x : in_t) : unit = {} + }. + + module type FinRO_Distinguisher(G : RO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.sample} + } + + lemma RO_FinRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ MainD(D, RO).distinguish ~ MainD(D, FinRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_RO_FinRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ) &m (x : d_in_t) (p : + d_out_t -> bool), + Pr[MainD(D, RO).distinguish(x) @ &m : p res] = Pr[MainD(D, FinRO).distinguish(x) @ &m : p res]. + + theory MUniFinFun. + op mfun ['u] (d : in_t -> 'u distr) (f : in_t -> 'u) : real = + (big predT (fun (x : in_t) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma mfunE ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mfun d f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + lemma isdistr_dfun ['u]: forall (d : in_t -> 'u distr), isdistr (mfun d). + + op dfun ['u] (d : in_t -> 'u distr) : (in_t -> 'u) distr = mk (mfun d). + + lemma dfun1E ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mu1 (dfun d) f = (big predT (fun (x : in_t) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun1E_djoin ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mu1 (dfun d) f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + op tofun ['u] (us : 'u list) (x : in_t) : 'u = nth witness us (index x FinFrom.enum). + + op offun ['u] (f : in_t -> 'u) : 'u list = map f FinFrom.enum. + + lemma offunK ['u]: cancel offun tofun. + + lemma tofunK ['u]: forall (us : 'u list), size us = FinFrom.card => offun (tofun us) = us. + + lemma dfun_dmap ['u]: forall (d : in_t -> 'u distr), dfun d = dmap (djoinmap d FinFrom.enum) tofun. + + lemma dfun_supp ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), f \in dfun d <=> forall (x : in_t), f x \in d x. + + lemma dfun_fu ['u]: + forall (d : in_t -> 'u distr), (forall (x : in_t), is_full (d x)) => is_full (dfun d). + + lemma dfun_uni ['u]: + forall (d : in_t -> 'u distr), (forall (x : in_t), is_uniform (d x)) => is_uniform (dfun d). + + lemma dfun_funi ['u]: + forall (d : in_t -> 'u distr), + (forall (x : in_t), is_uniform (d x)) => (forall (x : in_t), is_full (d x)) => is_funiform (dfun d). + + lemma dfunE_djoin ['u]: + forall (du : in_t -> 'u distr) (E : (in_t -> 'u) -> bool), + mu (dfun du) E = mu (djoinmap du FinFrom.enum) (E \o tofun). + + lemma dfunE ['u]: + forall (du : in_t -> 'u distr) (p : in_t -> 'u -> bool), + mu (dfun du) (fun (f : in_t -> 'u) => forall (x : in_t), p x (f x)) = + (big predT (fun (x : in_t) => mu (du x) (p x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma weight_dfun ['u]: + forall (df : in_t -> 'u distr), + weight (dfun df) = (big predT (fun (x : in_t) => weight (df x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun_ll ['u]: + forall (d : in_t -> 'u distr), (forall (x : in_t), is_lossless (d x)) => is_lossless (dfun d). + + lemma dlet_dfun ['u, 'v]: + forall (d1 : in_t -> 'u distr) (d2 : in_t -> 'u -> 'v distr), + dlet (dfun d1) (fun (f1 : in_t -> 'u) => dfun (fun (x : in_t) => d2 x (f1 x))) = + dfun (fun (x : in_t) => dlet (d1 x) (fun (x1 : 'u) => d2 x x1)). + + lemma dfun_unit ['u]: forall (f : in_t -> 'u), dfun (fun (x : in_t) => dunit (f x)) = dunit f. + + lemma dmap_dfun ['u, 'v]: + forall (d : in_t -> 'u distr) (F : in_t -> 'u -> 'v), + dmap (dfun d) (fun (f : in_t -> 'u) (x : in_t) => F x (f x)) = + dfun (fun (x : in_t) => dmap (d x) (fun (fx : 'u) => F x fx)). + + lemma dfun1E_fix1 ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t) (f : in_t -> 'u), + mu1 (dfun d) f = mu1 (d x0) (f x0) * mu1 (dfun d.[x0 <- dunit (f x0)]) f. + + lemma dlet_dfun_fupdate_ll ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t) (v : 'u), + dlet (dfun d.[x0 <- dunit v]) (fun (f : in_t -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + dfun d. + + lemma dfunE_dlet_fix1 ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t), + dfun d = dlet (d x0) (fun (v : 'u) => dfun d.[x0 <- dunit v]). + + lemma dlet_dfun_update ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t), + dlet (dfun d) (fun (f : in_t -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + (weight (d x0) \cdot dfun d). + + lemma dfun_projE ['u]: + forall (df : in_t -> 'u distr) (x : in_t), + dmap (dfun df) (fun (f : in_t -> 'u) => f x) = (weight (dfun df) / weight (df x) \cdot df x). + + lemma eq_from_dfun_proj_eq ['u]: + forall (df1 df2 : in_t -> 'u distr), + (forall (x : in_t), is_lossless (df1 x)) => + (forall (x : in_t), is_lossless (df2 x)) => + (forall (x : in_t), + dmap (dfun df1) (fun (f : in_t -> 'u) => f x) = dmap (dfun df2) (fun (f : in_t -> 'u) => f x)) => + df1 == df2. + + lemma dfun_prod1E ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr) (f : + in_t -> 'u) (g : in_t -> 'v), + mu1 (dfun (fun (x : in_t) => df x `*` dg x)) (fun (x : in_t) => (f x, g x)) = + mu1 (dfun df) f * mu1 (dfun dg) g. + + lemma dprod_funE ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr), + dfun df `*` dfun dg = + dmap (dfun (fun (x : in_t) => df x `*` dg x)) + (fun (fg : in_t -> 'u * 'v) => + ((fun (p : 'u * 'v) => p.`1) \o fg, (fun (p : 'u * 'v) => p.`2) \o fg)). + + lemma dfun_prodE ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr), + dfun (fun (x : in_t) => df x `*` dg x) = + dmap (dfun df `*` dfun dg) (fun (fg : (in_t -> 'u) * (in_t -> 'v)) (x : in_t) => (fg.`1 x, fg.`2 x)). + + lemma dfun_condE ['u]: + forall (X : in_t -> bool) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dmap (dfun dt `*` dfun df) + (fun (tf : (in_t -> 'u) * (in_t -> 'u)) (x : in_t) => if X x then tf.`1 x else tf.`2 x) = + dfun (fun (x : in_t) => if X x then dt x else df x). + + lemma dlet_dfun_cond ['u]: + forall (dX : in_t -> bool distr) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dX x)) => + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dlet (dfun dX) (fun (X : in_t -> bool) => dfun (fun (x : in_t) => if X x then dt x else df x)) = + dfun (fun (x : in_t) => dlet (dX x) (fun (b : bool) => if b then dt x else df x)). + + lemma dfun_dcondE ['u]: + forall (dX : in_t -> bool distr) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dX x)) => + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : in_t -> bool) => + dmap (dfun dt `*` dfun df) + (fun (tf : (in_t -> 'u) * (in_t -> 'u)) (x : in_t) => if X x then tf.`1 x else tf.`2 x)) = + dfun (fun (x : in_t) => (mu1 (dX x) true \cdot dt x) + (mu1 (dX x) false \cdot df x)). + end MUniFinFun. + + module FunRO = { + var f : in_t -> out_t + + proc init() : unit = { + FunRO.f <$ (dfun dout)%MUniFinFun; + } + + proc get(x : in_t) : out_t = { + return FunRO.f x; + } + + proc set(x : in_t, y : out_t) : unit = { + FunRO.f <- fun (z : in_t) => if z = x then y else FunRO.f z; + } + + proc sample(x : in_t) : unit = {} + + proc rem(x : in_t) : unit = {} + }. + + lemma FinRO_FunRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ), + equiv[ MainD(D, FinRO).distinguish ~ MainD(D, FunRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_FinRO_FunRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ) &m (x : d_in_t) + (p : d_out_t -> bool), + Pr[MainD(D, FinRO).distinguish(x) @ &m : p res] = Pr[MainD(D, FunRO).distinguish(x) @ &m : p res]. + end FinEager. + end RO_SMP. + + module type SMP_RO = { + proc get(x : RO_SMP.in_t) : RO_SMP.out_t {} + } + + module type Sampler(O : SMP_RO) = { + proc sampleA(sd : W8.t Array32.t) : polymat {O.get} + + proc sampleAT(sd : W8.t Array32.t) : polymat {O.get} + } + + module type PSampler = { + proc sampleA(sd : W8.t Array32.t) : polymat {} + + proc sampleAT(sd : W8.t Array32.t) : polymat {} + } + + module type SAdv_T(O : SMP_RO) = { + proc interact(sd : W8.t Array32.t, t : polyvec) : unit {O.get} + + proc guess(uv : polyvec * poly) : bool {O.get} + } + + module MLWE_SMP(Adv : SAdv_T, S : Sampler, O : RO_SMP.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var _A : polymat; + var u0 : polyvec; + var u1 : polyvec; + var t : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + O.init(); + sd <$ srand; + t <$ duni; + Adv(O).interact(sd, t); + if (tr) + _A <@ S(O).sampleAT(sd); + else + _A <@ S(O).sampleA(sd); + s <$ dshort; + e <$ dshort; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ Adv(O).guess(if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + end MLWE_SMP. + + module S(H : MLWE_SMP.SMP_RO) = { + proc sampleA(sd : W8.t Array32.t) : polymat = { + var _A : MLWE_SMP.RO_SMP.out_t; + + _A <@ H.get(sd); + + return _A; + } + + proc sampleAT(sd : W8.t Array32.t) : polymat = { + var _A : MLWE_SMP.RO_SMP.out_t; + + _A <@ H.get(sd); + + return (trmx _A)%Matrix_.Matrix; + } + }. + + module BS(Adv : MLWE_SMP.SAdv_T, S0 : MLWE_SMP.Sampler, O : MLWE_SMP.SMP_RO) = { + proc guess(sd : W8.t Array32.t, t : polyvec, uv : polyvec * poly) : bool = { + var b : bool; + + Adv(O).interact(sd, t); + b <@ Adv(O).guess(uv); + + return b; + } + }. + + module MLWE_SMPs(Adv : MLWE_SMP.SAdv_T, S0 : MLWE_SMP.Sampler, O : MLWE_ROM.RO_H.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var _A : polymat; + var u0 : polyvec; + var u1 : polyvec; + var t : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + O.init(); + sd <$ srand; + t <$ duni; + O.sample(sd); + Adv(O).interact(sd, t); + if (tr) + _A <@ S0(O).sampleAT(sd); + else + _A <@ S0(O).sampleA(sd); + s <$ dshort; + e <$ dshort; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ Adv(O).guess(if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + module MLWE_ROs(Adv : MLWE_ROM.ROAdv_T, O : MLWE_ROM.RO_H.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var _A : MLWE_ROM.RO_H.out_t; + var u0 : polyvec; + var u1 : polyvec; + var t : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + O.init(); + sd <$ srand; + s <$ dshort; + e <$ dshort; + O.sample(sd); + _A <@ O.get(sd); + _A <- if tr then (trmx _A)%Matrix_.Matrix else _A; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + t <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ Adv(O).guess(sd, t, if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + module DLeftAux(A : MLWE_SMP.SAdv_T, O : MLWE_ROM.RO_H.RO) = { + proc run(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var t : polyvec; + var _A : MLWE_ROM.RO_H.out_t; + var s : polyvec; + var e : polyvec; + var u0 : polyvec; + var u1 : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + sd <$ srand; + s <$ dshort; + e <$ dshort; + O.sample(sd); + _A <@ O.get(sd); + _A <- if tr then (trmx _A)%Matrix_.Matrix else _A; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + t <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ BS(A, S, O).guess(sd, t, if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + module DLeft(A : MLWE_SMP.SAdv_T, O : MLWE_ROM.RO_H.RO) = { + proc distinguish(b : bool) : bool = { + var b' : bool; + + b' <@ DLeftAux(A, O).run(false, b); + + return b'; + } + }. + + module DLeftT(A : MLWE_SMP.SAdv_T, O : MLWE_ROM.RO_H.RO) = { + proc distinguish(b : bool) : bool = { + var b' : bool; + + b' <@ DLeftAux(A, O).run(true, b); + + return b'; + } + }. + + module DRightAux(A : MLWE_SMP.SAdv_T, O : MLWE_ROM.RO_H.RO) = { + proc run(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var t : polyvec; + var _A : polymat; + var s : polyvec; + var e : polyvec; + var u0 : polyvec; + var u1 : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + sd <$ srand; + t <$ duni; + O.sample(sd); + A(O).interact(sd, t); + if (tr) + _A <@ S(O).sampleAT(sd); + else + _A <@ S(O).sampleA(sd); + s <$ dshort; + e <$ dshort; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ A(O).guess(if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + module DRight(A : MLWE_SMP.SAdv_T, O : MLWE_ROM.RO_H.RO) = { + proc distinguish(b : bool) : bool = { + var b' : bool; + + b' <@ DRightAux(A, O).run(false, b); + + return b'; + } + }. + + module DRightT(A : MLWE_SMP.SAdv_T, O : MLWE_ROM.RO_H.RO) = { + proc distinguish(b : bool) : bool = { + var b' : bool; + + b' <@ DRightAux(A, O).run(true, b); + + return b'; + } + }. + + lemma MLWE_SMP_equiv_lel: + forall (tr b : bool) &m + (A(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.SAdv_T{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS} + ), + Pr[MLWE_ROs(BS(A, S), MLWE_ROM.RO_H.LRO).main(tr, b) @ &m : res] = + Pr[MLWE_ROs(BS(A, S), MLWE_ROM.RO_H.RO).main(tr, b) @ &m : res]. + + lemma MLWE_SMP_equiv_ler: + forall (tr b : bool) &m + (A(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.SAdv_T{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS} + ), + Pr[MLWE_SMPs(A, S, MLWE_ROM.RO_H.LRO).main(tr, b) @ &m : res] = + Pr[MLWE_SMPs(A, S, MLWE_ROM.RO_H.RO).main(tr, b) @ &m : res]. + + lemma MLWE_SMP_equiv: + forall (b : bool) &m + (A(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.SAdv_T{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS} + ), + Pr[MLWE(MLWE_ROM.MLWE_vs_MLWE_ROM.B(BS(A, S), MLWE_ROM.RO_H.LRO)).main(b) @ &m : res] = + Pr[MLWE_SMP.MLWE_SMP(A, S, MLWE_ROM.RO_H.LRO).main(false, b) @ &m : res]. + + lemma MLWE_SMP_equiv_t: + forall (b : bool) &m + (A(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.SAdv_T{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS} + ), + Pr[MLWE(MLWE_ROM.MLWE_vs_MLWE_ROM.Bt(BS(A, S), MLWE_ROM.RO_H.LRO)).main(b) @ &m : res] = + Pr[MLWE_SMP.MLWE_SMP(A, S, MLWE_ROM.RO_H.LRO).main(true, b) @ &m : res]. + end SMP_vs_ROM. + + theory SMP_vs_ROM_IND. + module type Simulator_t(O : MLWE_ROM.Ideal_RO) = { + proc init() : unit {} + + proc get(x : MLWE_SMP.RO_SMP.in_t) : MLWE_SMP.RO_SMP.out_t {O.get} + } + + module type Distinguisher_t(S : MLWE_SMP.PSampler, H : MLWE_SMP.SMP_RO) = { + proc distinguish(tr : bool, b : bool, sd : W8.t Array32.t) : bool {H.get, S.sampleA, S.sampleAT} + } + + module WIndfReal(D0 : Distinguisher_t, S : MLWE_SMP.Sampler, O : MLWE_SMP.RO_SMP.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var b' : bool; + + O.init(); + sd <$ srand; + b' <@ D0(S(O), O).distinguish(tr, b, sd); + + return b'; + } + }. + + module WIndfIdeal(D0 : Distinguisher_t, Sim : Simulator_t, O : MLWE_ROM.RO_H.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var b' : bool; + + O.init(); + Sim(O).init(); + sd <$ srand; + b' <@ D0(SMP_vs_ROM.S(O), Sim(O)).distinguish(tr, b, sd); + + return b'; + } + }. + + module BS(Adv : MLWE_SMP.SAdv_T, Sim : Simulator_t, H : MLWE_ROM.Ideal_RO) = { + proc guess(sd : W8.t Array32.t, t : polyvec, uv : polyvec * poly) : bool = { + var b : bool; + + Sim(H).init(); + Adv(Sim(H)).interact(sd, t); + b <@ Adv(Sim(H)).guess(uv); + + return b; + } + }. + + module D(A : MLWE_SMP.SAdv_T, S : MLWE_SMP.PSampler, H : MLWE_SMP.SMP_RO) = { + proc distinguish(tr : bool, b : bool, sd : W8.t Array32.t) : bool = { + var _A : polymat; + var t : polyvec; + var s : polyvec; + var e : polyvec; + var u0 : polyvec; + var u1 : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + t <$ duni; + A(H).interact(sd, t); + if (tr) + _A <@ S.sampleAT(sd); + else + _A <@ S.sampleA(sd); + s <$ dshort; + e <$ dshort; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ A(H).guess(if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + module DLeftAux(A : MLWE_SMP.SAdv_T, Sim : Simulator_t, O : MLWE_ROM.RO_H.RO) = { + proc run(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var t : polyvec; + var _A : MLWE_ROM.RO_H.out_t; + var s : polyvec; + var e : polyvec; + var u0 : polyvec; + var u1 : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + sd <$ srand; + s <$ dshort; + e <$ dshort; + O.sample(sd); + _A <@ O.get(sd); + _A <- if tr then (trmx _A)%Matrix_.Matrix else _A; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + t <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ BS(A, Sim, O).guess(sd, t, if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + module DLeft(A : MLWE_SMP.SAdv_T, Sim : Simulator_t, O : MLWE_ROM.RO_H.RO) = { + proc distinguish(b : bool) : bool = { + var b' : bool; + + b' <@ DLeftAux(A, Sim, O).run(false, b); + + return b'; + } + }. + + module DLeftT(A : MLWE_SMP.SAdv_T, Sim : Simulator_t, O : MLWE_ROM.RO_H.RO) = { + proc distinguish(b : bool) : bool = { + var b' : bool; + + b' <@ DLeftAux(A, Sim, O).run(true, b); + + return b'; + } + }. + + module DRightAux(A : MLWE_SMP.SAdv_T, Sim : Simulator_t, O : MLWE_ROM.RO_H.RO) = { + proc run(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var t : polyvec; + var _A : MLWE_ROM.RO_H.out_t; + var s : polyvec; + var e : polyvec; + var u0 : polyvec; + var u1 : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + sd <$ srand; + t <$ duni; + O.sample(sd); + Sim(O).init(); + A(Sim(O)).interact(sd, t); + if (tr) { + _A <@ O.get(sd); + _A <- (trmx _A)%Matrix_.Matrix; + } + else + _A <@ O.get(sd); + s <$ dshort; + e <$ dshort; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ A(Sim(O)).guess(if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + module DRight(A : MLWE_SMP.SAdv_T, Sim : Simulator_t, O : MLWE_ROM.RO_H.RO) = { + proc distinguish(b : bool) : bool = { + var b' : bool; + + b' <@ DRightAux(A, Sim, O).run(false, b); + + return b'; + } + }. + + module DRightT(A : MLWE_SMP.SAdv_T, Sim : Simulator_t, O : MLWE_ROM.RO_H.RO) = { + proc distinguish(b : bool) : bool = { + var b' : bool; + + b' <@ DRightAux(A, Sim, O).run(true, b); + + return b'; + } + }. + + module MLWE_ROs(Adv : MLWE_ROM.ROAdv_T, O : MLWE_ROM.RO_H.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var _A : MLWE_ROM.RO_H.out_t; + var u0 : polyvec; + var u1 : polyvec; + var t : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + O.init(); + sd <$ srand; + s <$ dshort; + e <$ dshort; + O.sample(sd); + _A <@ O.get(sd); + _A <- if tr then (trmx _A)%Matrix_.Matrix else _A; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + t <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ Adv(O).guess(sd, t, if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + module WIndfIdeals(D0 : Distinguisher_t, Sim : Simulator_t, O : MLWE_ROM.RO_H.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var b' : bool; + + O.init(); + Sim(O).init(); + sd <$ srand; + O.sample(sd); + b' <@ D0(SMP_vs_ROM.S(O), Sim(O)).distinguish(tr, b, sd); + + return b'; + } + }. + + lemma MLWE_SMP_equiv_lel: + forall (tr b : bool) &m + (A(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.SAdv_T{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS} + ) + (Sim(O : MLWE_ROM.Ideal_RO) <: + Simulator_t{+all mem, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS, -A} + ), + Pr[MLWE_ROs(BS(A, Sim), MLWE_ROM.RO_H.LRO).main(tr, b) @ &m : res] = + Pr[MLWE_ROs(BS(A, Sim), MLWE_ROM.RO_H.RO).main(tr, b) @ &m : res]. + + lemma MLWE_SMP_equiv_ler: + forall (tr b : bool) &m + (A(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.SAdv_T{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS, -D} + ) + (Sim(O : MLWE_ROM.Ideal_RO) <: + Simulator_t{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS, -D, -A} + ), + Pr[WIndfIdeals(D(A), Sim, MLWE_ROM.RO_H.LRO).main(tr, b) @ &m : res] = + Pr[WIndfIdeals(D(A), Sim, MLWE_ROM.RO_H.RO).main(tr, b) @ &m : res]. + + lemma MLWE_SMP_equiv: + forall (_b : bool) &m + (S(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.Sampler{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_SMP.RO_SMP.LRO, -D} + ) + (A(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.SAdv_T{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_SMP.RO_SMP.LRO, -D, -S} + ) + (Sim(O : MLWE_ROM.Ideal_RO) <: + Simulator_t{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS, -D, -A} + ), + Pr[MLWE_SMP.MLWE_SMP(A, S, MLWE_SMP.RO_SMP.LRO).main(false, _b) @ &m : res] - + Pr[MLWE(MLWE_ROM.MLWE_vs_MLWE_ROM.B(BS(A, Sim), MLWE_ROM.RO_H.LRO)).main(_b) @ &m : res] = + Pr[WIndfReal(D(A), S, MLWE_SMP.RO_SMP.LRO).main(false, _b) @ &m : res] - + Pr[WIndfIdeal(D(A), Sim, MLWE_ROM.RO_H.LRO).main(false, _b) @ &m : res]. + + lemma MLWE_SMP_equiv_t: + forall (_b : bool) &m + (S(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.Sampler{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_SMP.RO_SMP.LRO, -D} + ) + (A(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.SAdv_T{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_SMP.RO_SMP.LRO, -D, -S} + ) + (Sim(O : MLWE_ROM.Ideal_RO) <: + Simulator_t{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS, -D, -A} + ), + Pr[MLWE_SMP.MLWE_SMP(A, S, MLWE_SMP.RO_SMP.LRO).main(true, _b) @ &m : res] - + Pr[MLWE(MLWE_ROM.MLWE_vs_MLWE_ROM.Bt(BS(A, Sim), MLWE_ROM.RO_H.LRO)).main(_b) @ &m : res] = + Pr[WIndfReal(D(A), S, MLWE_SMP.RO_SMP.LRO).main(true, _b) @ &m : res] - + Pr[WIndfIdeal(D(A), Sim, MLWE_ROM.RO_H.LRO).main(true, _b) @ &m : res]. + end SMP_vs_ROM_IND. + end MLWE_. + + type raw_ciphertext = polyvec * poly. + + type raw_pkey = W8.t Array32.t * polyvec. + + type raw_skey = polyvec. + + lemma pk_encodeK: cancel pk_encode pk_decode. + + lemma sk_encodeK: cancel sk_encode sk_decode. + + lemma drand_ll: is_lossless srand. + + hint solve 0 lossless : drand_ll. + + hint solve 0 random : drand_ll. + + lemma drand_uni: is_uniform srand. + + hint solve 0 random : drand_uni. + + lemma drand_fu: is_full srand. + + hint solve 0 random : drand_fu. + + op prg_kg_ideal : (W8.t Array32.t * polyvec * polyvec) distr = + dlet srand + (fun (sd : W8.t Array32.t) => + dlet MLWE_.dshort (fun (s : polyvec) => dmap MLWE_.dshort (fun (e : polyvec) => (sd, s, e)))). + + op prg_enc_ideal : (polyvec * polyvec * poly) distr = + dlet MLWE_.dshort + (fun (r : polyvec) => dlet MLWE_.dshort (fun (e1 : polyvec) => dmap dshort_R (fun (e2 : poly) => (r, e1, e2)))). + + op kg (r : W8.t Array32.t) : publickey * W8.t Array1152.t = + let (sd, s, e) = prg_kg_inner r in + let t = ((H sd *^ s)%MLWE_.Matrix_.Matrix + e)%Vector in (pk_encode (sd, t), sk_encode s). + + op enc (rr : W8.t Array32.t) (pk : publickey) (m : plaintext) : W8.t Array960.t * W8.t Array128.t = + let (sd, t) = pk_decode pk in + let (r, e1, e2) = prg_enc_inner rr in + let u = ((trmx (H sd) *^ r)%MLWE_.Matrix_.Matrix + e1)%Vector in + let v = ((t `<*>` r) &+ e2 &+ m_encode m)%MLWE_ in c_encode (u, v). + + op dec (sk : W8.t Array1152.t) (c : W8.t Array960.t * W8.t Array128.t) : plaintext option = + let (u, v) = c_decode c in Some (m_decode (v &- (sk_decode sk `<*>` u))%MLWE_). + + theory FO_MLKEM. + theory UU. + theory TT. + lemma perm_eq_set ['a, 'b, 'c]: + forall (m : ('a, 'b) SmtMap.fmap) (l : 'a list) (x : 'a) (y : 'b), + perm_eq l ((elems ((fdom m))%SmtMap))%FSet => + ! (x \in l) => perm_eq (l ++ [x]) ((elems ((fdom (m.[x <- y])%SmtMap))%SmtMap))%FSet. + + lemma card_set ['a, 'b]: + forall (m : ('a, 'b) SmtMap.fmap) (x : 'a) (y : 'b) (n : int), + (card ((fdom m))%SmtMap)%FSet = n => (card ((fdom (m.[x <- y])%SmtMap))%SmtMap)%FSet <= n + 1. + + lemma card_set_bnd ['a, 'b]: + forall (m : ('a, 'b) SmtMap.fmap) (x : 'a) (y : 'b), + (card ((fdom (m.[x <- y])%SmtMap))%SmtMap)%FSet <= (card ((fdom m))%SmtMap)%FSet + 1. + + op find ['a, 'b] (f : 'a -> 'b -> bool) (m : ('a, 'b) SmtMap.fmap) : ('a * 'b) option = + let bindings = map (fun (a : 'a) => (a, oget (m.[a])%SmtMap)) ((elems ((fdom m))%SmtMap))%FSet in + let n = find (fun (p : 'a * 'b) => f p.`1 p.`2) bindings in + if n < size bindings then Some (nth witness bindings n) else None. + + lemma find_map ['a, 'b]: + forall (l : 'a list) (f : 'a -> 'b) (P : 'b -> bool), find P (map f l) = find (fun (x : 'a) => P (f x)) l. + + lemma findP_Some ['a, 'b]: + forall (f : 'a -> 'b -> bool) (m : ('a, 'b) SmtMap.fmap) (a : 'a) (b : 'b), + find f m = Some (a, b) => (m.[a])%SmtMap = Some b /\ f a b. + + lemma findP_None ['a, 'b]: + forall (f : 'a -> 'b -> bool) (m : ('a, 'b) SmtMap.fmap), + find f m = None => forall (a : 'a) (b : 'b), (m.[a])%SmtMap = Some b => ! f a b. + + lemma dplaintext_ll: is_lossless srand. + + hint solve 0 lossless : dplaintext_ll. + + hint solve 0 random : dplaintext_ll. + + lemma dplaintext_uni: is_uniform srand. + + hint solve 0 random : dplaintext_uni. + + lemma dplaintext_fu: is_full srand. + + hint solve 0 random : dplaintext_fu. + + theory FinT. + op enum : plaintext list. + + op card : int = size enum. + + axiom enum_spec: forall (x : plaintext), count (pred1 x) enum = 1. + + lemma enumP: forall (x : plaintext), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : plaintext list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinT. + + lemma randd_ll: is_lossless srand. + + hint solve 0 lossless : randd_ll. + + hint solve 0 random : randd_ll. + + theory PKE. + module type Scheme = { + proc kg() : publickey * W8.t Array1152.t {} + + proc enc(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t {} + + proc dec(sk : W8.t Array1152.t, c : W8.t Array960.t * W8.t Array128.t) : plaintext option {} + } + + module type CORR_ADV = { + proc find(pk : publickey, sk : W8.t Array1152.t) : plaintext {} + } + + module Correctness_Adv(S : Scheme, A : CORR_ADV) = { + proc main() : bool = { + var pk : publickey; + var sk : W8.t Array1152.t; + var c : W8.t Array960.t * W8.t Array128.t; + var m : plaintext; + var m' : plaintext option; + + (pk, sk) <@ S.kg(); + m <@ A.find(pk, sk); + c <@ S.enc(pk, m); + m' <@ S.dec(sk, c); + + return m' <> Some m; + } + }. + + module type Adversary = { + proc choose(pk : publickey) : plaintext * plaintext {} + + proc guess(c : W8.t Array960.t * W8.t Array128.t) : bool {} + } + + module CPA(S : Scheme, A : Adversary) = { + proc main() : bool = { + var pk : publickey; + var sk : W8.t Array1152.t; + var m0 : plaintext; + var m1 : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var b : bool; + var b' : bool; + + (pk, sk) <@ S.kg(); + (m0, m1) <@ A.choose(pk); + b <$ {0,1}; + c <@ S.enc(pk, if b then m1 else m0); + b' <@ A.guess(c); + + return b' = b; + } + }. + + module CPA_L(S : Scheme, A : Adversary) = { + proc main() : bool = { + var pk : publickey; + var sk : W8.t Array1152.t; + var m0 : plaintext; + var m1 : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var b' : bool; + + (pk, sk) <@ S.kg(); + (m0, m1) <@ A.choose(pk); + c <@ S.enc(pk, m0); + b' <@ A.guess(c); + + return b'; + } + }. + + module CPA_R(S : Scheme, A : Adversary) = { + proc main() : bool = { + var pk : publickey; + var sk : W8.t Array1152.t; + var m0 : plaintext; + var m1 : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var b' : bool; + + (pk, sk) <@ S.kg(); + (m0, m1) <@ A.choose(pk); + c <@ S.enc(pk, m1); + b' <@ A.guess(c); + + return b'; + } + }. + + theory LorR. + module type A = { + proc main(x : unit) : bool {} + } + + module RandomLR(L : A, R : A) = { + proc main(x : unit) : bool = { + var b : bool; + var r : bool; + + b <$ {0,1}; + if (b) + r <@ L.main(x); + else + r <@ R.main(x); + + return b = r; + } + }. + + lemma pr_AdvLR_AdvRndLR: + forall (L R <: A) &m (x' : unit), + Pr[R.main(x') @ &m : true] = 1%r => + `|Pr[L.main(x') @ &m : res] - Pr[R.main(x') @ &m : res]| = + 2%r * `|Pr[RandomLR(L, R).main(x') @ &m : res] - 1%r / 2%r|. + end LorR. + + lemma pr_CPA_LR: + forall (S <: Scheme) (A <: Adversary{+all mem, -S} ) &m, + islossless S.kg => + islossless S.enc => + islossless A.choose => + islossless A.guess => + `|Pr[CPA_L(S, A).main() @ &m : res] - Pr[CPA_R(S, A).main() @ &m : res]| = + 2%r * `|Pr[CPA(S, A).main() @ &m : res] - 1%r / 2%r|. + + module type OW_CPA_ADV = { + proc find(pk : publickey, c : W8.t Array960.t * W8.t Array128.t) : plaintext option {} + } + + theory MFinT. + op card : int = size FinT.enum. + + lemma enum_spec: forall (x : plaintext), count (pred1 x) FinT.enum = 1. + + lemma enumP: forall (x : plaintext), x \in FinT.enum. + + lemma enum_uniq: uniq FinT.enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : plaintext list), uniq xs => count (mem xs) FinT.enum = size xs. + + lemma is_finite_for: (is_finite_for predT FinT.enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq FinT.enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end MFinT. + + lemma dplaintext_ll: is_lossless srand. + + hint solve 0 lossless : dplaintext_ll. + + hint solve 0 random : dplaintext_ll. + + lemma dplaintext_uni: is_uniform srand. + + hint solve 0 random : dplaintext_uni. + + lemma dplaintext_fu: is_full srand. + + hint solve 0 random : dplaintext_fu. + + op eps_msg : real = 1%r / MFinT.card%r. + + lemma eps_msgE: forall (x : plaintext), mu1 srand x = eps_msg. + + module OW_CPA(S : Scheme, A : OW_CPA_ADV) = { + var pk : publickey + + var sk : W8.t Array1152.t + + var m : plaintext + + var cc : W8.t Array960.t * W8.t Array128.t + + var m' : plaintext option + + proc main_perfect() : bool = { + (OW_CPA.pk, OW_CPA.sk) <@ S.kg(); + OW_CPA.m <$ srand; + OW_CPA.cc <@ S.enc(OW_CPA.pk, OW_CPA.m); + OW_CPA.m' <@ A.find(OW_CPA.pk, OW_CPA.cc); + + return OW_CPA.m' = Some OW_CPA.m; + } + + module O = { + proc pco(sk : W8.t Array1152.t, m : plaintext, c : W8.t Array960.t * W8.t Array128.t) : bool = { + var m'' : plaintext option; + + m'' <@ S.dec(sk, c); + + return m'' = Some m; + } + } + + proc main() : bool = { + var b : bool; + + (OW_CPA.pk, OW_CPA.sk) <@ S.kg(); + OW_CPA.m <$ srand; + OW_CPA.cc <@ S.enc(OW_CPA.pk, OW_CPA.m); + OW_CPA.m' <@ A.find(OW_CPA.pk, OW_CPA.cc); + b <@ OW_CPA(S, A).O.pco(OW_CPA.sk, oget OW_CPA.m', OW_CPA.cc); + + return if OW_CPA.m' = None then false else b; + } + }. + + module BOWp(S : Scheme, A : OW_CPA_ADV) = { + var m'' : plaintext option + + proc find(pk : publickey, sk : W8.t Array1152.t) : plaintext = { + OW_CPA.m <$ srand; + + return OW_CPA.m; + } + + proc main() : bool = { + var pk : publickey; + var sk : W8.t Array1152.t; + + (pk, sk) <@ S.kg(); + BOWp(S, A).find(pk, sk); + OW_CPA.cc <@ S.enc(pk, OW_CPA.m); + OW_CPA.m' <@ A.find(pk, OW_CPA.cc); + BOWp.m'' <@ S.dec(sk, OW_CPA.cc); + + return BOWp.m'' <> Some OW_CPA.m; + } + }. + + lemma ow_perfect: + forall (S <: Scheme{+all mem, -OW_CPA, -BOWp} ) (A <: OW_CPA_ADV{+all mem, -OW_CPA, -BOWp, -S} ) &m, + islossless A.find => + islossless S.enc => + islossless S.dec => + `|Pr[OW_CPA(S, A).main() @ &m : res] - Pr[OW_CPA(S, A).main_perfect() @ &m : res]| <= + Pr[Correctness_Adv(S, BOWp(S, A)).main() @ &m : res]. + + module type OWL_CPA_ADV = { + proc find(pk : publickey, c : W8.t Array960.t * W8.t Array128.t) : plaintext list {} + } + + module OWL_CPA(S : Scheme, A : OWL_CPA_ADV) = { + var pk : publickey + + var sk : W8.t Array1152.t + + var m : plaintext + + var cc : W8.t Array960.t * W8.t Array128.t + + var l : plaintext list + + proc main() : bool = { + (OWL_CPA.pk, OWL_CPA.sk) <@ S.kg(); + OWL_CPA.m <$ srand; + OWL_CPA.cc <@ S.enc(OWL_CPA.pk, OWL_CPA.m); + OWL_CPA.l <@ A.find(OWL_CPA.pk, OWL_CPA.cc); + + return OWL_CPA.m \in OWL_CPA.l; + } + }. + + theory OWvsIND. + module Bowl(A : OWL_CPA_ADV) = { + var m0 : plaintext + + var m1 : plaintext + + var pk : publickey + + var l : plaintext list + + proc choose(_pk : publickey) : plaintext * plaintext = { + Bowl.pk <- _pk; + Bowl.m0 <$ srand; + Bowl.m1 <$ srand; + + return (Bowl.m0, Bowl.m1); + } + + proc guess(c : W8.t Array960.t * W8.t Array128.t) : bool = { + var b : bool; + + b <$ {0,1}; + Bowl.l <@ A.find(Bowl.pk, c); + + return + if (Bowl.m0 \in Bowl.l) = (Bowl.m1 \in Bowl.l) then b else if Bowl.m0 \in Bowl.l then false else true; + } + }. + + lemma boundl: + forall (l : plaintext list) (MAX : int), + 0 <= MAX => mu srand (fun (x : plaintext) => size l <= MAX /\ (x \in l)) <= MAX%r * eps_msg. + + pred bad (gB : plaintext list * plaintext * plaintext * publickey) = (gB.`2 \in gB.`1) = (gB.`3 \in gB.`1). + + lemma ow_ind_l: + forall (S <: Scheme{+all mem, -BOWp, -OWL_CPA, -Bowl} ) + (A <: OWL_CPA_ADV{+all mem, -BOWp, -OWL_CPA, -Bowl, -S} ) &m (MAX : int), + 0 <= MAX => + islossless S.kg => + islossless S.enc => + islossless S.dec => + islossless A.find => + hoare[ A.find : true ==> size res <= MAX] => + Pr[OWL_CPA(S, A).main() @ &m : OWL_CPA.m \in OWL_CPA.l] <= + 2%r * (MAX%r * eps_msg + `|Pr[CPA(S, Bowl(A)).main() @ &m : res] - 1%r / 2%r|). + + module BL(A : OW_CPA_ADV) = { + proc find(pk : publickey, c : W8.t Array960.t * W8.t Array128.t) : plaintext list = { + var m' : plaintext option; + + m' <@ A.find(pk, c); + + return if m' = None then [] else [oget m']; + } + }. + + lemma ow_ind: + forall (S <: Scheme{+all mem, -OW_CPA, -BOWp, -OWL_CPA, -Bowl} ) + (A <: OW_CPA_ADV{+all mem, -OW_CPA, -BOWp, -OWL_CPA, -Bowl, -S} ) &m, + islossless S.kg => + islossless S.enc => + islossless S.dec => + islossless A.find => + Pr[OW_CPA(S, A).main() @ &m : res] <= + 2%r * (eps_msg + `|Pr[CPA(S, Bowl(BL(A))).main() @ &m : res] - 1%r / 2%r|) + + Pr[Correctness_Adv(S, BOWp(S, A)).main() @ &m : res]. + end OWvsIND. + end PKE. + + op kg : (publickey * W8.t Array1152.t) distr = dmap srand MLWEPKEHash.kg. + + lemma kg_ll: is_lossless UU.TT.kg. + + hint solve 0 lossless : kg_ll. + + hint solve 0 random : kg_ll. + + module BasePKE = { + proc kg() : publickey * W8.t Array1152.t = { + var kpair : publickey * W8.t Array1152.t; + + kpair <$ UU.TT.kg; + + return kpair; + } + + proc enc(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = { + var r : W8.t Array32.t; + var c : W8.t Array960.t * W8.t Array128.t; + + r <$ srand; + c <- enc r pk m; + + return c; + } + + proc dec(sk : W8.t Array1152.t, c : W8.t Array960.t * W8.t Array128.t) : plaintext option = { + return dec sk c; + } + }. + + theory PKEROM. + type skey = publickey * W8.t Array1152.t. + + theory RO. + module type RO = { + proc init() : unit {} + + proc get(x : plaintext) : W8.t Array32.t {} + + proc set(x : plaintext, y : W8.t Array32.t) : unit {} + + proc rem(x : plaintext) : unit {} + + proc sample(x : plaintext) : unit {} + } + + module type RO_Distinguisher(G : RO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.rem, G.sample} + } + + module MainD(D : RO_Distinguisher, RO0 : RO) = { + proc distinguish(x : unit) : bool = { + var r : bool; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + module type ROmap = { + proc init() : unit {} + + proc get(x : plaintext) : W8.t Array32.t {} + + proc set(x : plaintext, y : W8.t Array32.t) : unit {} + + proc rem(x : plaintext) : unit {} + + proc sample(x : plaintext) : unit {} + + proc restrK() : (plaintext, W8.t Array32.t) SmtMap.fmap {} + } + + module type FRO = { + proc init() : unit {} + + proc get(x : plaintext) : W8.t Array32.t {} + + proc set(x : plaintext, y : W8.t Array32.t) : unit {} + + proc rem(x : plaintext) : unit {} + + proc sample(x : plaintext) : unit {} + + proc queried(x : plaintext, f : PROM.flag) : bool {} + + proc allKnown() : (plaintext, W8.t Array32.t) SmtMap.fmap {} + } + + module type FRO_Distinguisher(G : FRO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.rem, G.sample, G.queried, G.allKnown} + } + + abstract theory MkRO. + module RO = { + var m : (plaintext, W8.t Array32.t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : plaintext) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : plaintext) : unit = { + RO.get(x); + } + + proc restrK() : (plaintext, W8.t Array32.t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : plaintext) : W8.t Array32.t = RO.get + + proc set(x : plaintext, y : W8.t Array32.t) : unit = RO.set + + proc rem(x : plaintext) : unit = RO.rem + + proc sample(x : plaintext) : unit = {} + }. + end MkRO. + + module RO = { + var m : (plaintext, W8.t Array32.t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : plaintext) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : plaintext) : unit = { + RO.get(x); + } + + proc restrK() : (plaintext, W8.t Array32.t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : plaintext) : W8.t Array32.t = RO.get + + proc set(x : plaintext, y : W8.t Array32.t) : unit = RO.set + + proc rem(x : plaintext) : unit = RO.rem + + proc sample(x : plaintext) : unit = {} + }. + + module FRO = { + var m : (plaintext, W8.t Array32.t * PROM.flag) SmtMap.fmap + + proc init() : unit = { + FRO.m <- SmtMap.empty; + } + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \in FRO.m)%SmtMap) + r <- (oget (FRO.m.[x])%SmtMap).`1; + FRO.m.[x] <- (r, PROM.Known); + + return r; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = { + FRO.m.[x] <- (y, PROM.Known); + } + + proc rem(x : plaintext) : unit = { + FRO.m <- (rem FRO.m x)%SmtMap; + } + + proc sample(x : plaintext) : unit = { + var c : W8.t Array32.t; + + c <$ srand; + if ((x \notin FRO.m)%SmtMap) + FRO.m.[x] <- (c, PROM.Unknown); + } + + proc queried(x : plaintext, f : PROM.flag) : bool = { + return (x \in FRO.m)%SmtMap /\ ((FRO.m.[x])%SmtMap \is f)%PROM; + } + + proc allKnown() : (plaintext, W8.t Array32.t) SmtMap.fmap = { + return (restr PROM.Known FRO.m)%SmtMap; + } + }. + + lemma RO_FRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_get: + equiv[ RO.get ~ FRO.get : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> ={res} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_sample: + equiv[ RO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(RO).distinguish ~ D(FRO).distinguish : + ={arg, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_init_ll: islossless RO.init. + + lemma FRO_init_ll: islossless FRO.init. + + lemma FRO_in_dom_ll: islossless FRO.queried. + + lemma FRO_restrK_ll: islossless FRO.allKnown. + + lemma RO_set_ll: islossless RO.set. + + lemma FRO_set_ll: islossless FRO.set. + + lemma RO_get_ll: (forall (_ : plaintext), is_lossless srand) => islossless RO.get. + + lemma FRO_get_ll: (forall (_ : plaintext), is_lossless srand) => islossless FRO.get. + + lemma RO_sample_ll: (forall (_ : plaintext), is_lossless srand) => islossless RO.sample. + + lemma FRO_sample_ll: (forall (_ : plaintext), is_lossless srand) => islossless FRO.sample. + + module type RM_Distinguisher(G : ROmap) = { + proc distinguish(_ : unit) : bool {G.get, G.sample, G.restrK} + } + + module MainRM(D : RM_Distinguisher, RO0 : ROmap) = { + proc distinguish(x : unit) : bool = { + var r : bool; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + lemma fcoll_bound ['rT]: + forall (f : W8.t Array32.t -> 'rT) (Pc : real), + 0%r <= Pc => + (forall (_ _ : plaintext) (y : 'rT), y \in dmap srand f => mu1 (dmap srand f) y <= Pc) => + forall (q : int), + 0 <= q => + forall (D(G : ROmap) <: RM_Distinguisher{+all mem, -RO} ) &m (z : unit), + Pr[MainRM(D, RO).distinguish(z) @ &m : (fcoll f RO.m)%SmtMap /\ (fsize RO.m)%SmtMap <= q] <= + (q * (q - 1))%r / 2%r * Pc. + + theory FullEager. + abstract theory EagerCore. + axiom dout_ll: forall (_ : plaintext), is_lossless srand. + + module type Orcl = { + proc f(x : plaintext) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : plaintext list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : plaintext list, l2 : plaintext list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : plaintext, l : plaintext list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : plaintext list) (i : plaintext) (s2 : plaintext list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : + ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : plaintext), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : plaintext), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = FRO.set + + proc rem(x : plaintext) : unit = FRO.rem + + proc sample(x : plaintext) : unit = FRO.sample + + proc queried(x : plaintext, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (plaintext, W8.t Array32.t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : plaintext) : unit = { + var c : W8.t Array32.t; + + c <$ srand; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: + eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : plaintext) (mx1 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : plaintext) (mx1 mx2 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : plaintext) (r1 : W8.t Array32.t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + end FullEager. + + abstract theory FinEager. + abstract theory EagerCore. + axiom dout_ll: forall (_ : plaintext), is_lossless srand. + + module type Orcl = { + proc f(x : plaintext) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : plaintext list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : plaintext list, l2 : plaintext list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : plaintext, l : plaintext list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : plaintext list) (i : plaintext) (s2 : plaintext list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : + ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : plaintext), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : plaintext), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = FRO.set + + proc rem(x : plaintext) : unit = FRO.rem + + proc sample(x : plaintext) : unit = FRO.sample + + proc queried(x : plaintext, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (plaintext, W8.t Array32.t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : plaintext) : unit = { + var c : W8.t Array32.t; + + c <$ srand; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: + eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : plaintext) (mx1 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : plaintext) (mx1 mx2 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : plaintext) (r1 : W8.t Array32.t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + theory FinFrom. + type t = plaintext. + + op enum : t list. + + op card : int = size enum. + + axiom enum_spec: forall (x : t), count (pred1 x) enum = 1. + + lemma enumP: forall (x : t), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : t list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinFrom. + + module FinRO = { + proc rem(x : plaintext) : unit = RO.rem + + proc set(x : plaintext, y : W8.t Array32.t) : unit = RO.set + + proc init() : unit = { + var l : FinFrom.t list; + + l <- FinFrom.enum; + RO.init(); + while (l <> []){ + RO.sample(head witness l); + l <- behead l; + } + } + + proc get(x : plaintext) : W8.t Array32.t = { + return oget (RO.m.[x])%SmtMap; + } + + proc sample(x : plaintext) : unit = {} + }. + + module type FinRO_Distinguisher(G : RO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.sample} + } + + lemma RO_FinRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ MainD(D, RO).distinguish ~ MainD(D, FinRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_RO_FinRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ) &m (x : unit) (p : + bool -> bool), + Pr[MainD(D, RO).distinguish(x) @ &m : p res] = Pr[MainD(D, FinRO).distinguish(x) @ &m : p res]. + + theory MUniFinFun. + op mfun ['u] (d : plaintext -> 'u distr) (f : plaintext -> 'u) : real = + (big predT (fun (x : plaintext) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma mfunE ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mfun d f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + lemma isdistr_dfun ['u]: forall (d : plaintext -> 'u distr), isdistr (mfun d). + + op dfun ['u] (d : plaintext -> 'u distr) : (plaintext -> 'u) distr = mk (mfun d). + + lemma dfun1E ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mu1 (dfun d) f = + (big predT (fun (x : plaintext) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun1E_djoin ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mu1 (dfun d) f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + op tofun ['u] (us : 'u list) (x : plaintext) : 'u = nth witness us (index x FinFrom.enum). + + op offun ['u] (f : plaintext -> 'u) : 'u list = map f FinFrom.enum. + + lemma offunK ['u]: cancel offun tofun. + + lemma tofunK ['u]: forall (us : 'u list), size us = FinFrom.card => offun (tofun us) = us. + + lemma dfun_dmap ['u]: + forall (d : plaintext -> 'u distr), dfun d = dmap (djoinmap d FinFrom.enum) tofun. + + lemma dfun_supp ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + f \in dfun d <=> forall (x : plaintext), f x \in d x. + + lemma dfun_fu ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_full (d x)) => is_full (dfun d). + + lemma dfun_uni ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_uniform (d x)) => is_uniform (dfun d). + + lemma dfun_funi ['u]: + forall (d : plaintext -> 'u distr), + (forall (x : plaintext), is_uniform (d x)) => + (forall (x : plaintext), is_full (d x)) => is_funiform (dfun d). + + lemma dfunE_djoin ['u]: + forall (du : plaintext -> 'u distr) (E : (plaintext -> 'u) -> bool), + mu (dfun du) E = mu (djoinmap du FinFrom.enum) (E \o tofun). + + lemma dfunE ['u]: + forall (du : plaintext -> 'u distr) (p : plaintext -> 'u -> bool), + mu (dfun du) (fun (f : plaintext -> 'u) => forall (x : plaintext), p x (f x)) = + (big predT (fun (x : plaintext) => mu (du x) (p x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma weight_dfun ['u]: + forall (df : plaintext -> 'u distr), + weight (dfun df) = + (big predT (fun (x : plaintext) => weight (df x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun_ll ['u]: + forall (d : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (d x)) => is_lossless (dfun d). + + lemma dlet_dfun ['u, 'v]: + forall (d1 : plaintext -> 'u distr) (d2 : plaintext -> 'u -> 'v distr), + dlet (dfun d1) (fun (f1 : plaintext -> 'u) => dfun (fun (x : plaintext) => d2 x (f1 x))) = + dfun (fun (x : plaintext) => dlet (d1 x) (fun (x1 : 'u) => d2 x x1)). + + lemma dfun_unit ['u]: + forall (f : plaintext -> 'u), dfun (fun (x : plaintext) => dunit (f x)) = dunit f. + + lemma dmap_dfun ['u, 'v]: + forall (d : plaintext -> 'u distr) (F : plaintext -> 'u -> 'v), + dmap (dfun d) (fun (f : plaintext -> 'u) (x : plaintext) => F x (f x)) = + dfun (fun (x : plaintext) => dmap (d x) (fun (fx : 'u) => F x fx)). + + lemma dfun1E_fix1 ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext) (f : + plaintext -> 'u), mu1 (dfun d) f = mu1 (d x0) (f x0) * mu1 (dfun d.[x0 <- dunit (f x0)]) f. + + lemma dlet_dfun_fupdate_ll ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext) (v : 'u), + dlet (dfun d.[x0 <- dunit v]) + (fun (f : plaintext -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + dfun d. + + lemma dfunE_dlet_fix1 ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext), + dfun d = dlet (d x0) (fun (v : 'u) => dfun d.[x0 <- dunit v]). + + lemma dlet_dfun_update ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext), + dlet (dfun d) (fun (f : plaintext -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + (weight (d x0) \cdot dfun d). + + lemma dfun_projE ['u]: + forall (df : plaintext -> 'u distr) (x : plaintext), + dmap (dfun df) (fun (f : plaintext -> 'u) => f x) = (weight (dfun df) / weight (df x) \cdot df x). + + lemma eq_from_dfun_proj_eq ['u]: + forall (df1 df2 : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (df1 x)) => + (forall (x : plaintext), is_lossless (df2 x)) => + (forall (x : plaintext), + dmap (dfun df1) (fun (f : plaintext -> 'u) => f x) = + dmap (dfun df2) (fun (f : plaintext -> 'u) => f x)) => + df1 == df2. + + lemma dfun_prod1E ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr) (f : + plaintext -> 'u) (g : plaintext -> 'v), + mu1 (dfun (fun (x : plaintext) => df x `*` dg x)) (fun (x : plaintext) => (f x, g x)) = + mu1 (dfun df) f * mu1 (dfun dg) g. + + lemma dprod_funE ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr), + dfun df `*` dfun dg = + dmap (dfun (fun (x : plaintext) => df x `*` dg x)) + (fun (fg : plaintext -> 'u * 'v) => + ((fun (p : 'u * 'v) => p.`1) \o fg, (fun (p : 'u * 'v) => p.`2) \o fg)). + + lemma dfun_prodE ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr), + dfun (fun (x : plaintext) => df x `*` dg x) = + dmap (dfun df `*` dfun dg) + (fun (fg : (plaintext -> 'u) * (plaintext -> 'v)) (x : plaintext) => (fg.`1 x, fg.`2 x)). + + lemma dfun_condE ['u]: + forall (X : plaintext -> bool) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext -> 'u) * (plaintext -> 'u)) (x : plaintext) => + if X x then tf.`1 x else tf.`2 x) = + dfun (fun (x : plaintext) => if X x then dt x else df x). + + lemma dlet_dfun_cond ['u]: + forall (dX : plaintext -> bool distr) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dX x)) => + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : plaintext -> bool) => dfun (fun (x : plaintext) => if X x then dt x else df x)) = + dfun (fun (x : plaintext) => dlet (dX x) (fun (b : bool) => if b then dt x else df x)). + + lemma dfun_dcondE ['u]: + forall (dX : plaintext -> bool distr) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dX x)) => + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : plaintext -> bool) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext -> 'u) * (plaintext -> 'u)) (x : plaintext) => + if X x then tf.`1 x else tf.`2 x)) = + dfun (fun (x : plaintext) => (mu1 (dX x) true \cdot dt x) + (mu1 (dX x) false \cdot df x)). + end MUniFinFun. + + module FunRO = { + var f : plaintext -> W8.t Array32.t + + proc init() : unit = { + FunRO.f <$ (dfun (fun (_ : plaintext) => srand))%MUniFinFun; + } + + proc get(x : plaintext) : W8.t Array32.t = { + return FunRO.f x; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = { + FunRO.f <- fun (z : plaintext) => if z = x then y else FunRO.f z; + } + + proc sample(x : plaintext) : unit = {} + + proc rem(x : plaintext) : unit = {} + }. + + lemma FinRO_FunRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ), + equiv[ MainD(D, FinRO).distinguish ~ MainD(D, FunRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_FinRO_FunRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ) &m (x : unit) + (p : bool -> bool), + Pr[MainD(D, FinRO).distinguish(x) @ &m : p res] = Pr[MainD(D, FunRO).distinguish(x) @ &m : p res]. + end FinEager. + end RO. + + module type Oracle = { + proc init() : unit {} + + proc get(x : plaintext) : W8.t Array32.t {} + } + + module type POracle = { + proc get(x : plaintext) : W8.t Array32.t {} + } + + module type Scheme(H : POracle) = { + proc kg() : publickey * skey {H.get} + + proc enc(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t {H.get} + + proc dec(sk : skey, c : W8.t Array960.t * W8.t Array128.t) : plaintext option {H.get} + } + + module type Adversary(H : POracle) = { + proc choose(pk : publickey) : plaintext * plaintext {H.get} + + proc guess(c : W8.t Array960.t * W8.t Array128.t) : bool {H.get} + } + + module CPA(H : Oracle, S : Scheme, A : Adversary) = { + module A = A(H) + + proc main() : bool = { + var pk : publickey; + var sk : skey; + var m0 : plaintext; + var m1 : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var b : bool; + var b' : bool; + + H.init(); + (pk, sk) <@ S(H).kg(); + (m0, m1) <@ CPA(H, S, A).A.choose(pk); + b <$ {0,1}; + c <@ S(H).enc(pk, if b then m1 else m0); + b' <@ CPA(H, S, A).A.guess(c); + + return b' = b; + } + }. + + module CPA_L(H : Oracle, S : Scheme, A : Adversary) = { + module A = A(H) + + proc main() : bool = { + var pk : publickey; + var sk : skey; + var m0 : plaintext; + var m1 : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var b' : bool; + + H.init(); + (pk, sk) <@ S(H).kg(); + (m0, m1) <@ CPA_L(H, S, A).A.choose(pk); + c <@ S(H).enc(pk, m0); + b' <@ CPA_L(H, S, A).A.guess(c); + + return b'; + } + }. + + module CPA_R(H : Oracle, S : Scheme, A : Adversary) = { + module A = A(H) + + proc main() : bool = { + var pk : publickey; + var sk : skey; + var m0 : plaintext; + var m1 : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var b' : bool; + + H.init(); + (pk, sk) <@ S(H).kg(); + (m0, m1) <@ CPA_R(H, S, A).A.choose(pk); + c <@ S(H).enc(pk, m1); + b' <@ CPA_R(H, S, A).A.guess(c); + + return b'; + } + }. + + theory LorR. + module type A = { + proc main(x : unit) : bool {} + } + + module RandomLR(L : A, R : A) = { + proc main(x : unit) : bool = { + var b : bool; + var r : bool; + + b <$ {0,1}; + if (b) + r <@ L.main(x); + else + r <@ R.main(x); + + return b = r; + } + }. + + lemma pr_AdvLR_AdvRndLR: + forall (L R <: A) &m (x' : unit), + Pr[R.main(x') @ &m : true] = 1%r => + `|Pr[L.main(x') @ &m : res] - Pr[R.main(x') @ &m : res]| = + 2%r * `|Pr[RandomLR(L, R).main(x') @ &m : res] - 1%r / 2%r|. + end LorR. + + lemma pr_CPA_LR: + forall (S(H : POracle) <: Scheme) (H <: Oracle{+all mem, -S} ) + (A(H0 : POracle) <: Adversary{+all mem, -S, -H} ) &m, + islossless S(H).kg => + islossless S(H).enc => + islossless A(H).choose => + islossless A(H).guess => + islossless H.init => + `|Pr[CPA_L(H, S, A).main() @ &m : res] - Pr[CPA_R(H, S, A).main() @ &m : res]| = + 2%r * `|Pr[CPA(H, S, A).main() @ &m : res] - 1%r / 2%r|. + + module type CCA_ORC = { + proc dec(c : W8.t Array960.t * W8.t Array128.t) : plaintext option {} + } + + module type CCA_ADV(H : POracle, O : CCA_ORC) = { + proc choose(pk : publickey) : plaintext * plaintext {O.dec, H.get} + + proc guess(c : W8.t Array960.t * W8.t Array128.t) : bool {O.dec} + } + + module CCA(H : Oracle, S : Scheme, A : CCA_ADV) = { + var cstar : (W8.t Array960.t * W8.t Array128.t) option + + var sk : skey + + module O = { + proc dec(c : W8.t Array960.t * W8.t Array128.t) : plaintext option = { + var m : plaintext option; + + m <- None; + if (Some c <> CCA.cstar) + m <@ S(H).dec(CCA.sk, c); + + return m; + } + } + + module A = A(H, CCA(H, S, A).O) + + proc main() : bool = { + var pk : publickey; + var m0 : plaintext; + var m1 : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var b : bool; + var b' : bool; + + H.init(); + CCA.cstar <- None; + (pk, CCA.sk) <@ S(H).kg(); + (m0, m1) <@ CCA(H, S, A).A.choose(pk); + b <$ {0,1}; + c <@ S(H).enc(pk, if b then m1 else m0); + CCA.cstar <- Some c; + b' <@ CCA(H, S, A).A.guess(c); + + return b' = b; + } + }. + + module type CORR_ADV(H : POracle) = { + proc find(pk : publickey, sk : skey) : plaintext {H.get} + } + + module Correctness_Adv(H : Oracle, S : Scheme, A : CORR_ADV) = { + module A = A(H) + + proc main() : bool = { + var pk : publickey; + var sk : skey; + var c : W8.t Array960.t * W8.t Array128.t; + var m : plaintext; + var m' : plaintext option; + + H.init(); + (pk, sk) <@ S(H).kg(); + m <@ Correctness_Adv(H, S, A).A.find(pk, sk); + c <@ S(H).enc(pk, m); + m' <@ S(H).dec(sk, c); + + return m' <> Some m; + } + }. + + module type VA_ORC = { + proc cvo(c : W8.t Array960.t * W8.t Array128.t) : bool {} + + proc pco(m : plaintext, c : W8.t Array960.t * W8.t Array128.t) : bool {} + } + + module type PCVA_ADV(H : POracle, O : VA_ORC) = { + proc find(pk : publickey, c : W8.t Array960.t * W8.t Array128.t) : plaintext option {O.cvo, O.pco, H.get} + } + + hint solve 0 lossless : . + + hint solve 0 random : . + + module OW_PCVA(H : Oracle, S : Scheme, A : PCVA_ADV) = { + var sk : skey + + var cc : W8.t Array960.t * W8.t Array128.t + + module O = { + proc cvo(c : W8.t Array960.t * W8.t Array128.t) : bool = { + var m : plaintext option; + + m <- None; + if (c <> OW_PCVA.cc) + m <@ S(H).dec(OW_PCVA.sk, c); + + return m <> None; + } + + proc pco(m : plaintext, c : W8.t Array960.t * W8.t Array128.t) : bool = { + var m' : plaintext option; + + m' <@ S(H).dec(OW_PCVA.sk, c); + + return m' = Some m; + } + } + + module A = A(H, OW_PCVA(H, S, A).O) + + proc main() : bool = { + var pk : publickey; + var m : plaintext; + var m' : plaintext option; + var b : bool; + + H.init(); + (pk, OW_PCVA.sk) <@ S(H).kg(); + m <$ srand; + OW_PCVA.cc <@ S(H).enc(pk, m); + m' <@ OW_PCVA(H, S, A).A.find(pk, OW_PCVA.cc); + b <@ OW_PCVA(H, S, A).O.pco(oget m', OW_PCVA.cc); + + return if m' = None then false else b; + } + }. + end PKEROM. + + op qH : int. + + axiom ge0_qH: 0 <= qH. + + op qV : int. + + axiom ge0_qV: 0 <= qV. + + op qP : int. + + axiom ge0_qP: 0 <= qP. + + op qHC : int. + + axiom ge0_qHC: 0 <= qHC. + + module TT(H0 : PKEROM.POracle) = { + proc kg() : publickey * PKEROM.skey = { + var kpair : publickey * W8.t Array1152.t; + + kpair <$ UU.TT.kg; + + return (kpair.`1, kpair); + } + + proc enc(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = { + var r : W8.t Array32.t; + var c : W8.t Array960.t * W8.t Array128.t; + + r <@ H0.get(m); + c <- enc r pk m; + + return c; + } + + proc dec(sk : PKEROM.skey, c : W8.t Array960.t * W8.t Array128.t) : plaintext option = { + var m' : plaintext option; + var r : W8.t Array32.t; + var c' : W8.t Array960.t * W8.t Array128.t; + var rv : plaintext option; + + rv <- None; + m' <- dec sk.`2 c; + if (m' <> None) { + r <@ H0.get(oget m'); + c' <- enc r sk.`1 (oget m'); + rv <- if c = c' then m' else None; + } + + return rv; + } + }. + + module CO1(O : PKEROM.RO.RO) = { + var pk : publickey + + var sk : W8.t Array1152.t + + var counter : int + + var i : int + + var queried : plaintext list + + var bad : bool + + proc get(x : plaintext) : W8.t Array32.t = { + var y : W8.t Array32.t; + + y <- witness; + if (! (x \in CO1.queried)) { + if (size CO1.queried = CO1.i) { + O.sample(x); + CO1.bad <- true; + } + else + y <@ O.get(x); + CO1.queried <- CO1.queried ++ [x]; + } + else + if (find (pred1 x) CO1.queried <> CO1.i) + y <@ O.get(x); + CO1.counter <- CO1.counter + 1; + + return y; + } + }. + + module Correctness_Adv1(O : PKEROM.RO.RO, A : PKEROM.CORR_ADV) = { + module A = A(CO1(O)) + + proc main'(pk : publickey, sk : W8.t Array1152.t, i : int) : plaintext = { + var m : plaintext; + + CO1.i <- i; + O.init(); + CO1.counter <- 0; + CO1.pk <- pk; + CO1.sk <- sk; + CO1.queried <- []; + CO1.bad <- false; + m <@ Correctness_Adv1(O, A).A.find(CO1.pk, (CO1.pk, CO1.sk)); + + return m; + } + + proc main() : unit = { + var m : plaintext; + + (CO1.pk, CO1.sk) <@ BasePKE.kg(); + m <@ Correctness_Adv1(O, A).main'(CO1.pk, CO1.sk, -1); + CO1(O).get(m); + } + }. + + module B(A : PKEROM.CORR_ADV, O : PKEROM.RO.RO) = { + proc find(pk : publickey, sk : W8.t Array1152.t) : plaintext = { + var m : plaintext; + + CO1.i <$ [0..qHC]; + m <@ Correctness_Adv1(O, A).main'(pk, sk, CO1.i); + CO1(O).get(m); + + return + if 0 <= CO1.i && CO1.i < size CO1.queried then nth witness CO1.queried CO1.i + else head witness (filter (fun (x : plaintext) => ! (x \in CO1.queried)) FinT.enum); + } + + proc main() : bool = { + var m : plaintext; + var r : W8.t Array32.t; + var c : W8.t Array960.t * W8.t Array128.t; + var m' : plaintext option; + + (CO1.pk, CO1.sk) <@ BasePKE.kg(); + m <@ B(A, O).find(CO1.pk, CO1.sk); + r <@ O.get(m); + c <- enc r CO1.pk m; + m' <@ BasePKE.dec(CO1.sk, c); + + return m' <> Some m; + } + }. + + module DC0(O : PKEROM.RO.RO) = { + proc distinguish() : bool = PKE.Correctness_Adv(BasePKE, B(A/206960, O)).main + }. + + module DC1(O : PKEROM.RO.RO) = { + proc distinguish() : bool = B(A/206960, O).main + }. + + lemma CO1_lossless: islossless CO1(PKEROM.RO.RO).get. + + lemma corr_pnp: + forall (A(H0 : PKEROM.POracle) <: + PKEROM.CORR_ADV{+all mem, -PKEROM.RO.RO, -PKEROM.RO.FRO, -Correctness_Adv1, -B} ) &m, + qHC < FinT.card - 1 => + (forall (RO0 <: PKEROM.RO.RO{+all mem, -CO1, -A} ), + hoare[ Correctness_Adv1(RO0, A).A.find : CO1.counter = 0 ==> CO1.counter <= qHC]) => + (forall (H0 <: PKEROM.POracle{+all mem, -A} ), islossless H0.get => islossless A(H0).find) => + Pr[Correctness_Adv1(PKEROM.RO.RO, A).main() @ &m : + has (fun (m : plaintext) => Some m <> dec CO1.sk (enc (oget (PKEROM.RO.RO.m.[m])%SmtMap) CO1.pk m)) + CO1.queried] <= + (qHC + 1)%r * Pr[PKE.Correctness_Adv(BasePKE, B(A, PKEROM.RO.RO)).main() @ &m : res]. + + lemma correctness: + forall (A(H0 : PKEROM.POracle) <: + PKEROM.CORR_ADV{+all mem, -PKEROM.RO.RO, -PKEROM.RO.FRO, -Correctness_Adv1, -B} ) &m, + qHC < FinT.card - 1 => + (forall (RO0 <: PKEROM.RO.RO{+all mem, -CO1, -A} ), + hoare[ Correctness_Adv1(RO0, A).A.find : CO1.counter = 0 ==> CO1.counter <= qHC]) => + (forall (H0 <: PKEROM.POracle{+all mem, -A} ), islossless H0.get => islossless A(H0).find) => + Pr[PKEROM.Correctness_Adv(PKEROM.RO.RO, TT, A).main() @ &m : res] <= + (qHC + 1)%r * Pr[PKE.Correctness_Adv(BasePKE, B(A, PKEROM.RO.RO)).main() @ &m : res]. + + module CountO(O : PKEROM.VA_ORC) = { + var c_cvo : int + + var c_pco : int + + var c_h : int + + proc init() : unit = { + CountO.c_h <- 0; + CountO.c_cvo <- 0; + CountO.c_pco <- 0; + } + + proc cvo(c : W8.t Array960.t * W8.t Array128.t) : bool = { + var r : bool; + + r <@ O.cvo(c); + CountO.c_cvo <- CountO.c_cvo + 1; + + return r; + } + + proc pco(m : plaintext, c : W8.t Array960.t * W8.t Array128.t) : bool = { + var r : bool; + + r <@ O.pco(m, c); + CountO.c_pco <- CountO.c_pco + 1; + + return r; + } + }. + + module CountH(H0 : PKEROM.POracle) = { + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <@ H0.get(x); + CountO.c_h <- CountO.c_h + 1; + + return r; + } + }. + + module Gm = { + var m : plaintext + + var r : W8.t Array32.t + + var log : (plaintext, W8.t Array32.t) SmtMap.fmap + + var bad_corr : plaintext option + }. + + module O_AdvOW = { + var pk : publickey + + proc pco(m : plaintext, c : W8.t Array960.t * W8.t Array128.t) : bool = { + var r : W8.t Array32.t; + var c' : W8.t Array960.t * W8.t Array128.t; + + r <@ PKEROM.RO.RO.get(m); + c' <- enc r O_AdvOW.pk m; + + return c = c'; + } + + proc cvo(c : W8.t Array960.t * W8.t Array128.t) : bool = { + var rv : bool; + + rv <- false; + if (c <> PKEROM.OW_PCVA.cc) + rv <- find (fun (m : plaintext) (r : W8.t Array32.t) => c = enc r O_AdvOW.pk m) PKEROM.RO.RO.m <> None; + + return rv; + } + }. + + module AdvOW(A : PKEROM.PCVA_ADV) = { + module A = A(CountH(PKEROM.RO.RO), CountO(O_AdvOW)) + + proc find(pk0 : publickey, c : W8.t Array960.t * W8.t Array128.t) : plaintext option = { + var m' : plaintext option; + + O_AdvOW.pk <- pk0; + PKEROM.RO.RO.init(); + PKEROM.OW_PCVA.cc <- c; + CountO(O_AdvOW).init(); + m' <@ AdvOW(A).A.find(O_AdvOW.pk, PKEROM.OW_PCVA.cc); + + return m'; + } + }. + + module AdvOW_query(A : PKEROM.PCVA_ADV) = { + module A = A(CountH(PKEROM.RO.RO), CountO(O_AdvOW)) + + proc main(pk0 : publickey, c : W8.t Array960.t * W8.t Array128.t) : unit = { + var m' : plaintext option; + + O_AdvOW.pk <- pk0; + PKEROM.RO.RO.init(); + PKEROM.OW_PCVA.cc <- c; + CountO(O_AdvOW).init(); + m' <@ AdvOW_query(A).A.find(O_AdvOW.pk, PKEROM.OW_PCVA.cc); + } + + proc find(pk0 : publickey, c : W8.t Array960.t * W8.t Array128.t) : plaintext option = { + var i : int; + + AdvOW_query(A).main(pk0, c); + i <$ [0..qH + qP - 1]; + + return Some (nth witness ((elems ((fdom PKEROM.RO.RO.m))%SmtMap))%FSet i); + } + }. + + module AdvOWL_query(A : PKEROM.PCVA_ADV) = { + proc find(pk0 : publickey, c : W8.t Array960.t * W8.t Array128.t) : plaintext list = { + AdvOW_query(A).find(pk0, c); + + return (elems ((fdom PKEROM.RO.RO.m))%SmtMap)%FSet; + } + }. + + module type PCOT = { + proc pco(m : plaintext, c : W8.t Array960.t * W8.t Array128.t) : bool {} + } + + module type GT(PCO : PCOT, RO0 : PKEROM.RO.RO) = { + proc distinguish() : bool {RO0.init, RO0.get, RO0.set, RO0.rem, RO0.sample, PCO.pco} + } + + module H(RO0 : PKEROM.POracle) = { + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + if ((x \notin Gm.log)%SmtMap) { + r <@ RO0.get(x); + Gm.log.[x] <- r; + } + + return oget (Gm.log.[x])%SmtMap; + } + }. + + op incl ['a, 'b] (m1 m2 : ('a, 'b) SmtMap.fmap) : bool = + forall (x : 'a), (x \in m1)%SmtMap => (m1.[x])%SmtMap = (m2.[x])%SmtMap. + + pred gamma_spread_ok (gamma_spread : real) = + forall (pk : publickey) (sk : W8.t Array1152.t) (m : plaintext) (c : W8.t Array960.t * W8.t Array128.t), + (pk, sk) \in UU.TT.kg => mu srand (fun (r : W8.t Array32.t) => enc r pk m = c) <= gamma_spread. + + module G2_O(RO0 : PKEROM.POracle) = { + proc pco(m : plaintext, c : W8.t Array960.t * W8.t Array128.t) : bool = { + var m' : plaintext option; + var r : W8.t Array32.t; + var c' : W8.t Array960.t * W8.t Array128.t; + + m' <- dec PKEROM.OW_PCVA.sk.`2 c; + r <@ H(RO0).get(m); + c' <- enc r PKEROM.OW_PCVA.sk.`1 m; + Gm.bad_corr <- if c = c' /\ m' <> Some m then Some m else Gm.bad_corr; + + return c = c'; + } + + proc cvo(c : W8.t Array960.t * W8.t Array128.t) : bool = { + var rv : bool; + var m' : plaintext option; + + rv <- false; + if (c <> PKEROM.OW_PCVA.cc) { + m' <- dec PKEROM.OW_PCVA.sk.`2 c; + rv <- find (fun (m : plaintext) (r : W8.t Array32.t) => c = enc r PKEROM.OW_PCVA.sk.`1 m) Gm.log <> None; + if (m' <> None) + if ((oget m' \in Gm.log)%SmtMap) + Gm.bad_corr <- + let f = find (fun (m : plaintext) (r : W8.t Array32.t) => c = enc r PKEROM.OW_PCVA.sk.`1 m) Gm.log + in if f <> None /\ (oget f).`1 <> oget m' then Some (oget f).`1 else Gm.bad_corr; + else + if (oget m' = Gm.m) + Gm.bad_corr <- + let f = + find (fun (m : plaintext) (r : W8.t Array32.t) => c = enc r PKEROM.OW_PCVA.sk.`1 m) Gm.log in + if f <> None /\ (oget f).`1 <> oget m' then Some (oget f).`1 else Gm.bad_corr; + else + Gm.bad_corr <- + let f = + find (fun (m : plaintext) (r : W8.t Array32.t) => c = enc r PKEROM.OW_PCVA.sk.`1 m) Gm.log in + if f <> None then Some (oget f).`1 else Gm.bad_corr; + else + Gm.bad_corr <- + let f = find (fun (m : plaintext) (r : W8.t Array32.t) => c = enc r PKEROM.OW_PCVA.sk.`1 m) Gm.log in + if f <> None then Some (oget f).`1 else Gm.bad_corr; + } + + return rv; + } + }. + + module AdvCorr(A : PKEROM.PCVA_ADV, RO0 : PKEROM.POracle) = { + module H = H(RO0) + + module O = G2_O(RO0) + + module A = A(CountH(AdvCorr(A, RO0).H), CountO(AdvCorr(A, RO0).O)) + + proc find(pk : publickey, sk : PKEROM.skey) : plaintext = { + var m' : plaintext option; + + PKEROM.OW_PCVA.sk <- sk; + Gm.log <- SmtMap.empty; + Gm.bad_corr <- None; + Gm.m <$ srand; + Gm.r <@ RO0.get(Gm.m); + PKEROM.OW_PCVA.cc <- enc Gm.r pk Gm.m; + CountO(AdvCorr(A, RO0).O).init(); + m' <@ AdvCorr(A, RO0).A.find(pk, PKEROM.OW_PCVA.cc); + Gm.bad_corr <- if dec sk.`2 PKEROM.OW_PCVA.cc = m' /\ Some Gm.m <> m' then Some Gm.m else Gm.bad_corr; + + return oget Gm.bad_corr; + } + }. + + op inv_G2_corr (log ro : (plaintext, W8.t Array32.t) SmtMap.fmap) (sk : PKEROM.skey) + (bad_corr : plaintext option) (gm : plaintext) (gr : W8.t Array32.t) : bool = + incl log ro /\ + (gm \in ro)%SmtMap /\ + gr = oget (ro.[gm])%SmtMap /\ + (bad_corr <> None => + let m = oget bad_corr in + let r = oget (ro.[m])%SmtMap in + let c = enc r sk.`1 m in let m' = dec sk.`2 c in (m \in ro)%SmtMap /\ m' <> Some m). + + lemma corr_red_count: + forall (A(H0 : PKEROM.POracle, O : PKEROM.VA_ORC) <: + PKEROM.PCVA_ADV{+all mem, -PKE.OW_CPA, -PKE.BOWp, -PKE.OWL_CPA, -PKE.OWvsIND.Bowl, -PKEROM.RO.RO, -PKEROM.RO.FRO, -PKEROM.OW_PCVA, -Correctness_Adv1, -B, -CountO, -Gm, -O_AdvOW} + ) (RO0 <: PKEROM.RO.RO{+all mem, -PKEROM.OW_PCVA, -CO1, -CountO, -Gm, -A} ), + (forall (RO1 <: PKEROM.POracle{+all mem, -CountO, -A} ) (O <: PKEROM.VA_ORC{+all mem, -CountO, -A} ), + hoare[ A(CountH(RO1), CountO(O)).find : + CountO.c_h = 0 /\ CountO.c_cvo = 0 /\ CountO.c_pco = 0 ==> + CountO.c_h <= qH /\ CountO.c_cvo <= qV /\ CountO.c_pco <= qP]) => + hoare[ AdvCorr(A, CO1(RO0)).A.find : + CO1.counter = 1 /\ CountO.c_h = 0 /\ CountO.c_cvo = 0 /\ CountO.c_pco = 0 ==> + CO1.counter <= 1 + qH + qP]. + + lemma pre_conclusion: + forall (A(H0 : PKEROM.POracle, O : PKEROM.VA_ORC) <: + PKEROM.PCVA_ADV{+all mem, -PKE.OW_CPA, -PKE.BOWp, -PKE.OWL_CPA, -PKE.OWvsIND.Bowl, -PKEROM.RO.RO, -PKEROM.RO.FRO, -PKEROM.OW_PCVA, -Correctness_Adv1, -B, -CountO, -Gm, -O_AdvOW} + ) &m (gamma_spread : real), + gamma_spread_ok gamma_spread => + (forall (RO0 <: PKEROM.POracle{+all mem, -CountO, -A} ) (O <: PKEROM.VA_ORC{+all mem, -CountO, -A} ), + hoare[ A(CountH(RO0), CountO(O)).find : + CountO.c_h = 0 /\ CountO.c_cvo = 0 /\ CountO.c_pco = 0 ==> + CountO.c_h <= qH /\ CountO.c_cvo <= qV /\ CountO.c_pco <= qP]) => + (forall (H0 <: PKEROM.POracle{+all mem, -A} ) (O <: PKEROM.VA_ORC{+all mem, -A} ), + islossless O.cvo => islossless O.pco => islossless H0.get => islossless A(H0, O).find) => + Pr[PKEROM.OW_PCVA(PKEROM.RO.LRO, TT, A).main() @ &m : res] <= + Pr[PKE.OW_CPA(BasePKE, AdvOW(A)).main() @ &m : res] + + (qH + qP)%r * Pr[PKE.OW_CPA(BasePKE, AdvOW_query(A)).main() @ &m : res] + + Pr[PKEROM.Correctness_Adv(PKEROM.RO.RO, TT, AdvCorr(A)).main() @ &m : res] + + qV%r * gamma_spread. + + lemma conclusion: + forall (A(H0 : PKEROM.POracle, O : PKEROM.VA_ORC) <: + PKEROM.PCVA_ADV{+all mem, -PKE.OW_CPA, -PKE.BOWp, -PKE.OWL_CPA, -PKE.OWvsIND.Bowl, -PKEROM.RO.RO, -PKEROM.RO.FRO, -PKEROM.OW_PCVA, -Correctness_Adv1, -B, -CountO, -Gm, -O_AdvOW} + ) &m (gamma_spread : real), + qH + qP + 1 = qHC => + qHC < FinT.card - 1 => + gamma_spread_ok gamma_spread => + (forall (RO0 <: PKEROM.POracle{+all mem, -CountO, -A} ) (O <: PKEROM.VA_ORC{+all mem, -CountO, -A} ), + hoare[ A(CountH(RO0), CountO(O)).find : + CountO.c_h = 0 /\ CountO.c_cvo = 0 /\ CountO.c_pco = 0 ==> + CountO.c_h <= qH /\ CountO.c_cvo <= qV /\ CountO.c_pco <= qP]) => + (forall (H0 <: PKEROM.POracle{+all mem, -A} ) (O <: PKEROM.VA_ORC{+all mem, -A} ), + islossless O.cvo => islossless O.pco => islossless H0.get => islossless A(H0, O).find) => + Pr[PKEROM.OW_PCVA(PKEROM.RO.LRO, TT, A).main() @ &m : res] <= + Pr[PKE.OW_CPA(BasePKE, AdvOW(A)).main() @ &m : res] + + (qH + qP)%r * Pr[PKE.OW_CPA(BasePKE, AdvOW_query(A)).main() @ &m : res] + + (qH + qP + 2)%r * Pr[PKE.Correctness_Adv(BasePKE, B(AdvCorr(A), PKEROM.RO.RO)).main() @ &m : res] + + qV%r * gamma_spread. + + lemma pre_conclusion_cpa: + forall (A(H0 : PKEROM.POracle, O : PKEROM.VA_ORC) <: + PKEROM.PCVA_ADV{+all mem, -PKE.OW_CPA, -PKE.BOWp, -PKE.OWL_CPA, -PKE.OWvsIND.Bowl, -PKEROM.RO.RO, -PKEROM.RO.FRO, -PKEROM.OW_PCVA, -Correctness_Adv1, -B, -CountO, -Gm, -O_AdvOW} + ) &m (gamma_spread : real), + gamma_spread_ok gamma_spread => + (forall (RO0 <: PKEROM.POracle{+all mem, -CountO, -A} ) (O <: PKEROM.VA_ORC{+all mem, -CountO, -A} ), + hoare[ A(CountH(RO0), CountO(O)).find : + CountO.c_h = 0 /\ CountO.c_cvo = 0 /\ CountO.c_pco = 0 ==> + CountO.c_h <= qH /\ CountO.c_cvo <= qV /\ CountO.c_pco <= qP]) => + (forall (H0 <: PKEROM.POracle{+all mem, -A} ) (O <: PKEROM.VA_ORC{+all mem, -A} ), + islossless O.cvo => islossless O.pco => islossless H0.get => islossless A(H0, O).find) => + Pr[PKEROM.OW_PCVA(PKEROM.RO.LRO, TT, A).main() @ &m : res] <= + 2%r * `|Pr[PKE.CPA(BasePKE, PKE.OWvsIND.Bowl(PKE.OWvsIND.BL(AdvOW(A)))).main() @ &m : res] - 1%r / 2%r| + + 2%r * `|Pr[PKE.CPA(BasePKE, PKE.OWvsIND.Bowl(AdvOWL_query(A))).main() @ &m : res] - 1%r / 2%r| + + Pr[PKE.Correctness_Adv(BasePKE, PKE.BOWp(BasePKE, AdvOW(A))).main() @ &m : res] + + Pr[PKEROM.Correctness_Adv(PKEROM.RO.RO, TT, AdvCorr(A)).main() @ &m : res] + + qV%r * gamma_spread + 2%r * (qH + qP + 1)%r * PKE.eps_msg. + + lemma conclusion_cpa: + forall (A(H0 : PKEROM.POracle, O : PKEROM.VA_ORC) <: + PKEROM.PCVA_ADV{+all mem, -PKE.OW_CPA, -PKE.BOWp, -PKE.OWL_CPA, -PKE.OWvsIND.Bowl, -PKEROM.RO.RO, -PKEROM.RO.FRO, -PKEROM.OW_PCVA, -Correctness_Adv1, -B, -CountO, -Gm, -O_AdvOW} + ) &m (gamma_spread : real), + qH + qP + 1 = qHC => + qHC < FinT.card - 1 => + gamma_spread_ok gamma_spread => + (forall (RO0 <: PKEROM.POracle{+all mem, -CountO, -A} ) (O <: PKEROM.VA_ORC{+all mem, -CountO, -A} ), + hoare[ A(CountH(RO0), CountO(O)).find : + CountO.c_h = 0 /\ CountO.c_cvo = 0 /\ CountO.c_pco = 0 ==> + CountO.c_h <= qH /\ CountO.c_cvo <= qV /\ CountO.c_pco <= qP]) => + (forall (H0 <: PKEROM.POracle{+all mem, -A} ) (O <: PKEROM.VA_ORC{+all mem, -A} ), + islossless O.cvo => islossless O.pco => islossless H0.get => islossless A(H0, O).find) => + Pr[PKEROM.OW_PCVA(PKEROM.RO.LRO, TT, A).main() @ &m : res] <= + 2%r * `|Pr[PKE.CPA(BasePKE, PKE.OWvsIND.Bowl(PKE.OWvsIND.BL(AdvOW(A)))).main() @ &m : res] - 1%r / 2%r| + + 2%r * `|Pr[PKE.CPA(BasePKE, PKE.OWvsIND.Bowl(AdvOWL_query(A))).main() @ &m : res] - 1%r / 2%r| + + Pr[PKE.Correctness_Adv(BasePKE, PKE.BOWp(BasePKE, AdvOW(A))).main() @ &m : res] + + (qH + qP + 2)%r * Pr[PKE.Correctness_Adv(BasePKE, B(AdvCorr(A), PKEROM.RO.RO)).main() @ &m : res] + + qV%r * gamma_spread + 2%r * (qH + qP + 1)%r * PKE.eps_msg. + end TT. + + lemma dkey_ll: is_lossless srand. + + hint solve 0 lossless : dkey_ll. + + hint solve 0 random : dkey_ll. + + lemma dkey_uni: is_uniform srand. + + hint solve 0 random : dkey_uni. + + lemma dkey_fu: is_full srand. + + hint solve 0 random : dkey_fu. + + theory J. + module type PRF = { + proc init() : unit {} + + proc f(_ : W8.t Array960.t * W8.t Array128.t) : sharedsecret {} + } + + module type PRF_Oracles = { + proc f(_ : W8.t Array960.t * W8.t Array128.t) : sharedsecret {} + } + + module type Distinguisher(F : PRF_Oracles) = { + proc distinguish() : bool {F.f} + } + + module IND(F : PRF, D : Distinguisher) = { + proc main() : bool = { + var b : bool; + + F.init(); + b <@ D(F).distinguish(); + + return b; + } + }. + + abstract theory RF. + op dR : W8.t Array960.t * W8.t Array128.t -> sharedsecret distr. + + axiom dR_ll: forall (x : W8.t Array960.t * W8.t Array128.t), is_lossless (dR x). + + module RF = { + var m : (W8.t Array960.t * W8.t Array128.t, sharedsecret) SmtMap.fmap + + proc init() : unit = { + RF.m <- SmtMap.empty; + } + + proc f(x : W8.t Array960.t * W8.t Array128.t) : sharedsecret = { + var r : sharedsecret; + + if ((x \notin RF.m)%SmtMap) { + r <$ dR x; + RF.m.[x] <- r; + } + + return oget (RF.m.[x])%SmtMap; + } + }. + end RF. + + abstract theory PseudoRF. + type K. + + op dK : K distr. + + axiom dK_ll: is_lossless dK. + + op F : K -> W8.t Array960.t * W8.t Array128.t -> sharedsecret. + + module type PseudoRF = { + proc keygen() : K {} + + proc f(_ : K * (W8.t Array960.t * W8.t Array128.t)) : sharedsecret {} + } + + module PseudoRF = { + proc keygen() : K = { + var k : K; + + k <$ dK; + + return k; + } + + proc f(k : K, x : W8.t Array960.t * W8.t Array128.t) : sharedsecret = { + return F k x; + } + }. + + module PRF = { + var k : K + + proc init() : unit = { + PRF.k <$ dK; + } + + proc f(x : W8.t Array960.t * W8.t Array128.t) : sharedsecret = { + return F PRF.k x; + } + }. + end PseudoRF. + end J. + + theory RF. + lemma dR_ll: forall (_ : W8.t Array960.t * W8.t Array128.t), is_lossless srand. + + module RF = { + var m : (W8.t Array960.t * W8.t Array128.t, sharedsecret) SmtMap.fmap + + proc init() : unit = { + RF.m <- SmtMap.empty; + } + + proc f(x : W8.t Array960.t * W8.t Array128.t) : sharedsecret = { + var r : sharedsecret; + + if ((x \notin RF.m)%SmtMap) { + r <$ srand; + RF.m.[x] <- r; + } + + return oget (RF.m.[x])%SmtMap; + } + }. + end RF. + + theory PseudoRF. + lemma dK_ll: is_lossless srand. + + module type PseudoRF = { + proc keygen() : sharedsecret {} + + proc f(_ : sharedsecret * (W8.t Array960.t * W8.t Array128.t)) : sharedsecret {} + } + + module PseudoRF = { + proc keygen() : sharedsecret = { + var k : sharedsecret; + + k <$ srand; + + return k; + } + + proc f(k : sharedsecret, x : W8.t Array960.t * W8.t Array128.t) : sharedsecret = { + return J k x; + } + }. + + module PRF = { + var k : sharedsecret + + proc init() : unit = { + PRF.k <$ srand; + } + + proc f(x : W8.t Array960.t * W8.t Array128.t) : sharedsecret = { + return J PRF.k x; + } + }. + end PseudoRF. + + theory KEMROMx2. + type skey = (publickey * W8.t Array1152.t) * sharedsecret. + + lemma dkey_ll: is_lossless srand. + + hint solve 0 lossless : dkey_ll. + + hint solve 0 random : dkey_ll. + + lemma dkey_uni: is_uniform srand. + + hint solve 0 random : dkey_uni. + + lemma dkey_fu: is_full srand. + + hint solve 0 random : dkey_fu. + + theory RO1. + module type RO = { + proc init() : unit {} + + proc get(x : plaintext) : W8.t Array32.t {} + + proc set(x : plaintext, y : W8.t Array32.t) : unit {} + + proc rem(x : plaintext) : unit {} + + proc sample(x : plaintext) : unit {} + } + + module type RO_Distinguisher(G : RO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.rem, G.sample} + } + + module MainD(D : RO_Distinguisher, RO0 : RO) = { + proc distinguish(x : unit) : bool = { + var r : bool; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + module type ROmap = { + proc init() : unit {} + + proc get(x : plaintext) : W8.t Array32.t {} + + proc set(x : plaintext, y : W8.t Array32.t) : unit {} + + proc rem(x : plaintext) : unit {} + + proc sample(x : plaintext) : unit {} + + proc restrK() : (plaintext, W8.t Array32.t) SmtMap.fmap {} + } + + module type FRO = { + proc init() : unit {} + + proc get(x : plaintext) : W8.t Array32.t {} + + proc set(x : plaintext, y : W8.t Array32.t) : unit {} + + proc rem(x : plaintext) : unit {} + + proc sample(x : plaintext) : unit {} + + proc queried(x : plaintext, f : PROM.flag) : bool {} + + proc allKnown() : (plaintext, W8.t Array32.t) SmtMap.fmap {} + } + + module type FRO_Distinguisher(G : FRO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.rem, G.sample, G.queried, G.allKnown} + } + + abstract theory MkRO. + module RO = { + var m : (plaintext, W8.t Array32.t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : plaintext) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : plaintext) : unit = { + RO.get(x); + } + + proc restrK() : (plaintext, W8.t Array32.t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : plaintext) : W8.t Array32.t = RO.get + + proc set(x : plaintext, y : W8.t Array32.t) : unit = RO.set + + proc rem(x : plaintext) : unit = RO.rem + + proc sample(x : plaintext) : unit = {} + }. + end MkRO. + + module RO = { + var m : (plaintext, W8.t Array32.t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : plaintext) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : plaintext) : unit = { + RO.get(x); + } + + proc restrK() : (plaintext, W8.t Array32.t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : plaintext) : W8.t Array32.t = RO.get + + proc set(x : plaintext, y : W8.t Array32.t) : unit = RO.set + + proc rem(x : plaintext) : unit = RO.rem + + proc sample(x : plaintext) : unit = {} + }. + + module FRO = { + var m : (plaintext, W8.t Array32.t * PROM.flag) SmtMap.fmap + + proc init() : unit = { + FRO.m <- SmtMap.empty; + } + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \in FRO.m)%SmtMap) + r <- (oget (FRO.m.[x])%SmtMap).`1; + FRO.m.[x] <- (r, PROM.Known); + + return r; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = { + FRO.m.[x] <- (y, PROM.Known); + } + + proc rem(x : plaintext) : unit = { + FRO.m <- (rem FRO.m x)%SmtMap; + } + + proc sample(x : plaintext) : unit = { + var c : W8.t Array32.t; + + c <$ srand; + if ((x \notin FRO.m)%SmtMap) + FRO.m.[x] <- (c, PROM.Unknown); + } + + proc queried(x : plaintext, f : PROM.flag) : bool = { + return (x \in FRO.m)%SmtMap /\ ((FRO.m.[x])%SmtMap \is f)%PROM; + } + + proc allKnown() : (plaintext, W8.t Array32.t) SmtMap.fmap = { + return (restr PROM.Known FRO.m)%SmtMap; + } + }. + + lemma RO_FRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_get: + equiv[ RO.get ~ FRO.get : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> ={res} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_sample: + equiv[ RO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(RO).distinguish ~ D(FRO).distinguish : + ={arg, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_init_ll: islossless RO.init. + + lemma FRO_init_ll: islossless FRO.init. + + lemma FRO_in_dom_ll: islossless FRO.queried. + + lemma FRO_restrK_ll: islossless FRO.allKnown. + + lemma RO_set_ll: islossless RO.set. + + lemma FRO_set_ll: islossless FRO.set. + + lemma RO_get_ll: (forall (_ : plaintext), is_lossless srand) => islossless RO.get. + + lemma FRO_get_ll: (forall (_ : plaintext), is_lossless srand) => islossless FRO.get. + + lemma RO_sample_ll: (forall (_ : plaintext), is_lossless srand) => islossless RO.sample. + + lemma FRO_sample_ll: (forall (_ : plaintext), is_lossless srand) => islossless FRO.sample. + + module type RM_Distinguisher(G : ROmap) = { + proc distinguish(_ : unit) : bool {G.get, G.sample, G.restrK} + } + + module MainRM(D : RM_Distinguisher, RO0 : ROmap) = { + proc distinguish(x : unit) : bool = { + var r : bool; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + lemma fcoll_bound ['rT]: + forall (f : W8.t Array32.t -> 'rT) (Pc : real), + 0%r <= Pc => + (forall (_ _ : plaintext) (y : 'rT), y \in dmap srand f => mu1 (dmap srand f) y <= Pc) => + forall (q : int), + 0 <= q => + forall (D(G : ROmap) <: RM_Distinguisher{+all mem, -RO} ) &m (z : unit), + Pr[MainRM(D, RO).distinguish(z) @ &m : (fcoll f RO.m)%SmtMap /\ (fsize RO.m)%SmtMap <= q] <= + (q * (q - 1))%r / 2%r * Pc. + + theory FullEager. + abstract theory EagerCore. + axiom dout_ll: forall (_ : plaintext), is_lossless srand. + + module type Orcl = { + proc f(x : plaintext) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : plaintext list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : plaintext list, l2 : plaintext list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : plaintext, l : plaintext list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : plaintext list) (i : plaintext) (s2 : plaintext list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : plaintext), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : plaintext), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = FRO.set + + proc rem(x : plaintext) : unit = FRO.rem + + proc sample(x : plaintext) : unit = FRO.sample + + proc queried(x : plaintext, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (plaintext, W8.t Array32.t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : plaintext) : unit = { + var c : W8.t Array32.t; + + c <$ srand; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : plaintext) (mx1 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : plaintext) (mx1 mx2 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : plaintext) (r1 : W8.t Array32.t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + end FullEager. + + abstract theory FinEager. + abstract theory EagerCore. + axiom dout_ll: forall (_ : plaintext), is_lossless srand. + + module type Orcl = { + proc f(x : plaintext) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : plaintext list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : plaintext list, l2 : plaintext list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : plaintext, l : plaintext list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : plaintext list) (i : plaintext) (s2 : plaintext list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : plaintext), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : plaintext), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = FRO.set + + proc rem(x : plaintext) : unit = FRO.rem + + proc sample(x : plaintext) : unit = FRO.sample + + proc queried(x : plaintext, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (plaintext, W8.t Array32.t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : plaintext) : unit = { + var c : W8.t Array32.t; + + c <$ srand; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : plaintext) (mx1 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : plaintext) (mx1 mx2 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : plaintext) (r1 : W8.t Array32.t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + theory FinFrom. + type t = plaintext. + + op enum : t list. + + op card : int = size enum. + + axiom enum_spec: forall (x : t), count (pred1 x) enum = 1. + + lemma enumP: forall (x : t), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : t list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinFrom. + + module FinRO = { + proc rem(x : plaintext) : unit = RO.rem + + proc set(x : plaintext, y : W8.t Array32.t) : unit = RO.set + + proc init() : unit = { + var l : FinFrom.t list; + + l <- FinFrom.enum; + RO.init(); + while (l <> []){ + RO.sample(head witness l); + l <- behead l; + } + } + + proc get(x : plaintext) : W8.t Array32.t = { + return oget (RO.m.[x])%SmtMap; + } + + proc sample(x : plaintext) : unit = {} + }. + + module type FinRO_Distinguisher(G : RO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.sample} + } + + lemma RO_FinRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ MainD(D, RO).distinguish ~ MainD(D, FinRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_RO_FinRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ) &m (x : unit) (p : + bool -> bool), + Pr[MainD(D, RO).distinguish(x) @ &m : p res] = Pr[MainD(D, FinRO).distinguish(x) @ &m : p res]. + + theory MUniFinFun. + op mfun ['u] (d : plaintext -> 'u distr) (f : plaintext -> 'u) : real = + (big predT (fun (x : plaintext) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma mfunE ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mfun d f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + lemma isdistr_dfun ['u]: forall (d : plaintext -> 'u distr), isdistr (mfun d). + + op dfun ['u] (d : plaintext -> 'u distr) : (plaintext -> 'u) distr = mk (mfun d). + + lemma dfun1E ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mu1 (dfun d) f = + (big predT (fun (x : plaintext) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun1E_djoin ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mu1 (dfun d) f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + op tofun ['u] (us : 'u list) (x : plaintext) : 'u = nth witness us (index x FinFrom.enum). + + op offun ['u] (f : plaintext -> 'u) : 'u list = map f FinFrom.enum. + + lemma offunK ['u]: cancel offun tofun. + + lemma tofunK ['u]: forall (us : 'u list), size us = FinFrom.card => offun (tofun us) = us. + + lemma dfun_dmap ['u]: forall (d : plaintext -> 'u distr), dfun d = dmap (djoinmap d FinFrom.enum) tofun. + + lemma dfun_supp ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + f \in dfun d <=> forall (x : plaintext), f x \in d x. + + lemma dfun_fu ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_full (d x)) => is_full (dfun d). + + lemma dfun_uni ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_uniform (d x)) => is_uniform (dfun d). + + lemma dfun_funi ['u]: + forall (d : plaintext -> 'u distr), + (forall (x : plaintext), is_uniform (d x)) => + (forall (x : plaintext), is_full (d x)) => is_funiform (dfun d). + + lemma dfunE_djoin ['u]: + forall (du : plaintext -> 'u distr) (E : (plaintext -> 'u) -> bool), + mu (dfun du) E = mu (djoinmap du FinFrom.enum) (E \o tofun). + + lemma dfunE ['u]: + forall (du : plaintext -> 'u distr) (p : plaintext -> 'u -> bool), + mu (dfun du) (fun (f : plaintext -> 'u) => forall (x : plaintext), p x (f x)) = + (big predT (fun (x : plaintext) => mu (du x) (p x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma weight_dfun ['u]: + forall (df : plaintext -> 'u distr), + weight (dfun df) = + (big predT (fun (x : plaintext) => weight (df x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun_ll ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_lossless (d x)) => is_lossless (dfun d). + + lemma dlet_dfun ['u, 'v]: + forall (d1 : plaintext -> 'u distr) (d2 : plaintext -> 'u -> 'v distr), + dlet (dfun d1) (fun (f1 : plaintext -> 'u) => dfun (fun (x : plaintext) => d2 x (f1 x))) = + dfun (fun (x : plaintext) => dlet (d1 x) (fun (x1 : 'u) => d2 x x1)). + + lemma dfun_unit ['u]: forall (f : plaintext -> 'u), dfun (fun (x : plaintext) => dunit (f x)) = dunit f. + + lemma dmap_dfun ['u, 'v]: + forall (d : plaintext -> 'u distr) (F : plaintext -> 'u -> 'v), + dmap (dfun d) (fun (f : plaintext -> 'u) (x : plaintext) => F x (f x)) = + dfun (fun (x : plaintext) => dmap (d x) (fun (fx : 'u) => F x fx)). + + lemma dfun1E_fix1 ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext) (f : + plaintext -> 'u), mu1 (dfun d) f = mu1 (d x0) (f x0) * mu1 (dfun d.[x0 <- dunit (f x0)]) f. + + lemma dlet_dfun_fupdate_ll ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext) (v : 'u), + dlet (dfun d.[x0 <- dunit v]) + (fun (f : plaintext -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + dfun d. + + lemma dfunE_dlet_fix1 ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext), + dfun d = dlet (d x0) (fun (v : 'u) => dfun d.[x0 <- dunit v]). + + lemma dlet_dfun_update ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext), + dlet (dfun d) (fun (f : plaintext -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + (weight (d x0) \cdot dfun d). + + lemma dfun_projE ['u]: + forall (df : plaintext -> 'u distr) (x : plaintext), + dmap (dfun df) (fun (f : plaintext -> 'u) => f x) = (weight (dfun df) / weight (df x) \cdot df x). + + lemma eq_from_dfun_proj_eq ['u]: + forall (df1 df2 : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (df1 x)) => + (forall (x : plaintext), is_lossless (df2 x)) => + (forall (x : plaintext), + dmap (dfun df1) (fun (f : plaintext -> 'u) => f x) = + dmap (dfun df2) (fun (f : plaintext -> 'u) => f x)) => + df1 == df2. + + lemma dfun_prod1E ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr) (f : + plaintext -> 'u) (g : plaintext -> 'v), + mu1 (dfun (fun (x : plaintext) => df x `*` dg x)) (fun (x : plaintext) => (f x, g x)) = + mu1 (dfun df) f * mu1 (dfun dg) g. + + lemma dprod_funE ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr), + dfun df `*` dfun dg = + dmap (dfun (fun (x : plaintext) => df x `*` dg x)) + (fun (fg : plaintext -> 'u * 'v) => + ((fun (p : 'u * 'v) => p.`1) \o fg, (fun (p : 'u * 'v) => p.`2) \o fg)). + + lemma dfun_prodE ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr), + dfun (fun (x : plaintext) => df x `*` dg x) = + dmap (dfun df `*` dfun dg) + (fun (fg : (plaintext -> 'u) * (plaintext -> 'v)) (x : plaintext) => (fg.`1 x, fg.`2 x)). + + lemma dfun_condE ['u]: + forall (X : plaintext -> bool) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext -> 'u) * (plaintext -> 'u)) (x : plaintext) => + if X x then tf.`1 x else tf.`2 x) = + dfun (fun (x : plaintext) => if X x then dt x else df x). + + lemma dlet_dfun_cond ['u]: + forall (dX : plaintext -> bool distr) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dX x)) => + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : plaintext -> bool) => dfun (fun (x : plaintext) => if X x then dt x else df x)) = + dfun (fun (x : plaintext) => dlet (dX x) (fun (b : bool) => if b then dt x else df x)). + + lemma dfun_dcondE ['u]: + forall (dX : plaintext -> bool distr) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dX x)) => + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : plaintext -> bool) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext -> 'u) * (plaintext -> 'u)) (x : plaintext) => + if X x then tf.`1 x else tf.`2 x)) = + dfun (fun (x : plaintext) => (mu1 (dX x) true \cdot dt x) + (mu1 (dX x) false \cdot df x)). + end MUniFinFun. + + module FunRO = { + var f : plaintext -> W8.t Array32.t + + proc init() : unit = { + FunRO.f <$ (dfun (fun (_ : plaintext) => srand))%MUniFinFun; + } + + proc get(x : plaintext) : W8.t Array32.t = { + return FunRO.f x; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = { + FunRO.f <- fun (z : plaintext) => if z = x then y else FunRO.f z; + } + + proc sample(x : plaintext) : unit = {} + + proc rem(x : plaintext) : unit = {} + }. + + lemma FinRO_FunRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ), + equiv[ MainD(D, FinRO).distinguish ~ MainD(D, FunRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_FinRO_FunRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ) &m (x : unit) (p : + bool -> bool), + Pr[MainD(D, FinRO).distinguish(x) @ &m : p res] = Pr[MainD(D, FunRO).distinguish(x) @ &m : p res]. + end FinEager. + end RO1. + + theory RO2. + module type RO = { + proc init() : unit {} + + proc get(x : plaintext) : sharedsecret {} + + proc set(x : plaintext, y : sharedsecret) : unit {} + + proc rem(x : plaintext) : unit {} + + proc sample(x : plaintext) : unit {} + } + + module type RO_Distinguisher(G : RO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.rem, G.sample} + } + + module MainD(D : RO_Distinguisher, RO0 : RO) = { + proc distinguish(x : unit) : bool = { + var r : bool; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + module type ROmap = { + proc init() : unit {} + + proc get(x : plaintext) : sharedsecret {} + + proc set(x : plaintext, y : sharedsecret) : unit {} + + proc rem(x : plaintext) : unit {} + + proc sample(x : plaintext) : unit {} + + proc restrK() : (plaintext, sharedsecret) SmtMap.fmap {} + } + + module type FRO = { + proc init() : unit {} + + proc get(x : plaintext) : sharedsecret {} + + proc set(x : plaintext, y : sharedsecret) : unit {} + + proc rem(x : plaintext) : unit {} + + proc sample(x : plaintext) : unit {} + + proc queried(x : plaintext, f : PROM.flag) : bool {} + + proc allKnown() : (plaintext, sharedsecret) SmtMap.fmap {} + } + + module type FRO_Distinguisher(G : FRO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.rem, G.sample, G.queried, G.allKnown} + } + + abstract theory MkRO. + module RO = { + var m : (plaintext, sharedsecret) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : plaintext) : sharedsecret = { + var r : sharedsecret; + + r <$ srand; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : plaintext, y : sharedsecret) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : plaintext) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : plaintext) : unit = { + RO.get(x); + } + + proc restrK() : (plaintext, sharedsecret) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : plaintext) : sharedsecret = RO.get + + proc set(x : plaintext, y : sharedsecret) : unit = RO.set + + proc rem(x : plaintext) : unit = RO.rem + + proc sample(x : plaintext) : unit = {} + }. + end MkRO. + + module RO = { + var m : (plaintext, sharedsecret) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : plaintext) : sharedsecret = { + var r : sharedsecret; + + r <$ srand; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : plaintext, y : sharedsecret) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : plaintext) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : plaintext) : unit = { + RO.get(x); + } + + proc restrK() : (plaintext, sharedsecret) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : plaintext) : sharedsecret = RO.get + + proc set(x : plaintext, y : sharedsecret) : unit = RO.set + + proc rem(x : plaintext) : unit = RO.rem + + proc sample(x : plaintext) : unit = {} + }. + + module FRO = { + var m : (plaintext, sharedsecret * PROM.flag) SmtMap.fmap + + proc init() : unit = { + FRO.m <- SmtMap.empty; + } + + proc get(x : plaintext) : sharedsecret = { + var r : sharedsecret; + + r <$ srand; + if ((x \in FRO.m)%SmtMap) + r <- (oget (FRO.m.[x])%SmtMap).`1; + FRO.m.[x] <- (r, PROM.Known); + + return r; + } + + proc set(x : plaintext, y : sharedsecret) : unit = { + FRO.m.[x] <- (y, PROM.Known); + } + + proc rem(x : plaintext) : unit = { + FRO.m <- (rem FRO.m x)%SmtMap; + } + + proc sample(x : plaintext) : unit = { + var c : sharedsecret; + + c <$ srand; + if ((x \notin FRO.m)%SmtMap) + FRO.m.[x] <- (c, PROM.Unknown); + } + + proc queried(x : plaintext, f : PROM.flag) : bool = { + return (x \in FRO.m)%SmtMap /\ ((FRO.m.[x])%SmtMap \is f)%PROM; + } + + proc allKnown() : (plaintext, sharedsecret) SmtMap.fmap = { + return (restr PROM.Known FRO.m)%SmtMap; + } + }. + + lemma RO_FRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_get: + equiv[ RO.get ~ FRO.get : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> ={res} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_sample: + equiv[ RO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(RO).distinguish ~ D(FRO).distinguish : + ={arg, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_init_ll: islossless RO.init. + + lemma FRO_init_ll: islossless FRO.init. + + lemma FRO_in_dom_ll: islossless FRO.queried. + + lemma FRO_restrK_ll: islossless FRO.allKnown. + + lemma RO_set_ll: islossless RO.set. + + lemma FRO_set_ll: islossless FRO.set. + + lemma RO_get_ll: (forall (_ : plaintext), is_lossless srand) => islossless RO.get. + + lemma FRO_get_ll: (forall (_ : plaintext), is_lossless srand) => islossless FRO.get. + + lemma RO_sample_ll: (forall (_ : plaintext), is_lossless srand) => islossless RO.sample. + + lemma FRO_sample_ll: (forall (_ : plaintext), is_lossless srand) => islossless FRO.sample. + + module type RM_Distinguisher(G : ROmap) = { + proc distinguish(_ : unit) : bool {G.get, G.sample, G.restrK} + } + + module MainRM(D : RM_Distinguisher, RO0 : ROmap) = { + proc distinguish(x : unit) : bool = { + var r : bool; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + lemma fcoll_bound ['rT]: + forall (f : sharedsecret -> 'rT) (Pc : real), + 0%r <= Pc => + (forall (_ _ : plaintext) (y : 'rT), y \in dmap srand f => mu1 (dmap srand f) y <= Pc) => + forall (q : int), + 0 <= q => + forall (D(G : ROmap) <: RM_Distinguisher{+all mem, -RO} ) &m (z : unit), + Pr[MainRM(D, RO).distinguish(z) @ &m : (fcoll f RO.m)%SmtMap /\ (fsize RO.m)%SmtMap <= q] <= + (q * (q - 1))%r / 2%r * Pc. + + theory FullEager. + abstract theory EagerCore. + axiom dout_ll: forall (_ : plaintext), is_lossless srand. + + module type Orcl = { + proc f(x : plaintext) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : plaintext list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : plaintext list, l2 : plaintext list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : plaintext, l : plaintext list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : plaintext list) (i : plaintext) (s2 : plaintext list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : plaintext), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : plaintext), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : plaintext) : sharedsecret = { + var r : sharedsecret; + + r <$ srand; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : plaintext, y : sharedsecret) : unit = FRO.set + + proc rem(x : plaintext) : unit = FRO.rem + + proc sample(x : plaintext) : unit = FRO.sample + + proc queried(x : plaintext, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (plaintext, sharedsecret) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : plaintext) : unit = { + var c : sharedsecret; + + c <$ srand; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : plaintext) (mx1 : (sharedsecret * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : plaintext) (mx1 mx2 : (sharedsecret * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : plaintext) (r1 : sharedsecret), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + end FullEager. + + abstract theory FinEager. + abstract theory EagerCore. + axiom dout_ll: forall (_ : plaintext), is_lossless srand. + + module type Orcl = { + proc f(x : plaintext) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : plaintext list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : plaintext list, l2 : plaintext list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : plaintext, l : plaintext list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : plaintext list) (i : plaintext) (s2 : plaintext list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : plaintext), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : plaintext), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : plaintext) : sharedsecret = { + var r : sharedsecret; + + r <$ srand; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : plaintext, y : sharedsecret) : unit = FRO.set + + proc rem(x : plaintext) : unit = FRO.rem + + proc sample(x : plaintext) : unit = FRO.sample + + proc queried(x : plaintext, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (plaintext, sharedsecret) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : plaintext) : unit = { + var c : sharedsecret; + + c <$ srand; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : plaintext) (mx1 : (sharedsecret * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : plaintext) (mx1 mx2 : (sharedsecret * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : plaintext) (r1 : sharedsecret), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + theory FinFrom. + type t = plaintext. + + op enum : t list. + + op card : int = size enum. + + axiom enum_spec: forall (x : t), count (pred1 x) enum = 1. + + lemma enumP: forall (x : t), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : t list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinFrom. + + module FinRO = { + proc rem(x : plaintext) : unit = RO.rem + + proc set(x : plaintext, y : sharedsecret) : unit = RO.set + + proc init() : unit = { + var l : FinFrom.t list; + + l <- FinFrom.enum; + RO.init(); + while (l <> []){ + RO.sample(head witness l); + l <- behead l; + } + } + + proc get(x : plaintext) : sharedsecret = { + return oget (RO.m.[x])%SmtMap; + } + + proc sample(x : plaintext) : unit = {} + }. + + module type FinRO_Distinguisher(G : RO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.sample} + } + + lemma RO_FinRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ MainD(D, RO).distinguish ~ MainD(D, FinRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_RO_FinRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ) &m (x : unit) (p : + bool -> bool), + Pr[MainD(D, RO).distinguish(x) @ &m : p res] = Pr[MainD(D, FinRO).distinguish(x) @ &m : p res]. + + theory MUniFinFun. + op mfun ['u] (d : plaintext -> 'u distr) (f : plaintext -> 'u) : real = + (big predT (fun (x : plaintext) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma mfunE ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mfun d f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + lemma isdistr_dfun ['u]: forall (d : plaintext -> 'u distr), isdistr (mfun d). + + op dfun ['u] (d : plaintext -> 'u distr) : (plaintext -> 'u) distr = mk (mfun d). + + lemma dfun1E ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mu1 (dfun d) f = + (big predT (fun (x : plaintext) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun1E_djoin ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mu1 (dfun d) f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + op tofun ['u] (us : 'u list) (x : plaintext) : 'u = nth witness us (index x FinFrom.enum). + + op offun ['u] (f : plaintext -> 'u) : 'u list = map f FinFrom.enum. + + lemma offunK ['u]: cancel offun tofun. + + lemma tofunK ['u]: forall (us : 'u list), size us = FinFrom.card => offun (tofun us) = us. + + lemma dfun_dmap ['u]: forall (d : plaintext -> 'u distr), dfun d = dmap (djoinmap d FinFrom.enum) tofun. + + lemma dfun_supp ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + f \in dfun d <=> forall (x : plaintext), f x \in d x. + + lemma dfun_fu ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_full (d x)) => is_full (dfun d). + + lemma dfun_uni ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_uniform (d x)) => is_uniform (dfun d). + + lemma dfun_funi ['u]: + forall (d : plaintext -> 'u distr), + (forall (x : plaintext), is_uniform (d x)) => + (forall (x : plaintext), is_full (d x)) => is_funiform (dfun d). + + lemma dfunE_djoin ['u]: + forall (du : plaintext -> 'u distr) (E : (plaintext -> 'u) -> bool), + mu (dfun du) E = mu (djoinmap du FinFrom.enum) (E \o tofun). + + lemma dfunE ['u]: + forall (du : plaintext -> 'u distr) (p : plaintext -> 'u -> bool), + mu (dfun du) (fun (f : plaintext -> 'u) => forall (x : plaintext), p x (f x)) = + (big predT (fun (x : plaintext) => mu (du x) (p x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma weight_dfun ['u]: + forall (df : plaintext -> 'u distr), + weight (dfun df) = + (big predT (fun (x : plaintext) => weight (df x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun_ll ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_lossless (d x)) => is_lossless (dfun d). + + lemma dlet_dfun ['u, 'v]: + forall (d1 : plaintext -> 'u distr) (d2 : plaintext -> 'u -> 'v distr), + dlet (dfun d1) (fun (f1 : plaintext -> 'u) => dfun (fun (x : plaintext) => d2 x (f1 x))) = + dfun (fun (x : plaintext) => dlet (d1 x) (fun (x1 : 'u) => d2 x x1)). + + lemma dfun_unit ['u]: forall (f : plaintext -> 'u), dfun (fun (x : plaintext) => dunit (f x)) = dunit f. + + lemma dmap_dfun ['u, 'v]: + forall (d : plaintext -> 'u distr) (F : plaintext -> 'u -> 'v), + dmap (dfun d) (fun (f : plaintext -> 'u) (x : plaintext) => F x (f x)) = + dfun (fun (x : plaintext) => dmap (d x) (fun (fx : 'u) => F x fx)). + + lemma dfun1E_fix1 ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext) (f : + plaintext -> 'u), mu1 (dfun d) f = mu1 (d x0) (f x0) * mu1 (dfun d.[x0 <- dunit (f x0)]) f. + + lemma dlet_dfun_fupdate_ll ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext) (v : 'u), + dlet (dfun d.[x0 <- dunit v]) + (fun (f : plaintext -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + dfun d. + + lemma dfunE_dlet_fix1 ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext), + dfun d = dlet (d x0) (fun (v : 'u) => dfun d.[x0 <- dunit v]). + + lemma dlet_dfun_update ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext), + dlet (dfun d) (fun (f : plaintext -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + (weight (d x0) \cdot dfun d). + + lemma dfun_projE ['u]: + forall (df : plaintext -> 'u distr) (x : plaintext), + dmap (dfun df) (fun (f : plaintext -> 'u) => f x) = (weight (dfun df) / weight (df x) \cdot df x). + + lemma eq_from_dfun_proj_eq ['u]: + forall (df1 df2 : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (df1 x)) => + (forall (x : plaintext), is_lossless (df2 x)) => + (forall (x : plaintext), + dmap (dfun df1) (fun (f : plaintext -> 'u) => f x) = + dmap (dfun df2) (fun (f : plaintext -> 'u) => f x)) => + df1 == df2. + + lemma dfun_prod1E ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr) (f : + plaintext -> 'u) (g : plaintext -> 'v), + mu1 (dfun (fun (x : plaintext) => df x `*` dg x)) (fun (x : plaintext) => (f x, g x)) = + mu1 (dfun df) f * mu1 (dfun dg) g. + + lemma dprod_funE ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr), + dfun df `*` dfun dg = + dmap (dfun (fun (x : plaintext) => df x `*` dg x)) + (fun (fg : plaintext -> 'u * 'v) => + ((fun (p : 'u * 'v) => p.`1) \o fg, (fun (p : 'u * 'v) => p.`2) \o fg)). + + lemma dfun_prodE ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr), + dfun (fun (x : plaintext) => df x `*` dg x) = + dmap (dfun df `*` dfun dg) + (fun (fg : (plaintext -> 'u) * (plaintext -> 'v)) (x : plaintext) => (fg.`1 x, fg.`2 x)). + + lemma dfun_condE ['u]: + forall (X : plaintext -> bool) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext -> 'u) * (plaintext -> 'u)) (x : plaintext) => + if X x then tf.`1 x else tf.`2 x) = + dfun (fun (x : plaintext) => if X x then dt x else df x). + + lemma dlet_dfun_cond ['u]: + forall (dX : plaintext -> bool distr) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dX x)) => + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : plaintext -> bool) => dfun (fun (x : plaintext) => if X x then dt x else df x)) = + dfun (fun (x : plaintext) => dlet (dX x) (fun (b : bool) => if b then dt x else df x)). + + lemma dfun_dcondE ['u]: + forall (dX : plaintext -> bool distr) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dX x)) => + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : plaintext -> bool) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext -> 'u) * (plaintext -> 'u)) (x : plaintext) => + if X x then tf.`1 x else tf.`2 x)) = + dfun (fun (x : plaintext) => (mu1 (dX x) true \cdot dt x) + (mu1 (dX x) false \cdot df x)). + end MUniFinFun. + + module FunRO = { + var f : plaintext -> sharedsecret + + proc init() : unit = { + FunRO.f <$ (dfun (fun (_ : plaintext) => srand))%MUniFinFun; + } + + proc get(x : plaintext) : sharedsecret = { + return FunRO.f x; + } + + proc set(x : plaintext, y : sharedsecret) : unit = { + FunRO.f <- fun (z : plaintext) => if z = x then y else FunRO.f z; + } + + proc sample(x : plaintext) : unit = {} + + proc rem(x : plaintext) : unit = {} + }. + + lemma FinRO_FunRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ), + equiv[ MainD(D, FinRO).distinguish ~ MainD(D, FunRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_FinRO_FunRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ) &m (x : unit) (p : + bool -> bool), + Pr[MainD(D, FinRO).distinguish(x) @ &m : p res] = Pr[MainD(D, FunRO).distinguish(x) @ &m : p res]. + end FinEager. + end RO2. + + module type Oracle_x2 = { + proc init() : unit {} + + proc get1(_ : plaintext) : W8.t Array32.t {} + + proc get2(_ : plaintext) : sharedsecret {} + } + + module type POracle_x2 = { + proc get1(_ : plaintext) : W8.t Array32.t {} + + proc get2(_ : plaintext) : sharedsecret {} + } + + module RO_x2(H1 : RO1.RO, H2 : RO2.RO) = { + proc init() : unit = { + H1.init(); + H2.init(); + } + + proc get1(x : plaintext) : W8.t Array32.t = H1.get + + proc get2(x : plaintext) : sharedsecret = H2.get + }. + + module type Scheme(O : POracle_x2) = { + proc kg() : publickey * skey {O.get1, O.get2} + + proc enc(pk : publickey) : (W8.t Array960.t * W8.t Array128.t) * sharedsecret {O.get1, O.get2} + + proc dec(sk : skey, c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option {O.get1, O.get2} + } + + module Correctness(H : Oracle_x2, S : Scheme) = { + proc main() : bool = { + var pk : publickey; + var sk : skey; + var c : W8.t Array960.t * W8.t Array128.t; + var k : sharedsecret; + var k' : sharedsecret option; + + H.init(); + (pk, sk) <@ S(H).kg(); + (c, k) <@ S(H).enc(pk); + k' <@ S(H).dec(sk, c); + + return k' <> Some k; + } + }. + + module type CCA_ORC = { + proc dec(c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option {} + } + + module type CCA_ADV(H : POracle_x2, O : CCA_ORC) = { + proc guess(pk : publickey, c : W8.t Array960.t * W8.t Array128.t, k : sharedsecret) : bool + {O.dec, H.get1, H.get2} + } + + module CCA(H : Oracle_x2, S : Scheme, A : CCA_ADV) = { + var cstar : (W8.t Array960.t * W8.t Array128.t) option + + var sk : skey + + module O = { + proc dec(c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var k : sharedsecret option; + + k <- None; + if (Some c <> CCA.cstar) + k <@ S(H).dec(CCA.sk, c); + + return k; + } + } + + module A = A(H, CCA(H, S, A).O) + + proc main() : bool = { + var pk : publickey; + var k1 : sharedsecret; + var ck0 : (W8.t Array960.t * W8.t Array128.t) * sharedsecret; + var b : bool; + var b' : bool; + + H.init(); + CCA.cstar <- None; + (pk, CCA.sk) <@ S(H).kg(); + k1 <$ srand; + b <$ {0,1}; + ck0 <@ S(H).enc(pk); + CCA.cstar <- Some ck0.`1; + b' <@ CCA(H, S, A).A.guess(pk, ck0.`1, if b then k1 else ck0.`2); + + return b' = b; + } + }. + end KEMROMx2. + + op qHT : int. + + axiom ge0_qHT: 0 <= qHT. + + op qHU : int. + + axiom ge0_qHU: 0 <= qHU. + + op qD : int. + + axiom ge0_qD: 0 <= qD. + + module UU(H : KEMROMx2.POracle_x2) = { + module HT = { + proc get(_ : plaintext) : W8.t Array32.t = H.get1 + } + + module HU = { + proc get(_ : plaintext) : sharedsecret = H.get2 + } + + proc kg() : publickey * KEMROMx2.skey = { + var pk : publickey; + var sk : W8.t Array1152.t; + var k : sharedsecret; + + (pk, sk) <$ TT.kg; + k <$ srand; + + return (pk, ((pk, sk), k)); + } + + proc enc(pk : publickey) : (W8.t Array960.t * W8.t Array128.t) * sharedsecret = { + var m : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var k : sharedsecret; + + m <$ srand; + c <@ TT.TT(UU(H).HT).enc(pk, m); + k <@ UU(H).HU.get(m); + + return (c, k); + } + + proc dec(sk : KEMROMx2.skey, c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var m' : plaintext option; + var k : sharedsecret; + + k <- witness; + m' <@ TT.TT(UU(H).HT).dec(sk.`1, c); + if (m' = None) + k <- J sk.`2 c; + else + k <@ UU(H).HU.get(oget m'); + + return Some k; + } + }. + + module B_UC(HT : TT.PKEROM.POracle) = { + proc find(pk : publickey, sk : TT.PKEROM.skey) : plaintext = { + var m : plaintext; + + m <$ srand; + + return m; + } + }. + + lemma correctness_rel: + forall &m, + Pr[KEMROMx2.Correctness(KEMROMx2.RO_x2(KEMROMx2.RO1.RO, KEMROMx2.RO2.RO), UU).main() @ &m : res] <= + Pr[TT.PKEROM.Correctness_Adv(TT.PKEROM.RO.RO, TT.TT, B_UC).main() @ &m : res]. + + lemma correctness: + forall &m, + TT.qHC = 0 => + 1 < TT.FinT.card => + Pr[KEMROMx2.Correctness(KEMROMx2.RO_x2(KEMROMx2.RO1.RO, KEMROMx2.RO2.RO), UU).main() @ &m : res] <= + Pr[TT.PKE.Correctness_Adv(TT.BasePKE, TT.B(B_UC, TT.PKEROM.RO.RO)).main() @ &m : res]. + + module CountHx2(H : KEMROMx2.POracle_x2) = { + var c_hu : int + + var c_ht : int + + proc init() : unit = { + CountHx2.c_ht <- 0; + CountHx2.c_hu <- 0; + } + + proc get1(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <@ H.get1(x); + CountHx2.c_ht <- CountHx2.c_ht + 1; + + return r; + } + + proc get2(x : plaintext) : sharedsecret = { + var r : sharedsecret; + + r <@ H.get2(x); + CountHx2.c_hu <- CountHx2.c_hu + 1; + + return r; + } + }. + + module UU1(PRFO : J.PRF_Oracles, H : KEMROMx2.POracle_x2) = { + proc enc(pk : publickey) : (W8.t Array960.t * W8.t Array128.t) * sharedsecret = UU(H).enc + + proc kg() : publickey * KEMROMx2.skey = { + var pk : publickey; + var sk : W8.t Array1152.t; + + (pk, sk) <$ TT.kg; + + return (pk, ((pk, sk), witness)); + } + + proc dec(sk : KEMROMx2.skey, c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var m' : plaintext option; + var k : sharedsecret; + + k <- witness; + m' <@ TT.TT(UU(H).HT).dec(sk.`1, c); + if (m' = None) + k <@ PRFO.f(c); + else + k <@ UU(H).HU.get(oget m'); + + return Some k; + } + }. + + module Gm1P(H : KEMROMx2.Oracle_x2, A : KEMROMx2.CCA_ADV, PRFO : J.PRF_Oracles) = { + proc main'() : bool = { + var pk : publickey; + var k1 : sharedsecret; + var ck0 : (W8.t Array960.t * W8.t Array128.t) * sharedsecret; + var b : bool; + var b' : bool; + + H.init(); + KEMROMx2.CCA.cstar <- None; + (pk, KEMROMx2.CCA.sk) <@ UU1(PRFO, H).kg(); + k1 <$ srand; + b <$ {0,1}; + ck0 <@ UU1(PRFO, H).enc(pk); + KEMROMx2.CCA.cstar <- Some ck0.`1; + b' <@ KEMROMx2.CCA(H, UU1(PRFO), A).A.guess(pk, ck0.`1, if b then k1 else ck0.`2); + + return b' = b; + } + }. + + module Gm1(H : KEMROMx2.Oracle_x2, A : KEMROMx2.CCA_ADV) = { + proc main() : bool = { + var b : bool; + + RF.RF.init(); + b <@ Gm1P(H, A, RF.RF).main'(); + + return b; + } + }. + + module D(A : KEMROMx2.CCA_ADV, PRFO : J.PRF_Oracles) = { + proc distinguish() : bool = Gm1P(KEMROMx2.RO_x2(KEMROMx2.RO1.RO, KEMROMx2.RO2.RO), A, PRFO).main' + }. + + theory RO1E. + abstract theory EagerCore. + axiom dout_ll: forall (_ : plaintext), is_lossless srand. + + module type Orcl = { + proc f(x : plaintext) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : plaintext list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : plaintext list, l2 : plaintext list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : plaintext, l : plaintext list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : plaintext list) (i : plaintext) (s2 : plaintext list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : plaintext), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : plaintext), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = KEMROMx2.RO1.FRO.init + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \notin KEMROMx2.RO1.FRO.m)%SmtMap \/ ((KEMROMx2.RO1.FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + KEMROMx2.RO1.FRO.m.[x] <- (r, PROM.Known); + + return (oget (KEMROMx2.RO1.FRO.m.[x])%SmtMap).`1; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = KEMROMx2.RO1.FRO.set + + proc rem(x : plaintext) : unit = KEMROMx2.RO1.FRO.rem + + proc sample(x : plaintext) : unit = KEMROMx2.RO1.FRO.sample + + proc queried(x : plaintext, f : PROM.flag) : bool = KEMROMx2.RO1.FRO.queried + + proc allKnown() : (plaintext, W8.t Array32.t) SmtMap.fmap = KEMROMx2.RO1.FRO.allKnown + + module I = { + proc f(x : plaintext) : unit = { + var c : W8.t Array32.t; + + c <$ srand; + KEMROMx2.RO1.FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown KEMROMx2.RO1.FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: + eager[ RRO.resample();, KEMROMx2.RO1.FRO.init ~ RRO.init, RRO.resample( + ); : ={KEMROMx2.RO1.FRO.m} ==> ={KEMROMx2.RO1.FRO.m}]. + + lemma iter_perm2: + equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={KEMROMx2.RO1.FRO.m, t1, t2} ==> ={KEMROMx2.RO1.FRO.m}]. + + lemma I_f_neq: + forall (x1 : plaintext) (mx1 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, KEMROMx2.RO1.FRO.m} /\ x1 <> arg{1} /\ (KEMROMx2.RO1.FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={KEMROMx2.RO1.FRO.m} /\ (KEMROMx2.RO1.FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : plaintext) (mx1 mx2 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) KEMROMx2.RO1.FRO.m{1} KEMROMx2.RO1.FRO.m{2})%SmtMap /\ + (KEMROMx2.RO1.FRO.m{1}.[x1])%SmtMap = mx1 /\ (KEMROMx2.RO1.FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) KEMROMx2.RO1.FRO.m{1} KEMROMx2.RO1.FRO.m{2})%SmtMap /\ + (KEMROMx2.RO1.FRO.m{1}.[x1])%SmtMap = mx1 /\ (KEMROMx2.RO1.FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : plaintext) (r1 : W8.t Array32.t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (KEMROMx2.RO1.FRO.m{1}.[x1])%SmtMap = None /\ + KEMROMx2.RO1.FRO.m{2} = (KEMROMx2.RO1.FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (KEMROMx2.RO1.FRO.m{1}.[x1])%SmtMap = None /\ + KEMROMx2.RO1.FRO.m{2} = (KEMROMx2.RO1.FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, KEMROMx2.RO1.FRO.get ~ RRO.get, RRO.resample( + ); : ={arg, KEMROMx2.RO1.FRO.m} ==> ={res, KEMROMx2.RO1.FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, KEMROMx2.RO1.FRO.set ~ RRO.set, RRO.resample( + ); : ={x, y} /\ ={KEMROMx2.RO1.FRO.m} ==> ={res, KEMROMx2.RO1.FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, KEMROMx2.RO1.FRO.rem ~ RRO.rem, RRO.resample( + ); : ={arg, KEMROMx2.RO1.FRO.m} ==> ={res, KEMROMx2.RO1.FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, KEMROMx2.RO1.FRO.sample ~ RRO.sample, RRO.resample( + ); : ={arg, KEMROMx2.RO1.FRO.m} ==> ={res, KEMROMx2.RO1.FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, KEMROMx2.RO1.FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={KEMROMx2.RO1.FRO.m} ==> ={res, KEMROMx2.RO1.FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, KEMROMx2.RO1.FRO.allKnown ~ RRO.allKnown, RRO.resample( + ); : ={KEMROMx2.RO1.FRO.m} ==> ={res, KEMROMx2.RO1.FRO.m}]. + + lemma eager_D: + forall (D0(G : KEMROMx2.RO1.FRO) <: KEMROMx2.RO1.FRO_Distinguisher{+all mem, -KEMROMx2.RO1.FRO} ), + eager[ RRO.resample();, D0(KEMROMx2.RO1.FRO).distinguish ~ D0(RRO).distinguish, RRO.resample( + ); : ={glob D0, KEMROMx2.RO1.FRO.m, arg} ==> ={KEMROMx2.RO1.FRO.m, glob D0} /\ ={res}]. + + module Eager(D0 : KEMROMx2.RO1.FRO_Distinguisher) = { + proc main1(x : unit) : bool = { + var b : bool; + + KEMROMx2.RO1.FRO.init(); + b <@ D0(KEMROMx2.RO1.FRO).distinguish(x); + + return b; + } + + proc main2(x : unit) : bool = { + var b : bool; + + KEMROMx2.RO1.FRO.init(); + b <@ D0(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D0(G : KEMROMx2.RO1.FRO) <: KEMROMx2.RO1.FRO_Distinguisher{+all mem, -KEMROMx2.RO1.FRO} ), + equiv[ Eager(D0).main1 ~ Eager(D0).main2 : ={glob D0, arg} ==> ={res, KEMROMx2.RO1.FRO.m, glob D0}]. + + lemma LRO_RRO_init: + equiv[ KEMROMx2.RO1.RO.init ~ KEMROMx2.RO1.FRO.init : + true ==> KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ KEMROMx2.RO1.RO.get ~ RRO.get : + ={arg} /\ KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap ==> + ={res} /\ KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ KEMROMx2.RO1.RO.set ~ KEMROMx2.RO1.FRO.set : + ={x, y} /\ KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap ==> + KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ KEMROMx2.RO1.RO.rem ~ KEMROMx2.RO1.FRO.rem : + ={arg} /\ KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap ==> + KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ KEMROMx2.RO1.LRO.sample ~ KEMROMx2.RO1.FRO.sample : + ={arg} /\ KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap ==> + KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D0(G : KEMROMx2.RO1.RO) <: + KEMROMx2.RO1.RO_Distinguisher{+all mem, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO} ), + equiv[ D0(KEMROMx2.RO1.LRO).distinguish ~ D0(RRO).distinguish : + ={glob D0, arg} /\ KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap ==> + ={res, glob D0} /\ KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D0(G : KEMROMx2.RO1.RO) <: + KEMROMx2.RO1.RO_Distinguisher{+all mem, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ D0(KEMROMx2.RO1.RO).distinguish ~ D0(KEMROMx2.RO1.LRO).distinguish : + ={glob D0, KEMROMx2.RO1.RO.m, arg} ==> ={res, glob D0}]. + + lemma RO_LRO: + forall (D0(G : KEMROMx2.RO1.RO) <: + KEMROMx2.RO1.RO_Distinguisher{+all mem, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ KEMROMx2.RO1.MainD(D0, KEMROMx2.RO1.RO).distinguish ~ + KEMROMx2.RO1.MainD(D0, KEMROMx2.RO1.LRO).distinguish : + ={glob D0, arg} ==> ={res, glob D0}]. + + theory FinFrom. + type t = plaintext. + + op enum : plaintext list = TT.FinT.enum. + + op card : int = size enum. + + lemma enum_spec: forall (x : t), count (pred1 x) enum = 1. + + lemma enumP: forall (x : t), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : t list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinFrom. + + module FinRO = { + proc rem(x : plaintext) : unit = KEMROMx2.RO1.RO.rem + + proc set(x : plaintext, y : W8.t Array32.t) : unit = KEMROMx2.RO1.RO.set + + proc init() : unit = { + var l : FinFrom.t list; + + l <- FinFrom.enum; + KEMROMx2.RO1.RO.init(); + while (l <> []){ + KEMROMx2.RO1.RO.sample(head witness l); + l <- behead l; + } + } + + proc get(x : plaintext) : W8.t Array32.t = { + return oget (KEMROMx2.RO1.RO.m.[x])%SmtMap; + } + + proc sample(x : plaintext) : unit = {} + }. + + module type FinRO_Distinguisher(G : KEMROMx2.RO1.RO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.sample} + } + + lemma RO_FinRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D0(G : KEMROMx2.RO1.RO) <: FinRO_Distinguisher{+all mem, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO} ), + equiv[ KEMROMx2.RO1.MainD(D0, KEMROMx2.RO1.RO).distinguish ~ KEMROMx2.RO1.MainD(D0, FinRO).distinguish : + ={glob D0, arg} ==> ={res, glob D0}]. + + lemma pr_RO_FinRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D0(G : KEMROMx2.RO1.RO) <: FinRO_Distinguisher{+all mem, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO} ) &m + (x : unit) (p : bool -> bool), + Pr[KEMROMx2.RO1.MainD(D0, KEMROMx2.RO1.RO).distinguish(x) @ &m : p res] = + Pr[KEMROMx2.RO1.MainD(D0, FinRO).distinguish(x) @ &m : p res]. + + theory MUniFinFun. + op mfun ['u] (d : plaintext -> 'u distr) (f : plaintext -> 'u) : real = + (big predT (fun (x : plaintext) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma mfunE ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mfun d f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + lemma isdistr_dfun ['u]: forall (d : plaintext -> 'u distr), isdistr (mfun d). + + op dfun ['u] (d : plaintext -> 'u distr) : (plaintext -> 'u) distr = mk (mfun d). + + lemma dfun1E ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mu1 (dfun d) f = (big predT (fun (x : plaintext) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun1E_djoin ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mu1 (dfun d) f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + op tofun ['u] (us : 'u list) (x : plaintext) : 'u = nth witness us (index x FinFrom.enum). + + op offun ['u] (f : plaintext -> 'u) : 'u list = map f FinFrom.enum. + + lemma offunK ['u]: cancel offun tofun. + + lemma tofunK ['u]: forall (us : 'u list), size us = FinFrom.card => offun (tofun us) = us. + + lemma dfun_dmap ['u]: forall (d : plaintext -> 'u distr), dfun d = dmap (djoinmap d FinFrom.enum) tofun. + + lemma dfun_supp ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + f \in dfun d <=> forall (x : plaintext), f x \in d x. + + lemma dfun_fu ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_full (d x)) => is_full (dfun d). + + lemma dfun_uni ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_uniform (d x)) => is_uniform (dfun d). + + lemma dfun_funi ['u]: + forall (d : plaintext -> 'u distr), + (forall (x : plaintext), is_uniform (d x)) => + (forall (x : plaintext), is_full (d x)) => is_funiform (dfun d). + + lemma dfunE_djoin ['u]: + forall (du : plaintext -> 'u distr) (E : (plaintext -> 'u) -> bool), + mu (dfun du) E = mu (djoinmap du FinFrom.enum) (E \o tofun). + + lemma dfunE ['u]: + forall (du : plaintext -> 'u distr) (p : plaintext -> 'u -> bool), + mu (dfun du) (fun (f : plaintext -> 'u) => forall (x : plaintext), p x (f x)) = + (big predT (fun (x : plaintext) => mu (du x) (p x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma weight_dfun ['u]: + forall (df : plaintext -> 'u distr), + weight (dfun df) = (big predT (fun (x : plaintext) => weight (df x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun_ll ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_lossless (d x)) => is_lossless (dfun d). + + lemma dlet_dfun ['u, 'v]: + forall (d1 : plaintext -> 'u distr) (d2 : plaintext -> 'u -> 'v distr), + dlet (dfun d1) (fun (f1 : plaintext -> 'u) => dfun (fun (x : plaintext) => d2 x (f1 x))) = + dfun (fun (x : plaintext) => dlet (d1 x) (fun (x1 : 'u) => d2 x x1)). + + lemma dfun_unit ['u]: forall (f : plaintext -> 'u), dfun (fun (x : plaintext) => dunit (f x)) = dunit f. + + lemma dmap_dfun ['u, 'v]: + forall (d : plaintext -> 'u distr) (F : plaintext -> 'u -> 'v), + dmap (dfun d) (fun (f : plaintext -> 'u) (x : plaintext) => F x (f x)) = + dfun (fun (x : plaintext) => dmap (d x) (fun (fx : 'u) => F x fx)). + + lemma dfun1E_fix1 ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext) (f : + plaintext -> 'u), mu1 (dfun d) f = mu1 (d x0) (f x0) * mu1 (dfun d.[x0 <- dunit (f x0)]) f. + + lemma dlet_dfun_fupdate_ll ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext) (v : 'u), + dlet (dfun d.[x0 <- dunit v]) (fun (f : plaintext -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + dfun d. + + lemma dfunE_dlet_fix1 ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext), + dfun d = dlet (d x0) (fun (v : 'u) => dfun d.[x0 <- dunit v]). + + lemma dlet_dfun_update ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext), + dlet (dfun d) (fun (f : plaintext -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + (weight (d x0) \cdot dfun d). + + lemma dfun_projE ['u]: + forall (df : plaintext -> 'u distr) (x : plaintext), + dmap (dfun df) (fun (f : plaintext -> 'u) => f x) = (weight (dfun df) / weight (df x) \cdot df x). + + lemma eq_from_dfun_proj_eq ['u]: + forall (df1 df2 : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (df1 x)) => + (forall (x : plaintext), is_lossless (df2 x)) => + (forall (x : plaintext), + dmap (dfun df1) (fun (f : plaintext -> 'u) => f x) = + dmap (dfun df2) (fun (f : plaintext -> 'u) => f x)) => + df1 == df2. + + lemma dfun_prod1E ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr) (f : + plaintext -> 'u) (g : plaintext -> 'v), + mu1 (dfun (fun (x : plaintext) => df x `*` dg x)) (fun (x : plaintext) => (f x, g x)) = + mu1 (dfun df) f * mu1 (dfun dg) g. + + lemma dprod_funE ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr), + dfun df `*` dfun dg = + dmap (dfun (fun (x : plaintext) => df x `*` dg x)) + (fun (fg : plaintext -> 'u * 'v) => + ((fun (p : 'u * 'v) => p.`1) \o fg, (fun (p : 'u * 'v) => p.`2) \o fg)). + + lemma dfun_prodE ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr), + dfun (fun (x : plaintext) => df x `*` dg x) = + dmap (dfun df `*` dfun dg) + (fun (fg : (plaintext -> 'u) * (plaintext -> 'v)) (x : plaintext) => (fg.`1 x, fg.`2 x)). + + lemma dfun_condE ['u]: + forall (X : plaintext -> bool) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext -> 'u) * (plaintext -> 'u)) (x : plaintext) => if X x then tf.`1 x else tf.`2 x) = + dfun (fun (x : plaintext) => if X x then dt x else df x). + + lemma dlet_dfun_cond ['u]: + forall (dX : plaintext -> bool distr) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dX x)) => + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dlet (dfun dX) (fun (X : plaintext -> bool) => dfun (fun (x : plaintext) => if X x then dt x else df x)) = + dfun (fun (x : plaintext) => dlet (dX x) (fun (b : bool) => if b then dt x else df x)). + + lemma dfun_dcondE ['u]: + forall (dX : plaintext -> bool distr) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dX x)) => + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : plaintext -> bool) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext -> 'u) * (plaintext -> 'u)) (x : plaintext) => + if X x then tf.`1 x else tf.`2 x)) = + dfun (fun (x : plaintext) => (mu1 (dX x) true \cdot dt x) + (mu1 (dX x) false \cdot df x)). + end MUniFinFun. + + module FunRO = { + var f : plaintext -> W8.t Array32.t + + proc init() : unit = { + FunRO.f <$ (dfun (fun (_ : plaintext) => srand))%MUniFinFun; + } + + proc get(x : plaintext) : W8.t Array32.t = { + return FunRO.f x; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = { + FunRO.f <- fun (z : plaintext) => if z = x then y else FunRO.f z; + } + + proc sample(x : plaintext) : unit = {} + + proc rem(x : plaintext) : unit = {} + }. + + lemma FinRO_FunRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D0(G : KEMROMx2.RO1.RO) <: + FinRO_Distinguisher{+all mem, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -FunRO} ), + equiv[ KEMROMx2.RO1.MainD(D0, FinRO).distinguish ~ KEMROMx2.RO1.MainD(D0, FunRO).distinguish : + ={glob D0, arg} ==> ={res, glob D0}]. + + lemma pr_FinRO_FunRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D0(G : KEMROMx2.RO1.RO) <: + FinRO_Distinguisher{+all mem, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -FunRO} ) &m (x : unit) + (p : bool -> bool), + Pr[KEMROMx2.RO1.MainD(D0, FinRO).distinguish(x) @ &m : p res] = + Pr[KEMROMx2.RO1.MainD(D0, FunRO).distinguish(x) @ &m : p res]. + end RO1E. + + module RO_x2E = KEMROMx2.RO_x2(RO1E.FunRO, KEMROMx2.RO2.RO). + + module UU2(H : KEMROMx2.POracle_x2) = { + proc kg() : publickey * KEMROMx2.skey = UU1(RF.RF, H).kg + + proc enc(pk : publickey) : (W8.t Array960.t * W8.t Array128.t) * sharedsecret = UU1(RF.RF, H).enc + + var lD : ((W8.t Array960.t * W8.t Array128.t) * sharedsecret) list + + proc dec(sk : KEMROMx2.skey, c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var k : sharedsecret; + var ko : sharedsecret option; + + ko <- None; + if (assoc UU2.lD c <> None) + ko <- assoc UU2.lD c; + else { + k <$ srand; + ko <- Some k; + UU2.lD <- (c, k) :: UU2.lD; + } + + return ko; + } + }. + + module H1 = { + var bad : bool + + proc init() : unit = {} + + proc get1(x : plaintext) : W8.t Array32.t = RO_x2E.get1 + + proc get2(m : plaintext) : sharedsecret = { + var k : sharedsecret; + var cm : W8.t Array960.t * W8.t Array128.t; + + cm <- enc (RO1E.FunRO.f m) KEMROMx2.CCA.sk.`1.`1 m; + H1.bad <- if dec KEMROMx2.CCA.sk.`1.`2 cm <> Some m then true else H1.bad; + k <$ srand; + if ((m \notin KEMROMx2.RO2.RO.m)%SmtMap) + KEMROMx2.RO2.RO.m.[m] <- k; + + return oget (KEMROMx2.RO2.RO.m.[m])%SmtMap; + } + }. + + module H2(O1 : TT.PKEROM.POracle) = { + var merr : plaintext option + + var invert : bool + + var mtgt : plaintext + + proc init() : unit = {} + + proc get1(x : plaintext) : W8.t Array32.t = RO_x2E.get1 + + proc get2(m : plaintext) : sharedsecret = { + var k : sharedsecret; + var cm : W8.t Array960.t * W8.t Array128.t; + var r : W8.t Array32.t; + + H2.mtgt <- if KEMROMx2.CCA.cstar = None then m else H2.mtgt; + r <@ O1.get(m); + cm <- enc r KEMROMx2.CCA.sk.`1.`1 m; + H1.bad <- if dec KEMROMx2.CCA.sk.`1.`2 cm <> Some m then true else H1.bad; + H2.merr <- if H2.merr = None && H1.bad then Some m else H2.merr; + H2.invert <- + if KEMROMx2.CCA.cstar <> None && + m = H2.mtgt && dec KEMROMx2.CCA.sk.`1.`2 (oget KEMROMx2.CCA.cstar) = Some H2.mtgt then true + else H2.invert; + k <$ srand; + if ((m \notin KEMROMx2.RO2.RO.m)%SmtMap) { + if (assoc UU2.lD cm <> None) + k <- oget (assoc UU2.lD cm); + else + UU2.lD <- if H1.bad then UU2.lD else (cm, k) :: UU2.lD; + KEMROMx2.RO2.RO.m <- if H1.bad then KEMROMx2.RO2.RO.m else (KEMROMx2.RO2.RO.m.[m <- k])%SmtMap; + } + + return if H1.bad then witness else oget (KEMROMx2.RO2.RO.m.[m])%SmtMap; + } + }. + + module Gm2(H : KEMROMx2.Oracle_x2, S : KEMROMx2.Scheme, A : KEMROMx2.CCA_ADV) = { + module O = { + proc dec(c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var k : sharedsecret option; + + k <- None; + if (Some c <> KEMROMx2.CCA.cstar) + k <@ S(H).dec(KEMROMx2.CCA.sk, c); + + return k; + } + } + + proc main2() : bool = { + var pk : publickey; + var k1 : sharedsecret; + var ck0 : (W8.t Array960.t * W8.t Array128.t) * sharedsecret; + var cstar : (W8.t Array960.t * W8.t Array128.t) option; + var b : bool; + var b' : bool; + + H1.bad <- false; + H2.merr <- None; + H2.invert <- false; + RF.RF.init(); + RO_x2E.init(); + UU2.lD <- []; + KEMROMx2.CCA.cstar <- None; + (pk, KEMROMx2.CCA.sk) <@ S(H).kg(); + k1 <$ srand; + b <$ {0,1}; + ck0 <@ UU2(H).enc(pk); + KEMROMx2.CCA.cstar <- Some ck0.`1; + b' <@ KEMROMx2.CCA(H, S, A).A.guess(pk, ck0.`1, if b then k1 else ck0.`2); + + return b' = b; + } + + proc main() : bool = { + var win : bool; + var nobias : bool; + + win <@ Gm2(H, S, A).main2(); + nobias <$ {0,1}; + + return if H1.bad then nobias else win; + } + }. + + module BUUC(A : KEMROMx2.CCA_ADV, H : TT.PKEROM.POracle) = { + module H2B = { + proc get2(m : plaintext) : sharedsecret = H2(H).get2 + + proc init() : unit = H2(H).init + + proc get1(x : plaintext) : W8.t Array32.t = H.get + } + + proc find(pk : publickey, sk : TT.PKEROM.skey) : plaintext = { + var k1 : sharedsecret; + var ck0 : (W8.t Array960.t * W8.t Array128.t) * sharedsecret; + var cstar : (W8.t Array960.t * W8.t Array128.t) option; + var b : bool; + var b' : bool; + var z : sharedsecret; + + H1.bad <- false; + H2.merr <- None; + H2.invert <- false; + RF.RF.init(); + KEMROMx2.RO2.RO.init(); + UU2.lD <- []; + KEMROMx2.CCA.cstar <- None; + KEMROMx2.CCA.sk <- (sk, witness); + k1 <$ srand; + b <$ {0,1}; + ck0 <@ UU2(BUUC(A, H).H2B).enc(pk); + KEMROMx2.CCA.cstar <- Some ck0.`1; + CountHx2(BUUC(A, H).H2B).init(); + b' <@ KEMROMx2.CCA(CountHx2(BUUC(A, H).H2B), UU2, A).A.guess(pk, ck0.`1, if b then k1 else ck0.`2); + + return oget H2.merr; + } + }. + + module Gm3(H : KEMROMx2.Oracle_x2, S : KEMROMx2.Scheme, A : KEMROMx2.CCA_ADV) = { + module O = { + proc dec(c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var k : sharedsecret option; + + k <- None; + if (Some c <> KEMROMx2.CCA.cstar) + k <@ S(H).dec(KEMROMx2.CCA.sk, c); + + return k; + } + } + + proc main() : bool = { + var pk : publickey; + var k1 : sharedsecret; + var k2 : sharedsecret; + var b : bool; + var b' : bool; + var r : W8.t Array32.t; + var cm : W8.t Array960.t * W8.t Array128.t; + var nobias : bool; + + H1.bad <- false; + H2.merr <- None; + H2.invert <- false; + RF.RF.init(); + RO_x2E.init(); + UU2.lD <- []; + KEMROMx2.CCA.cstar <- None; + (pk, KEMROMx2.CCA.sk) <@ S(H).kg(); + k1 <$ srand; + k2 <$ srand; + b <$ {0,1}; + H2.mtgt <$ srand; + r <@ H.get1(H2.mtgt); + cm <- enc r pk H2.mtgt; + H1.bad <- if dec KEMROMx2.CCA.sk.`1.`2 cm <> Some H2.mtgt then true else H1.bad; + H2.merr <- if H2.merr = None && H1.bad then Some H2.mtgt else H2.merr; + UU2.lD <- (cm, witness) :: UU2.lD; + KEMROMx2.CCA.cstar <- Some cm; + b' <@ KEMROMx2.CCA(H, S, A).A.guess(pk, cm, if b then k1 else k2); + nobias <$ {0,1}; + + return if H1.bad then nobias else b' = b; + } + + proc main_0adv() : bool = { + var pk : publickey; + var k : sharedsecret; + var b : bool; + var b' : bool; + var r : W8.t Array32.t; + var cm : W8.t Array960.t * W8.t Array128.t; + var nobias : bool; + + H1.bad <- false; + H2.merr <- None; + H2.invert <- false; + RF.RF.init(); + RO_x2E.init(); + UU2.lD <- []; + KEMROMx2.CCA.cstar <- None; + (pk, KEMROMx2.CCA.sk) <@ S(H).kg(); + k <$ srand; + H2.mtgt <$ srand; + r <@ H.get1(H2.mtgt); + cm <- enc r pk H2.mtgt; + H1.bad <- if dec KEMROMx2.CCA.sk.`1.`2 cm <> Some H2.mtgt then true else H1.bad; + H2.merr <- if H2.merr = None && H1.bad then Some H2.mtgt else H2.merr; + UU2.lD <- (cm, witness) :: UU2.lD; + KEMROMx2.CCA.cstar <- Some cm; + b' <@ KEMROMx2.CCA(H, S, A).A.guess(pk, cm, k); + nobias <$ {0,1}; + b <$ {0,1}; + + return if H1.bad then nobias else b' = b; + } + }. + + module H2BOW(OO1 : TT.PKEROM.POracle) = { + proc init() : unit = {} + + proc get1(x : plaintext) : W8.t Array32.t = RO_x2E.get1 + + proc get2(m : plaintext) : sharedsecret = { + var k : sharedsecret; + var cm : W8.t Array960.t * W8.t Array128.t; + var r : W8.t Array32.t; + + r <@ OO1.get(m); + cm <- enc r KEMROMx2.CCA.sk.`1.`1 m; + H1.bad <- if dec KEMROMx2.CCA.sk.`1.`2 cm <> Some m then true else H1.bad; + H2.merr <- if H2.merr = None && H1.bad then Some m else H2.merr; + H2.invert <- + if KEMROMx2.CCA.cstar <> None && + m = H2.mtgt && dec KEMROMx2.CCA.sk.`1.`2 (oget KEMROMx2.CCA.cstar) = Some H2.mtgt then true + else H2.invert; + k <$ srand; + if ((m \notin KEMROMx2.RO2.RO.m)%SmtMap) { + if (assoc UU2.lD cm <> None) + k <- oget (assoc UU2.lD cm); + else + UU2.lD <- (cm, k) :: UU2.lD; + KEMROMx2.RO2.RO.m.[m] <- k; + } + + return oget (KEMROMx2.RO2.RO.m.[m])%SmtMap; + } + }. + + module BUUOW(A : KEMROMx2.CCA_ADV, H : TT.PKEROM.POracle, O : TT.PKEROM.VA_ORC) = { + module H2B = { + proc get2(m : plaintext) : sharedsecret = H2BOW(H).get2 + + proc init() : unit = H2BOW(H).init + + proc get1(x : plaintext) : W8.t Array32.t = H.get + } + + proc find(pk : publickey, cm : W8.t Array960.t * W8.t Array128.t) : plaintext option = { + var k1 : sharedsecret; + var k2 : sharedsecret; + var b : bool; + var b' : bool; + var r : W8.t Array32.t; + + H1.bad <- false; + H2.merr <- None; + H2.invert <- false; + RF.RF.init(); + KEMROMx2.RO2.RO.init(); + UU2.lD <- []; + KEMROMx2.CCA.cstar <- None; + KEMROMx2.CCA.sk <- ((pk, witness), witness); + k1 <$ srand; + k2 <$ srand; + b <$ {0,1}; + UU2.lD <- (cm, witness) :: UU2.lD; + KEMROMx2.CCA.cstar <- Some cm; + b' <@ KEMROMx2.CCA(BUUOW(A, H, O).H2B, UU2, A).A.guess(pk, cm, if b then k1 else k2); + + return + if (card + ((filter (fun (m0 : plaintext) => enc (RO1E.FunRO.f m0) pk m0 = oget KEMROMx2.CCA.cstar) + ((fdom KEMROMx2.RO2.RO.m))%SmtMap))%FSet)%FSet = + 1 then + Some + (head witness + ((elems + ((filter (fun (m0 : plaintext) => enc (RO1E.FunRO.f m0) pk m0 = oget KEMROMx2.CCA.cstar) + ((fdom KEMROMx2.RO2.RO.m))%SmtMap))%FSet))%FSet) + else None; + } + }. + + module H2BOWMod(OO1 : TT.PKEROM.POracle) = { + var crd : int + + var mf : plaintext option + + proc init() : unit = {} + + proc get1(x : plaintext) : W8.t Array32.t = RO_x2E.get1 + + proc get2(m : plaintext) : sharedsecret = { + var k : sharedsecret; + var cm : W8.t Array960.t * W8.t Array128.t; + var r : W8.t Array32.t; + + r <@ OO1.get(m); + cm <- enc r KEMROMx2.CCA.sk.`1.`1 m; + H1.bad <- if dec KEMROMx2.CCA.sk.`1.`2 cm <> Some m then true else H1.bad; + H2.merr <- if H2.merr = None && H1.bad then Some m else H2.merr; + H2.invert <- + if KEMROMx2.CCA.cstar <> None && + m = H2.mtgt && dec KEMROMx2.CCA.sk.`1.`2 (oget KEMROMx2.CCA.cstar) = Some H2.mtgt then true + else H2.invert; + k <$ srand; + if ((m \notin KEMROMx2.RO2.RO.m)%SmtMap) { + H2BOWMod.crd <- H2BOWMod.crd + b2i (Some cm = KEMROMx2.CCA.cstar); + H2BOWMod.mf <- if Some cm = KEMROMx2.CCA.cstar then Some m else H2BOWMod.mf; + if (assoc UU2.lD cm <> None) + k <- oget (assoc UU2.lD cm); + else + UU2.lD <- (cm, k) :: UU2.lD; + KEMROMx2.RO2.RO.m.[m] <- k; + } + + return oget (KEMROMx2.RO2.RO.m.[m])%SmtMap; + } + }. + + module BUUOWMod(A : KEMROMx2.CCA_ADV, H : TT.PKEROM.POracle, O : TT.PKEROM.VA_ORC) = { + module H2B = { + proc get2(m : plaintext) : sharedsecret = H2BOWMod(H).get2 + + proc init() : unit = H2BOWMod(H).init + + proc get1(x : plaintext) : W8.t Array32.t = H.get + } + + proc find(pk : publickey, cm : W8.t Array960.t * W8.t Array128.t) : plaintext option = { + var k1 : sharedsecret; + var k2 : sharedsecret; + var b : bool; + var b' : bool; + + H1.bad <- false; + H2.merr <- None; + H2.invert <- false; + RF.RF.init(); + KEMROMx2.RO2.RO.init(); + UU2.lD <- []; + KEMROMx2.CCA.cstar <- None; + KEMROMx2.CCA.sk <- ((pk, witness), witness); + k1 <$ srand; + k2 <$ srand; + b <$ {0,1}; + UU2.lD <- (cm, witness) :: UU2.lD; + KEMROMx2.CCA.cstar <- Some cm; + H2BOWMod.crd <- 0; + H2BOWMod.mf <- None; + CountHx2(BUUOWMod(A, H, O).H2B).init(); + b' <@ KEMROMx2.CCA(CountHx2(BUUOWMod(A, H, O).H2B), UU2, A).A.guess(pk, cm, if b then k1 else k2); + + return if H2BOWMod.crd = 1 then H2BOWMod.mf else None; + } + }. + + module BUUCI(A : KEMROMx2.CCA_ADV, H : TT.PKEROM.POracle) = { + module H2B = { + proc get2(m : plaintext) : sharedsecret = H2(H).get2 + + proc init() : unit = H2(H).init + + proc get1(x : plaintext) : W8.t Array32.t = H.get + } + + proc find(pk : publickey, sk : TT.PKEROM.skey) : plaintext = { + var k1 : sharedsecret; + var k2 : sharedsecret; + var b : bool; + var b' : bool; + var r : W8.t Array32.t; + var cm : W8.t Array960.t * W8.t Array128.t; + var nobias : bool; + + H1.bad <- false; + H2.merr <- None; + H2.invert <- false; + RF.RF.init(); + KEMROMx2.RO2.RO.init(); + UU2.lD <- []; + KEMROMx2.CCA.cstar <- None; + KEMROMx2.CCA.sk <- (sk, witness); + k1 <$ srand; + k2 <$ srand; + b <$ {0,1}; + H2.mtgt <$ srand; + r <@ BUUCI(A, H).H2B.get1(H2.mtgt); + cm <- enc r pk H2.mtgt; + H1.bad <- if dec KEMROMx2.CCA.sk.`1.`2 cm <> Some H2.mtgt then true else H1.bad; + H2.merr <- if H2.merr = None && H1.bad then Some H2.mtgt else H2.merr; + UU2.lD <- (cm, witness) :: UU2.lD; + KEMROMx2.CCA.cstar <- Some cm; + CountHx2(BUUCI(A, H).H2B).init(); + b' <@ KEMROMx2.CCA(CountHx2(BUUCI(A, H).H2B), UU2, A).A.guess(pk, cm, if b then k1 else k2); + + return oget H2.merr; + } + }. + + lemma Gm0_Gm1: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + Pr[KEMROMx2.CCA(KEMROMx2.RO_x2(KEMROMx2.RO1.RO, KEMROMx2.RO2.RO), UU, A).main() @ &m : res] - + Pr[Gm1(KEMROMx2.RO_x2(KEMROMx2.RO1.RO, KEMROMx2.RO2.RO), A).main() @ &m : res] = + Pr[J.IND(PseudoRF.PRF, D(A)).main() @ &m : res] - Pr[J.IND(RF.RF, D(A)).main() @ &m : res]. + + lemma uu_goal_eager: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + Pr[Gm1(KEMROMx2.RO_x2(KEMROMx2.RO1.RO, KEMROMx2.RO2.RO), A).main() @ &m : res] = + Pr[Gm1(RO_x2E, A).main() @ &m : res]. + + op c2m (c : W8.t Array960.t * W8.t Array128.t) (sk : TT.PKEROM.skey) : plaintext option = dec sk.`2 c. + + op oc2m (c : W8.t Array960.t * W8.t Array128.t) (sk : TT.PKEROM.skey) : plaintext = oget (dec sk.`2 c). + + op m2c (m : plaintext) (sk : TT.PKEROM.skey) (f : plaintext -> W8.t Array32.t) : W8.t Array960.t * + W8.t Array128.t = enc (f m) sk.`1 m. + + op goodc (c : W8.t Array960.t * W8.t Array128.t) (sk : TT.PKEROM.skey) (f : + plaintext -> W8.t Array32.t) : bool = c2m c sk <> None /\ m2c (oc2m c sk) sk f = c. + + lemma bound_bad: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + Pr[Gm2(H2(RO1E.FunRO), UU2, A).main() @ &m : H1.bad] <= + Pr[TT.PKEROM.Correctness_Adv(RO1E.FunRO, TT.TT, BUUC(A)).main() @ &m : res]. + + lemma bound_invert: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + (forall (H0 <: KEMROMx2.POracle_x2{+all mem, -A} ) (O <: KEMROMx2.CCA_ORC{+all mem, -A} ), + islossless O.dec => islossless H0.get1 => islossless H0.get2 => islossless A(H0, O).guess) => + `|Pr[TT.PKEROM.OW_PCVA(RO1E.FunRO, TT.TT, BUUOW(A)).main() @ &m : res] - + Pr[Gm3(H2(RO1E.FunRO), UU2, A).main() @ &m : H2.invert]| <= + Pr[Gm3(H2(RO1E.FunRO), UU2, A).main() @ &m : H1.bad]. + + lemma bound_bad2: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + Pr[Gm3(H2(RO1E.FunRO), UU2, A).main() @ &m : H1.bad] <= + Pr[TT.PKEROM.Correctness_Adv(RO1E.FunRO, TT.TT, BUUCI(A)).main() @ &m : res]. + + lemma G3adv: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + (forall (H0 <: KEMROMx2.POracle_x2{+all mem, -A} ) (O <: KEMROMx2.CCA_ORC{+all mem, -A} ), + islossless O.dec => islossless H0.get1 => islossless H0.get2 => islossless A(H0, O).guess) => + Pr[Gm3(H2(RO1E.FunRO), UU2, A).main() @ &m : res] = 1%r / 2%r. + + lemma corr_goal_eagerC: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + Pr[TT.PKEROM.Correctness_Adv(RO1E.FunRO, TT.TT, BUUC(A)).main() @ &m : res] = + Pr[TT.PKEROM.Correctness_Adv(KEMROMx2.RO1.RO, TT.TT, BUUC(A)).main() @ &m : res]. + + lemma corr_goal_eagerCI: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + Pr[TT.PKEROM.Correctness_Adv(RO1E.FunRO, TT.TT, BUUCI(A)).main() @ &m : res] = + Pr[TT.PKEROM.Correctness_Adv(KEMROMx2.RO1.RO, TT.TT, BUUCI(A)).main() @ &m : res]. + + lemma owmod: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + Pr[TT.PKEROM.OW_PCVA(RO1E.FunRO, TT.TT, BUUOW(A)).main() @ &m : res] = + Pr[TT.PKEROM.OW_PCVA(RO1E.FunRO, TT.TT, BUUOWMod(A)).main() @ &m : res]. + + lemma corr_goal_eagerOW: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + Pr[TT.PKEROM.OW_PCVA(KEMROMx2.RO1.RO, TT.TT, BUUOWMod(A)).main() @ &m : res] = + Pr[TT.PKEROM.OW_PCVA(RO1E.FunRO, TT.TT, BUUOW(A)).main() @ &m : res]. + + lemma count_buuc: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) (H1_0 <: TT.PKEROM.RO.RO{+all mem, -TT.CO1, -BUUC(A)} ), + qHT + qHU + 2 <= TT.qHC => + (forall (RO0 <: KEMROMx2.POracle_x2{+all mem, -CountHx2, -A} ) + (O <: KEMROMx2.CCA_ORC{+all mem, -CountHx2, -A} ), + hoare[ A(CountHx2(RO0), O).guess : + CountHx2.c_ht = 0 /\ CountHx2.c_hu = 0 ==> CountHx2.c_ht <= qHT /\ CountHx2.c_hu <= qHU]) => + hoare[ TT.Correctness_Adv1(H1_0, BUUC(A)).A.find : TT.CO1.counter = 0 ==> TT.CO1.counter <= TT.qHC]. + + lemma count_buuci: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) (H1_0 <: TT.PKEROM.RO.RO{+all mem, -TT.CO1, -BUUCI(A)} ), + qHT + qHU + 1 <= TT.qHC => + (forall (RO0 <: KEMROMx2.POracle_x2{+all mem, -CountHx2, -A} ) + (O <: KEMROMx2.CCA_ORC{+all mem, -CountHx2, -A} ), + hoare[ A(CountHx2(RO0), O).guess : + CountHx2.c_ht = 0 /\ CountHx2.c_hu = 0 ==> CountHx2.c_ht <= qHT /\ CountHx2.c_hu <= qHU]) => + hoare[ TT.Correctness_Adv1(H1_0, BUUCI(A)).A.find : TT.CO1.counter = 0 ==> TT.CO1.counter <= TT.qHC]. + + lemma count_buuowmod: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) (RO0 <: TT.PKEROM.POracle{+all mem, -TT.CountO, -BUUOWMod(A)} ) + (O <: TT.PKEROM.VA_ORC{+all mem, -TT.CountO, -BUUOWMod(A)} ), + TT.qP = 0 => + TT.qV = 0 => + qHT + qHU + 1 <= TT.qH => + (forall (RO1 <: KEMROMx2.POracle_x2{+all mem, -CountHx2, -A} ) + (O0 <: KEMROMx2.CCA_ORC{+all mem, -CountHx2, -A} ), + hoare[ A(CountHx2(RO1), O0).guess : + CountHx2.c_ht = 0 /\ CountHx2.c_hu = 0 ==> CountHx2.c_ht <= qHT /\ CountHx2.c_hu <= qHU]) => + hoare[ BUUOWMod(A, TT.CountH(RO0), TT.CountO(O)).find : + TT.CountO.c_h = 0 /\ TT.CountO.c_cvo = 0 /\ TT.CountO.c_pco = 0 ==> + TT.CountO.c_h <= TT.qH /\ TT.CountO.c_cvo <= TT.qV /\ TT.CountO.c_pco <= TT.qP]. + + lemma conclusion_cca_pre: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + (forall (H0 <: KEMROMx2.POracle_x2{+all mem, -A} ) (O <: KEMROMx2.CCA_ORC{+all mem, -A} ), + islossless O.dec => islossless H0.get1 => islossless H0.get2 => islossless A(H0, O).guess) => + `|Pr[KEMROMx2.CCA(KEMROMx2.RO_x2(KEMROMx2.RO1.RO, KEMROMx2.RO2.RO), UU, A).main() @ &m : res] - 1%r / 2%r| <= + `|Pr[J.IND(PseudoRF.PRF, D(A)).main() @ &m : res] - Pr[J.IND(RF.RF, D(A)).main() @ &m : res]| + + Pr[TT.PKEROM.Correctness_Adv(KEMROMx2.RO1.RO, TT.TT, BUUC(A)).main() @ &m : res] + + Pr[TT.PKEROM.Correctness_Adv(KEMROMx2.RO1.RO, TT.TT, BUUCI(A)).main() @ &m : res] + + Pr[TT.PKEROM.OW_PCVA(KEMROMx2.RO1.RO, TT.TT, BUUOWMod(A)).main() @ &m : res]. + + lemma conclusion: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKE.OW_CPA, -TT.PKE.BOWp, -TT.PKE.OWL_CPA, -TT.PKE.OWvsIND.Bowl, -TT.BasePKE, -TT.PKEROM.RO.RO, -TT.PKEROM.RO.FRO, -TT.PKEROM.OW_PCVA, -TT.Correctness_Adv1, -TT.B, -TT.CountO, -TT.Gm, -TT.O_AdvOW, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + TT.qH = qHT + qHU + 1 => + TT.qV = 0 => + TT.qP = 0 => + TT.qH + 1 = TT.qHC => + TT.qHC < TT.FinT.card - 1 => + (forall (RO0 <: KEMROMx2.POracle_x2{+all mem, -CountHx2, -A} ) + (O <: KEMROMx2.CCA_ORC{+all mem, -CountHx2, -A} ), + hoare[ A(CountHx2(RO0), O).guess : + CountHx2.c_ht = 0 /\ CountHx2.c_hu = 0 ==> CountHx2.c_ht <= qHT /\ CountHx2.c_hu <= qHU]) => + (forall (H0 <: KEMROMx2.POracle_x2{+all mem, -A} ) (O <: KEMROMx2.CCA_ORC{+all mem, -A} ), + islossless O.dec => islossless H0.get1 => islossless H0.get2 => islossless A(H0, O).guess) => + `|Pr[KEMROMx2.CCA(KEMROMx2.RO_x2(KEMROMx2.RO1.RO, KEMROMx2.RO2.RO), UU, A).main() @ &m : res] - 1%r / 2%r| <= + `|Pr[J.IND(PseudoRF.PRF, D(A)).main() @ &m : res] - Pr[J.IND(RF.RF, D(A)).main() @ &m : res]| + + (qHT + qHU + 3)%r * Pr[TT.PKE.Correctness_Adv(TT.BasePKE, TT.B(BUUC(A), TT.PKEROM.RO.RO)).main() @ &m : res] + + (qHT + qHU + 3)%r * Pr[TT.PKE.Correctness_Adv(TT.BasePKE, TT.B(BUUCI(A), TT.PKEROM.RO.RO)).main() @ &m : res] + + (qHT + qHU + 3)%r * + Pr[TT.PKE.Correctness_Adv(TT.BasePKE, TT.B(TT.AdvCorr(BUUOWMod(A)), TT.PKEROM.RO.RO)).main() @ &m : res] + + Pr[TT.PKE.Correctness_Adv(TT.BasePKE, TT.PKE.BOWp(TT.BasePKE, TT.AdvOW(BUUOWMod(A)))).main() @ &m : res] + + 2%r * + `|Pr[TT.PKE.CPA(TT.BasePKE, TT.PKE.OWvsIND.Bowl(TT.PKE.OWvsIND.BL(TT.AdvOW(BUUOWMod(A))))).main() @ &m : res] - + 1%r / 2%r| + + (qHT + qHU + 1)%r * Pr[TT.PKE.OW_CPA(TT.BasePKE, TT.AdvOW_query(BUUOWMod(A))).main() @ &m : res] + + 2%r * TT.PKE.eps_msg. + + module X(O : TT.PKEROM.POracle) = BUUC(A/218906, O).H2B. + + module XI(O : TT.PKEROM.POracle) = BUUCI(A/218906, O).H2B. + + lemma conclusion_cpa: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKE.OW_CPA, -TT.PKE.BOWp, -TT.PKE.OWL_CPA, -TT.PKE.OWvsIND.Bowl, -TT.BasePKE, -TT.PKEROM.RO.RO, -TT.PKEROM.RO.FRO, -TT.PKEROM.OW_PCVA, -TT.Correctness_Adv1, -TT.B, -TT.CountO, -TT.Gm, -TT.O_AdvOW, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + TT.qH = qHT + qHU + 1 => + TT.qV = 0 => + TT.qP = 0 => + TT.qH + 1 = TT.qHC => + TT.qHC < TT.FinT.card - 1 => + (forall (RO0 <: KEMROMx2.POracle_x2{+all mem, -CountHx2, -A} ) + (O <: KEMROMx2.CCA_ORC{+all mem, -CountHx2, -A} ), + hoare[ A(CountHx2(RO0), O).guess : + CountHx2.c_ht = 0 /\ CountHx2.c_hu = 0 ==> CountHx2.c_ht <= qHT /\ CountHx2.c_hu <= qHU]) => + (forall (H0 <: KEMROMx2.POracle_x2{+all mem, -A} ) (O <: KEMROMx2.CCA_ORC{+all mem, -A} ), + islossless O.dec => islossless H0.get1 => islossless H0.get2 => islossless A(H0, O).guess) => + `|Pr[KEMROMx2.CCA(KEMROMx2.RO_x2(KEMROMx2.RO1.RO, KEMROMx2.RO2.RO), UU, A).main() @ &m : res] - 1%r / 2%r| <= + 2%r * + `|Pr[TT.PKE.CPA(TT.BasePKE, TT.PKE.OWvsIND.Bowl(TT.PKE.OWvsIND.BL(TT.AdvOW(BUUOWMod(A))))).main() @ &m : res] - + 1%r / 2%r| + + 2%r * + `|Pr[TT.PKE.CPA(TT.BasePKE, TT.PKE.OWvsIND.Bowl(TT.AdvOWL_query(BUUOWMod(A)))).main() @ &m : res] - 1%r / 2%r| + + (qHT + qHU + 3)%r * Pr[TT.PKE.Correctness_Adv(TT.BasePKE, TT.B(BUUC(A), TT.PKEROM.RO.RO)).main() @ &m : res] + + (qHT + qHU + 3)%r * Pr[TT.PKE.Correctness_Adv(TT.BasePKE, TT.B(BUUCI(A), TT.PKEROM.RO.RO)).main() @ &m : res] + + (qHT + qHU + 3)%r * + Pr[TT.PKE.Correctness_Adv(TT.BasePKE, TT.B(TT.AdvCorr(BUUOWMod(A)), TT.PKEROM.RO.RO)).main() @ &m : res] + + Pr[TT.PKE.Correctness_Adv(TT.BasePKE, TT.PKE.BOWp(TT.BasePKE, TT.AdvOW(BUUOWMod(A)))).main() @ &m : res] + + `|Pr[J.IND(PseudoRF.PRF, D(A)).main() @ &m : res] - Pr[J.IND(RF.RF, D(A)).main() @ &m : res]| + + 2%r * (qHT + qHU + 2)%r * TT.PKE.eps_msg. + end UU. + + theory KEMROM. + type skey = (publickey * W8.t Array1152.t) * sharedsecret. + + lemma dkey_ll: is_lossless srand. + + hint solve 0 lossless : dkey_ll. + + hint solve 0 random : dkey_ll. + + lemma dkey_uni: is_uniform srand. + + hint solve 0 random : dkey_uni. + + lemma dkey_fu: is_full srand. + + hint solve 0 random : dkey_fu. + + theory RO. + module type RO = { + proc init() : unit {} + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t {} + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit {} + + proc rem(x : plaintext * W8.t Array32.t) : unit {} + + proc sample(x : plaintext * W8.t Array32.t) : unit {} + } + + module type RO_Distinguisher(G : RO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.rem, G.sample} + } + + module MainD(D : RO_Distinguisher, RO0 : RO) = { + proc distinguish(x : unit) : bool = { + var r : bool; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + module type ROmap = { + proc init() : unit {} + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t {} + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit {} + + proc rem(x : plaintext * W8.t Array32.t) : unit {} + + proc sample(x : plaintext * W8.t Array32.t) : unit {} + + proc restrK() : (plaintext * W8.t Array32.t, sharedsecret * W8.t Array32.t) SmtMap.fmap {} + } + + module type FRO = { + proc init() : unit {} + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t {} + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit {} + + proc rem(x : plaintext * W8.t Array32.t) : unit {} + + proc sample(x : plaintext * W8.t Array32.t) : unit {} + + proc queried(x : plaintext * W8.t Array32.t, f : PROM.flag) : bool {} + + proc allKnown() : (plaintext * W8.t Array32.t, sharedsecret * W8.t Array32.t) SmtMap.fmap {} + } + + module type FRO_Distinguisher(G : FRO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.rem, G.sample, G.queried, G.allKnown} + } + + abstract theory MkRO. + module RO = { + var m : (plaintext * W8.t Array32.t, sharedsecret * W8.t Array32.t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = { + var r : sharedsecret * W8.t Array32.t; + + r <$ dRO; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : plaintext * W8.t Array32.t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : plaintext * W8.t Array32.t) : unit = { + RO.get(x); + } + + proc restrK() : (plaintext * W8.t Array32.t, sharedsecret * W8.t Array32.t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = RO.get + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit = RO.set + + proc rem(x : plaintext * W8.t Array32.t) : unit = RO.rem + + proc sample(x : plaintext * W8.t Array32.t) : unit = {} + }. + end MkRO. + + module RO = { + var m : (plaintext * W8.t Array32.t, sharedsecret * W8.t Array32.t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = { + var r : sharedsecret * W8.t Array32.t; + + r <$ dRO; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : plaintext * W8.t Array32.t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : plaintext * W8.t Array32.t) : unit = { + RO.get(x); + } + + proc restrK() : (plaintext * W8.t Array32.t, sharedsecret * W8.t Array32.t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = RO.get + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit = RO.set + + proc rem(x : plaintext * W8.t Array32.t) : unit = RO.rem + + proc sample(x : plaintext * W8.t Array32.t) : unit = {} + }. + + module FRO = { + var m : (plaintext * W8.t Array32.t, (sharedsecret * W8.t Array32.t) * PROM.flag) SmtMap.fmap + + proc init() : unit = { + FRO.m <- SmtMap.empty; + } + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = { + var r : sharedsecret * W8.t Array32.t; + + r <$ dRO; + if ((x \in FRO.m)%SmtMap) + r <- (oget (FRO.m.[x])%SmtMap).`1; + FRO.m.[x] <- (r, PROM.Known); + + return r; + } + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit = { + FRO.m.[x] <- (y, PROM.Known); + } + + proc rem(x : plaintext * W8.t Array32.t) : unit = { + FRO.m <- (rem FRO.m x)%SmtMap; + } + + proc sample(x : plaintext * W8.t Array32.t) : unit = { + var c : sharedsecret * W8.t Array32.t; + + c <$ dRO; + if ((x \notin FRO.m)%SmtMap) + FRO.m.[x] <- (c, PROM.Unknown); + } + + proc queried(x : plaintext * W8.t Array32.t, f : PROM.flag) : bool = { + return (x \in FRO.m)%SmtMap /\ ((FRO.m.[x])%SmtMap \is f)%PROM; + } + + proc allKnown() : (plaintext * W8.t Array32.t, sharedsecret * W8.t Array32.t) SmtMap.fmap = { + return (restr PROM.Known FRO.m)%SmtMap; + } + }. + + lemma RO_FRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_get: + equiv[ RO.get ~ FRO.get : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> ={res} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_sample: + equiv[ RO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(RO).distinguish ~ D(FRO).distinguish : + ={arg, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_init_ll: islossless RO.init. + + lemma FRO_init_ll: islossless FRO.init. + + lemma FRO_in_dom_ll: islossless FRO.queried. + + lemma FRO_restrK_ll: islossless FRO.allKnown. + + lemma RO_set_ll: islossless RO.set. + + lemma FRO_set_ll: islossless FRO.set. + + lemma RO_get_ll: (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => islossless RO.get. + + lemma FRO_get_ll: (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => islossless FRO.get. + + lemma RO_sample_ll: (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => islossless RO.sample. + + lemma FRO_sample_ll: (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => islossless FRO.sample. + + module type RM_Distinguisher(G : ROmap) = { + proc distinguish(_ : unit) : bool {G.get, G.sample, G.restrK} + } + + module MainRM(D : RM_Distinguisher, RO0 : ROmap) = { + proc distinguish(x : unit) : bool = { + var r : bool; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + lemma fcoll_bound ['rT]: + forall (f : sharedsecret * W8.t Array32.t -> 'rT) (Pc : real), + 0%r <= Pc => + (forall (_ _ : plaintext * W8.t Array32.t) (y : 'rT), y \in dmap dRO f => mu1 (dmap dRO f) y <= Pc) => + forall (q : int), + 0 <= q => + forall (D(G : ROmap) <: RM_Distinguisher{+all mem, -RO} ) &m (z : unit), + Pr[MainRM(D, RO).distinguish(z) @ &m : (fcoll f RO.m)%SmtMap /\ (fsize RO.m)%SmtMap <= q] <= + (q * (q - 1))%r / 2%r * Pc. + + theory FullEager. + abstract theory EagerCore. + axiom dout_ll: forall (_ : plaintext * W8.t Array32.t), is_lossless dRO. + + module type Orcl = { + proc f(x : plaintext * W8.t Array32.t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : (plaintext * W8.t Array32.t) list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : (plaintext * W8.t Array32.t) list, l2 : (plaintext * W8.t Array32.t) list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : plaintext * W8.t Array32.t, l : (plaintext * W8.t Array32.t) list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : plaintext * W8.t Array32.t, t2 : plaintext * W8.t Array32.t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : plaintext * W8.t Array32.t, t2 : plaintext * W8.t Array32.t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : (plaintext * W8.t Array32.t) list) (i : plaintext * W8.t Array32.t) (s2 : (plaintext * + W8.t Array32.t) list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : plaintext * W8.t Array32.t -> bool) (Inv : + (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ + (forall (x : plaintext * W8.t Array32.t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : plaintext * W8.t Array32.t -> bool) (Inv : + (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : + (forall (x : plaintext * W8.t Array32.t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = { + var r : sharedsecret * W8.t Array32.t; + + r <$ dRO; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit = FRO.set + + proc rem(x : plaintext * W8.t Array32.t) : unit = FRO.rem + + proc sample(x : plaintext * W8.t Array32.t) : unit = FRO.sample + + proc queried(x : plaintext * W8.t Array32.t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (plaintext * W8.t Array32.t, sharedsecret * W8.t Array32.t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : plaintext * W8.t Array32.t) : unit = { + var c : sharedsecret * W8.t Array32.t; + + c <$ dRO; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : plaintext * W8.t Array32.t) (mx1 : ((sharedsecret * W8.t Array32.t) * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : plaintext * W8.t Array32.t) (mx1 mx2 : ((sharedsecret * W8.t Array32.t) * + PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : plaintext * W8.t Array32.t) (r1 : sharedsecret * W8.t Array32.t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + end FullEager. + + abstract theory FinEager. + abstract theory EagerCore. + axiom dout_ll: forall (_ : plaintext * W8.t Array32.t), is_lossless dRO. + + module type Orcl = { + proc f(x : plaintext * W8.t Array32.t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : (plaintext * W8.t Array32.t) list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : (plaintext * W8.t Array32.t) list, l2 : (plaintext * W8.t Array32.t) list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : plaintext * W8.t Array32.t, l : (plaintext * W8.t Array32.t) list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : plaintext * W8.t Array32.t, t2 : plaintext * W8.t Array32.t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : plaintext * W8.t Array32.t, t2 : plaintext * W8.t Array32.t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : (plaintext * W8.t Array32.t) list) (i : plaintext * W8.t Array32.t) (s2 : (plaintext * + W8.t Array32.t) list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : plaintext * W8.t Array32.t -> bool) (Inv : + (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ + (forall (x : plaintext * W8.t Array32.t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : plaintext * W8.t Array32.t -> bool) (Inv : + (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : + (forall (x : plaintext * W8.t Array32.t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = { + var r : sharedsecret * W8.t Array32.t; + + r <$ dRO; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit = FRO.set + + proc rem(x : plaintext * W8.t Array32.t) : unit = FRO.rem + + proc sample(x : plaintext * W8.t Array32.t) : unit = FRO.sample + + proc queried(x : plaintext * W8.t Array32.t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (plaintext * W8.t Array32.t, sharedsecret * W8.t Array32.t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : plaintext * W8.t Array32.t) : unit = { + var c : sharedsecret * W8.t Array32.t; + + c <$ dRO; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : plaintext * W8.t Array32.t) (mx1 : ((sharedsecret * W8.t Array32.t) * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : plaintext * W8.t Array32.t) (mx1 mx2 : ((sharedsecret * W8.t Array32.t) * + PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : plaintext * W8.t Array32.t) (r1 : sharedsecret * W8.t Array32.t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + theory FinFrom. + type t = plaintext * W8.t Array32.t. + + op enum : t list. + + op card : int = size enum. + + axiom enum_spec: forall (x : t), count (pred1 x) enum = 1. + + lemma enumP: forall (x : t), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : t list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinFrom. + + module FinRO = { + proc rem(x : plaintext * W8.t Array32.t) : unit = RO.rem + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit = RO.set + + proc init() : unit = { + var l : FinFrom.t list; + + l <- FinFrom.enum; + RO.init(); + while (l <> []){ + RO.sample(head witness l); + l <- behead l; + } + } + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = { + return oget (RO.m.[x])%SmtMap; + } + + proc sample(x : plaintext * W8.t Array32.t) : unit = {} + }. + + module type FinRO_Distinguisher(G : RO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.sample} + } + + lemma RO_FinRO_D: + (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ MainD(D, RO).distinguish ~ MainD(D, FinRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_RO_FinRO_D: + (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ) &m (x : unit) (p : + bool -> bool), + Pr[MainD(D, RO).distinguish(x) @ &m : p res] = Pr[MainD(D, FinRO).distinguish(x) @ &m : p res]. + + theory MUniFinFun. + op mfun ['u] (d : plaintext * W8.t Array32.t -> 'u distr) (f : + plaintext * W8.t Array32.t -> 'u) : real = + (big predT (fun (x : plaintext * W8.t Array32.t) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma mfunE ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr) (f : + plaintext * W8.t Array32.t -> 'u), mfun d f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + lemma isdistr_dfun ['u]: forall (d : plaintext * W8.t Array32.t -> 'u distr), isdistr (mfun d). + + op dfun ['u] (d : plaintext * W8.t Array32.t -> 'u distr) : ( + plaintext * W8.t Array32.t -> 'u) distr = mk (mfun d). + + lemma dfun1E ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr) (f : + plaintext * W8.t Array32.t -> 'u), + mu1 (dfun d) f = + (big predT (fun (x : plaintext * W8.t Array32.t) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun1E_djoin ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr) (f : + plaintext * W8.t Array32.t -> 'u), mu1 (dfun d) f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + op tofun ['u] (us : 'u list) (x : plaintext * W8.t Array32.t) : 'u = nth witness us (index x FinFrom.enum). + + op offun ['u] (f : plaintext * W8.t Array32.t -> 'u) : 'u list = map f FinFrom.enum. + + lemma offunK ['u]: cancel offun tofun. + + lemma tofunK ['u]: forall (us : 'u list), size us = FinFrom.card => offun (tofun us) = us. + + lemma dfun_dmap ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr), dfun d = dmap (djoinmap d FinFrom.enum) tofun. + + lemma dfun_supp ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr) (f : + plaintext * W8.t Array32.t -> 'u), + f \in dfun d <=> forall (x : plaintext * W8.t Array32.t), f x \in d x. + + lemma dfun_fu ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr), + (forall (x : plaintext * W8.t Array32.t), is_full (d x)) => is_full (dfun d). + + lemma dfun_uni ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr), + (forall (x : plaintext * W8.t Array32.t), is_uniform (d x)) => is_uniform (dfun d). + + lemma dfun_funi ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr), + (forall (x : plaintext * W8.t Array32.t), is_uniform (d x)) => + (forall (x : plaintext * W8.t Array32.t), is_full (d x)) => is_funiform (dfun d). + + lemma dfunE_djoin ['u]: + forall (du : plaintext * W8.t Array32.t -> 'u distr) (E : + (plaintext * W8.t Array32.t -> 'u) -> bool), + mu (dfun du) E = mu (djoinmap du FinFrom.enum) (E \o tofun). + + lemma dfunE ['u]: + forall (du : plaintext * W8.t Array32.t -> 'u distr) (p : + plaintext * W8.t Array32.t -> 'u -> bool), + mu (dfun du) + (fun (f : plaintext * W8.t Array32.t -> 'u) => forall (x : plaintext * W8.t Array32.t), p x (f x)) = + (big predT (fun (x : plaintext * W8.t Array32.t) => mu (du x) (p x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma weight_dfun ['u]: + forall (df : plaintext * W8.t Array32.t -> 'u distr), + weight (dfun df) = + (big predT (fun (x : plaintext * W8.t Array32.t) => weight (df x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun_ll ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr), + (forall (x : plaintext * W8.t Array32.t), is_lossless (d x)) => is_lossless (dfun d). + + lemma dlet_dfun ['u, 'v]: + forall (d1 : plaintext * W8.t Array32.t -> 'u distr) (d2 : + plaintext * W8.t Array32.t -> 'u -> 'v distr), + dlet (dfun d1) + (fun (f1 : plaintext * W8.t Array32.t -> 'u) => + dfun (fun (x : plaintext * W8.t Array32.t) => d2 x (f1 x))) = + dfun (fun (x : plaintext * W8.t Array32.t) => dlet (d1 x) (fun (x1 : 'u) => d2 x x1)). + + lemma dfun_unit ['u]: + forall (f : plaintext * W8.t Array32.t -> 'u), + dfun (fun (x : plaintext * W8.t Array32.t) => dunit (f x)) = dunit f. + + lemma dmap_dfun ['u, 'v]: + forall (d : plaintext * W8.t Array32.t -> 'u distr) (F : + plaintext * W8.t Array32.t -> 'u -> 'v), + dmap (dfun d) + (fun (f : plaintext * W8.t Array32.t -> 'u) (x : plaintext * W8.t Array32.t) => F x (f x)) = + dfun (fun (x : plaintext * W8.t Array32.t) => dmap (d x) (fun (fx : 'u) => F x fx)). + + lemma dfun1E_fix1 ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr) (x0 : plaintext * W8.t Array32.t) + (f : plaintext * W8.t Array32.t -> 'u), + mu1 (dfun d) f = mu1 (d x0) (f x0) * mu1 (dfun d.[x0 <- dunit (f x0)]) f. + + lemma dlet_dfun_fupdate_ll ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr) (x0 : plaintext * W8.t Array32.t) (v : 'u), + dlet (dfun d.[x0 <- dunit v]) + (fun (f : plaintext * W8.t Array32.t -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + dfun d. + + lemma dfunE_dlet_fix1 ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr) (x0 : plaintext * W8.t Array32.t), + dfun d = dlet (d x0) (fun (v : 'u) => dfun d.[x0 <- dunit v]). + + lemma dlet_dfun_update ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr) (x0 : plaintext * W8.t Array32.t), + dlet (dfun d) (fun (f : plaintext * W8.t Array32.t -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + (weight (d x0) \cdot dfun d). + + lemma dfun_projE ['u]: + forall (df : plaintext * W8.t Array32.t -> 'u distr) (x : plaintext * W8.t Array32.t), + dmap (dfun df) (fun (f : plaintext * W8.t Array32.t -> 'u) => f x) = + (weight (dfun df) / weight (df x) \cdot df x). + + lemma eq_from_dfun_proj_eq ['u]: + forall (df1 df2 : plaintext * W8.t Array32.t -> 'u distr), + (forall (x : plaintext * W8.t Array32.t), is_lossless (df1 x)) => + (forall (x : plaintext * W8.t Array32.t), is_lossless (df2 x)) => + (forall (x : plaintext * W8.t Array32.t), + dmap (dfun df1) (fun (f : plaintext * W8.t Array32.t -> 'u) => f x) = + dmap (dfun df2) (fun (f : plaintext * W8.t Array32.t -> 'u) => f x)) => + df1 == df2. + + lemma dfun_prod1E ['u, 'v]: + forall (df : plaintext * W8.t Array32.t -> 'u distr) (dg : + plaintext * W8.t Array32.t -> 'v distr) (f : plaintext * W8.t Array32.t -> 'u) + (g : plaintext * W8.t Array32.t -> 'v), + mu1 (dfun (fun (x : plaintext * W8.t Array32.t) => df x `*` dg x)) + (fun (x : plaintext * W8.t Array32.t) => (f x, g x)) = + mu1 (dfun df) f * mu1 (dfun dg) g. + + lemma dprod_funE ['u, 'v]: + forall (df : plaintext * W8.t Array32.t -> 'u distr) (dg : + plaintext * W8.t Array32.t -> 'v distr), + dfun df `*` dfun dg = + dmap (dfun (fun (x : plaintext * W8.t Array32.t) => df x `*` dg x)) + (fun (fg : plaintext * W8.t Array32.t -> 'u * 'v) => + ((fun (p : 'u * 'v) => p.`1) \o fg, (fun (p : 'u * 'v) => p.`2) \o fg)). + + lemma dfun_prodE ['u, 'v]: + forall (df : plaintext * W8.t Array32.t -> 'u distr) (dg : + plaintext * W8.t Array32.t -> 'v distr), + dfun (fun (x : plaintext * W8.t Array32.t) => df x `*` dg x) = + dmap (dfun df `*` dfun dg) + (fun (fg : (plaintext * W8.t Array32.t -> 'u) * (plaintext * W8.t Array32.t -> 'v)) (x : plaintext * + W8.t Array32.t) => (fg.`1 x, fg.`2 x)). + + lemma dfun_condE ['u]: + forall (X : plaintext * W8.t Array32.t -> bool) (dt df : + plaintext * W8.t Array32.t -> 'u distr), + (forall (x : plaintext * W8.t Array32.t), is_lossless (dt x)) => + (forall (x : plaintext * W8.t Array32.t), is_lossless (df x)) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext * W8.t Array32.t -> 'u) * (plaintext * W8.t Array32.t -> 'u)) (x : plaintext * + W8.t Array32.t) => if X x then tf.`1 x else tf.`2 x) = + dfun (fun (x : plaintext * W8.t Array32.t) => if X x then dt x else df x). + + lemma dlet_dfun_cond ['u]: + forall (dX : plaintext * W8.t Array32.t -> bool distr) (dt df : + plaintext * W8.t Array32.t -> 'u distr), + (forall (x : plaintext * W8.t Array32.t), is_lossless (dX x)) => + (forall (x : plaintext * W8.t Array32.t), is_lossless (dt x)) => + (forall (x : plaintext * W8.t Array32.t), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : plaintext * W8.t Array32.t -> bool) => + dfun (fun (x : plaintext * W8.t Array32.t) => if X x then dt x else df x)) = + dfun (fun (x : plaintext * W8.t Array32.t) => dlet (dX x) (fun (b : bool) => if b then dt x else df x)). + + lemma dfun_dcondE ['u]: + forall (dX : plaintext * W8.t Array32.t -> bool distr) (dt df : + plaintext * W8.t Array32.t -> 'u distr), + (forall (x : plaintext * W8.t Array32.t), is_lossless (dX x)) => + (forall (x : plaintext * W8.t Array32.t), is_lossless (dt x)) => + (forall (x : plaintext * W8.t Array32.t), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : plaintext * W8.t Array32.t -> bool) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext * W8.t Array32.t -> 'u) * ( + plaintext * W8.t Array32.t -> 'u)) (x : plaintext * W8.t Array32.t) => + if X x then tf.`1 x else tf.`2 x)) = + dfun + (fun (x : plaintext * W8.t Array32.t) => (mu1 (dX x) true \cdot dt x) + (mu1 (dX x) false \cdot df x)). + end MUniFinFun. + + module FunRO = { + var f : plaintext * W8.t Array32.t -> sharedsecret * W8.t Array32.t + + proc init() : unit = { + FunRO.f <$ (dfun (fun (_ : plaintext * W8.t Array32.t) => dRO))%MUniFinFun; + } + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = { + return FunRO.f x; + } + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit = { + FunRO.f <- fun (z : plaintext * W8.t Array32.t) => if z = x then y else FunRO.f z; + } + + proc sample(x : plaintext * W8.t Array32.t) : unit = {} + + proc rem(x : plaintext * W8.t Array32.t) : unit = {} + }. + + lemma FinRO_FunRO_D: + (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ), + equiv[ MainD(D, FinRO).distinguish ~ MainD(D, FunRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_FinRO_FunRO_D: + (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ) &m (x : unit) (p : + bool -> bool), + Pr[MainD(D, FinRO).distinguish(x) @ &m : p res] = Pr[MainD(D, FunRO).distinguish(x) @ &m : p res]. + end FinEager. + end RO. + + module type Oracle = { + proc init() : unit {} + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t {} + } + + module type POracle = { + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t {} + } + + module type Scheme(O : POracle) = { + proc kg() : publickey * skey {O.get} + + proc enc(pk : publickey) : (W8.t Array960.t * W8.t Array128.t) * sharedsecret {O.get} + + proc dec(sk : skey, c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option {O.get} + } + + module Correctness(H : Oracle, S : Scheme) = { + proc main() : bool = { + var pk : publickey; + var sk : skey; + var c : W8.t Array960.t * W8.t Array128.t; + var k : sharedsecret; + var k' : sharedsecret option; + + H.init(); + (pk, sk) <@ S(H).kg(); + (c, k) <@ S(H).enc(pk); + k' <@ S(H).dec(sk, c); + + return k' <> Some k; + } + }. + + module type CCA_ORC = { + proc dec(c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option {} + } + + module type CCA_ADV(H : POracle, O : CCA_ORC) = { + proc guess(pk : publickey, c : W8.t Array960.t * W8.t Array128.t, k : sharedsecret) : bool {O.dec, H.get} + } + + module CCA(H : Oracle, S : Scheme, A : CCA_ADV) = { + var cstar : (W8.t Array960.t * W8.t Array128.t) option + + var sk : skey + + module O = { + proc dec(c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var k : sharedsecret option; + + k <- None; + if (Some c <> CCA.cstar) + k <@ S(H).dec(CCA.sk, c); + + return k; + } + } + + module A = A(H, CCA(H, S, A).O) + + proc main() : bool = { + var pk : publickey; + var k1 : sharedsecret; + var ck0 : (W8.t Array960.t * W8.t Array128.t) * sharedsecret; + var b : bool; + var b' : bool; + + H.init(); + CCA.cstar <- None; + (pk, CCA.sk) <@ S(H).kg(); + k1 <$ srand; + b <$ {0,1}; + ck0 <@ S(H).enc(pk); + CCA.cstar <- Some ck0.`1; + b' <@ CCA(H, S, A).A.guess(pk, ck0.`1, if b then k1 else ck0.`2); + + return b' = b; + } + }. + end KEMROM. + + op qHK : int. + + axiom ge0_qHK: 0 <= qHK. + + module CountH(H : KEMROM.POracle) = { + var c_h : int + + proc init() : unit = { + CountH.c_h <- 0; + } + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = { + var r : sharedsecret * W8.t Array32.t; + + r <@ H.get(x); + CountH.c_h <- CountH.c_h + 1; + + return r; + } + }. + + module FO_K(H : KEMROM.POracle) = { + proc kg() : publickey * KEMROM.skey = { + var pk : publickey; + var sk : W8.t Array1152.t; + var k : sharedsecret; + + (pk, sk) <$ UU.TT.kg; + k <$ srand; + + return (pk, ((pk, sk), k)); + } + + proc enc(pk : publickey) : (W8.t Array960.t * W8.t Array128.t) * sharedsecret = { + var m : plaintext; + var r : W8.t Array32.t; + var c : W8.t Array960.t * W8.t Array128.t; + var k : sharedsecret; + + m <$ srand; + (k, r) <@ H.get(m, H_pk pk); + c <- enc r pk m; + + return (c, k); + } + + proc dec(sk : KEMROM.skey, c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var m' : plaintext option; + var c' : W8.t Array960.t * W8.t Array128.t; + var ks : sharedsecret; + var r : W8.t Array32.t; + var kn : sharedsecret; + + m' <- dec sk.`1.`2 c; + (ks, r) <@ H.get(oget m', H_pk sk.`1.`1); + kn <- J sk.`2 c; + c' <- enc r sk.`1.`1 (oget m'); + + return if m' <> None /\ c' = c then Some ks else Some kn; + } + }. + + module UU_L(H1 : UU.KEMROMx2.RO1.RO, H2 : UU.KEMROMx2.RO2.RO) = { + proc enc(pk : publickey) : (W8.t Array960.t * W8.t Array128.t) * sharedsecret = UU.UU(UU.KEMROMx2.RO_x2(H1, H2)).enc + + proc kg() : publickey * UU.KEMROMx2.skey = UU.UU(UU.KEMROMx2.RO_x2(H1, H2)).kg + + proc dec(sk : KEMROM.skey, c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var m' : plaintext option; + var r : W8.t Array32.t; + var c' : W8.t Array960.t * W8.t Array128.t; + var rv : plaintext option; + var k : sharedsecret; + + rv <- None; + m' <- dec sk.`1.`2 c; + H1.sample(oget m'); + H2.sample(oget m'); + if (m' <> None) { + r <@ H1.get(oget m'); + c' <- enc r sk.`1.`1 (oget m'); + rv <- if c = c' then m' else None; + } + if (rv = None) + k <- J sk.`2 c; + else + k <@ H2.get(oget m'); + + return Some k; + } + }. + + module Correctness_L(H1 : UU.KEMROMx2.RO1.RO, H2 : UU.KEMROMx2.RO2.RO) = { + proc main() : bool = { + var pk : publickey; + var sk : UU.KEMROMx2.skey; + var c : W8.t Array960.t * W8.t Array128.t; + var k : sharedsecret; + var k' : sharedsecret option; + + H1.init(); + H2.init(); + (pk, sk) <@ UU_L(H1, H2).kg(); + (c, k) <@ UU_L(H1, H2).enc(pk); + k' <@ UU_L(H1, H2).dec(sk, c); + + return k' <> Some k; + } + }. + + lemma go_parametric_corr_lro: + forall &m, + Pr[UU.KEMROMx2.Correctness(UU.KEMROMx2.RO_x2(UU.KEMROMx2.RO1.RO, UU.KEMROMx2.RO2.RO), UU.UU).main() @ &m : res] = + Pr[Correctness_L(UU.KEMROMx2.RO1.LRO, UU.KEMROMx2.RO2.LRO).main() @ &m : res]. + + module DC1(G1 : UU.KEMROMx2.RO1.RO) = { + proc distinguish() : bool = Correctness_L(G1, UU.KEMROMx2.RO2.LRO).main + }. + + module DC2(G2 : UU.KEMROMx2.RO2.RO) = { + proc distinguish() : bool = Correctness_L(UU.KEMROMx2.RO1.RO, G2).main + }. + + lemma go_parametric_corr: + forall &m, + Pr[UU.KEMROMx2.Correctness(UU.KEMROMx2.RO_x2(UU.KEMROMx2.RO1.RO, UU.KEMROMx2.RO2.RO), UU.UU).main() @ &m : res] = + Pr[Correctness_L(UU.KEMROMx2.RO1.RO, UU.KEMROMx2.RO2.RO).main() @ &m : res]. + + lemma correctness_fo_k: + forall &m, + UU.TT.qHC = 0 => + 1 < UU.TT.FinT.card => + Pr[KEMROM.Correctness(KEMROM.RO.RO, FO_K).main() @ &m : res] <= + Pr[UU.TT.PKE.Correctness_Adv(UU.TT.BasePKE, UU.TT.B(UU.B_UC, UU.TT.PKEROM.RO.RO)).main() @ &m : res]. + + module CCAL(H1 : UU.KEMROMx2.RO1.RO, H2 : UU.KEMROMx2.RO2.RO, A : UU.KEMROMx2.CCA_ADV) = { + module O = { + proc dec(c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var k : sharedsecret option; + + k <- None; + if (Some c <> KEMROM.CCA.cstar) + k <@ UU_L(H1, H2).dec(KEMROM.CCA.sk, c); + + return k; + } + } + + module A = A(UU.KEMROMx2.RO_x2(H1, H2), CCAL(H1, H2, A).O) + + proc main() : bool = { + var pk : publickey; + var k1 : sharedsecret; + var ck0 : (W8.t Array960.t * W8.t Array128.t) * sharedsecret; + var b : bool; + var b' : bool; + + H1.init(); + H2.init(); + KEMROM.CCA.cstar <- None; + (pk, KEMROM.CCA.sk) <@ UU_L(H1, H2).kg(); + k1 <$ srand; + b <$ {0,1}; + ck0 <@ UU_L(H1, H2).enc(pk); + KEMROM.CCA.cstar <- Some ck0.`1; + b' <@ CCAL(H1, H2, A).A.guess(pk, ck0.`1, if b then k1 else ck0.`2); + + return b' = b; + } + }. + + module B1x2(A : KEMROM.CCA_ADV, H2x : UU.KEMROMx2.POracle_x2, DO : KEMROM.CCA_ORC) = { + var _pk : publickey + + module BH = { + proc get(m : plaintext, hpk : W8.t Array32.t) : sharedsecret * W8.t Array32.t = { + var r : W8.t Array32.t; + var k : sharedsecret; + + (k, r) <@ KEMROM.RO.RO.get(m, hpk); + if (hpk = H_pk B1x2._pk) { + r <@ H2x.get1(m); + k <@ H2x.get2(m); + } + + return (k, r); + } + } + + proc guess(pk : publickey, c : W8.t Array960.t * W8.t Array128.t, k : sharedsecret) : bool = { + var b : bool; + + B1x2._pk <- pk; + CountH(KEMROM.RO.RO).init(); + KEMROM.RO.RO.init(); + b <@ A(CountH(B1x2(A, H2x, DO).BH), DO).guess(pk, c, k); + + return b; + } + }. + + module DKK1(A : UU.KEMROMx2.CCA_ADV, G1 : UU.KEMROMx2.RO1.RO) = { + proc distinguish() : bool = CCAL(G1, UU.KEMROMx2.RO2.LRO, A).main + }. + + module DKK2(A : UU.KEMROMx2.CCA_ADV, G2 : UU.KEMROMx2.RO2.RO) = { + proc distinguish() : bool = CCAL(UU.KEMROMx2.RO1.RO, G2, A).main + }. + + lemma go_parametric: + forall (A(H : UU.KEMROMx2.POracle_x2, O : UU.KEMROMx2.CCA_ORC) <: + UU.KEMROMx2.CCA_ADV{+all mem, -UU.KEMROMx2.RO1.RO, -UU.KEMROMx2.RO1.FRO, -UU.KEMROMx2.RO2.RO, -UU.KEMROMx2.RO2.FRO, -UU.KEMROMx2.CCA, -KEMROM.CCA} + ) &m, + Pr[UU.KEMROMx2.CCA(UU.KEMROMx2.RO_x2(UU.KEMROMx2.RO1.RO, UU.KEMROMx2.RO2.RO), UU.UU, A).main() @ &m : res] = + Pr[CCAL(UU.KEMROMx2.RO1.RO, UU.KEMROMx2.RO2.RO, A).main() @ &m : res]. + + lemma same_scheme: + forall (A(H : KEMROM.POracle, O : KEMROM.CCA_ORC) <: + KEMROM.CCA_ADV{+all mem, -KEMROM.RO.RO.m, -UU.TT.PKE.OW_CPA, -UU.TT.PKE.BOWp, -UU.TT.PKE.OWL_CPA, -UU.TT.PKE.OWvsIND.Bowl, -UU.TT.BasePKE, -UU.TT.PKEROM.RO.RO, -UU.TT.PKEROM.RO.FRO, -UU.TT.PKEROM.OW_PCVA, -UU.TT.Correctness_Adv1, -UU.TT.B, -UU.TT.CountO, -UU.TT.Gm, -UU.TT.O_AdvOW, -UU.RF.RF, -UU.PseudoRF.PRF, -UU.KEMROMx2.RO1.RO, -UU.KEMROMx2.RO1.FRO, -UU.KEMROMx2.RO2.RO, -UU.KEMROMx2.RO2.FRO, -UU.KEMROMx2.CCA, -UU.CountHx2, -UU.RO1E.FunRO, -UU.UU2, -UU.H2, -UU.Gm2, -UU.Gm3, -UU.H2BOWMod, -KEMROM.CCA, -B1x2} + ) &m, + Pr[UU.KEMROMx2.CCA(UU.KEMROMx2.RO_x2(UU.KEMROMx2.RO1.RO, UU.KEMROMx2.RO2.RO), UU.UU, B1x2(A)).main() @ &m : res] = + Pr[KEMROM.CCA(KEMROM.RO.RO, FO_K, A).main() @ &m : res]. + + lemma countB1x2: + forall (A(H : KEMROM.POracle, O : KEMROM.CCA_ORC) <: + KEMROM.CCA_ADV{+all mem, -KEMROM.RO.RO.m, -UU.TT.PKE.OW_CPA, -UU.TT.PKE.BOWp, -UU.TT.PKE.OWL_CPA, -UU.TT.PKE.OWvsIND.Bowl, -UU.TT.BasePKE, -UU.TT.PKEROM.RO.RO, -UU.TT.PKEROM.RO.FRO, -UU.TT.PKEROM.OW_PCVA, -UU.TT.Correctness_Adv1, -UU.TT.B, -UU.TT.CountO, -UU.TT.Gm, -UU.TT.O_AdvOW, -UU.RF.RF, -UU.PseudoRF.PRF, -UU.KEMROMx2.RO1.RO, -UU.KEMROMx2.RO1.FRO, -UU.KEMROMx2.RO2.RO, -UU.KEMROMx2.RO2.FRO, -UU.KEMROMx2.CCA, -UU.CountHx2, -UU.RO1E.FunRO, -UU.UU2, -UU.H2, -UU.Gm2, -UU.Gm3, -UU.H2BOWMod, -KEMROM.CCA, -B1x2} + ) (RO0 <: UU.KEMROMx2.POracle_x2{+all mem, -UU.CountHx2, -B1x2(A)} ) + (O <: UU.KEMROMx2.CCA_ORC{+all mem, -UU.CountHx2, -B1x2(A)} ), + UU.qHT = qHK => + UU.qHU = qHK => + (forall (RO1 <: KEMROM.POracle{+all mem, -CountH, -A} ) (O0 <: KEMROM.CCA_ORC{+all mem, -CountH, -A} ), + hoare[ A(CountH(RO1), O0).guess : CountH.c_h = 0 ==> CountH.c_h <= qHK]) => + hoare[ B1x2(A, UU.CountHx2(RO0), O).guess : + UU.CountHx2.c_ht = 0 /\ UU.CountHx2.c_hu = 0 ==> + UU.CountHx2.c_ht <= UU.qHT /\ UU.CountHx2.c_hu <= UU.qHU]. + + lemma conclusion_fo_mlkem: + forall (A(H : KEMROM.POracle, O : KEMROM.CCA_ORC) <: + KEMROM.CCA_ADV{+all mem, -KEMROM.RO.RO.m, -UU.TT.PKE.OW_CPA, -UU.TT.PKE.BOWp, -UU.TT.PKE.OWL_CPA, -UU.TT.PKE.OWvsIND.Bowl, -UU.TT.BasePKE, -UU.TT.PKEROM.RO.RO, -UU.TT.PKEROM.RO.FRO, -UU.TT.PKEROM.OW_PCVA, -UU.TT.Correctness_Adv1, -UU.TT.B, -UU.TT.CountO, -UU.TT.Gm, -UU.TT.O_AdvOW, -UU.RF.RF, -UU.PseudoRF.PRF, -UU.KEMROMx2.RO1.RO, -UU.KEMROMx2.RO1.FRO, -UU.KEMROMx2.RO2.RO, -UU.KEMROMx2.RO2.FRO, -UU.KEMROMx2.CCA, -UU.CountHx2, -UU.RO1E.FunRO, -UU.UU2, -UU.H2, -UU.Gm2, -UU.Gm3, -UU.H2BOWMod, -KEMROM.CCA, -B1x2} + ) &m, + UU.qHT = qHK => + UU.qHU = qHK => + UU.TT.qH = UU.qHT + UU.qHU + 1 => + UU.TT.qV = 0 => + UU.TT.qP = 0 => + UU.TT.qH + 1 = UU.TT.qHC => + UU.TT.qHC < UU.TT.FinT.card - 1 => + (forall (RO0 <: KEMROM.POracle{+all mem, -CountH, -A} ) (O0 <: KEMROM.CCA_ORC{+all mem, -CountH, -A} ), + hoare[ A(CountH(RO0), O0).guess : CountH.c_h = 0 ==> CountH.c_h <= qHK]) => + (forall (H0 <: KEMROM.POracle{+all mem, -A} ) (O <: UU.KEMROMx2.CCA_ORC{+all mem, -A} ), + islossless O.dec => islossless H0.get => islossless A(H0, O).guess) => + `|Pr[KEMROM.CCA(KEMROM.RO.RO, FO_K, A).main() @ &m : res] - 1%r / 2%r| <= + 2%r * + `|Pr[UU.TT.PKE.CPA(UU.TT.BasePKE, UU.TT.PKE.OWvsIND.Bowl(UU.TT.PKE.OWvsIND.BL(UU.TT.AdvOW(UU.BUUOWMod(B1x2(A)))))).main + () @ &m : res] - + 1%r / 2%r| + + 2%r * + `|Pr[UU.TT.PKE.CPA(UU.TT.BasePKE, UU.TT.PKE.OWvsIND.Bowl(UU.TT.AdvOWL_query(UU.BUUOWMod(B1x2(A))))).main + () @ &m : res] - + 1%r / 2%r| + + (2 * qHK + 3)%r * + Pr[UU.TT.PKE.Correctness_Adv(UU.TT.BasePKE, UU.TT.B(UU.BUUC(B1x2(A)), UU.TT.PKEROM.RO.RO)).main() @ &m : res] + + (2 * qHK + 3)%r * + Pr[UU.TT.PKE.Correctness_Adv(UU.TT.BasePKE, UU.TT.B(UU.BUUCI(B1x2(A)), UU.TT.PKEROM.RO.RO)).main() @ &m : res] + + (2 * qHK + 3)%r * + Pr[UU.TT.PKE.Correctness_Adv(UU.TT.BasePKE, UU.TT.B(UU.TT.AdvCorr(UU.BUUOWMod(B1x2(A))), UU.TT.PKEROM.RO.RO)).main + () @ &m : res] + + Pr[UU.TT.PKE.Correctness_Adv(UU.TT.BasePKE, UU.TT.PKE.BOWp(UU.TT.BasePKE, UU.TT.AdvOW(UU.BUUOWMod(B1x2(A))))).main + () @ &m : res] + + `|Pr[UU.J.IND(UU.PseudoRF.PRF, UU.D(B1x2(A))).main() @ &m : res] - + Pr[UU.J.IND(UU.RF.RF, UU.D(B1x2(A))).main() @ &m : res]| + + 2%r * (2 * qHK + 2)%r * UU.TT.PKE.eps_msg. + end FO_MLKEM. + + module MLWE_PKE_HASH = { + proc kg() : publickey * W8.t Array1152.t = { + var r : W8.t Array32.t; + var pk : publickey; + var sk : W8.t Array1152.t; + + r <$ srand; + (pk, sk) <- kg r; + + return (pk, sk); + } + + proc enc(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = { + var rr : W8.t Array32.t; + var c : W8.t Array960.t * W8.t Array128.t; + + rr <$ srand; + c <- enc rr pk m; + + return c; + } + + proc dec(sk : W8.t Array1152.t, c : W8.t Array960.t * W8.t Array128.t) : plaintext option = { + var mo : plaintext option; + + mo <- dec sk c; + + return mo; + } + }. + + module MLWE_PKE_HASH_PROC = { + proc kg_bridge() : publickey * W8.t Array1152.t = { + var r : W8.t Array32.t; + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var t : polyvec; + + r <$ srand; + (sd, s, e) <- prg_kg_inner r; + t <- ((H sd *^ s)%MLWE_.Matrix_.Matrix + e)%Vector; + + return (pk_encode (sd, t), sk_encode s); + } + + proc kg() : publickey * W8.t Array1152.t = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var t : polyvec; + + sd <$ srand; + s <$ MLWE_.dshort; + e <$ MLWE_.dshort; + t <- ((H sd *^ s)%MLWE_.Matrix_.Matrix + e)%Vector; + + return (pk_encode (sd, t), sk_encode s); + } + + proc enc_bridge(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = { + var sd : W8.t Array32.t; + var t : polyvec; + var rr : W8.t Array32.t; + var r : polyvec; + var e1 : polyvec; + var e2 : poly; + var u : polyvec; + var v : poly; + + (sd, t) <- pk_decode pk; + rr <$ srand; + (r, e1, e2) <- prg_enc_inner rr; + u <- ((trmx (H sd) *^ r)%MLWE_.Matrix_.Matrix + e1)%Vector; + v <- ((t `<*>` r) &+ e2 &+ m_encode m)%MLWE_; + + return c_encode (u, v); + } + + proc enc(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = { + var sd : W8.t Array32.t; + var t : polyvec; + var r : polyvec; + var e1 : polyvec; + var e2 : poly; + var u : polyvec; + var v : poly; + + (sd, t) <- pk_decode pk; + r <$ MLWE_.dshort; + e1 <$ MLWE_.dshort; + e2 <$ dshort_R; + u <- ((trmx (H sd) *^ r)%MLWE_.Matrix_.Matrix + e1)%Vector; + v <- ((t `<*>` r) &+ e2 &+ m_encode m)%MLWE_; + + return c_encode (u, v); + } + + proc dec(sk : W8.t Array1152.t, c : W8.t Array960.t * W8.t Array128.t) : plaintext option = { + var u : polyvec; + var v : poly; + + (u, v) <- c_decode c; + + return Some (m_decode (v &- (sk_decode sk `<*>` u))%MLWE_); + } + }. + + theory PRG_KG. + module type RG = { + proc get(sd : W8.t Array32.t) : W8.t Array32.t * polyvec * polyvec {} + } + + module type Distinguisher = { + proc distinguish(x : W8.t Array32.t * polyvec * polyvec) : bool {} + } + + module IND(PRG : RG, D : Distinguisher) = { + proc main() : bool = { + var b : bool; + var sd : W8.t Array32.t; + var x : W8.t Array32.t * polyvec * polyvec; + + sd <$ srand; + x <@ PRG.get(sd); + b <@ D.distinguish(x); + + return b; + } + }. + + module PRGr = { + proc get(sd : W8.t Array32.t) : W8.t Array32.t * polyvec * polyvec = { + var r : W8.t Array32.t * polyvec * polyvec; + + r <- prg_kg_inner sd; + + return r; + } + }. + + module PRGi = { + proc get(sd : W8.t Array32.t) : W8.t Array32.t * polyvec * polyvec = { + var r : W8.t Array32.t * polyvec * polyvec; + + r <$ prg_kg_ideal; + + return r; + } + }. + end PRG_KG. + + theory PRG_ENC. + module type RG = { + proc get(sd : W8.t Array32.t) : polyvec * polyvec * poly {} + } + + module type Distinguisher = { + proc distinguish(x : polyvec * polyvec * poly) : bool {} + } + + module IND(PRG : RG, D : Distinguisher) = { + proc main() : bool = { + var b : bool; + var sd : W8.t Array32.t; + var x : polyvec * polyvec * poly; + + sd <$ srand; + x <@ PRG.get(sd); + b <@ D.distinguish(x); + + return b; + } + }. + + module PRGr = { + proc get(sd : W8.t Array32.t) : polyvec * polyvec * poly = { + var r : polyvec * polyvec * poly; + + r <- prg_enc_inner sd; + + return r; + } + }. + + module PRGi = { + proc get(sd : W8.t Array32.t) : polyvec * polyvec * poly = { + var r : polyvec * polyvec * poly; + + r <$ prg_enc_ideal; + + return r; + } + }. + end PRG_ENC. + + module MLWE_PKE_HASH_PRG = { + var sd : W8.t Array32.t + + var s : polyvec + + var e : polyvec + + var r : polyvec + + var e1 : polyvec + + var e2 : poly + + proc kg() : publickey * W8.t Array1152.t = { + var t : polyvec; + + t <- ((H MLWE_PKE_HASH_PRG.sd *^ MLWE_PKE_HASH_PRG.s)%MLWE_.Matrix_.Matrix + MLWE_PKE_HASH_PRG.e)%Vector; + + return (pk_encode (MLWE_PKE_HASH_PRG.sd, t), sk_encode MLWE_PKE_HASH_PRG.s); + } + + proc enc(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = { + var sd : W8.t Array32.t; + var t : polyvec; + var u : polyvec; + var v : poly; + + (sd, t) <- pk_decode pk; + u <- ((trmx (H sd) *^ MLWE_PKE_HASH_PRG.r)%MLWE_.Matrix_.Matrix + MLWE_PKE_HASH_PRG.e1)%Vector; + v <- ((t `<*>` MLWE_PKE_HASH_PRG.r) &+ MLWE_PKE_HASH_PRG.e2 &+ m_encode m)%MLWE_; + + return c_encode (u, v); + } + + proc dec(sk : W8.t Array1152.t, c : W8.t Array960.t * W8.t Array128.t) : plaintext option = MLWE_PKE_HASH.dec + }. + + module D_KG(A : FO_MLKEM.UU.TT.PKE.Adversary) = { + proc distinguish(sd : W8.t Array32.t, s : polyvec, e : polyvec) : bool = { + var coins : W8.t Array32.t; + var b : bool; + + MLWE_PKE_HASH_PRG.sd <- sd; + MLWE_PKE_HASH_PRG.s <- s; + MLWE_PKE_HASH_PRG.e <- e; + coins <$ srand; + (MLWE_PKE_HASH_PRG.r, MLWE_PKE_HASH_PRG.e1, MLWE_PKE_HASH_PRG.e2) <- prg_enc_inner coins; + b <@ FO_MLKEM.UU.TT.PKE.CPA(MLWE_PKE_HASH_PRG, A).main(); + + return b; + } + }. + + module D_ENC(A : FO_MLKEM.UU.TT.PKE.Adversary) = { + proc distinguish(r : polyvec, e1 : polyvec, e2 : poly) : bool = { + var b : bool; + + (MLWE_PKE_HASH_PRG.sd, MLWE_PKE_HASH_PRG.s, MLWE_PKE_HASH_PRG.e) <$ prg_kg_ideal; + MLWE_PKE_HASH_PRG.r <- r; + MLWE_PKE_HASH_PRG.e1 <- e1; + MLWE_PKE_HASH_PRG.e2 <- e2; + b <@ FO_MLKEM.UU.TT.PKE.CPA(MLWE_PKE_HASH_PRG, A).main(); + + return b; + } + }. + + lemma cpa_proc: + forall (A <: FO_MLKEM.UU.TT.PKE.Adversary{+all mem, -MLWE_PKE_HASH_PRG} ) &m, + Pr[FO_MLKEM.UU.TT.PKE.CPA(MLWE_PKE_HASH, A).main() @ &m : res] - + Pr[FO_MLKEM.UU.TT.PKE.CPA(MLWE_PKE_HASH_PROC, A).main() @ &m : res] = + Pr[PRG_KG.IND(PRG_KG.PRGr, D_KG(A)).main() @ &m : res] - Pr[PRG_KG.IND(PRG_KG.PRGi, D_KG(A)).main() @ &m : res] + + Pr[PRG_ENC.IND(PRG_ENC.PRGr, D_ENC(A)).main() @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, D_ENC(A)).main() @ &m : res]. + + module DC_KG(A : FO_MLKEM.UU.TT.PKE.CORR_ADV) = { + proc distinguish(sd : W8.t Array32.t, s : polyvec, e : polyvec) : bool = { + var coins : W8.t Array32.t; + var b : bool; + + MLWE_PKE_HASH_PRG.sd <- sd; + MLWE_PKE_HASH_PRG.s <- s; + MLWE_PKE_HASH_PRG.e <- e; + coins <$ srand; + (MLWE_PKE_HASH_PRG.r, MLWE_PKE_HASH_PRG.e1, MLWE_PKE_HASH_PRG.e2) <- prg_enc_inner coins; + b <@ FO_MLKEM.UU.TT.PKE.Correctness_Adv(MLWE_PKE_HASH_PRG, A).main(); + + return b; + } + }. + + module DC_ENC(A : FO_MLKEM.UU.TT.PKE.CORR_ADV) = { + proc distinguish(r : polyvec, e1 : polyvec, e2 : poly) : bool = { + var b : bool; + + (MLWE_PKE_HASH_PRG.sd, MLWE_PKE_HASH_PRG.s, MLWE_PKE_HASH_PRG.e) <$ prg_kg_ideal; + MLWE_PKE_HASH_PRG.r <- r; + MLWE_PKE_HASH_PRG.e1 <- e1; + MLWE_PKE_HASH_PRG.e2 <- e2; + b <@ FO_MLKEM.UU.TT.PKE.Correctness_Adv(MLWE_PKE_HASH_PRG, A).main(); + + return b; + } + }. + + lemma corr_proc: + forall (A <: FO_MLKEM.UU.TT.PKE.CORR_ADV{+all mem, -MLWE_PKE_HASH_PRG} ) &m, + Pr[FO_MLKEM.UU.TT.PKE.Correctness_Adv(MLWE_PKE_HASH, A).main() @ &m : res] - + Pr[FO_MLKEM.UU.TT.PKE.Correctness_Adv(MLWE_PKE_HASH_PROC, A).main() @ &m : res] = + Pr[PRG_KG.IND(PRG_KG.PRGr, DC_KG(A)).main() @ &m : res] - Pr[PRG_KG.IND(PRG_KG.PRGi, DC_KG(A)).main() @ &m : res] + + Pr[PRG_ENC.IND(PRG_ENC.PRGr, DC_ENC(A)).main() @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, DC_ENC(A)).main() @ &m : res]. + + module MLWE_PKE_HASH1 = { + proc kg() : publickey * W8.t Array1152.t = { + var sd : W8.t Array32.t; + var s : polyvec; + var t : polyvec; + + sd <$ srand; + s <$ MLWE_.dshort; + t <$ MLWE_.duni; + + return (pk_encode (sd, t), sk_encode s); + } + + proc dec(sk : W8.t Array1152.t, c : W8.t Array960.t * W8.t Array128.t) : plaintext option = MLWE_PKE_HASH_PROC.dec + + proc enc(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = MLWE_PKE_HASH_PROC.enc + + proc enc_bridge(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = MLWE_PKE_HASH_PROC.enc_bridge + + proc kg_bridge() : publickey * W8.t Array1152.t = MLWE_PKE_HASH_PROC.kg_bridge + }. + + module B1(A : FO_MLKEM.UU.TT.PKE.Adversary) = { + proc kg(sd : W8.t Array32.t, t : polyvec) : publickey * W8.t Array1152.t = { + return (pk_encode (sd, t), witness); + } + + proc guess(sd : W8.t Array32.t, t : polyvec, uv : polyvec * poly) : bool = { + var pk : publickey; + var sk : W8.t Array1152.t; + var m0 : plaintext; + var m1 : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var b : bool; + var b' : bool; + + (pk, sk) <@ B1(A).kg(sd, uv.`1); + (m0, m1) <@ A.choose(pk); + b <$ {0,1}; + c <@ MLWE_PKE_HASH1.enc(pk, if b then m1 else m0); + b' <@ A.guess(c); + + return b' = b; + } + }. + + lemma hop1_left: + forall (A <: FO_MLKEM.UU.TT.PKE.Adversary) &m, + Pr[FO_MLKEM.UU.TT.PKE.CPA(MLWE_PKE_HASH_PROC, A).main() @ &m : res] = + Pr[MLWE_.MLWE_H(B1(A)).main(false, false) @ &m : res]. + + lemma hop1_right: + forall (A <: FO_MLKEM.UU.TT.PKE.Adversary) &m, + Pr[MLWE_.MLWE_H(B1(A)).main(false, true) @ &m : res] = + Pr[FO_MLKEM.UU.TT.PKE.CPA(MLWE_PKE_HASH1, A).main() @ &m : res]. + + module MLWE_PKE_HASH2 = { + proc enc(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = { + var _A : polymat; + var u : polyvec; + var v : poly; + + _A <- (trmx (H (pk_decode pk).`1))%MLWE_.Matrix_.Matrix; + u <$ MLWE_.duni; + v <$ duni_R; + + return c_encode (u, (v &+ m_encode m)%MLWE_); + } + + proc kg_bridge() : publickey * W8.t Array1152.t = MLWE_PKE_HASH1.kg_bridge + + proc enc_bridge(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = MLWE_PKE_HASH1.enc_bridge + + proc dec(sk : W8.t Array1152.t, c : W8.t Array960.t * W8.t Array128.t) : plaintext option = MLWE_PKE_HASH1.dec + + proc kg() : publickey * W8.t Array1152.t = MLWE_PKE_HASH1.kg + }. + + module B2(A : FO_MLKEM.UU.TT.PKE.Adversary) = { + proc kg(sd : W8.t Array32.t, t : polyvec) : publickey * W8.t Array1152.t = { + return (pk_encode (sd, t), witness); + } + + proc enc(pk : publickey, m : plaintext, uv : polyvec * poly) : W8.t Array960.t * W8.t Array128.t = { + return c_encode (uv.`1, (uv.`2 &+ m_encode m)%MLWE_); + } + + proc guess(sd : W8.t Array32.t, t : polyvec, uv : polyvec * poly) : bool = { + var pk : publickey; + var sk : W8.t Array1152.t; + var m0 : plaintext; + var m1 : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var b : bool; + var b' : bool; + + (pk, sk) <@ B2(A).kg(sd, t); + (m0, m1) <@ A.choose(pk); + b <$ {0,1}; + c <@ B2(A).enc(pk, if b then m1 else m0, uv); + b' <@ A.guess(c); + + return b' = b; + } + }. + + lemma hop2_left: + forall (A <: FO_MLKEM.UU.TT.PKE.Adversary) &m, + Pr[FO_MLKEM.UU.TT.PKE.CPA(MLWE_PKE_HASH1, A).main() @ &m : res] = + Pr[MLWE_.MLWE_H(B2(A)).main(true, false) @ &m : res]. + + lemma hop2_right: + forall (A <: FO_MLKEM.UU.TT.PKE.Adversary) &m, + Pr[MLWE_.MLWE_H(B2(A)).main(true, true) @ &m : res] = + Pr[FO_MLKEM.UU.TT.PKE.CPA(MLWE_PKE_HASH2, A).main() @ &m : res]. + + lemma main_theorem: + forall (A <: FO_MLKEM.UU.TT.PKE.Adversary{+all mem, -MLWE_PKE_HASH_PRG} ) &m, + islossless A.guess => + islossless A.choose => + `|Pr[FO_MLKEM.UU.TT.PKE.CPA(MLWE_PKE_HASH, A).main() @ &m : res] - 1%r / 2%r| <= + `|Pr[MLWE_.MLWE_H(B1(A)).main(false, false) @ &m : res] - Pr[MLWE_.MLWE_H(B1(A)).main(false, true) @ &m : res]| + + `|Pr[MLWE_.MLWE_H(B2(A)).main(true, false) @ &m : res] - Pr[MLWE_.MLWE_H(B2(A)).main(true, true) @ &m : res]| + + `|Pr[PRG_KG.IND(PRG_KG.PRGr, D_KG(A)).main() @ &m : res] - Pr[PRG_KG.IND(PRG_KG.PRGi, D_KG(A)).main() @ &m : res]| + + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, D_ENC(A)).main() @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, D_ENC(A)).main() @ &m : res]|. + + op noise_exp (_A : polymat) (s e r e1 : polyvec) (e2 : poly) (m : plaintext) : poly = + let t = ((_A *^ s)%MLWE_.Matrix_.Matrix + e)%Vector in + let u = ((trmx _A *^ r)%MLWE_.Matrix_.Matrix + e1)%Vector in + let v = ((t `<*>` r) &+ e2 &+ m_encode m)%MLWE_ in + let (u', v') = c_decode (c_encode (u, v)) in (v' &- (s `<*>` u') &- m_encode m)%MLWE_. + + lemma encode_noise: + forall (u : polyvec) (v : poly), c_decode (c_encode (u, v)) = ((u + rnd_err_u u)%Vector, (v &+ rnd_err_v v)%MLWE_). + + lemma matrix_props1: + forall (_A : polymat) (s e r : polyvec), + (((_A *^ s)%MLWE_.Matrix_.Matrix + e)%Vector `<*>` r)%MLWE_ = + (((s ^* trmx _A)%MLWE_.Matrix_.Matrix `<*>` r) &+ (e `<*>` r))%MLWE_. + + lemma matrix_props2: + forall (s : polyvec) (_A : polymat) (r e1 cu : polyvec), + (s `<*>` ((trmx _A *^ r)%MLWE_.Matrix_.Matrix + e1 + cu)%Vector)%MLWE_ = + (((s ^* trmx _A)%MLWE_.Matrix_.Matrix `<*>` r) &+ (s `<*>` e1) &+ (s `<*>` cu))%MLWE_. + + lemma noise_exp_val: + forall (_A : polymat) (s e r e1 : polyvec) (e2 : poly) (m : plaintext), + noise_exp _A s e r e1 e2 m = + let t = ((_A *^ s)%MLWE_.Matrix_.Matrix + e)%Vector in + let u = ((trmx _A *^ r)%MLWE_.Matrix_.Matrix + e1)%Vector in + let v = ((t `<*>` r) &+ e2 &+ m_encode m)%MLWE_ in + let cu = rnd_err_u u in let cv = rnd_err_v v in ((e `<*>` r) &- (s `<*>` e1) &- (s `<*>` cu) &+ e2 &+ cv)%MLWE_. + + lemma good_decode: + forall (m : plaintext) (n : poly), under_noise_bound n max_noise => m_decode (m_encode m &+ n)%MLWE_ = m. + + module CorrectnessAdvNoise(A : FO_MLKEM.UU.TT.PKE.CORR_ADV) = { + proc main() : bool = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var _A : polymat; + var r : polyvec; + var e1 : polyvec; + var e2 : poly; + var m : plaintext; + var n : poly; + + sd <$ srand; + _A <- H sd; + r <$ MLWE_.dshort; + s <$ MLWE_.dshort; + e <$ MLWE_.dshort; + e1 <$ MLWE_.dshort; + e2 <$ dshort_R; + m <@ A.find(pk_encode (sd, ((_A *^ s)%MLWE_.Matrix_.Matrix + e)%Vector), sk_encode s); + n <- noise_exp _A s e r e1 e2 m; + + return ! under_noise_bound n max_noise; + } + }. + + lemma correctness_noise: + forall (A <: FO_MLKEM.UU.TT.PKE.CORR_ADV{+all mem, -MLWE_PKE_HASH_PRG} ) &m, + Pr[FO_MLKEM.UU.TT.PKE.Correctness_Adv(MLWE_PKE_HASH, A).main() @ &m : res] <= + Pr[CorrectnessAdvNoise(A).main() @ &m : res] + Pr[PRG_KG.IND(PRG_KG.PRGr, DC_KG(A)).main() @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, DC_KG(A)).main() @ &m : res] + + Pr[PRG_ENC.IND(PRG_ENC.PRGr, DC_ENC(A)).main() @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, DC_ENC(A)).main() @ &m : res]. + + lemma noise_commutes: + forall (n n' : poly) (maxn b : int), + under_noise_bound n' b => under_noise_bound n (maxn - b) => under_noise_bound (n &+ n')%MLWE_ maxn. + + lemma noise_preserved: forall (n : poly) (maxn : int), under_noise_bound n maxn = under_noise_bound ((&-) n) maxn. + + op noise_exp_part1 (_A : polymat) (s e r e1 : polyvec) (e2 : poly) : poly = + let u = ((trmx _A *^ r)%MLWE_.Matrix_.Matrix + e1)%Vector in + let cu = rnd_err_u u in ((e `<*>` r) &- (s `<*>` e1) &+ e2 &- (s `<*>` cu))%MLWE_. + + op noise_exp_part2 (_A : polymat) (s e r : polyvec) (e2 : poly) (m : plaintext) : poly = + let t = ((_A *^ s)%MLWE_.Matrix_.Matrix + e)%Vector in + let v = ((t `<*>` r) &+ e2 &+ m_encode m)%MLWE_ in let cv = rnd_err_v v in cv. + + lemma parts_work: + forall (_A : polymat) (s e r e1 : polyvec) (e2 : poly) (m : plaintext), + noise_exp _A s e r e1 e2 m = (noise_exp_part1 _A s e r e1 e2 &+ noise_exp_part2 _A s e r e2 m)%MLWE_. + + module CB(A : FO_MLKEM.UU.TT.PKE.CORR_ADV) = { + var s : polyvec + + var e : polyvec + + var _A : polymat + + var r : polyvec + + var e1 : polyvec + + var e2 : poly + + var n1 : poly + + var n2 : poly + + var u : polyvec + + var cu : polyvec + + var m : plaintext + + proc main() : unit = { + var sd : W8.t Array32.t; + + sd <$ srand; + CB._A <- H sd; + CB.r <$ MLWE_.dshort; + CB.s <$ MLWE_.dshort; + CB.e <$ MLWE_.dshort; + CB.e1 <$ MLWE_.dshort; + CB.e2 <$ dshort_R; + CB.m <@ A.find(pk_encode (sd, ((CB._A *^ CB.s)%MLWE_.Matrix_.Matrix + CB.e)%Vector), sk_encode CB.s); + CB.n1 <- noise_exp_part1 CB._A CB.s CB.e CB.r CB.e1 CB.e2; + CB.n2 <- noise_exp_part2 CB._A CB.s CB.e CB.r CB.e2 CB.m; + } + }. + + lemma cv_bound_valid: + forall (_A : polymat) (s e r : polyvec) (e2 : poly) (m : plaintext), + s \in MLWE_.dshort => + e \in MLWE_.dshort => + r \in MLWE_.dshort => + e2 \in dshort_R => + let t = ((_A *^ s)%MLWE_.Matrix_.Matrix + e)%Vector in + let v = ((t `<*>` r) &+ e2 &+ m_encode m)%MLWE_ in under_noise_bound (rnd_err_v v) cv_bound_max. + + module CorrectnessBound = { + proc main() : bool = { + var sd : W8.t Array32.t; + var _A : polymat; + var r : polyvec; + var s : polyvec; + var e : polyvec; + var e1 : polyvec; + var e2 : poly; + var n : poly; + + sd <$ srand; + _A <- H sd; + r <$ MLWE_.dshort; + s <$ MLWE_.dshort; + e <$ MLWE_.dshort; + e1 <$ MLWE_.dshort; + e2 <$ dshort_R; + n <- noise_exp_part1 _A s e r e1 e2; + + return ! under_noise_bound n (max_noise - cv_bound_max); + } + }. + + lemma correctness_split: + forall (A <: FO_MLKEM.UU.TT.PKE.CORR_ADV{+all mem, -MLWE_PKE_HASH_PRG, -CB} ) &m (cv_bound : int) (failprob1 + failprob2 : real), + Pr[CB(A).main() @ &m : ! under_noise_bound CB.n1 (max_noise - cv_bound)] <= failprob1 => + Pr[CB(A).main() @ &m : ! under_noise_bound CB.n2 cv_bound] <= failprob2 => + Pr[CorrectnessAdvNoise(A).main() @ &m : res] <= failprob1 + failprob2. + + lemma correctness_bound_aux: + forall (A <: FO_MLKEM.UU.TT.PKE.CORR_ADV{+all mem, -MLWE_PKE_HASH_PRG, -CB} ) &m, + islossless A.find => + Pr[CB(A).main() @ &m : ! under_noise_bound CB.n1 (max_noise - cv_bound_max)] = + Pr[CorrectnessBound.main() @ &m : res]. + + lemma cv_max: + forall (A <: FO_MLKEM.UU.TT.PKE.CORR_ADV{+all mem, -MLWE_PKE_HASH_PRG, -CB} ) &m, + Pr[CB(A).main() @ &m : ! under_noise_bound CB.n2 cv_bound_max] = 0%r. + + lemma correctness_theorem: + forall (A <: FO_MLKEM.UU.TT.PKE.CORR_ADV{+all mem, -MLWE_PKE_HASH_PRG, -CB} ) &m, + islossless A.find => + Pr[FO_MLKEM.UU.TT.PKE.Correctness_Adv(MLWE_PKE_HASH, A).main() @ &m : res] <= + Pr[CorrectnessBound.main() @ &m : res] + + `|Pr[PRG_KG.IND(PRG_KG.PRGr, DC_KG(A)).main() @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, DC_KG(A)).main() @ &m : res]| + + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, DC_ENC(A)).main() @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, DC_ENC(A)).main() @ &m : res]|. + + lemma kg_same: equiv[ FO_MLKEM.UU.TT.BasePKE.kg ~ MLWE_PKE_HASH.kg : true ==> ={res}]. + + lemma correctness: + forall &m (fail_prob : real), + Pr[CorrectnessBound.main() @ &m : res] <= fail_prob => + FO_MLKEM.UU.TT.qHC = 0 => + 1 < FO_MLKEM.UU.TT.FinT.card => + Pr[FO_MLKEM.KEMROM.Correctness(FO_MLKEM.KEMROM.RO.RO, FO_MLKEM.FO_K).main() @ &m : res] <= + fail_prob + + `|Pr[PRG_KG.IND(PRG_KG.PRGr, DC_KG(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.B_UC, FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, DC_KG(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.B_UC, FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res]| + + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, DC_ENC(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.B_UC, FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, DC_ENC(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.B_UC, FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res]|. + + module BUOOOWMod_Hx2 = FO_MLKEM.CountH(FO_MLKEM.B1x2(A/227860, FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CountH(FO_MLKEM.UU.TT.H(FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO))), FO_MLKEM.UU.TT.CountO(FO_MLKEM.UU.TT.G2_O(FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO)))).H2B), FO_MLKEM.UU.KEMROMx2.CCA(FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CountH(FO_MLKEM.UU.TT.H(FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO))), FO_MLKEM.UU.TT.CountO(FO_MLKEM.UU.TT.G2_O(FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO)))).H2B), FO_MLKEM.UU.UU2, FO_MLKEM.B1x2(A/227860)).O).BH). + + module BUOOOWMod_dec = FO_MLKEM.UU.KEMROMx2.CCA(FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CountH(FO_MLKEM.UU.TT.H(FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO))), FO_MLKEM.UU.TT.CountO(FO_MLKEM.UU.TT.G2_O(FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO)))).H2B), FO_MLKEM.UU.UU2, FO_MLKEM.B1x2(A/227860)).O. + + module BUOOOWModCPA_Hx2 = FO_MLKEM.CountH(FO_MLKEM.B1x2(A/227860, FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CountH(FO_MLKEM.UU.TT.PKEROM.RO.RO), FO_MLKEM.UU.TT.CountO(FO_MLKEM.UU.TT.O_AdvOW)).H2B), FO_MLKEM.UU.KEMROMx2.CCA(FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CountH(FO_MLKEM.UU.TT.PKEROM.RO.RO), FO_MLKEM.UU.TT.CountO(FO_MLKEM.UU.TT.O_AdvOW)).H2B), FO_MLKEM.UU.UU2, FO_MLKEM.B1x2(A/227860)).O).BH). + + module BUOOOWModCPA_dec = FO_MLKEM.UU.KEMROMx2.CCA(FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CountH(FO_MLKEM.UU.TT.PKEROM.RO.RO), FO_MLKEM.UU.TT.CountO(FO_MLKEM.UU.TT.O_AdvOW)).H2B), FO_MLKEM.UU.UU2, FO_MLKEM.B1x2(A/227860)).O. + + module BUUCI_Hx2 = FO_MLKEM.CountH(FO_MLKEM.B1x2(A/227860, FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUCI(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO)).H2B), FO_MLKEM.UU.KEMROMx2.CCA(FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUCI(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO)).H2B), FO_MLKEM.UU.UU2, FO_MLKEM.B1x2(A/227860)).O).BH). + + module BUUCI_dec = FO_MLKEM.UU.KEMROMx2.CCA(FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUCI(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO)).H2B), FO_MLKEM.UU.UU2, FO_MLKEM.B1x2(A/227860)).O. + + module BUUC_Hx2 = FO_MLKEM.CountH(FO_MLKEM.B1x2(A/227860, FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUC(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO)).H2B), FO_MLKEM.UU.KEMROMx2.CCA(FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUC(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO)).H2B), FO_MLKEM.UU.UU2, FO_MLKEM.B1x2(A/227860)).O).BH). + + module BUUC_dec = FO_MLKEM.UU.KEMROMx2.CCA(FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUC(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO)).H2B), FO_MLKEM.UU.UU2, FO_MLKEM.B1x2(A/227860)).O. + + lemma conclusion: + forall (A(H : FO_MLKEM.KEMROM.POracle, O : FO_MLKEM.KEMROM.CCA_ORC) <: + FO_MLKEM.KEMROM.CCA_ADV{+all mem, -FO_MLKEM.KEMROM.RO.RO.m, -FO_MLKEM.UU.TT.PKE.OW_CPA, -FO_MLKEM.UU.TT.PKE.BOWp, -FO_MLKEM.UU.TT.PKE.OWL_CPA, -FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl, -FO_MLKEM.UU.TT.BasePKE, -FO_MLKEM.UU.TT.PKEROM.RO.RO, -FO_MLKEM.UU.TT.PKEROM.RO.FRO, -FO_MLKEM.UU.TT.PKEROM.OW_PCVA, -FO_MLKEM.UU.TT.Correctness_Adv1, -FO_MLKEM.UU.TT.B, -FO_MLKEM.UU.TT.CountO, -FO_MLKEM.UU.TT.Gm, -FO_MLKEM.UU.TT.O_AdvOW, -FO_MLKEM.UU.RF.RF, -FO_MLKEM.UU.PseudoRF.PRF, -FO_MLKEM.UU.KEMROMx2.RO1.RO, -FO_MLKEM.UU.KEMROMx2.RO1.FRO, -FO_MLKEM.UU.KEMROMx2.RO2.RO, -FO_MLKEM.UU.KEMROMx2.RO2.FRO, -FO_MLKEM.UU.KEMROMx2.CCA, -FO_MLKEM.UU.CountHx2, -FO_MLKEM.UU.RO1E.FunRO, -FO_MLKEM.UU.UU2, -FO_MLKEM.UU.H2, -FO_MLKEM.UU.Gm2, -FO_MLKEM.UU.Gm3, -FO_MLKEM.UU.H2BOWMod, -FO_MLKEM.KEMROM.CCA, -FO_MLKEM.B1x2, -MLWE_PKE_HASH_PRG, -CB} + ) &m (fail_prob prg_kg_bound prg_enc_bound : real), + Pr[CorrectnessBound.main() @ &m : res] <= fail_prob => + `|Pr[PRG_KG.IND(PRG_KG.PRGr, DC_KG(FO_MLKEM.UU.TT.PKE.BOWp(FO_MLKEM.UU.TT.BasePKE, FO_MLKEM.UU.TT.AdvOW_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, DC_KG(FO_MLKEM.UU.TT.PKE.BOWp(FO_MLKEM.UU.TT.BasePKE, FO_MLKEM.UU.TT.AdvOW_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res]| <= + prg_kg_bound => + `|Pr[PRG_KG.IND(PRG_KG.PRGr, DC_KG(FO_MLKEM.UU.TT.PKE.BOWp(FO_MLKEM.UU.TT.BasePKE, FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, DC_KG(FO_MLKEM.UU.TT.PKE.BOWp(FO_MLKEM.UU.TT.BasePKE, FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res]| <= + prg_kg_bound => + `|Pr[PRG_KG.IND(PRG_KG.PRGr, DC_KG(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.TT.AdvCorr(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, DC_KG(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.TT.AdvCorr(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res]| <= + prg_kg_bound => + `|Pr[PRG_KG.IND(PRG_KG.PRGr, DC_KG(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.BUUCI(FO_MLKEM.B1x2(A)), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, DC_KG(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.BUUCI(FO_MLKEM.B1x2(A)), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res]| <= + prg_kg_bound => + `|Pr[PRG_KG.IND(PRG_KG.PRGr, DC_KG(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.BUUC(FO_MLKEM.B1x2(A)), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, DC_KG(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.BUUC(FO_MLKEM.B1x2(A)), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res]| <= + prg_kg_bound => + `|Pr[PRG_KG.IND(PRG_KG.PRGr, D_KG(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.PKE.OWvsIND.BL(FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))))))).main + () @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, D_KG(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.PKE.OWvsIND.BL(FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))))))).main + () @ &m : res]| <= + prg_kg_bound => + `|Pr[PRG_KG.IND(PRG_KG.PRGr, D_KG(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.AdvOWL_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, D_KG(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.AdvOWL_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res]| <= + prg_kg_bound => + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, DC_ENC(FO_MLKEM.UU.TT.PKE.BOWp(FO_MLKEM.UU.TT.BasePKE, FO_MLKEM.UU.TT.AdvOW_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, DC_ENC(FO_MLKEM.UU.TT.PKE.BOWp(FO_MLKEM.UU.TT.BasePKE, FO_MLKEM.UU.TT.AdvOW_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res]| <= + prg_enc_bound => + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, DC_ENC(FO_MLKEM.UU.TT.PKE.BOWp(FO_MLKEM.UU.TT.BasePKE, FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, DC_ENC(FO_MLKEM.UU.TT.PKE.BOWp(FO_MLKEM.UU.TT.BasePKE, FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res]| <= + prg_enc_bound => + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, DC_ENC(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.TT.AdvCorr(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, DC_ENC(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.TT.AdvCorr(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res]| <= + prg_enc_bound => + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, DC_ENC(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.BUUCI(FO_MLKEM.B1x2(A)), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, DC_ENC(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.BUUCI(FO_MLKEM.B1x2(A)), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res]| <= + prg_enc_bound => + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, DC_ENC(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.BUUC(FO_MLKEM.B1x2(A)), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, DC_ENC(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.BUUC(FO_MLKEM.B1x2(A)), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res]| <= + prg_enc_bound => + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, D_ENC(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.PKE.OWvsIND.BL(FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))))))).main + () @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, D_ENC(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.PKE.OWvsIND.BL(FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))))))).main + () @ &m : res]| <= + prg_enc_bound => + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, D_ENC(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.AdvOWL_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, D_ENC(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.AdvOWL_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res]| <= + prg_enc_bound => + FO_MLKEM.UU.qHT = FO_MLKEM.qHK => + FO_MLKEM.UU.qHU = FO_MLKEM.qHK => + FO_MLKEM.UU.TT.qH = FO_MLKEM.UU.qHT + FO_MLKEM.UU.qHU + 1 => + FO_MLKEM.UU.TT.qV = 0 => + FO_MLKEM.UU.TT.qP = 0 => + FO_MLKEM.UU.TT.qH + 1 = FO_MLKEM.UU.TT.qHC => + FO_MLKEM.UU.TT.qHC < FO_MLKEM.UU.TT.FinT.card - 1 => + (forall (RO0 <: FO_MLKEM.KEMROM.POracle{+all mem, -FO_MLKEM.CountH, -A} ) + (O0 <: FO_MLKEM.KEMROM.CCA_ORC{+all mem, -FO_MLKEM.CountH, -A} ), + hoare[ A(FO_MLKEM.CountH(RO0), O0).guess : FO_MLKEM.CountH.c_h = 0 ==> FO_MLKEM.CountH.c_h <= FO_MLKEM.qHK]) => + (forall (H0 <: FO_MLKEM.KEMROM.POracle{+all mem, -A} ) (O <: FO_MLKEM.UU.KEMROMx2.CCA_ORC{+all mem, -A} ), + islossless O.dec => islossless H0.get => islossless A(H0, O).guess) => + `|Pr[FO_MLKEM.KEMROM.CCA(FO_MLKEM.KEMROM.RO.RO, FO_MLKEM.FO_K, A).main() @ &m : res] - 1%r / 2%r| <= + 2%r * + (`|Pr[MLWE_.MLWE_H(B1(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.AdvOWL_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + (false, false) @ &m : res] - + Pr[MLWE_.MLWE_H(B1(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.AdvOWL_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + (false, true) @ &m : res]| + + `|Pr[MLWE_.MLWE_H(B2(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.AdvOWL_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + (true, false) @ &m : res] - + Pr[MLWE_.MLWE_H(B2(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.AdvOWL_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + (true, true) @ &m : res]| + + prg_kg_bound + prg_enc_bound) + + 2%r * + (`|Pr[MLWE_.MLWE_H(B1(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.PKE.OWvsIND.BL(FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))))))).main + (false, false) @ &m : res] - + Pr[MLWE_.MLWE_H(B1(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.PKE.OWvsIND.BL(FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))))))).main + (false, true) @ &m : res]| + + `|Pr[MLWE_.MLWE_H(B2(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.PKE.OWvsIND.BL(FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))))))).main + (true, false) @ &m : res] - + Pr[MLWE_.MLWE_H(B2(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.PKE.OWvsIND.BL(FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))))))).main + (true, true) @ &m : res]| + + prg_kg_bound + prg_enc_bound) + + (3%r * (2 * FO_MLKEM.qHK + 3)%r + 1%r) * (fail_prob + prg_kg_bound + prg_enc_bound) + + `|Pr[FO_MLKEM.UU.J.IND(FO_MLKEM.UU.PseudoRF.PRF, FO_MLKEM.UU.D(FO_MLKEM.B1x2(A))).main() @ &m : res] - + Pr[FO_MLKEM.UU.J.IND(FO_MLKEM.UU.RF.RF, FO_MLKEM.UU.D(FO_MLKEM.B1x2(A))).main() @ &m : res]| + + 2%r * (2 * FO_MLKEM.qHK + 2)%r * FO_MLKEM.UU.TT.PKE.eps_msg. +end MLWEPKEHash. + +[-] [0917] 39.8% (-1.0B / [frag -1.0B]) * In [theories]: + +theory MLWEPKEHash. + theory MLWE_. + theory Matrix_. + theory ZR. + lemma nosmt addrA: associative (&+). + + lemma nosmt addrC: commutative (&+). + + lemma nosmt add0r: left_id Rq.zero (&+). + + lemma nosmt addNr: left_inverse Rq.zero (&-) (&+). + + theory AddMonoid. + lemma addmA: associative (&+). + + lemma addmC: commutative (&+). + + lemma add0m: left_id Rq.zero (&+). + + lemma addm0: right_id Rq.zero (&+). + + lemma addmCA: left_commutative (&+). + + lemma addmAC: right_commutative (&+). + + lemma addmACA: interchange (&+) (&+). + + lemma iteropE: forall (n : int) (x : poly), iterop n (&+) x Rq.zero = iter n ((&+) x) Rq.zero. + end AddMonoid. + + abbrev (-) (x y : poly) : poly = (x - y)%KMatrix.ZR. + + lemma nosmt addr0: right_id Rq.zero (&+). + + lemma nosmt addrN: right_inverse Rq.zero (&-) (&+). + + lemma nosmt addrCA: left_commutative (&+). + + lemma nosmt addrAC: right_commutative (&+). + + lemma nosmt addrACA: interchange (&+) (&+). + + lemma nosmt subrr: forall (x : poly), x - x = Rq.zero. + + lemma nosmt addKr: left_loop (&-) (&+). + + lemma nosmt addNKr: rev_left_loop (&-) (&+). + + lemma nosmt addrK: right_loop (&-) (&+). + + lemma nosmt addrNK: rev_right_loop (&-) (&+). + + lemma nosmt subrK: forall (x y : poly), (x - y) &+ y = x. + + lemma nosmt addrI: right_injective (&+). + + lemma nosmt addIr: left_injective (&+). + + lemma nosmt opprK: involutive (&-). + + lemma oppr_inj: injective (&-). + + lemma nosmt oppr0: (&-) Rq.zero = Rq.zero. + + lemma oppr_eq0: forall (x : poly), (&-) x = Rq.zero <=> x = Rq.zero. + + lemma nosmt subr0: forall (x : poly), x - Rq.zero = x. + + lemma nosmt sub0r: forall (x : poly), Rq.zero - x = (&-) x. + + lemma nosmt opprD: forall (x y : poly), (&-) (x &+ y) = (&-) x - y. + + lemma nosmt opprB: forall (x y : poly), (&-) (x - y) = y - x. + + lemma nosmt subrACA: interchange (fun (x y : poly) => x - y) (&+). + + lemma nosmt subr_eq: forall (x y z : poly), x - z = y <=> x = y &+ z. + + lemma nosmt subr_eq0: forall (x y : poly), x - y = Rq.zero <=> x = y. + + lemma nosmt addr_eq0: forall (x y : poly), x &+ y = Rq.zero <=> x = (&-) y. + + lemma nosmt eqr_opp: forall (x y : poly), (&-) x = (&-) y <=> x = y. + + lemma eqr_oppLR: forall (x y : poly), (&-) x = y <=> x = (&-) y. + + lemma nosmt eqr_sub: forall (x y z t : poly), x - y = z - t <=> x &+ t = z &+ y. + + lemma subr_add2r: forall (z x y : poly), x &+ z - y &+ z = x - y. + + op intmul (x : poly) (n : int) : poly = + if n < 0 then (&-) (iterop (-n) (&+) x Rq.zero) else iterop n (&+) x Rq.zero. + + lemma intmulpE: forall (z : poly) (c : int), 0 <= c => intmul z c = iterop c (&+) z Rq.zero. + + lemma mulr0z: forall (x : poly), intmul x 0 = Rq.zero. + + lemma mulr1z: forall (x : poly), intmul x 1 = x. + + lemma mulr2z: forall (x : poly), intmul x 2 = x &+ x. + + lemma mulrNz: forall (x : poly) (n : int), intmul x (-n) = (&-) (intmul x n). + + lemma mulrS: forall (x : poly) (n : int), 0 <= n => intmul x (n + 1) = x &+ intmul x n. + + lemma mulNrz: forall (x : poly) (n : int), intmul ((&-) x) n = (&-) (intmul x n). + + lemma mulNrNz: forall (x : poly) (n : int), intmul ((&-) x) (-n) = intmul x n. + + lemma mulrSz: forall (x : poly) (n : int), intmul x (n + 1) = x &+ intmul x n. + + lemma mulrDz: forall (x : poly) (n m : int), intmul x (n + m) = intmul x n &+ intmul x m. + + abbrev (/) (x y : poly) : poly = (x / y)%KMatrix.ZR. + + lemma nosmt oner_neq0: Rq.one <> Rq.zero. + + lemma nosmt mulrA: associative ( &* ). + + lemma nosmt mulrC: commutative ( &* ). + + lemma nosmt mul1r: left_id Rq.one ( &* ). + + lemma nosmt mulrDl: left_distributive ( &* ) (&+). + + lemma nosmt mulVr: left_inverse_in KMatrix.ZR.unit Rq.one Top.Correctness.invr ( &* ). + + lemma nosmt unitP: forall (x y : poly), y &* x = Rq.one => (unit x)%KMatrix.ZR. + + lemma nosmt unitout: forall (x : poly), ! (unit x)%KMatrix.ZR => invr x = x. + + theory MulMonoid. + lemma addmA: associative ( &* ). + + lemma addmC: commutative ( &* ). + + lemma add0m: left_id Rq.one ( &* ). + + lemma addm0: right_id Rq.one ( &* ). + + lemma addmCA: left_commutative ( &* ). + + lemma addmAC: right_commutative ( &* ). + + lemma addmACA: interchange ( &* ) ( &* ). + + lemma iteropE: forall (n : int) (x : poly), iterop n ( &* ) x Rq.one = iter n (( &* ) x) Rq.one. + end MulMonoid. + + lemma nosmt mulr1: right_id Rq.one ( &* ). + + lemma nosmt mulrCA: left_commutative ( &* ). + + lemma nosmt mulrAC: right_commutative ( &* ). + + lemma nosmt mulrACA: interchange ( &* ) ( &* ). + + lemma nosmt mulrSl: forall (x y : poly), x &+ Rq.one &* y = x &* y &+ y. + + lemma nosmt mulrDr: right_distributive ( &* ) (&+). + + lemma nosmt mul0r: left_zero Rq.zero ( &* ). + + lemma nosmt mulr0: right_zero Rq.zero ( &* ). + + lemma nosmt mulrN: forall (x y : poly), x &* (&-) y = (&-) (x &* y). + + lemma nosmt mulNr: forall (x y : poly), (&-) x &* y = (&-) (x &* y). + + lemma nosmt mulrNN: forall (x y : poly), (&-) x &* (&-) y = x &* y. + + lemma nosmt mulN1r: forall (x : poly), (&-) Rq.one &* x = (&-) x. + + lemma nosmt mulrN1: forall (x : poly), x &* (&-) Rq.one = (&-) x. + + lemma nosmt mulrBl: left_distributive ( &* ) (fun (x y : poly) => x - y). + + lemma nosmt mulrBr: right_distributive ( &* ) (fun (x y : poly) => x - y). + + lemma mulrnAl: forall (x y : poly) (n : int), 0 <= n => intmul x n &* y = intmul (x &* y) n. + + lemma mulrnAr: forall (x y : poly) (n : int), 0 <= n => x &* intmul y n = intmul (x &* y) n. + + lemma mulrzAl: forall (x y : poly) (z : int), intmul x z &* y = intmul (x &* y) z. + + lemma mulrzAr: forall (x y : poly) (z : int), x &* intmul y z = intmul (x &* y) z. + + lemma nosmt mulrV: right_inverse_in KMatrix.ZR.unit Rq.one Top.Correctness.invr ( &* ). + + lemma nosmt divrr: forall (x : poly), (unit x)%KMatrix.ZR => x / x = Rq.one. + + lemma nosmt invr_out: forall (x : poly), ! (unit x)%KMatrix.ZR => invr x = x. + + lemma nosmt unitrP: forall (x : poly), (unit x)%KMatrix.ZR <=> exists (y : poly), y &* x = Rq.one. + + lemma nosmt mulKr: left_loop_in KMatrix.ZR.unit Top.Correctness.invr ( &* ). + + lemma nosmt mulrK: right_loop_in KMatrix.ZR.unit Top.Correctness.invr ( &* ). + + lemma nosmt mulVKr: rev_left_loop_in KMatrix.ZR.unit Top.Correctness.invr ( &* ). + + lemma nosmt mulrVK: rev_right_loop_in KMatrix.ZR.unit Top.Correctness.invr ( &* ). + + lemma nosmt mulrI: right_injective_in KMatrix.ZR.unit ( &* ). + + lemma nosmt mulIr: left_injective_in KMatrix.ZR.unit ( &* ). + + lemma nosmt unitrE: forall (x : poly), (unit x)%KMatrix.ZR <=> x / x = Rq.one. + + lemma nosmt invrK: involutive Top.Correctness.invr. + + lemma nosmt invr_inj: injective Top.Correctness.invr. + + lemma nosmt unitrV: forall (x : poly), (unit (invr x))%KMatrix.ZR <=> (unit x)%KMatrix.ZR. + + lemma nosmt unitr1: (unit Rq.one)%KMatrix.ZR. + + lemma nosmt invr1: invr Rq.one = Rq.one. + + lemma nosmt div1r: forall (x : poly), Rq.one / x = invr x. + + lemma nosmt divr1: forall (x : poly), x / Rq.one = x. + + lemma nosmt unitr0: ! (unit Rq.zero)%KMatrix.ZR. + + lemma nosmt invr0: invr Rq.zero = Rq.zero. + + lemma nosmt unitrN1: (unit ((&-) Rq.one))%KMatrix.ZR. + + lemma nosmt invrN1: invr ((&-) Rq.one) = (&-) Rq.one. + + lemma nosmt unitrMl: + forall (x y : poly), (unit y)%KMatrix.ZR => (unit (x &* y))%KMatrix.ZR <=> (unit x)%KMatrix.ZR. + + lemma nosmt unitrMr: + forall (x y : poly), (unit x)%KMatrix.ZR => (unit (x &* y))%KMatrix.ZR <=> (unit y)%KMatrix.ZR. + + lemma nosmt unitrM: + forall (x y : poly), (unit (x &* y))%KMatrix.ZR <=> (unit x)%KMatrix.ZR /\ (unit y)%KMatrix.ZR. + + lemma nosmt unitrN: forall (x : poly), (unit ((&-) x))%KMatrix.ZR <=> (unit x)%KMatrix.ZR. + + lemma nosmt invrM: + forall (x y : poly), (unit x)%KMatrix.ZR => (unit y)%KMatrix.ZR => invr (x &* y) = invr y / x. + + lemma nosmt invrN: forall (x : poly), invr ((&-) x) = (&-) (invr x). + + lemma nosmt invr_neq0: forall (x : poly), x <> Rq.zero => invr x <> Rq.zero. + + lemma nosmt invr_eq0: forall (x : poly), invr x = Rq.zero <=> x = Rq.zero. + + lemma nosmt invr_eq1: forall (x : poly), invr x = Rq.one <=> x = Rq.one. + + op ofint (n : int) : poly = intmul Rq.one n. + + lemma ofint0: (ofint 0)%ZR = Rq.zero. + + lemma ofint1: (ofint 1)%ZR = Rq.one. + + lemma ofintS: forall (i : int), 0 <= i => (ofint (i + 1))%ZR = Rq.one &+ (ofint i)%ZR. + + lemma ofintN: forall (i : int), (ofint (-i))%ZR = (&-) ((ofint i))%ZR. + + lemma mul1r0z: forall (x : poly), x &* (ofint 0)%ZR = Rq.zero. + + lemma mul1r1z: forall (x : poly), x &* (ofint 1)%ZR = x. + + lemma mul1r2z: forall (x : poly), x &* (ofint 2)%ZR = x &+ x. + + lemma mulr_intl: forall (x : poly) (z : int), (ofint z)%ZR &* x = intmul x z. + + lemma mulr_intr: forall (x : poly) (z : int), x &* (ofint z)%ZR = intmul x z. + + lemma fracrDE: + forall (n1 n2 d1 d2 : poly), + (unit d1)%KMatrix.ZR => (unit d2)%KMatrix.ZR => n1 / d1 &+ (n2 / d2) = n1 &* d2 &+ (n2 &* d1) / (d1 &* d2). + + op exp (x : poly) (n : int) : poly = + if n < 0 then invr (iterop (-n) ( &* ) x Rq.one) else iterop n ( &* ) x Rq.one. + + lemma expr0: forall (x : poly), exp x 0 = Rq.one. + + lemma expr1: forall (x : poly), exp x 1 = x. + + lemma exprS: forall (x : poly) (i : int), 0 <= i => exp x (i + 1) = x &* exp x i. + + lemma expr_pred: forall (x : poly) (i : int), 0 < i => exp x i = x &* exp x (i - 1). + + lemma exprSr: forall (x : poly) (i : int), 0 <= i => exp x (i + 1) = exp x i &* x. + + lemma expr2: forall (x : poly), exp x 2 = x &* x. + + lemma exprN: forall (x : poly) (i : int), exp x (-i) = invr (exp x i). + + lemma exprN1: forall (x : poly), exp x (-1) = invr x. + + lemma unitrX: forall (x : poly) (m : int), (unit x)%KMatrix.ZR => (unit (exp x m))%KMatrix.ZR. + + lemma unitrX_neq0: forall (x : poly) (m : int), m <> 0 => (unit (exp x m))%KMatrix.ZR => (unit x)%KMatrix.ZR. + + lemma exprV: forall (x : poly) (i : int), exp (invr x) i = exp x (-i). + + lemma exprVn: forall (x : poly) (n : int), 0 <= n => exp (invr x) n = invr (exp x n). + + lemma exprMn: forall (x y : poly) (n : int), 0 <= n => exp (x &* y) n = exp x n &* exp y n. + + lemma exprD_nneg: forall (x : poly) (m n : int), 0 <= m => 0 <= n => exp x (m + n) = exp x m &* exp x n. + + lemma exprD: forall (x : poly) (m n : int), (unit x)%KMatrix.ZR => exp x (m + n) = exp x m &* exp x n. + + lemma exprM: forall (x : poly) (m n : int), exp x (m * n) = exp (exp x m) n. + + lemma expr0n: forall (n : int), 0 <= n => exp Rq.zero n = if n = 0 then Rq.one else Rq.zero. + + lemma expr0z: forall (z : int), exp Rq.zero z = if z = 0 then Rq.one else Rq.zero. + + lemma expr1z: forall (z : int), exp Rq.one z = Rq.one. + + lemma sqrrD: forall (x y : poly), exp (x &+ y) 2 = exp x 2 &+ intmul (x &* y) 2 &+ exp y 2. + + lemma sqrrN: forall (x : poly), exp ((&-) x) 2 = exp x 2. + + lemma sqrrB: forall (x y : poly), exp (x - y) 2 = (exp x 2 - intmul (x &* y) 2) &+ exp y 2. + + lemma signr_odd: forall (n : int), 0 <= n => exp ((&-) Rq.one) (b2i (odd n)) = exp ((&-) Rq.one) n. + + lemma subr_sqr_1: forall (x : poly), exp x 2 - Rq.one = (x - Rq.one) &* (x &+ Rq.one). + + op lreg (x : poly) : bool = injective (fun (y : poly) => x &* y). + + lemma mulrI_eq0: forall (x y : poly), lreg x => x &* y = Rq.zero <=> y = Rq.zero. + + lemma lreg_neq0: forall (x : poly), lreg x => x <> Rq.zero. + + lemma mulrI0_lreg: forall (x : poly), (forall (y : poly), x &* y = Rq.zero => y = Rq.zero) => lreg x. + + lemma lregN: forall (x : poly), lreg x => lreg ((&-) x). + + lemma lreg1: lreg Rq.one. + + lemma lregM: forall (x y : poly), lreg x => lreg y => lreg (x &* y). + + lemma lregXn: forall (x : poly) (n : int), 0 <= n => lreg x => lreg (exp x n). + + instance ring with [()] poly + op rzero = Top.Rq.zero + op rone = Top.Rq.one + op add = Top.Rq.&+ + op opp = Top.Rq.&- + op mul = Top.Rq.&* + op expr = Top.MLWEPKEHash.MLWE_.Matrix_.ZR.exp + op ofint = Top.MLWEPKEHash.MLWE_.Matrix_.ZR.ofint + + instance poly with Top.Ring.ZModule.zmodule. + + instance poly with Top.MLWEPKEHash.MLWE_.Matrix_.ZR.ring. + + instance poly with Top.Ring.IDomain.idomain. + end ZR. + + theory Big. + theory CR. + instance ring with [()] poly + op rzero = Top.Rq.zero + op rone = Top.Rq.one + op add = Top.Rq.&+ + op opp = Top.Rq.&- + op mul = Top.Rq.&* + op expr = Top.MLWEPKEHash.MLWE_.Matrix_.ZR.exp + op ofint = Top.MLWEPKEHash.MLWE_.Matrix_.ZR.ofint + + instance poly with Top.Ring.ZModule.zmodule. + + instance poly with Top.MLWEPKEHash.MLWE_.Matrix_.Big.CR.ring. + + instance poly with Top.Ring.IDomain.idomain. + end CR. + + theory BAdd. + op big ['a] (P : 'a -> bool) (F : 'a -> poly) (r : 'a list) : poly = foldr (&+) Rq.zero (map F (filter P r)). + + abbrev bigi (P : int -> bool) (F : int -> poly) (i j : int) : poly = big P F (range i j). + + lemma big_nil ['a]: forall (P : 'a -> bool) (F : 'a -> poly), big P F [] = Rq.zero. + + lemma big_cons ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (x : 'a) (s : 'a list), + big P F (x :: s) = if P x then F x &+ big P F s else big P F s. + + lemma big_consT ['a]: + forall (F : 'a -> poly) (x : 'a) (s : 'a list), big predT F (x :: s) = F x &+ big predT F s. + + lemma nosmt big_rec ['a]: + forall (K : poly -> bool) (r : 'a list) (P : 'a -> bool) (F : + 'a -> poly), K Rq.zero => (forall (i : 'a) (x : poly), P i => K x => K (F i &+ x)) => K (big P F r). + + lemma nosmt big_ind ['a]: + forall (K : poly -> bool) (r : 'a list) (P : 'a -> bool) (F : + 'a -> poly), + (forall (x y : poly), K x => K y => K (x &+ y)) => + K Rq.zero => (forall (i : 'a), P i => K (F i)) => K (big P F r). + + lemma nosmt big_rec2 ['a]: + forall (K : poly -> poly -> bool) (r : 'a list) (P : 'a -> bool) (F1 F2 : + 'a -> poly), + K Rq.zero Rq.zero => + (forall (i : 'a) (y1 y2 : poly), P i => K y1 y2 => K (F1 i &+ y1) (F2 i &+ y2)) => + K (big P F1 r) (big P F2 r). + + lemma nosmt big_ind2 ['a]: + forall (K : poly -> poly -> bool) (r : 'a list) (P : 'a -> bool) (F1 F2 : + 'a -> poly), + (forall (x1 x2 y1 y2 : poly), K x1 x2 => K y1 y2 => K (x1 &+ y1) (x2 &+ y2)) => + K Rq.zero Rq.zero => (forall (i : 'a), P i => K (F1 i) (F2 i)) => K (big P F1 r) (big P F2 r). + + lemma nosmt big_endo ['a]: + forall (f : poly -> poly), + f Rq.zero = Rq.zero => + (forall (x y : poly), f (x &+ y) = f x &+ f y) => + forall (r : 'a list) (P : 'a -> bool) (F : 'a -> poly), f (big P F r) = big P (f \o F) r. + + lemma nosmt big_map ['a, 'b]: + forall (h : 'b -> 'a) (P : 'a -> bool) (F : 'a -> poly) (s : 'b list), + big P F (map h s) = big (P \o h) (F \o h) s. + + lemma nosmt big_mapT ['a, 'b]: + forall (h : 'b -> 'a) (F : 'a -> poly) (s : 'b list), big predT F (map h s) = big predT (F \o h) s. + + lemma nosmt big_comp ['a]: + forall (h : poly -> poly) (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + h Rq.zero = Rq.zero => morphism_2 h (&+) (&+) => h (big P F s) = big P (h \o F) s. + + lemma nosmt big_nth ['a]: + forall (x0 : 'a) (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = bigi (P \o nth x0 s) (F \o nth x0 s) 0 (size s). + + lemma nosmt big_const ['a]: + forall (P : 'a -> bool) (x : poly) (s : 'a list), + big P (fun (_ : 'a) => x) s = iter (count P s) ((&+) x) Rq.zero. + + lemma nosmt big_seq1 ['a]: forall (F : 'a -> poly) (x : 'a), big predT F [x] = F x. + + lemma nosmt big_mkcond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = big predT (fun (i : 'a) => if P i then F i else Rq.zero) s. + + lemma nosmt big_filter ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), big predT F (filter P s) = big P F s. + + lemma big_filter_cond ['a]: + forall (P1 P2 : 'a -> bool) (F : 'a -> poly) (s : 'a list), big P2 F (filter P1 s) = big (predI P1 P2) F s. + + lemma nosmt eq_bigl ['a]: + forall (P1 P2 : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P1 i <=> P2 i) => big P1 F s = big P2 F s. + + lemma nosmt eq_bigr ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i => F1 i = F2 i) => big P F1 s = big P F2 s. + + lemma nosmt big_distrl ['a]: + forall (op_ : poly -> poly -> poly) (P : 'a -> bool) (F : + 'a -> poly) (s : 'a list) (t : poly), + left_zero Rq.zero op_ => + left_distributive op_ (&+) => op_ (big P F s) t = big P (fun (a : 'a) => op_ (F a) t) s. + + lemma nosmt big_distrr ['a]: + forall (op_ : poly -> poly -> poly) (P : 'a -> bool) (F : + 'a -> poly) (s : 'a list) (t : poly), + right_zero Rq.zero op_ => + right_distributive op_ (&+) => op_ t (big P F s) = big P (fun (a : 'a) => op_ t (F a)) s. + + lemma nosmt big_distr ['a, 'b]: + forall (op_ : poly -> poly -> poly) (P1 : 'a -> bool) (P2 : + 'b -> bool) (F1 : 'a -> poly) (s1 : 'a list) (F2 : 'b -> poly) (s2 : 'b list), + commutative op_ => + left_zero Rq.zero op_ => + left_distributive op_ (&+) => + op_ (big P1 F1 s1) (big P2 F2 s2) = + big P1 (fun (a1 : 'a) => big P2 (fun (a2 : 'b) => op_ (F1 a1) (F2 a2)) s2) s1. + + lemma big_andbC ['a]: + forall (P Q : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big (fun (x : 'a) => P x /\ Q x) F s = big (fun (x : 'a) => Q x /\ P x) F s. + + lemma nosmt eq_big ['a]: + forall (P1 P2 : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + (forall (i : 'a), P1 i <=> P2 i) => (forall (i : 'a), P1 i => F1 i = F2 i) => big P1 F1 s = big P2 F2 s. + + lemma nosmt congr_big ['a]: + forall (r1 r2 : 'a list) (P1 P2 : 'a -> bool) (F1 F2 : 'a -> poly), + r1 = r2 => + (forall (x : 'a), P1 x <=> P2 x) => (forall (i : 'a), P1 i => F1 i = F2 i) => big P1 F1 r1 = big P2 F2 r2. + + lemma big_hasC ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), ! has P s => big P F s = Rq.zero. + + lemma big_pred0_eq ['a]: forall (F : 'a -> poly) (s : 'a list), big pred0 F s = Rq.zero. + + lemma big_pred0 ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i <=> false) => big P F s = Rq.zero. + + lemma big_cat ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), big P F (s1 ++ s2) = big P F s1 &+ big P F s2. + + lemma nosmt big_catl ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), ! has P s2 => big P F (s1 ++ s2) = big P F s1. + + lemma nosmt big_catr ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), ! has P s1 => big P F (s1 ++ s2) = big P F s2. + + lemma nosmt big_rcons ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + big P F (rcons s x) = if P x then big P F s &+ F x else big P F s. + + lemma nosmt eq_big_perm ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), perm_eq s1 s2 => big P F s1 = big P F s2. + + lemma nosmt eq_big_perm_map ['a]: + forall (F : 'a -> poly) (s1 s2 : 'a list), perm_eq (map F s1) (map F s2) => big predT F s1 = big predT F s2. + + lemma nosmt big_seq_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = big (fun (i : 'a) => (i \in s) /\ P i) F s. + + lemma nosmt big_seq ['a]: + forall (F : 'a -> poly) (s : 'a list), big predT F s = big (fun (i : 'a) => i \in s) F s. + + lemma nosmt big_rem ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + x \in s => big P F s = (if P x then F x else Rq.zero) &+ big P F (rem x s). + + lemma nosmt bigD1 ['a]: + forall (F : 'a -> poly) (s : 'a list) (x : 'a), + x \in s => uniq s => big predT F s = F x &+ big (predC1 x) F s. + + lemma nosmt bigD1_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + P x => x \in s => uniq s => big P F s = F x &+ big (predI P (predC1 x)) F s. + + lemma nosmt bigD1_cond_if ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + uniq s => big P F s = (if (x \in s) /\ P x then F x else Rq.zero) &+ big (predI P (predC1 x)) F s. + + lemma big_split ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + big P (fun (i : 'a) => F1 i &+ F2 i) s = big P F1 s &+ big P F2 s. + + lemma nosmt bigID ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (a : 'a -> bool) (s : 'a list), + big P F s = big (predI P a) F s &+ big (predI P (predC a)) F s. + + lemma bigU ['a]: + forall (P Q : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (x : 'a), ! (P x /\ Q x)) => big (predU P Q) F s = big P F s &+ big Q F s. + + lemma bigEM ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), big predT F s = big P F s &+ big (predC P) F s. + + lemma nosmt big_reindex ['a, 'b]: + forall (P : 'a -> bool) (F : 'a -> poly) (f : 'b -> 'a) (f' : + 'a -> 'b) (s : 'a list), + (forall (x : 'a), x \in s => f (f' x) = x) => big P F s = big (P \o f) (F \o f) (map f' s). + + lemma nosmt big_pair_pswap ['a, 'b]: + forall (p : 'a * 'b -> bool) (f : 'a * 'b -> poly) (s : ('a * 'b) list), + big p f s = big (p \o pswap) (f \o pswap) (map pswap s). + + lemma nosmt eq_big_seq ['a]: + forall (F1 F2 : 'a -> poly) (s : 'a list), + (forall (x : 'a), x \in s => F1 x = F2 x) => big predT F1 s = big predT F2 s. + + lemma nosmt congr_big_seq ['a]: + forall (P1 P2 : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + (forall (x : 'a), x \in s => P1 x = P2 x) => + (forall (x : 'a), x \in s => P1 x => P2 x => F1 x = F2 x) => big P1 F1 s = big P2 F2 s. + + lemma big1_eq ['a]: forall (P : 'a -> bool) (s : 'a list), big P (fun (_ : 'a) => Rq.zero) s = Rq.zero. + + lemma big1 ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i => F i = Rq.zero) => big P F s = Rq.zero. + + lemma big1_seq ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i /\ (i \in s) => F i = Rq.zero) => big P F s = Rq.zero. + + lemma big_eq_idm_filter ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (x : 'a), ! P x => F x = Rq.zero) => big predT F s = big P F s. + + lemma big_flatten ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (rr : 'a list list), + big P F (flatten rr) = big predT (fun (s : 'a list) => big P F s) rr. + + lemma nosmt big_pair ['a, 'b]: + forall (F : 'a * 'b -> poly) (s : ('a * 'b) list), + uniq s => + big predT F s = + big predT (fun (a : 'a) => big predT F (filter (fun (xy : 'a * 'b) => xy.`1 = a) s)) (undup (unzip1 s)). + + lemma big_nseq_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (n : int) (x : 'a), + big P F (nseq n x) = if P x then iter n ((&+) (F x)) Rq.zero else Rq.zero. + + lemma big_nseq ['a]: + forall (F : 'a -> poly) (n : int) (x : 'a), big predT F (nseq n x) = iter n ((&+) (F x)) Rq.zero. + + lemma big_undup ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = big P (fun (a : 'a) => iter (count (pred1 a) s) ((&+) (F a)) Rq.zero) (undup s). + + lemma nosmt exchange_big ['a, 'b]: + forall (P1 : 'a -> bool) (P2 : 'b -> bool) (F : 'a -> 'b -> poly) (s1 : 'a list) (s2 : 'b list), + big P1 (fun (a : 'a) => big P2 (F a) s2) s1 = + big P2 (fun (b : 'b) => big P1 (fun (a : 'a) => F a b) s1) s2. + + lemma partition_big ['a, 'b]: + forall (px : 'a -> 'b) (P : 'a -> bool) (Q : 'b -> bool) (F : + 'a -> poly) (s : 'a list) (s' : 'b list), + uniq s' => + (forall (x : 'a), x \in s => P x => (px x \in s') /\ Q (px x)) => + big P F s = big Q (fun (x : 'b) => big (fun (y : 'a) => P y /\ px y = x) F s) s'. + + lemma nosmt big_allpairs ['a, 'b, 'c]: + forall (f : 'a -> 'b -> 'c) (F : 'c -> poly) (s : 'a list) (t : 'b list), + big predT F (allpairs f s t) = big predT (fun (x : 'a) => big predT (fun (y : 'b) => F (f x y)) t) s. + + lemma nosmt big_int_cond: + forall (m n : int) (P : int -> bool) (F : int -> poly), + bigi P F m n = bigi (fun (i : int) => (m <= i && i < n) /\ P i) F m n. + + lemma nosmt big_int: + forall (m n : int) (F : int -> poly), bigi predT F m n = bigi (fun (i : int) => m <= i && i < n) F m n. + + lemma nosmt congr_big_int: + forall (m1 n1 m2 n2 : int) (P1 P2 : int -> bool) (F1 F2 : + int -> poly), + m1 = m2 => + n1 = n2 => + (forall (i : int), m1 <= i && i < n2 => P1 i = P2 i) => + (forall (i : int), P1 i /\ m1 <= i && i < n2 => F1 i = F2 i) => bigi P1 F1 m1 n1 = bigi P2 F2 m2 n2. + + lemma nosmt eq_big_int: + forall (m n : int) (F1 F2 : int -> poly), + (forall (i : int), m <= i && i < n => F1 i = F2 i) => bigi predT F1 m n = bigi predT F2 m n. + + lemma nosmt big_ltn_cond: + forall (m n : int) (P : int -> bool) (F : int -> poly), + m < n => let x = bigi P F (m + 1) n in bigi P F m n = if P m then F m &+ x else x. + + lemma nosmt big_ltn: + forall (m n : int) (F : int -> poly), m < n => bigi predT F m n = F m &+ bigi predT F (m + 1) n. + + lemma nosmt big_geq: + forall (m n : int) (P : int -> bool) (F : int -> poly), n <= m => bigi P F m n = Rq.zero. + + lemma nosmt big_addn: + forall (m n a : int) (P : int -> bool) (F : int -> poly), + bigi P F (m + a) n = bigi (fun (i : int) => P (i + a)) (fun (i : int) => F (i + a)) m (n - a). + + lemma nosmt big_int1: forall (n : int) (F : int -> poly), bigi predT F n (n + 1) = F n. + + lemma nosmt big_cat_int: + forall (n m p : int) (P : int -> bool) (F : int -> poly), + m <= n => n <= p => bigi P F m p = bigi P F m n &+ bigi P F n p. + + lemma nosmt big_int_recl: + forall (n m : int) (F : int -> poly), + m <= n => bigi predT F m (n + 1) = F m &+ bigi predT (fun (i : int) => F (i + 1)) m n. + + lemma nosmt big_int_recr: + forall (n m : int) (F : int -> poly), m <= n => bigi predT F m (n + 1) = bigi predT F m n &+ F n. + + lemma nosmt big_int_recl_cond: + forall (n m : int) (P : int -> bool) (F : int -> poly), + m <= n => + bigi P F m (n + 1) = + (if P m then F m else Rq.zero) &+ bigi (fun (i : int) => P (i + 1)) (fun (i : int) => F (i + 1)) m n. + + lemma nosmt big_int_recr_cond: + forall (n m : int) (P : int -> bool) (F : int -> poly), + m <= n => bigi P F m (n + 1) = bigi P F m n &+ if P n then F n else Rq.zero. + + lemma nosmt bigi_split_odd_even: + forall (n : int) (F : int -> poly), + 0 <= n => bigi predT (fun (i : int) => F (2 * i) &+ F (2 * i + 1)) 0 n = bigi predT F 0 (2 * n). + + lemma sumrD ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> poly) (r : 'a list), + big P F1 r &+ big P F2 r = big P (fun (x : 'a) => F1 x &+ F2 x) r. + + lemma sumrN ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (r : 'a list), + (&-) (big P F r) = big P (fun (x : 'a) => (&-) (F x)) r. + + lemma sumrB ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> poly) (r : 'a list), + (big P F1 r - big P F2 r)%ZR = big P (fun (x : 'a) => (F1 x - F2 x)%ZR) r. + + lemma nosmt sumr_const ['a]: + forall (P : 'a -> bool) (x : poly) (s : 'a list), big P (fun (_ : 'a) => x) s = (intmul x (count P s))%ZR. + + lemma sumri_const: + forall (k : poly) (n m : int), n <= m => bigi predT (fun (_ : int) => k) n m = (intmul k (m - n))%ZR. + + lemma sumr_undup ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = big P (fun (a : 'a) => (intmul (F a) (count (pred1 a) s))%ZR) (undup s). + + lemma telescoping_sum: + forall (F : int -> poly) (m n : int), + m <= n => (F m - F n)%ZR = bigi predT (fun (i : int) => (F i - F (i + 1))%ZR) m n. + + lemma telescoping_sum_down: + forall (F : int -> poly) (m n : int), + m <= n => (F n - F m)%ZR = bigi predT (fun (i : int) => (F (i + 1) - F i)%ZR) m n. + + lemma nosmt sumr_1 ['a]: + forall (P : 'a -> bool) (s : 'a list), big P (fun (_ : 'a) => Rq.one) s = (ofint (count P s))%ZR. + + lemma mulr_suml ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : poly), + big P F s &* x = big P (fun (i : 'a) => F i &* x) s. + + lemma mulr_sumr ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : poly), + x &* big P F s = big P (fun (i : 'a) => x &* F i) s. + + lemma divr_suml ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : poly), + (big P F s / x)%ZR = big P (fun (i : 'a) => (F i / x)%ZR) s. + + lemma nosmt sum_pair_dep ['a, 'b]: + forall (u : 'a -> poly) (v : 'a -> 'b -> poly) (J : ('a * 'b) list), + uniq J => + big predT (fun (ij : 'a * 'b) => u ij.`1 &* v ij.`1 ij.`2) J = + big predT + (fun (i : 'a) => + u i &* big predT (fun (ij : 'a * 'b) => v ij.`1 ij.`2) (filter (fun (ij : 'a * 'b) => ij.`1 = i) J)) + (undup (unzip1 J)). + + lemma nosmt sum_pair ['a, 'b]: + forall (u : 'a -> poly) (v : 'b -> poly) (J : ('a * 'b) list), + uniq J => + big predT (fun (ij : 'a * 'b) => u ij.`1 &* v ij.`2) J = + big predT (fun (i : 'a) => u i &* big predT v (unzip2 (filter (fun (ij : 'a * 'b) => ij.`1 = i) J))) + (undup (unzip1 J)). + end BAdd. + + theory BMul. + op big ['a] (P : 'a -> bool) (F : 'a -> poly) (r : 'a list) : poly = + foldr ( &* ) Rq.one (map F (filter P r)). + + abbrev bigi (P : int -> bool) (F : int -> poly) (i j : int) : poly = big P F (range i j). + + lemma big_nil ['a]: forall (P : 'a -> bool) (F : 'a -> poly), big P F [] = Rq.one. + + lemma big_cons ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (x : 'a) (s : 'a list), + big P F (x :: s) = if P x then F x &* big P F s else big P F s. + + lemma big_consT ['a]: + forall (F : 'a -> poly) (x : 'a) (s : 'a list), big predT F (x :: s) = F x &* big predT F s. + + lemma nosmt big_rec ['a]: + forall (K : poly -> bool) (r : 'a list) (P : 'a -> bool) (F : + 'a -> poly), K Rq.one => (forall (i : 'a) (x : poly), P i => K x => K (F i &* x)) => K (big P F r). + + lemma nosmt big_ind ['a]: + forall (K : poly -> bool) (r : 'a list) (P : 'a -> bool) (F : + 'a -> poly), + (forall (x y : poly), K x => K y => K (x &* y)) => + K Rq.one => (forall (i : 'a), P i => K (F i)) => K (big P F r). + + lemma nosmt big_rec2 ['a]: + forall (K : poly -> poly -> bool) (r : 'a list) (P : 'a -> bool) (F1 F2 : + 'a -> poly), + K Rq.one Rq.one => + (forall (i : 'a) (y1 y2 : poly), P i => K y1 y2 => K (F1 i &* y1) (F2 i &* y2)) => + K (big P F1 r) (big P F2 r). + + lemma nosmt big_ind2 ['a]: + forall (K : poly -> poly -> bool) (r : 'a list) (P : 'a -> bool) (F1 F2 : + 'a -> poly), + (forall (x1 x2 y1 y2 : poly), K x1 x2 => K y1 y2 => K (x1 &* y1) (x2 &* y2)) => + K Rq.one Rq.one => (forall (i : 'a), P i => K (F1 i) (F2 i)) => K (big P F1 r) (big P F2 r). + + lemma nosmt big_endo ['a]: + forall (f : poly -> poly), + f Rq.one = Rq.one => + (forall (x y : poly), f (x &* y) = f x &* f y) => + forall (r : 'a list) (P : 'a -> bool) (F : 'a -> poly), f (big P F r) = big P (f \o F) r. + + lemma nosmt big_map ['a, 'b]: + forall (h : 'b -> 'a) (P : 'a -> bool) (F : 'a -> poly) (s : 'b list), + big P F (map h s) = big (P \o h) (F \o h) s. + + lemma nosmt big_mapT ['a, 'b]: + forall (h : 'b -> 'a) (F : 'a -> poly) (s : 'b list), big predT F (map h s) = big predT (F \o h) s. + + lemma nosmt big_comp ['a]: + forall (h : poly -> poly) (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + h Rq.one = Rq.one => morphism_2 h ( &* ) ( &* ) => h (big P F s) = big P (h \o F) s. + + lemma nosmt big_nth ['a]: + forall (x0 : 'a) (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = bigi (P \o nth x0 s) (F \o nth x0 s) 0 (size s). + + lemma nosmt big_const ['a]: + forall (P : 'a -> bool) (x : poly) (s : 'a list), + big P (fun (_ : 'a) => x) s = iter (count P s) (( &* ) x) Rq.one. + + lemma nosmt big_seq1 ['a]: forall (F : 'a -> poly) (x : 'a), big predT F [x] = F x. + + lemma nosmt big_mkcond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = big predT (fun (i : 'a) => if P i then F i else Rq.one) s. + + lemma nosmt big_filter ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), big predT F (filter P s) = big P F s. + + lemma big_filter_cond ['a]: + forall (P1 P2 : 'a -> bool) (F : 'a -> poly) (s : 'a list), big P2 F (filter P1 s) = big (predI P1 P2) F s. + + lemma nosmt eq_bigl ['a]: + forall (P1 P2 : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P1 i <=> P2 i) => big P1 F s = big P2 F s. + + lemma nosmt eq_bigr ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i => F1 i = F2 i) => big P F1 s = big P F2 s. + + lemma nosmt big_distrl ['a]: + forall (op_ : poly -> poly -> poly) (P : 'a -> bool) (F : + 'a -> poly) (s : 'a list) (t : poly), + left_zero Rq.one op_ => + left_distributive op_ ( &* ) => op_ (big P F s) t = big P (fun (a : 'a) => op_ (F a) t) s. + + lemma nosmt big_distrr ['a]: + forall (op_ : poly -> poly -> poly) (P : 'a -> bool) (F : + 'a -> poly) (s : 'a list) (t : poly), + right_zero Rq.one op_ => + right_distributive op_ ( &* ) => op_ t (big P F s) = big P (fun (a : 'a) => op_ t (F a)) s. + + lemma nosmt big_distr ['a, 'b]: + forall (op_ : poly -> poly -> poly) (P1 : 'a -> bool) (P2 : + 'b -> bool) (F1 : 'a -> poly) (s1 : 'a list) (F2 : 'b -> poly) (s2 : 'b list), + commutative op_ => + left_zero Rq.one op_ => + left_distributive op_ ( &* ) => + op_ (big P1 F1 s1) (big P2 F2 s2) = + big P1 (fun (a1 : 'a) => big P2 (fun (a2 : 'b) => op_ (F1 a1) (F2 a2)) s2) s1. + + lemma big_andbC ['a]: + forall (P Q : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big (fun (x : 'a) => P x /\ Q x) F s = big (fun (x : 'a) => Q x /\ P x) F s. + + lemma nosmt eq_big ['a]: + forall (P1 P2 : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + (forall (i : 'a), P1 i <=> P2 i) => (forall (i : 'a), P1 i => F1 i = F2 i) => big P1 F1 s = big P2 F2 s. + + lemma nosmt congr_big ['a]: + forall (r1 r2 : 'a list) (P1 P2 : 'a -> bool) (F1 F2 : 'a -> poly), + r1 = r2 => + (forall (x : 'a), P1 x <=> P2 x) => (forall (i : 'a), P1 i => F1 i = F2 i) => big P1 F1 r1 = big P2 F2 r2. + + lemma big_hasC ['a]: forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), ! has P s => big P F s = Rq.one. + + lemma big_pred0_eq ['a]: forall (F : 'a -> poly) (s : 'a list), big pred0 F s = Rq.one. + + lemma big_pred0 ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i <=> false) => big P F s = Rq.one. + + lemma big_cat ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), big P F (s1 ++ s2) = big P F s1 &* big P F s2. + + lemma nosmt big_catl ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), ! has P s2 => big P F (s1 ++ s2) = big P F s1. + + lemma nosmt big_catr ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), ! has P s1 => big P F (s1 ++ s2) = big P F s2. + + lemma nosmt big_rcons ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + big P F (rcons s x) = if P x then big P F s &* F x else big P F s. + + lemma nosmt eq_big_perm ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), perm_eq s1 s2 => big P F s1 = big P F s2. + + lemma nosmt eq_big_perm_map ['a]: + forall (F : 'a -> poly) (s1 s2 : 'a list), perm_eq (map F s1) (map F s2) => big predT F s1 = big predT F s2. + + lemma nosmt big_seq_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = big (fun (i : 'a) => (i \in s) /\ P i) F s. + + lemma nosmt big_seq ['a]: + forall (F : 'a -> poly) (s : 'a list), big predT F s = big (fun (i : 'a) => i \in s) F s. + + lemma nosmt big_rem ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + x \in s => big P F s = (if P x then F x else Rq.one) &* big P F (rem x s). + + lemma nosmt bigD1 ['a]: + forall (F : 'a -> poly) (s : 'a list) (x : 'a), + x \in s => uniq s => big predT F s = F x &* big (predC1 x) F s. + + lemma nosmt bigD1_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + P x => x \in s => uniq s => big P F s = F x &* big (predI P (predC1 x)) F s. + + lemma nosmt bigD1_cond_if ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + uniq s => big P F s = (if (x \in s) /\ P x then F x else Rq.one) &* big (predI P (predC1 x)) F s. + + lemma big_split ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + big P (fun (i : 'a) => F1 i &* F2 i) s = big P F1 s &* big P F2 s. + + lemma nosmt bigID ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (a : 'a -> bool) (s : 'a list), + big P F s = big (predI P a) F s &* big (predI P (predC a)) F s. + + lemma bigU ['a]: + forall (P Q : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (x : 'a), ! (P x /\ Q x)) => big (predU P Q) F s = big P F s &* big Q F s. + + lemma bigEM ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), big predT F s = big P F s &* big (predC P) F s. + + lemma nosmt big_reindex ['a, 'b]: + forall (P : 'a -> bool) (F : 'a -> poly) (f : 'b -> 'a) (f' : + 'a -> 'b) (s : 'a list), + (forall (x : 'a), x \in s => f (f' x) = x) => big P F s = big (P \o f) (F \o f) (map f' s). + + lemma nosmt big_pair_pswap ['a, 'b]: + forall (p : 'a * 'b -> bool) (f : 'a * 'b -> poly) (s : ('a * 'b) list), + big p f s = big (p \o pswap) (f \o pswap) (map pswap s). + + lemma nosmt eq_big_seq ['a]: + forall (F1 F2 : 'a -> poly) (s : 'a list), + (forall (x : 'a), x \in s => F1 x = F2 x) => big predT F1 s = big predT F2 s. + + lemma nosmt congr_big_seq ['a]: + forall (P1 P2 : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + (forall (x : 'a), x \in s => P1 x = P2 x) => + (forall (x : 'a), x \in s => P1 x => P2 x => F1 x = F2 x) => big P1 F1 s = big P2 F2 s. + + lemma big1_eq ['a]: forall (P : 'a -> bool) (s : 'a list), big P (fun (_ : 'a) => Rq.one) s = Rq.one. + + lemma big1 ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i => F i = Rq.one) => big P F s = Rq.one. + + lemma big1_seq ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i /\ (i \in s) => F i = Rq.one) => big P F s = Rq.one. + + lemma big_eq_idm_filter ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (x : 'a), ! P x => F x = Rq.one) => big predT F s = big P F s. + + lemma big_flatten ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (rr : 'a list list), + big P F (flatten rr) = big predT (fun (s : 'a list) => big P F s) rr. + + lemma nosmt big_pair ['a, 'b]: + forall (F : 'a * 'b -> poly) (s : ('a * 'b) list), + uniq s => + big predT F s = + big predT (fun (a : 'a) => big predT F (filter (fun (xy : 'a * 'b) => xy.`1 = a) s)) (undup (unzip1 s)). + + lemma big_nseq_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (n : int) (x : 'a), + big P F (nseq n x) = if P x then iter n (( &* ) (F x)) Rq.one else Rq.one. + + lemma big_nseq ['a]: + forall (F : 'a -> poly) (n : int) (x : 'a), big predT F (nseq n x) = iter n (( &* ) (F x)) Rq.one. + + lemma big_undup ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = big P (fun (a : 'a) => iter (count (pred1 a) s) (( &* ) (F a)) Rq.one) (undup s). + + lemma nosmt exchange_big ['a, 'b]: + forall (P1 : 'a -> bool) (P2 : 'b -> bool) (F : 'a -> 'b -> poly) (s1 : 'a list) (s2 : 'b list), + big P1 (fun (a : 'a) => big P2 (F a) s2) s1 = + big P2 (fun (b : 'b) => big P1 (fun (a : 'a) => F a b) s1) s2. + + lemma partition_big ['a, 'b]: + forall (px : 'a -> 'b) (P : 'a -> bool) (Q : 'b -> bool) (F : + 'a -> poly) (s : 'a list) (s' : 'b list), + uniq s' => + (forall (x : 'a), x \in s => P x => (px x \in s') /\ Q (px x)) => + big P F s = big Q (fun (x : 'b) => big (fun (y : 'a) => P y /\ px y = x) F s) s'. + + lemma nosmt big_allpairs ['a, 'b, 'c]: + forall (f : 'a -> 'b -> 'c) (F : 'c -> poly) (s : 'a list) (t : 'b list), + big predT F (allpairs f s t) = big predT (fun (x : 'a) => big predT (fun (y : 'b) => F (f x y)) t) s. + + lemma nosmt big_int_cond: + forall (m n : int) (P : int -> bool) (F : int -> poly), + bigi P F m n = bigi (fun (i : int) => (m <= i && i < n) /\ P i) F m n. + + lemma nosmt big_int: + forall (m n : int) (F : int -> poly), bigi predT F m n = bigi (fun (i : int) => m <= i && i < n) F m n. + + lemma nosmt congr_big_int: + forall (m1 n1 m2 n2 : int) (P1 P2 : int -> bool) (F1 F2 : + int -> poly), + m1 = m2 => + n1 = n2 => + (forall (i : int), m1 <= i && i < n2 => P1 i = P2 i) => + (forall (i : int), P1 i /\ m1 <= i && i < n2 => F1 i = F2 i) => bigi P1 F1 m1 n1 = bigi P2 F2 m2 n2. + + lemma nosmt eq_big_int: + forall (m n : int) (F1 F2 : int -> poly), + (forall (i : int), m <= i && i < n => F1 i = F2 i) => bigi predT F1 m n = bigi predT F2 m n. + + lemma nosmt big_ltn_cond: + forall (m n : int) (P : int -> bool) (F : int -> poly), + m < n => let x = bigi P F (m + 1) n in bigi P F m n = if P m then F m &* x else x. + + lemma nosmt big_ltn: + forall (m n : int) (F : int -> poly), m < n => bigi predT F m n = F m &* bigi predT F (m + 1) n. + + lemma nosmt big_geq: forall (m n : int) (P : int -> bool) (F : int -> poly), n <= m => bigi P F m n = Rq.one. + + lemma nosmt big_addn: + forall (m n a : int) (P : int -> bool) (F : int -> poly), + bigi P F (m + a) n = bigi (fun (i : int) => P (i + a)) (fun (i : int) => F (i + a)) m (n - a). + + lemma nosmt big_int1: forall (n : int) (F : int -> poly), bigi predT F n (n + 1) = F n. + + lemma nosmt big_cat_int: + forall (n m p : int) (P : int -> bool) (F : int -> poly), + m <= n => n <= p => bigi P F m p = bigi P F m n &* bigi P F n p. + + lemma nosmt big_int_recl: + forall (n m : int) (F : int -> poly), + m <= n => bigi predT F m (n + 1) = F m &* bigi predT (fun (i : int) => F (i + 1)) m n. + + lemma nosmt big_int_recr: + forall (n m : int) (F : int -> poly), m <= n => bigi predT F m (n + 1) = bigi predT F m n &* F n. + + lemma nosmt big_int_recl_cond: + forall (n m : int) (P : int -> bool) (F : int -> poly), + m <= n => + bigi P F m (n + 1) = + (if P m then F m else Rq.one) &* bigi (fun (i : int) => P (i + 1)) (fun (i : int) => F (i + 1)) m n. + + lemma nosmt big_int_recr_cond: + forall (n m : int) (P : int -> bool) (F : int -> poly), + m <= n => bigi P F m (n + 1) = bigi P F m n &* if P n then F n else Rq.one. + + lemma nosmt bigi_split_odd_even: + forall (n : int) (F : int -> poly), + 0 <= n => bigi predT (fun (i : int) => F (2 * i) &* F (2 * i + 1)) 0 n = bigi predT F 0 (2 * n). + end BMul. + + lemma mulr_big ['a]: + forall (P Q : 'a -> bool) (f g : 'a -> poly) (r s : 'a list), + (big P f r)%BAdd &* (big Q g s)%BAdd = + (big P (fun (x : 'a) => (big Q (fun (y : 'a) => f x &* g y) s)%BAdd) r)%BAdd. + + lemma subrXX: + forall (x y : poly) (n : int), + 0 <= n => + (exp x n - exp y n)%ZR = + (x - y)%ZR &* (bigi predT (fun (i : int) => (exp x (n - 1 - i))%ZR &* (exp y i)%ZR) 0 n)%BAdd. + + lemma nosmt mulr_const_cond ['a]: + forall (p : 'a -> bool) (s : 'a list) (c : poly), (big p (fun (_ : 'a) => c) s)%BMul = (exp c (count p s))%ZR. + + lemma nosmt mulr_const ['a]: + forall (s : 'a list) (c : poly), (big predT (fun (_ : 'a) => c) s)%BMul = (exp c (size s))%ZR. + end Big. + + lemma ge0_size: 0 <= kvec. + + hint solve 0 : ge0_size. + + theory Vector. + lemma tofunv_prevector: forall (v : polyvec), prevector (tofunv v). + + lemma tofunvK: cancel tofunv offunv. + + lemma offunvK: forall (v : int -> poly), tofunv (offunv v) = vclamp v. + + op "_.[_]" (v : polyvec) (i : int) : poly = tofunv v i. + + lemma offunvE: forall (v : int -> poly) (i : int), 0 <= i && i < kvec => ((offunv v).[i])%Vector = v i. + + lemma getv_out: forall (v : polyvec) (i : int), ! (0 <= i && i < kvec) => (v.[i])%Vector = Rq.zero. + + lemma eq_vectorP: + forall (v1 v2 : polyvec), + v1 = v2 <=> forall (i : int), 0 <= i && i < kvec => (v1.[i])%Vector = (v2.[i])%Vector. + + lemma vectorW: + forall (P : polyvec -> bool), + (forall (f : int -> poly), prevector f => P (offunv f)) => forall (v : polyvec), P v. + + op vectc (c : poly) : polyvec = offunv (fun (_ : int) => c). + + abbrev zerov : polyvec = (vectc Rq.zero)%Vector. + + lemma offunCE: forall (c : poly) (i : int), 0 <= i && i < kvec => ((vectc c).[i])%Vector = c. + + lemma offun0E: forall (i : int), (Vector.zerov.[i])%Vector = Rq.zero. + + hint simplify. + + op [-] (v : polyvec) : polyvec = offunv (fun (i : int) => (&-) (v.[i])%Vector). + + lemma offunD: + forall (v1 v2 : polyvec) (i : int), + ((v1 + v2)%KMatrix.Vector.[i])%Vector = (v1.[i])%Vector &+ (v2.[i])%Vector. + + hint simplify. + + lemma offunN: forall (v : polyvec) (i : int), ((-v).[i])%Vector = (&-) (v.[i])%Vector. + + hint simplify. + + theory ZModule. + lemma nosmt addrA: associative KMatrix.Vector.(+). + + lemma nosmt addrC: commutative KMatrix.Vector.(+). + + lemma nosmt add0r: left_id Vector.zerov KMatrix.Vector.(+). + + lemma nosmt addNr: left_inverse Vector.zerov Vector.[-] KMatrix.Vector.(+). + + theory AddMonoid. + lemma addmA: associative KMatrix.Vector.(+). + + lemma addmC: commutative KMatrix.Vector.(+). + + lemma add0m: left_id Vector.zerov KMatrix.Vector.(+). + + lemma addm0: right_id Vector.zerov KMatrix.Vector.(+). + + lemma addmCA: left_commutative KMatrix.Vector.(+). + + lemma addmAC: right_commutative KMatrix.Vector.(+). + + lemma addmACA: interchange KMatrix.Vector.(+) KMatrix.Vector.(+). + + lemma iteropE: + forall (n : int) (x : polyvec), + iterop n KMatrix.Vector.(+) x Vector.zerov = iter n (((+) x))%KMatrix.Vector Vector.zerov. + end AddMonoid. + + abbrev (-) (x y : polyvec) : polyvec = (x + (-y)%Vector)%KMatrix.Vector. + + lemma nosmt addr0: right_id Vector.zerov KMatrix.Vector.(+). + + lemma nosmt addrN: right_inverse Vector.zerov Vector.[-] KMatrix.Vector.(+). + + lemma nosmt addrCA: left_commutative KMatrix.Vector.(+). + + lemma nosmt addrAC: right_commutative KMatrix.Vector.(+). + + lemma nosmt addrACA: interchange KMatrix.Vector.(+) KMatrix.Vector.(+). + + lemma nosmt subrr: forall (x : polyvec), x - x = Vector.zerov. + + lemma nosmt addKr: left_loop Vector.[-] KMatrix.Vector.(+). + + lemma nosmt addNKr: rev_left_loop Vector.[-] KMatrix.Vector.(+). + + lemma nosmt addrK: right_loop Vector.[-] KMatrix.Vector.(+). + + lemma nosmt addrNK: rev_right_loop Vector.[-] KMatrix.Vector.(+). + + lemma nosmt subrK: forall (x y : polyvec), (x - y + y)%KMatrix.Vector = x. + + lemma nosmt addrI: right_injective KMatrix.Vector.(+). + + lemma nosmt addIr: left_injective KMatrix.Vector.(+). + + lemma nosmt opprK: involutive Vector.[-]. + + lemma oppr_inj: injective Vector.[-]. + + lemma nosmt oppr0: (- Vector.zerov)%Vector = Vector.zerov. + + lemma oppr_eq0: forall (x : polyvec), (-x)%Vector = Vector.zerov <=> x = Vector.zerov. + + lemma nosmt subr0: forall (x : polyvec), x - Vector.zerov = x. + + lemma nosmt sub0r: forall (x : polyvec), Vector.zerov - x = (-x)%Vector. + + lemma nosmt opprD: forall (x y : polyvec), (- (x + y)%KMatrix.Vector)%Vector = (-x)%Vector - y. + + lemma nosmt opprB: forall (x y : polyvec), (- (x - y))%Vector = y - x. + + lemma nosmt subrACA: interchange (fun (x y : polyvec) => x - y) KMatrix.Vector.(+). + + lemma nosmt subr_eq: forall (x y z : polyvec), x - z = y <=> x = (y + z)%KMatrix.Vector. + + lemma nosmt subr_eq0: forall (x y : polyvec), x - y = Vector.zerov <=> x = y. + + lemma nosmt addr_eq0: forall (x y : polyvec), (x + y)%KMatrix.Vector = Vector.zerov <=> x = (-y)%Vector. + + lemma nosmt eqr_opp: forall (x y : polyvec), (-x)%Vector = (-y)%Vector <=> x = y. + + lemma eqr_oppLR: forall (x y : polyvec), (-x)%Vector = y <=> x = (-y)%Vector. + + lemma nosmt eqr_sub: + forall (x y z t : polyvec), x - y = z - t <=> (x + t)%KMatrix.Vector = (z + y)%KMatrix.Vector. + + lemma subr_add2r: forall (z x y : polyvec), (x + z)%KMatrix.Vector - (y + z)%KMatrix.Vector = x - y. + + op intmul (x : polyvec) (n : int) : polyvec = + if n < 0 then (- iterop (-n) KMatrix.Vector.(+) x Vector.zerov)%Vector + else iterop n KMatrix.Vector.(+) x Vector.zerov. + + lemma intmulpE: + forall (z : polyvec) (c : int), 0 <= c => intmul z c = iterop c KMatrix.Vector.(+) z Vector.zerov. + + lemma mulr0z: forall (x : polyvec), intmul x 0 = Vector.zerov. + + lemma mulr1z: forall (x : polyvec), intmul x 1 = x. + + lemma mulr2z: forall (x : polyvec), intmul x 2 = (x + x)%KMatrix.Vector. + + lemma mulrNz: forall (x : polyvec) (n : int), intmul x (-n) = (- intmul x n)%Vector. + + lemma mulrS: forall (x : polyvec) (n : int), 0 <= n => intmul x (n + 1) = (x + intmul x n)%KMatrix.Vector. + + lemma mulNrz: forall (x : polyvec) (n : int), intmul (-x)%Vector n = (- intmul x n)%Vector. + + lemma mulNrNz: forall (x : polyvec) (n : int), intmul (-x)%Vector (-n) = intmul x n. + + lemma mulrSz: forall (x : polyvec) (n : int), intmul x (n + 1) = (x + intmul x n)%KMatrix.Vector. + + lemma mulrDz: forall (x : polyvec) (n m : int), intmul x (n + m) = (intmul x n + intmul x m)%KMatrix.Vector. + end ZModule. + + lemma offunB: + forall (v1 v2 : polyvec) (i : int), ((v1 - v2)%ZModule.[i])%Vector = ((v1.[i])%Vector - (v2.[i])%Vector)%ZR. + + lemma dotpC: commutative dotp. + + lemma dotpDr: forall (v1 v2 v3 : polyvec), dotp v1 (v2 + v3)%KMatrix.Vector = dotp v1 v2 &+ dotp v1 v3. + + lemma dotpDl: forall (v1 v2 v3 : polyvec), dotp (v1 + v2)%KMatrix.Vector v3 = dotp v1 v3 &+ dotp v2 v3. + + op scalev (a : poly) (v : polyvec) : polyvec = offunv (fun (i : int) => a &* (v.[i])%Vector). + + abbrev ( ** ) : poly -> polyvec -> polyvec = Vector.scalev. + + lemma scalevE: forall (a : poly) (v : polyvec) (i : int), ((a ** v).[i])%Vector = a &* (v.[i])%Vector. + + lemma scalevDl: + forall (a b : poly) (v : polyvec), (a &+ b ** v)%Vector = ((a ** v)%Vector + (b ** v)%Vector)%KMatrix.Vector. + + lemma scalevDr: + forall (a : poly) (v w : polyvec), + (a ** (v + w)%KMatrix.Vector)%Vector = ((a ** v)%Vector + (a ** w)%Vector)%KMatrix.Vector. + + lemma scalevA: forall (a b : poly) (v : polyvec), (a &* b ** v)%Vector = (a ** (b ** v))%Vector. + + lemma scalevAC: forall (a b : poly) (v : polyvec), (a ** (b ** v))%Vector = (b ** (a ** v))%Vector. + end Vector. + + export Vector. + + theory Matrix. + abbrev mrange (i j : int) : bool = (mrange i j)%KMatrix.Matrix. + + lemma nosmt mrangeL: forall (i j : int), (mrange i j)%Matrix => 0 <= i && i < kvec. + + lemma nosmt mrangeR: forall (i j : int), (mrange i j)%Matrix => 0 <= j && j < kvec. + + lemma nosmt mrangeC: forall (i j : int), (mrange i j)%Matrix = (mrange j i)%Matrix. + + lemma tofunm_prematrix: forall (m : polymat), prematrix (tofunm m). + + lemma tofunmK: cancel tofunm offunm. + + lemma offunmK: forall (m : int -> int -> poly), tofunm (offunm m) = mclamp m. + + op "_.[_]" (m : polymat) (ij : int * int) : poly = tofunm m ij.`1 ij.`2. + + lemma offunmE: + forall (m : int -> int -> poly) (i j : int), (mrange i j)%Matrix => ((offunm m).[i, j])%Matrix = m i j. + + lemma getm_out: forall (m : polymat) (i j : int), ! (mrange i j)%Matrix => (m.[i, j])%Matrix = Rq.zero. + + lemma getm_outL: forall (m : polymat) (i j : int), ! (0 <= i && i < kvec) => (m.[i, j])%Matrix = Rq.zero. + + lemma getm_outR: forall (m : polymat) (i j : int), ! (0 <= j && j < kvec) => (m.[i, j])%Matrix = Rq.zero. + + lemma eq_matrixP: + forall (m1 m2 : polymat), + m1 = m2 <=> forall (i j : int), (mrange i j)%Matrix => (m1.[i, j])%Matrix = (m2.[i, j])%Matrix. + + lemma matrixW: + forall (P : polymat -> bool), + (forall (f : int -> int -> poly), prematrix f => P (offunm f)) => forall (v : polymat), P v. + + op matrixc (c : poly) : polymat = offunm (fun (_ _ : int) => c). + + op diagmx (v : polyvec) : polymat = offunm (fun (i j : int) => if i = j then (v.[i])%Vector else Rq.zero). + + abbrev diagc (c : poly) : polymat = (diagmx ((vectc c))%Vector)%Matrix. + + abbrev zerom : polymat = (matrixc Rq.zero)%Matrix. + + abbrev onem : polymat = (diagc Rq.one)%Matrix. + + lemma offunCE: forall (c : poly) (i j : int), (mrange i j)%Matrix => ((matrixc c).[i, j])%Matrix = c. + + lemma diagmxE: + forall (v : polyvec) (i j : int), ((diagmx v).[i, j])%Matrix = if i = j then (v.[i])%Vector else Rq.zero. + + lemma offun0E: forall (i j : int), (Matrix.zerom.[i, j])%Matrix = Rq.zero. + + lemma offun1E: + forall (i j : int), (mrange i j)%Matrix => (Matrix.onem.[i, j])%Matrix = if i = j then Rq.one else Rq.zero. + + lemma offun1_neqE: forall (i j : int), i <> j => (Matrix.onem.[i, j])%Matrix = Rq.zero. + + hint simplify. + + op (+) (m1 m2 : polymat) : polymat = offunm (fun (i j : int) => (m1.[i, j])%Matrix &+ (m2.[i, j])%Matrix). + + op [-] (m : polymat) : polymat = offunm (fun (i j : int) => (&-) (m.[i, j])%Matrix). + + lemma offunD: + forall (m1 m2 : polymat) (i j : int), ((m1 + m2).[i, j])%Matrix = (m1.[i, j])%Matrix &+ (m2.[i, j])%Matrix. + + hint simplify. + + lemma offunN: forall (m : polymat) (i j : int), ((-m).[i, j])%Matrix = (&-) (m.[i, j])%Matrix. + + hint simplify. + + theory ZModule. + lemma nosmt addrA: associative Matrix.(+). + + lemma nosmt addrC: commutative Matrix.(+). + + lemma nosmt add0r: left_id Matrix.zerom Matrix.(+). + + lemma nosmt addNr: left_inverse Matrix.zerom Matrix.[-] Matrix.(+). + + theory AddMonoid. + lemma addmA: associative Matrix.(+). + + lemma addmC: commutative Matrix.(+). + + lemma add0m: left_id Matrix.zerom Matrix.(+). + + lemma addm0: right_id Matrix.zerom Matrix.(+). + + lemma addmCA: left_commutative Matrix.(+). + + lemma addmAC: right_commutative Matrix.(+). + + lemma addmACA: interchange Matrix.(+) Matrix.(+). + + lemma iteropE: + forall (n : int) (x : polymat), iterop n Matrix.(+) x Matrix.zerom = iter n (((+) x))%Matrix Matrix.zerom. + end AddMonoid. + + abbrev (-) (x y : polymat) : polymat = (x + -y)%Matrix. + + lemma nosmt addr0: right_id Matrix.zerom Matrix.(+). + + lemma nosmt addrN: right_inverse Matrix.zerom Matrix.[-] Matrix.(+). + + lemma nosmt addrCA: left_commutative Matrix.(+). + + lemma nosmt addrAC: right_commutative Matrix.(+). + + lemma nosmt addrACA: interchange Matrix.(+) Matrix.(+). + + lemma nosmt subrr: forall (x : polymat), x - x = Matrix.zerom. + + lemma nosmt addKr: left_loop Matrix.[-] Matrix.(+). + + lemma nosmt addNKr: rev_left_loop Matrix.[-] Matrix.(+). + + lemma nosmt addrK: right_loop Matrix.[-] Matrix.(+). + + lemma nosmt addrNK: rev_right_loop Matrix.[-] Matrix.(+). + + lemma nosmt subrK: forall (x y : polymat), (x - y + y)%Matrix = x. + + lemma nosmt addrI: right_injective Matrix.(+). + + lemma nosmt addIr: left_injective Matrix.(+). + + lemma nosmt opprK: involutive Matrix.[-]. + + lemma oppr_inj: injective Matrix.[-]. + + lemma nosmt oppr0: (- Matrix.zerom)%Matrix = Matrix.zerom. + + lemma oppr_eq0: forall (x : polymat), (-x)%Matrix = Matrix.zerom <=> x = Matrix.zerom. + + lemma nosmt subr0: forall (x : polymat), x - Matrix.zerom = x. + + lemma nosmt sub0r: forall (x : polymat), Matrix.zerom - x = (-x)%Matrix. + + lemma nosmt opprD: forall (x y : polymat), (- (x + y))%Matrix = (-x)%Matrix - y. + + lemma nosmt opprB: forall (x y : polymat), (- (x - y))%Matrix = y - x. + + lemma nosmt subrACA: interchange (fun (x y : polymat) => x - y) Matrix.(+). + + lemma nosmt subr_eq: forall (x y z : polymat), x - z = y <=> x = (y + z)%Matrix. + + lemma nosmt subr_eq0: forall (x y : polymat), x - y = Matrix.zerom <=> x = y. + + lemma nosmt addr_eq0: forall (x y : polymat), (x + y)%Matrix = Matrix.zerom <=> x = (-y)%Matrix. + + lemma nosmt eqr_opp: forall (x y : polymat), (-x)%Matrix = (-y)%Matrix <=> x = y. + + lemma eqr_oppLR: forall (x y : polymat), (-x)%Matrix = y <=> x = (-y)%Matrix. + + lemma nosmt eqr_sub: forall (x y z t : polymat), x - y = z - t <=> (x + t)%Matrix = (z + y)%Matrix. + + lemma subr_add2r: forall (z x y : polymat), (x + z)%Matrix - (y + z)%Matrix = x - y. + + op intmul (x : polymat) (n : int) : polymat = + if n < 0 then (- iterop (-n) Matrix.(+) x Matrix.zerom)%Matrix else iterop n Matrix.(+) x Matrix.zerom. + + lemma intmulpE: forall (z : polymat) (c : int), 0 <= c => intmul z c = iterop c Matrix.(+) z Matrix.zerom. + + lemma mulr0z: forall (x : polymat), intmul x 0 = Matrix.zerom. + + lemma mulr1z: forall (x : polymat), intmul x 1 = x. + + lemma mulr2z: forall (x : polymat), intmul x 2 = (x + x)%Matrix. + + lemma mulrNz: forall (x : polymat) (n : int), intmul x (-n) = (- intmul x n)%Matrix. + + lemma mulrS: forall (x : polymat) (n : int), 0 <= n => intmul x (n + 1) = (x + intmul x n)%Matrix. + + lemma mulNrz: forall (x : polymat) (n : int), intmul (-x)%Matrix n = (- intmul x n)%Matrix. + + lemma mulNrNz: forall (x : polymat) (n : int), intmul (-x)%Matrix (-n) = intmul x n. + + lemma mulrSz: forall (x : polymat) (n : int), intmul x (n + 1) = (x + intmul x n)%Matrix. + + lemma mulrDz: forall (x : polymat) (n m : int), intmul x (n + m) = (intmul x n + intmul x m)%Matrix. + end ZModule. + + lemma offunB: + forall (m1 m2 : polymat) (i j : int), + ((m1 - m2)%ZModule.[i, j])%Matrix = ((m1.[i, j])%Matrix - (m2.[i, j])%Matrix)%ZR. + + op trace (m : polymat) : poly = (bigi predT (fun (i : int) => (m.[i, i])%Matrix) 0 kvec)%Big.BAdd. + + op trmx (m : polymat) : polymat = offunm (fun (i j : int) => (m.[j, i])%Matrix). + + lemma trmxE: forall (m : polymat) (i j : int), ((trmx m).[i, j])%Matrix = (m.[j, i])%Matrix. + + lemma trmxK: forall (m : polymat), (trmx ((trmx m))%Matrix)%Matrix = m. + + lemma trmx1: (trmx Matrix.onem)%Matrix = Matrix.onem. + + lemma trmxD: forall (m1 m2 : polymat), (trmx (m1 + m2)%Matrix)%Matrix = (trmx m1 + trmx m2)%Matrix. + + lemma trace_trmx: forall (m : polymat), (trace ((trmx m))%Matrix)%Matrix = (trace m)%Matrix. + + op ( * ) (m1 m2 : polymat) : polymat = + offunm + (fun (i j : int) => + (bigi predT (fun (k : int) => (m1.[i, k])%Matrix &* (m2.[k, j])%Matrix) 0 kvec)%Big.BAdd). + + lemma offunM: + forall (m1 m2 : polymat) (i j : int), + ((m1 * m2).[i, j])%Matrix = + (bigi predT (fun (k : int) => (m1.[i, k])%Matrix &* (m2.[k, j])%Matrix) 0 kvec)%Big.BAdd. + + hint simplify. + + lemma mulmx1: right_id Matrix.onem Matrix.( * ). + + lemma mul1mx: left_id Matrix.onem Matrix.( * ). + + lemma mulmxDl: forall (m1 m2 m : polymat), ((m1 + m2) * m)%Matrix = (m1 * m + m2 * m)%Matrix. + + lemma mulmxDr: forall (m1 m2 m : polymat), (m * (m1 + m2))%Matrix = (m * m1 + m * m2)%Matrix. + + lemma mulmxA: associative Matrix.( * ). + + lemma trmxM: forall (m1 m2 : polymat), (trmx (m1 * m2)%Matrix)%Matrix = (trmx m2 * trmx m1)%Matrix. + + op ( *^ ) (m : polymat) (v : polyvec) : polyvec = + offunv (fun (i : int) => (bigi predT (fun (j : int) => (m.[i, j])%Matrix &* (v.[j])%Vector) 0 kvec)%Big.BAdd). + + op ( ^* ) (v : polyvec) (m : polymat) : polyvec = + offunv (fun (j : int) => (bigi predT (fun (i : int) => (v.[i])%Vector &* (m.[i, j])%Matrix) 0 kvec)%Big.BAdd). + + lemma mulmxTv: forall (m : polymat) (v : polyvec), (trmx m *^ v)%Matrix = (v ^* m)%Matrix. + + lemma mulmxv0: forall (m : polymat), (m *^ Vector.zerov)%Matrix = Vector.zerov. + + lemma mulmx1v: forall (v : polyvec), (Matrix.onem *^ v)%Matrix = v. + + lemma mulmxvDl: + forall (m1 m2 : polymat) (v : polyvec), + ((m1 + m2) *^ v)%Matrix = ((m1 *^ v)%Matrix + (m2 *^ v)%Matrix)%KMatrix.Vector. + + lemma mulmxvDr: + forall (m : polymat) (v1 v2 : polyvec), + (m *^ (v1 + v2)%KMatrix.Vector)%Matrix = ((m *^ v1)%Matrix + (m *^ v2)%Matrix)%KMatrix.Vector. + + lemma mulmxvA: forall (m1 m2 : polymat) (v : polyvec), (m1 * m2 *^ v)%Matrix = (m1 *^ (m2 *^ v))%Matrix. + + lemma mulvmxT: forall (v : polyvec) (m : polymat), (v ^* trmx m)%Matrix = (m *^ v)%Matrix. + + lemma mulv0mx: forall (m : polymat), (Vector.zerov ^* m)%Matrix = Vector.zerov. + + lemma mulvmx1: forall (v : polyvec), (v ^* Matrix.onem)%Matrix = v. + + lemma mulvmxDr: + forall (v : polyvec) (m1 m2 : polymat), + (v ^* (m1 + m2))%Matrix = ((v ^* m1)%Matrix + (v ^* m2)%Matrix)%KMatrix.Vector. + + lemma mulvmxDl: + forall (v1 v2 : polyvec) (m : polymat), + ((v1 + v2)%KMatrix.Vector ^* m)%Matrix = ((v1 ^* m)%Matrix + (v2 ^* m)%Matrix)%KMatrix.Vector. + + lemma mulvmxA: forall (v : polyvec) (m1 m2 : polymat), (v ^* m1 ^* m2)%Matrix = (v ^* (m1 * m2))%Matrix. + + lemma mulmxvE: + forall (m : polymat) (v : polyvec) (i : int), + ((m *^ v)%Matrix.[i])%Vector = + (bigi predT (fun (j : int) => (m.[i, j])%Matrix &* (v.[j])%Vector) 0 kvec)%Big.BAdd. + + lemma mulvmxE: + forall (m : polymat) (v : polyvec) (j : int), + ((v ^* m)%Matrix.[j])%Vector = + (bigi predT (fun (i : int) => (v.[i])%Vector &* (m.[i, j])%Matrix) 0 kvec)%Big.BAdd. + + op colmx (v : polyvec) : polymat = offunm (fun (i _ : int) => (v.[i])%Vector). + + op rowmx (v : polyvec) : polymat = offunm (fun (_ j : int) => (v.[j])%Vector). + + lemma colmxT: forall (v : polyvec), (trmx ((colmx v))%Matrix)%Matrix = (rowmx v)%Matrix. + + lemma rowmxT: forall (v : polyvec), (trmx ((rowmx v))%Matrix)%Matrix = (colmx v)%Matrix. + + lemma colmxE: + forall (v : polyvec) (i j : int), 0 <= j && j < kvec => ((colmx v).[i, j])%Matrix = (v.[i])%Vector. + + lemma rowmxE: + forall (v : polyvec) (i j : int), 0 <= i && i < kvec => ((rowmx v).[i, j])%Matrix = (v.[j])%Vector. + + lemma colmx_mulmxv: forall (m : polymat) (v : polyvec), (colmx (m *^ v)%Matrix)%Matrix = (m * colmx v)%Matrix. + + lemma rowmx_mulvmx: forall (v : polyvec) (m : polymat), (rowmx (v ^* m)%Matrix)%Matrix = (rowmx v * m)%Matrix. + + lemma mulmx_diag: + forall (v1 v2 : polyvec), + (diagmx v1 * diagmx v2)%Matrix = + (diagmx (offunv (fun (i : int) => (v1.[i])%Vector &* (v2.[i])%Vector)))%Matrix. + + lemma dotp_tr: forall (v1 v2 : polyvec), dotp v1 v2 = (trace (diagmx v1 * diagmx v2)%Matrix)%Matrix. + + lemma dotp_mulmxv: forall (m : polymat) (v1 v2 : polyvec), dotp (m *^ v1)%Matrix v2 = dotp v1 (v2 ^* m)%Matrix. + + op dvector (d : poly distr) : polyvec distr = + dmap (djoin (nseq kvec d)) (fun (xs : poly list) => offunv (nth witness xs)). + + lemma dvector1E: + forall (d : poly distr) (v : polyvec), + mu1 ((dvector d))%Matrix v = + (bigi predT (fun (i : int) => mu1 d (v.[i])%Vector) 0 kvec)%StdBigop.Bigreal.BRM. + + lemma dvector_uni: forall (d : poly distr), is_uniform d => is_uniform ((dvector d))%Matrix. + + lemma dvector_ll: forall (d : poly distr), is_lossless d => is_lossless ((dvector d))%Matrix. + + lemma dvector_fu: forall (d : poly distr), is_full d => is_full ((dvector d))%Matrix. + + lemma dvector_funi: forall (d : poly distr), is_full d => is_uniform d => is_funiform ((dvector d))%Matrix. + + op dmatrix (d : poly distr) : polymat distr = + dmap (djoin (nseq kvec (djoin (nseq kvec d)))) + (fun (xs : poly list list) => offunm (fun (i j : int) => nth witness (nth witness xs j) i)). + + lemma dmatrix1E: + forall (d : poly distr) (m : polymat), + mu1 ((dmatrix d))%Matrix m = + (bigi predT + (fun (i : int) => (bigi predT (fun (j : int) => mu1 d (m.[i, j])%Matrix) 0 kvec)%StdBigop.Bigreal.BRM) 0 + kvec)%StdBigop.Bigreal.BRM. + + lemma dmatrix_dvector: + forall (d : poly distr), + (dmatrix d)%Matrix = + dmap (djoin (nseq kvec ((dvector d))%Matrix)) + (fun (vs : polyvec list) => offunm (fun (i j : int) => ((nth witness vs j).[i])%Vector)). + + lemma dmatrix_uni: forall (d : poly distr), is_uniform d => is_uniform ((dmatrix d))%Matrix. + + lemma dmatrix_ll: forall (d : poly distr), is_lossless d => is_lossless ((dmatrix d))%Matrix. + + lemma dmatrix_fu: forall (d : poly distr), is_full d => is_full ((dmatrix d))%Matrix. + + lemma dmatrix_funi: forall (d : poly distr), is_full d => is_uniform d => is_funiform ((dmatrix d))%Matrix. + end Matrix. + + export Matrix. + end Matrix_. + + instance ring with [()] poly + op rzero = Top.Rq.zero + op rone = Top.Rq.one + op add = Top.Rq.&+ + op opp = Top.Rq.&- + op mul = Top.Rq.&* + op expr = Top.MLWEPKEHash.MLWE_.Matrix_.ZR.exp + op ofint = Top.MLWEPKEHash.MLWE_.Matrix_.ZR.ofint + + instance poly with Top.Ring.ZModule.zmodule. + + instance poly with Top.Ring.ComRing.ring. + + instance poly with Top.Ring.IDomain.idomain. + + lemma duni_R_ll: is_lossless duni_R. + + hint solve 0 lossless : duni_R_ll. + + hint solve 0 random : duni_R_ll. + + lemma duni_R_uni: is_uniform duni_R. + + hint solve 0 random : duni_R_uni. + + lemma duni_R_fu: is_full duni_R. + + hint solve 0 random : duni_R_fu. + + lemma duni_R_funi: is_funiform duni_R. + + lemma dshort_R_ll: is_lossless dshort_R. + + hint solve 0 lossless : dshort_R_ll. + + hint solve 0 random : dshort_R_ll. + + op duni : polyvec distr = (dvector duni_R)%Matrix_.Matrix. + + op dshort : polyvec distr = (dvector dshort_R)%Matrix_.Matrix. + + lemma duni_ll: is_lossless duni. + + lemma duni_fu: is_full duni. + + lemma duni_uni: is_uniform duni. + + lemma duni_funi: is_funiform duni. + + lemma dshort_ll: is_lossless dshort. + + op duni_matrix : polymat distr = (dmatrix duni_R)%Matrix_.Matrix. + + lemma duni_matrix_ll: is_lossless duni_matrix. + + lemma duni_matrix_fu: is_full duni_matrix. + + lemma duni_matrix_uni: is_uniform duni_matrix. + + lemma duni_matrix_funi: is_funiform duni_matrix. + + module type Adv_T = { + proc guess(A : polymat, t : polyvec, uv : polyvec * poly) : bool {} + } + + abbrev m_transpose : polymat -> polymat = Matrix_.Matrix.trmx. + + abbrev (`<*>`) : polyvec -> polyvec -> poly = dotp. + + abbrev (&+) : poly -> poly -> poly = Rq.(&+). + + abbrev (&-) : poly -> poly -> poly = fun (x y : poly) => (x &+ ((&-) y)%Rq)%MLWE_. + + module MLWE(Adv : Adv_T) = { + proc main(b : bool) : bool = { + var s : polyvec; + var e : polyvec; + var _A : polymat; + var u0 : polyvec; + var u1 : polyvec; + var t : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + _A <$ duni_matrix; + s <$ dshort; + e <$ dshort; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + t <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ Adv.guess(_A, t, if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + lemma dseed_ll: is_lossless srand. + + hint solve 0 lossless : dseed_ll. + + hint solve 0 random : dseed_ll. + + module type HAdv_T = { + proc guess(sd : W8.t Array32.t, t : polyvec, uv : polyvec * poly) : bool {} + } + + module MLWE_H(Adv : HAdv_T) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var _A : polymat; + var u0 : polyvec; + var u1 : polyvec; + var t : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + sd <$ srand; + s <$ dshort; + e <$ dshort; + _A <- if tr then (trmx (H sd))%Matrix_.Matrix else H sd; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + t <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ Adv.guess(sd, t, if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + theory MLWE_ROM. + theory RO_H. + type in_t = W8.t Array32.t. + + type out_t = polymat. + + op dout (_ : W8.t Array32.t) : polymat distr = duni_matrix. + + type d_in_t = bool. + + type d_out_t = bool. + + module type RO = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + } + + module type RO_Distinguisher(G : RO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.rem, G.sample} + } + + module MainD(D : RO_Distinguisher, RO0 : RO) = { + proc distinguish(x : d_in_t) : d_out_t = { + var r : d_out_t; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + module type ROmap = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + + proc restrK() : (in_t, out_t) SmtMap.fmap {} + } + + module type FRO = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + + proc queried(x : in_t, f : PROM.flag) : bool {} + + proc allKnown() : (in_t, out_t) SmtMap.fmap {} + } + + module type FRO_Distinguisher(G : FRO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.rem, G.sample, G.queried, G.allKnown} + } + + abstract theory MkRO. + module RO = { + var m : (in_t, out_t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : in_t, y : out_t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : in_t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + RO.get(x); + } + + proc restrK() : (in_t, out_t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : in_t) : out_t = RO.get + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc rem(x : in_t) : unit = RO.rem + + proc sample(x : in_t) : unit = {} + }. + end MkRO. + + module RO = { + var m : (in_t, out_t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : in_t, y : out_t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : in_t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + RO.get(x); + } + + proc restrK() : (in_t, out_t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : in_t) : out_t = RO.get + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc rem(x : in_t) : unit = RO.rem + + proc sample(x : in_t) : unit = {} + }. + + module FRO = { + var m : (in_t, out_t * PROM.flag) SmtMap.fmap + + proc init() : unit = { + FRO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \in FRO.m)%SmtMap) + r <- (oget (FRO.m.[x])%SmtMap).`1; + FRO.m.[x] <- (r, PROM.Known); + + return r; + } + + proc set(x : in_t, y : out_t) : unit = { + FRO.m.[x] <- (y, PROM.Known); + } + + proc rem(x : in_t) : unit = { + FRO.m <- (rem FRO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + if ((x \notin FRO.m)%SmtMap) + FRO.m.[x] <- (c, PROM.Unknown); + } + + proc queried(x : in_t, f : PROM.flag) : bool = { + return (x \in FRO.m)%SmtMap /\ ((FRO.m.[x])%SmtMap \is f)%PROM; + } + + proc allKnown() : (in_t, out_t) SmtMap.fmap = { + return (restr PROM.Known FRO.m)%SmtMap; + } + }. + + lemma RO_FRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_get: + equiv[ RO.get ~ FRO.get : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> ={res} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_sample: + equiv[ RO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(RO).distinguish ~ D(FRO).distinguish : + ={arg, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_init_ll: islossless RO.init. + + lemma FRO_init_ll: islossless FRO.init. + + lemma FRO_in_dom_ll: islossless FRO.queried. + + lemma FRO_restrK_ll: islossless FRO.allKnown. + + lemma RO_set_ll: islossless RO.set. + + lemma FRO_set_ll: islossless FRO.set. + + lemma RO_get_ll: (forall (x : in_t), is_lossless (dout x)) => islossless RO.get. + + lemma FRO_get_ll: (forall (x : in_t), is_lossless (dout x)) => islossless FRO.get. + + lemma RO_sample_ll: (forall (x : in_t), is_lossless (dout x)) => islossless RO.sample. + + lemma FRO_sample_ll: (forall (x : in_t), is_lossless (dout x)) => islossless FRO.sample. + + module type RM_Distinguisher(G : ROmap) = { + proc distinguish(_ : d_in_t) : d_out_t {G.get, G.sample, G.restrK} + } + + module MainRM(D : RM_Distinguisher, RO0 : ROmap) = { + proc distinguish(x : d_in_t) : d_out_t = { + var r : d_out_t; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + lemma fcoll_bound ['rT]: + forall (f : out_t -> 'rT) (Pc : real), + 0%r <= Pc => + (forall (x1 x2 : in_t) (y : 'rT), y \in dmap (dout x1) f => mu1 (dmap (dout x2) f) y <= Pc) => + forall (q : int), + 0 <= q => + forall (D(G : ROmap) <: RM_Distinguisher{+all mem, -RO} ) &m (z : d_in_t), + Pr[MainRM(D, RO).distinguish(z) @ &m : (fcoll f RO.m)%SmtMap /\ (fsize RO.m)%SmtMap <= q] <= + (q * (q - 1))%r / 2%r * Pc. + + theory FullEager. + abstract theory EagerCore. + axiom dout_ll: forall (x : in_t), is_lossless (dout x). + + module type Orcl = { + proc f(x : in_t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : in_t list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : in_t list, l2 : in_t list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : in_t, l : in_t list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : in_t, t2 : in_t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : in_t, t2 : in_t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : in_t list) (i : in_t) (s2 : in_t list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : in_t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : in_t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : in_t, y : out_t) : unit = FRO.set + + proc rem(x : in_t) : unit = FRO.rem + + proc sample(x : in_t) : unit = FRO.sample + + proc queried(x : in_t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (in_t, out_t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : in_t) (mx1 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : in_t) (mx1 mx2 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : in_t) (r1 : out_t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + end FullEager. + + abstract theory FinEager. + abstract theory EagerCore. + axiom dout_ll: forall (x : in_t), is_lossless (dout x). + + module type Orcl = { + proc f(x : in_t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : in_t list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : in_t list, l2 : in_t list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : in_t, l : in_t list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : in_t, t2 : in_t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : in_t, t2 : in_t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : in_t list) (i : in_t) (s2 : in_t list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : in_t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : in_t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : in_t, y : out_t) : unit = FRO.set + + proc rem(x : in_t) : unit = FRO.rem + + proc sample(x : in_t) : unit = FRO.sample + + proc queried(x : in_t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (in_t, out_t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : in_t) (mx1 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : in_t) (mx1 mx2 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : in_t) (r1 : out_t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + theory FinFrom. + type t = in_t. + + op enum : t list. + + op card : int = size enum. + + axiom enum_spec: forall (x : t), count (pred1 x) enum = 1. + + lemma enumP: forall (x : t), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : t list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinFrom. + + module FinRO = { + proc rem(x : in_t) : unit = RO.rem + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc init() : unit = { + var l : FinFrom.t list; + + l <- FinFrom.enum; + RO.init(); + while (l <> []){ + RO.sample(head witness l); + l <- behead l; + } + } + + proc get(x : in_t) : out_t = { + return oget (RO.m.[x])%SmtMap; + } + + proc sample(x : in_t) : unit = {} + }. + + module type FinRO_Distinguisher(G : RO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.sample} + } + + lemma RO_FinRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ MainD(D, RO).distinguish ~ MainD(D, FinRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_RO_FinRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ) &m (x : d_in_t) (p : + d_out_t -> bool), + Pr[MainD(D, RO).distinguish(x) @ &m : p res] = Pr[MainD(D, FinRO).distinguish(x) @ &m : p res]. + + theory MUniFinFun. + op mfun ['u] (d : in_t -> 'u distr) (f : in_t -> 'u) : real = + (big predT (fun (x : in_t) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma mfunE ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mfun d f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + lemma isdistr_dfun ['u]: forall (d : in_t -> 'u distr), isdistr (mfun d). + + op dfun ['u] (d : in_t -> 'u distr) : (in_t -> 'u) distr = mk (mfun d). + + lemma dfun1E ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mu1 (dfun d) f = (big predT (fun (x : in_t) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun1E_djoin ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mu1 (dfun d) f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + op tofun ['u] (us : 'u list) (x : in_t) : 'u = nth witness us (index x FinFrom.enum). + + op offun ['u] (f : in_t -> 'u) : 'u list = map f FinFrom.enum. + + lemma offunK ['u]: cancel offun tofun. + + lemma tofunK ['u]: forall (us : 'u list), size us = FinFrom.card => offun (tofun us) = us. + + lemma dfun_dmap ['u]: forall (d : in_t -> 'u distr), dfun d = dmap (djoinmap d FinFrom.enum) tofun. + + lemma dfun_supp ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), f \in dfun d <=> forall (x : in_t), f x \in d x. + + lemma dfun_fu ['u]: forall (d : in_t -> 'u distr), (forall (x : in_t), is_full (d x)) => is_full (dfun d). + + lemma dfun_uni ['u]: + forall (d : in_t -> 'u distr), (forall (x : in_t), is_uniform (d x)) => is_uniform (dfun d). + + lemma dfun_funi ['u]: + forall (d : in_t -> 'u distr), + (forall (x : in_t), is_uniform (d x)) => (forall (x : in_t), is_full (d x)) => is_funiform (dfun d). + + lemma dfunE_djoin ['u]: + forall (du : in_t -> 'u distr) (E : (in_t -> 'u) -> bool), + mu (dfun du) E = mu (djoinmap du FinFrom.enum) (E \o tofun). + + lemma dfunE ['u]: + forall (du : in_t -> 'u distr) (p : in_t -> 'u -> bool), + mu (dfun du) (fun (f : in_t -> 'u) => forall (x : in_t), p x (f x)) = + (big predT (fun (x : in_t) => mu (du x) (p x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma weight_dfun ['u]: + forall (df : in_t -> 'u distr), + weight (dfun df) = (big predT (fun (x : in_t) => weight (df x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun_ll ['u]: + forall (d : in_t -> 'u distr), (forall (x : in_t), is_lossless (d x)) => is_lossless (dfun d). + + lemma dlet_dfun ['u, 'v]: + forall (d1 : in_t -> 'u distr) (d2 : in_t -> 'u -> 'v distr), + dlet (dfun d1) (fun (f1 : in_t -> 'u) => dfun (fun (x : in_t) => d2 x (f1 x))) = + dfun (fun (x : in_t) => dlet (d1 x) (fun (x1 : 'u) => d2 x x1)). + + lemma dfun_unit ['u]: forall (f : in_t -> 'u), dfun (fun (x : in_t) => dunit (f x)) = dunit f. + + lemma dmap_dfun ['u, 'v]: + forall (d : in_t -> 'u distr) (F : in_t -> 'u -> 'v), + dmap (dfun d) (fun (f : in_t -> 'u) (x : in_t) => F x (f x)) = + dfun (fun (x : in_t) => dmap (d x) (fun (fx : 'u) => F x fx)). + + lemma dfun1E_fix1 ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t) (f : in_t -> 'u), + mu1 (dfun d) f = mu1 (d x0) (f x0) * mu1 (dfun d.[x0 <- dunit (f x0)]) f. + + lemma dlet_dfun_fupdate_ll ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t) (v : 'u), + dlet (dfun d.[x0 <- dunit v]) (fun (f : in_t -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + dfun d. + + lemma dfunE_dlet_fix1 ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t), dfun d = dlet (d x0) (fun (v : 'u) => dfun d.[x0 <- dunit v]). + + lemma dlet_dfun_update ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t), + dlet (dfun d) (fun (f : in_t -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + (weight (d x0) \cdot dfun d). + + lemma dfun_projE ['u]: + forall (df : in_t -> 'u distr) (x : in_t), + dmap (dfun df) (fun (f : in_t -> 'u) => f x) = (weight (dfun df) / weight (df x) \cdot df x). + + lemma eq_from_dfun_proj_eq ['u]: + forall (df1 df2 : in_t -> 'u distr), + (forall (x : in_t), is_lossless (df1 x)) => + (forall (x : in_t), is_lossless (df2 x)) => + (forall (x : in_t), + dmap (dfun df1) (fun (f : in_t -> 'u) => f x) = dmap (dfun df2) (fun (f : in_t -> 'u) => f x)) => + df1 == df2. + + lemma dfun_prod1E ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr) (f : + in_t -> 'u) (g : in_t -> 'v), + mu1 (dfun (fun (x : in_t) => df x `*` dg x)) (fun (x : in_t) => (f x, g x)) = + mu1 (dfun df) f * mu1 (dfun dg) g. + + lemma dprod_funE ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr), + dfun df `*` dfun dg = + dmap (dfun (fun (x : in_t) => df x `*` dg x)) + (fun (fg : in_t -> 'u * 'v) => ((fun (p : 'u * 'v) => p.`1) \o fg, (fun (p : 'u * 'v) => p.`2) \o fg)). + + lemma dfun_prodE ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr), + dfun (fun (x : in_t) => df x `*` dg x) = + dmap (dfun df `*` dfun dg) (fun (fg : (in_t -> 'u) * (in_t -> 'v)) (x : in_t) => (fg.`1 x, fg.`2 x)). + + lemma dfun_condE ['u]: + forall (X : in_t -> bool) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dmap (dfun dt `*` dfun df) + (fun (tf : (in_t -> 'u) * (in_t -> 'u)) (x : in_t) => if X x then tf.`1 x else tf.`2 x) = + dfun (fun (x : in_t) => if X x then dt x else df x). + + lemma dlet_dfun_cond ['u]: + forall (dX : in_t -> bool distr) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dX x)) => + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dlet (dfun dX) (fun (X : in_t -> bool) => dfun (fun (x : in_t) => if X x then dt x else df x)) = + dfun (fun (x : in_t) => dlet (dX x) (fun (b : bool) => if b then dt x else df x)). + + lemma dfun_dcondE ['u]: + forall (dX : in_t -> bool distr) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dX x)) => + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : in_t -> bool) => + dmap (dfun dt `*` dfun df) + (fun (tf : (in_t -> 'u) * (in_t -> 'u)) (x : in_t) => if X x then tf.`1 x else tf.`2 x)) = + dfun (fun (x : in_t) => (mu1 (dX x) true \cdot dt x) + (mu1 (dX x) false \cdot df x)). + end MUniFinFun. + + module FunRO = { + var f : in_t -> out_t + + proc init() : unit = { + FunRO.f <$ (dfun dout)%MUniFinFun; + } + + proc get(x : in_t) : out_t = { + return FunRO.f x; + } + + proc set(x : in_t, y : out_t) : unit = { + FunRO.f <- fun (z : in_t) => if z = x then y else FunRO.f z; + } + + proc sample(x : in_t) : unit = {} + + proc rem(x : in_t) : unit = {} + }. + + lemma FinRO_FunRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ), + equiv[ MainD(D, FinRO).distinguish ~ MainD(D, FunRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_FinRO_FunRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ) &m (x : d_in_t) + (p : d_out_t -> bool), + Pr[MainD(D, FinRO).distinguish(x) @ &m : p res] = Pr[MainD(D, FunRO).distinguish(x) @ &m : p res]. + end FinEager. + end RO_H. + + module type Ideal_RO = { + proc get(x : RO_H.in_t) : RO_H.out_t {} + } + + module type ROAdv_T(O : Ideal_RO) = { + proc guess(sd : W8.t Array32.t, t : polyvec, uv : polyvec * poly) : bool {O.get} + } + + module MLWE_RO(Adv : ROAdv_T, O : RO_H.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var _A : RO_H.out_t; + var u0 : polyvec; + var u1 : polyvec; + var t : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + O.init(); + sd <$ srand; + s <$ dshort; + e <$ dshort; + _A <@ O.get(sd); + _A <- if tr then (trmx _A)%Matrix_.Matrix else _A; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + t <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ Adv(O).guess(sd, t, if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + theory MLWE_vs_MLWE_ROM. + module B(A : ROAdv_T, O : RO_H.RO) = { + var _sd : W8.t Array32.t + + var __A : polymat + + module FakeRO = { + proc get(sd : W8.t Array32.t) : polymat = { + var _Ares : polymat; + + _Ares <- B.__A; + if (sd <> B._sd) { + O.sample(sd); + _Ares <@ O.get(sd); + } + + return _Ares; + } + } + + proc guess(_A : polymat, t : polyvec, uv : polyvec * poly) : bool = { + var sd : W8.t Array32.t; + var b : bool; + + sd <$ srand; + B._sd <- sd; + B.__A <- _A; + O.init(); + b <@ A(B(A, O).FakeRO).guess(sd, t, uv); + + return b; + } + }. + + module Bt(A : ROAdv_T, O : RO_H.RO) = { + var _sd : W8.t Array32.t + + var __A : polymat + + module FakeRO = { + proc get(sd : W8.t Array32.t) : polymat = { + var _Ares : polymat; + + _Ares <- Bt.__A; + if (sd <> Bt._sd) { + O.sample(sd); + _Ares <@ O.get(sd); + } + + return _Ares; + } + } + + proc guess(_A : polymat, t : polyvec, uv : polyvec * poly) : bool = { + var sd : W8.t Array32.t; + var b : bool; + + sd <$ srand; + Bt._sd <- sd; + Bt.__A <- (trmx _A)%Matrix_.Matrix; + O.init(); + b <@ A(Bt(A, O).FakeRO).guess(sd, t, uv); + + return b; + } + }. + + lemma MLWE_RO_equiv: + forall (b : bool) &m (A(O : Ideal_RO) <: ROAdv_T{+all mem, -RO_H.LRO, -B} ), + Pr[MLWE_RO(A, RO_H.LRO).main(false, b) @ &m : res] = Pr[MLWE(B(A, RO_H.LRO)).main(b) @ &m : res]. + + lemma MLWE_RO_equiv_t: + forall (b : bool) &m (A(O : Ideal_RO) <: ROAdv_T{+all mem, -RO_H.LRO, -Bt} ), + Pr[MLWE_RO(A, RO_H.LRO).main(true, b) @ &m : res] = Pr[MLWE(Bt(A, RO_H.LRO)).main(b) @ &m : res]. + end MLWE_vs_MLWE_ROM. + end MLWE_ROM. + + theory MLWE_SMP. + theory RO_SMP. + type in_t. + + type out_t. + + op dout : in_t -> out_t distr. + + type d_in_t. + + type d_out_t. + + module type RO = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + } + + module type RO_Distinguisher(G : RO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.rem, G.sample} + } + + module MainD(D : RO_Distinguisher, RO0 : RO) = { + proc distinguish(x : d_in_t) : d_out_t = { + var r : d_out_t; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + module type ROmap = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + + proc restrK() : (in_t, out_t) SmtMap.fmap {} + } + + module type FRO = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + + proc queried(x : in_t, f : PROM.flag) : bool {} + + proc allKnown() : (in_t, out_t) SmtMap.fmap {} + } + + module type FRO_Distinguisher(G : FRO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.rem, G.sample, G.queried, G.allKnown} + } + + abstract theory MkRO. + module RO = { + var m : (in_t, out_t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : in_t, y : out_t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : in_t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + RO.get(x); + } + + proc restrK() : (in_t, out_t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : in_t) : out_t = RO.get + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc rem(x : in_t) : unit = RO.rem + + proc sample(x : in_t) : unit = {} + }. + end MkRO. + + module RO = { + var m : (in_t, out_t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : in_t, y : out_t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : in_t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + RO.get(x); + } + + proc restrK() : (in_t, out_t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : in_t) : out_t = RO.get + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc rem(x : in_t) : unit = RO.rem + + proc sample(x : in_t) : unit = {} + }. + + module FRO = { + var m : (in_t, out_t * PROM.flag) SmtMap.fmap + + proc init() : unit = { + FRO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \in FRO.m)%SmtMap) + r <- (oget (FRO.m.[x])%SmtMap).`1; + FRO.m.[x] <- (r, PROM.Known); + + return r; + } + + proc set(x : in_t, y : out_t) : unit = { + FRO.m.[x] <- (y, PROM.Known); + } + + proc rem(x : in_t) : unit = { + FRO.m <- (rem FRO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + if ((x \notin FRO.m)%SmtMap) + FRO.m.[x] <- (c, PROM.Unknown); + } + + proc queried(x : in_t, f : PROM.flag) : bool = { + return (x \in FRO.m)%SmtMap /\ ((FRO.m.[x])%SmtMap \is f)%PROM; + } + + proc allKnown() : (in_t, out_t) SmtMap.fmap = { + return (restr PROM.Known FRO.m)%SmtMap; + } + }. + + lemma RO_FRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_get: + equiv[ RO.get ~ FRO.get : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> ={res} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_sample: + equiv[ RO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(RO).distinguish ~ D(FRO).distinguish : + ={arg, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_init_ll: islossless RO.init. + + lemma FRO_init_ll: islossless FRO.init. + + lemma FRO_in_dom_ll: islossless FRO.queried. + + lemma FRO_restrK_ll: islossless FRO.allKnown. + + lemma RO_set_ll: islossless RO.set. + + lemma FRO_set_ll: islossless FRO.set. + + lemma RO_get_ll: (forall (x : in_t), is_lossless (dout x)) => islossless RO.get. + + lemma FRO_get_ll: (forall (x : in_t), is_lossless (dout x)) => islossless FRO.get. + + lemma RO_sample_ll: (forall (x : in_t), is_lossless (dout x)) => islossless RO.sample. + + lemma FRO_sample_ll: (forall (x : in_t), is_lossless (dout x)) => islossless FRO.sample. + + module type RM_Distinguisher(G : ROmap) = { + proc distinguish(_ : d_in_t) : d_out_t {G.get, G.sample, G.restrK} + } + + module MainRM(D : RM_Distinguisher, RO0 : ROmap) = { + proc distinguish(x : d_in_t) : d_out_t = { + var r : d_out_t; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + lemma fcoll_bound ['rT]: + forall (f : out_t -> 'rT) (Pc : real), + 0%r <= Pc => + (forall (x1 x2 : in_t) (y : 'rT), y \in dmap (dout x1) f => mu1 (dmap (dout x2) f) y <= Pc) => + forall (q : int), + 0 <= q => + forall (D(G : ROmap) <: RM_Distinguisher{+all mem, -RO} ) &m (z : d_in_t), + Pr[MainRM(D, RO).distinguish(z) @ &m : (fcoll f RO.m)%SmtMap /\ (fsize RO.m)%SmtMap <= q] <= + (q * (q - 1))%r / 2%r * Pc. + + theory FullEager. + abstract theory EagerCore. + axiom dout_ll: forall (x : in_t), is_lossless (dout x). + + module type Orcl = { + proc f(x : in_t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : in_t list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : in_t list, l2 : in_t list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : in_t, l : in_t list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : in_t, t2 : in_t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : in_t, t2 : in_t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : in_t list) (i : in_t) (s2 : in_t list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : in_t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : in_t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : in_t, y : out_t) : unit = FRO.set + + proc rem(x : in_t) : unit = FRO.rem + + proc sample(x : in_t) : unit = FRO.sample + + proc queried(x : in_t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (in_t, out_t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : in_t) (mx1 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : in_t) (mx1 mx2 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : in_t) (r1 : out_t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + end FullEager. + + abstract theory FinEager. + abstract theory EagerCore. + axiom dout_ll: forall (x : in_t), is_lossless (dout x). + + module type Orcl = { + proc f(x : in_t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : in_t list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : in_t list, l2 : in_t list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : in_t, l : in_t list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : in_t, t2 : in_t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : in_t, t2 : in_t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : in_t list) (i : in_t) (s2 : in_t list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : in_t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : in_t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : in_t, y : out_t) : unit = FRO.set + + proc rem(x : in_t) : unit = FRO.rem + + proc sample(x : in_t) : unit = FRO.sample + + proc queried(x : in_t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (in_t, out_t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : in_t) (mx1 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : in_t) (mx1 mx2 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : in_t) (r1 : out_t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + theory FinFrom. + type t = in_t. + + op enum : t list. + + op card : int = size enum. + + axiom enum_spec: forall (x : t), count (pred1 x) enum = 1. + + lemma enumP: forall (x : t), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : t list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinFrom. + + module FinRO = { + proc rem(x : in_t) : unit = RO.rem + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc init() : unit = { + var l : FinFrom.t list; + + l <- FinFrom.enum; + RO.init(); + while (l <> []){ + RO.sample(head witness l); + l <- behead l; + } + } + + proc get(x : in_t) : out_t = { + return oget (RO.m.[x])%SmtMap; + } + + proc sample(x : in_t) : unit = {} + }. + + module type FinRO_Distinguisher(G : RO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.sample} + } + + lemma RO_FinRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ MainD(D, RO).distinguish ~ MainD(D, FinRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_RO_FinRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ) &m (x : d_in_t) (p : + d_out_t -> bool), + Pr[MainD(D, RO).distinguish(x) @ &m : p res] = Pr[MainD(D, FinRO).distinguish(x) @ &m : p res]. + + theory MUniFinFun. + op mfun ['u] (d : in_t -> 'u distr) (f : in_t -> 'u) : real = + (big predT (fun (x : in_t) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma mfunE ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mfun d f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + lemma isdistr_dfun ['u]: forall (d : in_t -> 'u distr), isdistr (mfun d). + + op dfun ['u] (d : in_t -> 'u distr) : (in_t -> 'u) distr = mk (mfun d). + + lemma dfun1E ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mu1 (dfun d) f = (big predT (fun (x : in_t) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun1E_djoin ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mu1 (dfun d) f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + op tofun ['u] (us : 'u list) (x : in_t) : 'u = nth witness us (index x FinFrom.enum). + + op offun ['u] (f : in_t -> 'u) : 'u list = map f FinFrom.enum. + + lemma offunK ['u]: cancel offun tofun. + + lemma tofunK ['u]: forall (us : 'u list), size us = FinFrom.card => offun (tofun us) = us. + + lemma dfun_dmap ['u]: forall (d : in_t -> 'u distr), dfun d = dmap (djoinmap d FinFrom.enum) tofun. + + lemma dfun_supp ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), f \in dfun d <=> forall (x : in_t), f x \in d x. + + lemma dfun_fu ['u]: forall (d : in_t -> 'u distr), (forall (x : in_t), is_full (d x)) => is_full (dfun d). + + lemma dfun_uni ['u]: + forall (d : in_t -> 'u distr), (forall (x : in_t), is_uniform (d x)) => is_uniform (dfun d). + + lemma dfun_funi ['u]: + forall (d : in_t -> 'u distr), + (forall (x : in_t), is_uniform (d x)) => (forall (x : in_t), is_full (d x)) => is_funiform (dfun d). + + lemma dfunE_djoin ['u]: + forall (du : in_t -> 'u distr) (E : (in_t -> 'u) -> bool), + mu (dfun du) E = mu (djoinmap du FinFrom.enum) (E \o tofun). + + lemma dfunE ['u]: + forall (du : in_t -> 'u distr) (p : in_t -> 'u -> bool), + mu (dfun du) (fun (f : in_t -> 'u) => forall (x : in_t), p x (f x)) = + (big predT (fun (x : in_t) => mu (du x) (p x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma weight_dfun ['u]: + forall (df : in_t -> 'u distr), + weight (dfun df) = (big predT (fun (x : in_t) => weight (df x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun_ll ['u]: + forall (d : in_t -> 'u distr), (forall (x : in_t), is_lossless (d x)) => is_lossless (dfun d). + + lemma dlet_dfun ['u, 'v]: + forall (d1 : in_t -> 'u distr) (d2 : in_t -> 'u -> 'v distr), + dlet (dfun d1) (fun (f1 : in_t -> 'u) => dfun (fun (x : in_t) => d2 x (f1 x))) = + dfun (fun (x : in_t) => dlet (d1 x) (fun (x1 : 'u) => d2 x x1)). + + lemma dfun_unit ['u]: forall (f : in_t -> 'u), dfun (fun (x : in_t) => dunit (f x)) = dunit f. + + lemma dmap_dfun ['u, 'v]: + forall (d : in_t -> 'u distr) (F : in_t -> 'u -> 'v), + dmap (dfun d) (fun (f : in_t -> 'u) (x : in_t) => F x (f x)) = + dfun (fun (x : in_t) => dmap (d x) (fun (fx : 'u) => F x fx)). + + lemma dfun1E_fix1 ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t) (f : in_t -> 'u), + mu1 (dfun d) f = mu1 (d x0) (f x0) * mu1 (dfun d.[x0 <- dunit (f x0)]) f. + + lemma dlet_dfun_fupdate_ll ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t) (v : 'u), + dlet (dfun d.[x0 <- dunit v]) (fun (f : in_t -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + dfun d. + + lemma dfunE_dlet_fix1 ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t), dfun d = dlet (d x0) (fun (v : 'u) => dfun d.[x0 <- dunit v]). + + lemma dlet_dfun_update ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t), + dlet (dfun d) (fun (f : in_t -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + (weight (d x0) \cdot dfun d). + + lemma dfun_projE ['u]: + forall (df : in_t -> 'u distr) (x : in_t), + dmap (dfun df) (fun (f : in_t -> 'u) => f x) = (weight (dfun df) / weight (df x) \cdot df x). + + lemma eq_from_dfun_proj_eq ['u]: + forall (df1 df2 : in_t -> 'u distr), + (forall (x : in_t), is_lossless (df1 x)) => + (forall (x : in_t), is_lossless (df2 x)) => + (forall (x : in_t), + dmap (dfun df1) (fun (f : in_t -> 'u) => f x) = dmap (dfun df2) (fun (f : in_t -> 'u) => f x)) => + df1 == df2. + + lemma dfun_prod1E ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr) (f : + in_t -> 'u) (g : in_t -> 'v), + mu1 (dfun (fun (x : in_t) => df x `*` dg x)) (fun (x : in_t) => (f x, g x)) = + mu1 (dfun df) f * mu1 (dfun dg) g. + + lemma dprod_funE ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr), + dfun df `*` dfun dg = + dmap (dfun (fun (x : in_t) => df x `*` dg x)) + (fun (fg : in_t -> 'u * 'v) => ((fun (p : 'u * 'v) => p.`1) \o fg, (fun (p : 'u * 'v) => p.`2) \o fg)). + + lemma dfun_prodE ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr), + dfun (fun (x : in_t) => df x `*` dg x) = + dmap (dfun df `*` dfun dg) (fun (fg : (in_t -> 'u) * (in_t -> 'v)) (x : in_t) => (fg.`1 x, fg.`2 x)). + + lemma dfun_condE ['u]: + forall (X : in_t -> bool) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dmap (dfun dt `*` dfun df) + (fun (tf : (in_t -> 'u) * (in_t -> 'u)) (x : in_t) => if X x then tf.`1 x else tf.`2 x) = + dfun (fun (x : in_t) => if X x then dt x else df x). + + lemma dlet_dfun_cond ['u]: + forall (dX : in_t -> bool distr) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dX x)) => + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dlet (dfun dX) (fun (X : in_t -> bool) => dfun (fun (x : in_t) => if X x then dt x else df x)) = + dfun (fun (x : in_t) => dlet (dX x) (fun (b : bool) => if b then dt x else df x)). + + lemma dfun_dcondE ['u]: + forall (dX : in_t -> bool distr) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dX x)) => + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : in_t -> bool) => + dmap (dfun dt `*` dfun df) + (fun (tf : (in_t -> 'u) * (in_t -> 'u)) (x : in_t) => if X x then tf.`1 x else tf.`2 x)) = + dfun (fun (x : in_t) => (mu1 (dX x) true \cdot dt x) + (mu1 (dX x) false \cdot df x)). + end MUniFinFun. + + module FunRO = { + var f : in_t -> out_t + + proc init() : unit = { + FunRO.f <$ (dfun dout)%MUniFinFun; + } + + proc get(x : in_t) : out_t = { + return FunRO.f x; + } + + proc set(x : in_t, y : out_t) : unit = { + FunRO.f <- fun (z : in_t) => if z = x then y else FunRO.f z; + } + + proc sample(x : in_t) : unit = {} + + proc rem(x : in_t) : unit = {} + }. + + lemma FinRO_FunRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ), + equiv[ MainD(D, FinRO).distinguish ~ MainD(D, FunRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_FinRO_FunRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ) &m (x : d_in_t) + (p : d_out_t -> bool), + Pr[MainD(D, FinRO).distinguish(x) @ &m : p res] = Pr[MainD(D, FunRO).distinguish(x) @ &m : p res]. + end FinEager. + end RO_SMP. + + module type SMP_RO = { + proc get(x : RO_SMP.in_t) : RO_SMP.out_t {} + } + + module type Sampler(O : SMP_RO) = { + proc sampleA(sd : W8.t Array32.t) : polymat {O.get} + + proc sampleAT(sd : W8.t Array32.t) : polymat {O.get} + } + + module type PSampler = { + proc sampleA(sd : W8.t Array32.t) : polymat {} + + proc sampleAT(sd : W8.t Array32.t) : polymat {} + } + + module type SAdv_T(O : SMP_RO) = { + proc interact(sd : W8.t Array32.t, t : polyvec) : unit {O.get} + + proc guess(uv : polyvec * poly) : bool {O.get} + } + + module MLWE_SMP(Adv : SAdv_T, S : Sampler, O : RO_SMP.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var _A : polymat; + var u0 : polyvec; + var u1 : polyvec; + var t : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + O.init(); + sd <$ srand; + t <$ duni; + Adv(O).interact(sd, t); + if (tr) + _A <@ S(O).sampleAT(sd); + else + _A <@ S(O).sampleA(sd); + s <$ dshort; + e <$ dshort; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ Adv(O).guess(if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + end MLWE_SMP. + + theory SMP_vs_ROM. + theory MLWE_SMP. + theory RO_SMP. + type in_t = W8.t Array32.t. + + type out_t = polymat. + + op dout (_ : W8.t Array32.t) : polymat distr = duni_matrix. + + type d_in_t = bool. + + type d_out_t = bool. + + module type RO = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + } + + module type RO_Distinguisher(G : RO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.rem, G.sample} + } + + module MainD(D : RO_Distinguisher, RO0 : RO) = { + proc distinguish(x : d_in_t) : d_out_t = { + var r : d_out_t; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + module type ROmap = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + + proc restrK() : (in_t, out_t) SmtMap.fmap {} + } + + module type FRO = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + + proc queried(x : in_t, f : PROM.flag) : bool {} + + proc allKnown() : (in_t, out_t) SmtMap.fmap {} + } + + module type FRO_Distinguisher(G : FRO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.rem, G.sample, G.queried, G.allKnown} + } + + abstract theory MkRO. + module RO = { + var m : (in_t, out_t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : in_t, y : out_t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : in_t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + RO.get(x); + } + + proc restrK() : (in_t, out_t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : in_t) : out_t = RO.get + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc rem(x : in_t) : unit = RO.rem + + proc sample(x : in_t) : unit = {} + }. + end MkRO. + + module RO = { + var m : (in_t, out_t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : in_t, y : out_t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : in_t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + RO.get(x); + } + + proc restrK() : (in_t, out_t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : in_t) : out_t = RO.get + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc rem(x : in_t) : unit = RO.rem + + proc sample(x : in_t) : unit = {} + }. + + module FRO = { + var m : (in_t, out_t * PROM.flag) SmtMap.fmap + + proc init() : unit = { + FRO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \in FRO.m)%SmtMap) + r <- (oget (FRO.m.[x])%SmtMap).`1; + FRO.m.[x] <- (r, PROM.Known); + + return r; + } + + proc set(x : in_t, y : out_t) : unit = { + FRO.m.[x] <- (y, PROM.Known); + } + + proc rem(x : in_t) : unit = { + FRO.m <- (rem FRO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + if ((x \notin FRO.m)%SmtMap) + FRO.m.[x] <- (c, PROM.Unknown); + } + + proc queried(x : in_t, f : PROM.flag) : bool = { + return (x \in FRO.m)%SmtMap /\ ((FRO.m.[x])%SmtMap \is f)%PROM; + } + + proc allKnown() : (in_t, out_t) SmtMap.fmap = { + return (restr PROM.Known FRO.m)%SmtMap; + } + }. + + lemma RO_FRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_get: + equiv[ RO.get ~ FRO.get : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> ={res} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_sample: + equiv[ RO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(RO).distinguish ~ D(FRO).distinguish : + ={arg, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_init_ll: islossless RO.init. + + lemma FRO_init_ll: islossless FRO.init. + + lemma FRO_in_dom_ll: islossless FRO.queried. + + lemma FRO_restrK_ll: islossless FRO.allKnown. + + lemma RO_set_ll: islossless RO.set. + + lemma FRO_set_ll: islossless FRO.set. + + lemma RO_get_ll: (forall (x : in_t), is_lossless (dout x)) => islossless RO.get. + + lemma FRO_get_ll: (forall (x : in_t), is_lossless (dout x)) => islossless FRO.get. + + lemma RO_sample_ll: (forall (x : in_t), is_lossless (dout x)) => islossless RO.sample. + + lemma FRO_sample_ll: (forall (x : in_t), is_lossless (dout x)) => islossless FRO.sample. + + module type RM_Distinguisher(G : ROmap) = { + proc distinguish(_ : d_in_t) : d_out_t {G.get, G.sample, G.restrK} + } + + module MainRM(D : RM_Distinguisher, RO0 : ROmap) = { + proc distinguish(x : d_in_t) : d_out_t = { + var r : d_out_t; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + lemma fcoll_bound ['rT]: + forall (f : out_t -> 'rT) (Pc : real), + 0%r <= Pc => + (forall (x1 x2 : in_t) (y : 'rT), y \in dmap (dout x1) f => mu1 (dmap (dout x2) f) y <= Pc) => + forall (q : int), + 0 <= q => + forall (D(G : ROmap) <: RM_Distinguisher{+all mem, -RO} ) &m (z : d_in_t), + Pr[MainRM(D, RO).distinguish(z) @ &m : (fcoll f RO.m)%SmtMap /\ (fsize RO.m)%SmtMap <= q] <= + (q * (q - 1))%r / 2%r * Pc. + + theory FullEager. + abstract theory EagerCore. + axiom dout_ll: forall (x : in_t), is_lossless (dout x). + + module type Orcl = { + proc f(x : in_t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : in_t list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : in_t list, l2 : in_t list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : in_t, l : in_t list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : in_t, t2 : in_t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : in_t, t2 : in_t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : in_t list) (i : in_t) (s2 : in_t list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : in_t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : in_t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : in_t, y : out_t) : unit = FRO.set + + proc rem(x : in_t) : unit = FRO.rem + + proc sample(x : in_t) : unit = FRO.sample + + proc queried(x : in_t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (in_t, out_t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : in_t) (mx1 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : in_t) (mx1 mx2 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : in_t) (r1 : out_t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + end FullEager. + + abstract theory FinEager. + abstract theory EagerCore. + axiom dout_ll: forall (x : in_t), is_lossless (dout x). + + module type Orcl = { + proc f(x : in_t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : in_t list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : in_t list, l2 : in_t list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : in_t, l : in_t list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : in_t, t2 : in_t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : in_t, t2 : in_t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : in_t list) (i : in_t) (s2 : in_t list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : in_t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : in_t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : in_t, y : out_t) : unit = FRO.set + + proc rem(x : in_t) : unit = FRO.rem + + proc sample(x : in_t) : unit = FRO.sample + + proc queried(x : in_t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (in_t, out_t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : in_t) (mx1 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : in_t) (mx1 mx2 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : in_t) (r1 : out_t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + theory FinFrom. + type t = in_t. + + op enum : t list. + + op card : int = size enum. + + axiom enum_spec: forall (x : t), count (pred1 x) enum = 1. + + lemma enumP: forall (x : t), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : t list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinFrom. + + module FinRO = { + proc rem(x : in_t) : unit = RO.rem + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc init() : unit = { + var l : FinFrom.t list; + + l <- FinFrom.enum; + RO.init(); + while (l <> []){ + RO.sample(head witness l); + l <- behead l; + } + } + + proc get(x : in_t) : out_t = { + return oget (RO.m.[x])%SmtMap; + } + + proc sample(x : in_t) : unit = {} + }. + + module type FinRO_Distinguisher(G : RO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.sample} + } + + lemma RO_FinRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ MainD(D, RO).distinguish ~ MainD(D, FinRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_RO_FinRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ) &m (x : d_in_t) (p : + d_out_t -> bool), + Pr[MainD(D, RO).distinguish(x) @ &m : p res] = Pr[MainD(D, FinRO).distinguish(x) @ &m : p res]. + + theory MUniFinFun. + op mfun ['u] (d : in_t -> 'u distr) (f : in_t -> 'u) : real = + (big predT (fun (x : in_t) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma mfunE ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mfun d f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + lemma isdistr_dfun ['u]: forall (d : in_t -> 'u distr), isdistr (mfun d). + + op dfun ['u] (d : in_t -> 'u distr) : (in_t -> 'u) distr = mk (mfun d). + + lemma dfun1E ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mu1 (dfun d) f = (big predT (fun (x : in_t) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun1E_djoin ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mu1 (dfun d) f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + op tofun ['u] (us : 'u list) (x : in_t) : 'u = nth witness us (index x FinFrom.enum). + + op offun ['u] (f : in_t -> 'u) : 'u list = map f FinFrom.enum. + + lemma offunK ['u]: cancel offun tofun. + + lemma tofunK ['u]: forall (us : 'u list), size us = FinFrom.card => offun (tofun us) = us. + + lemma dfun_dmap ['u]: forall (d : in_t -> 'u distr), dfun d = dmap (djoinmap d FinFrom.enum) tofun. + + lemma dfun_supp ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), f \in dfun d <=> forall (x : in_t), f x \in d x. + + lemma dfun_fu ['u]: + forall (d : in_t -> 'u distr), (forall (x : in_t), is_full (d x)) => is_full (dfun d). + + lemma dfun_uni ['u]: + forall (d : in_t -> 'u distr), (forall (x : in_t), is_uniform (d x)) => is_uniform (dfun d). + + lemma dfun_funi ['u]: + forall (d : in_t -> 'u distr), + (forall (x : in_t), is_uniform (d x)) => (forall (x : in_t), is_full (d x)) => is_funiform (dfun d). + + lemma dfunE_djoin ['u]: + forall (du : in_t -> 'u distr) (E : (in_t -> 'u) -> bool), + mu (dfun du) E = mu (djoinmap du FinFrom.enum) (E \o tofun). + + lemma dfunE ['u]: + forall (du : in_t -> 'u distr) (p : in_t -> 'u -> bool), + mu (dfun du) (fun (f : in_t -> 'u) => forall (x : in_t), p x (f x)) = + (big predT (fun (x : in_t) => mu (du x) (p x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma weight_dfun ['u]: + forall (df : in_t -> 'u distr), + weight (dfun df) = (big predT (fun (x : in_t) => weight (df x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun_ll ['u]: + forall (d : in_t -> 'u distr), (forall (x : in_t), is_lossless (d x)) => is_lossless (dfun d). + + lemma dlet_dfun ['u, 'v]: + forall (d1 : in_t -> 'u distr) (d2 : in_t -> 'u -> 'v distr), + dlet (dfun d1) (fun (f1 : in_t -> 'u) => dfun (fun (x : in_t) => d2 x (f1 x))) = + dfun (fun (x : in_t) => dlet (d1 x) (fun (x1 : 'u) => d2 x x1)). + + lemma dfun_unit ['u]: forall (f : in_t -> 'u), dfun (fun (x : in_t) => dunit (f x)) = dunit f. + + lemma dmap_dfun ['u, 'v]: + forall (d : in_t -> 'u distr) (F : in_t -> 'u -> 'v), + dmap (dfun d) (fun (f : in_t -> 'u) (x : in_t) => F x (f x)) = + dfun (fun (x : in_t) => dmap (d x) (fun (fx : 'u) => F x fx)). + + lemma dfun1E_fix1 ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t) (f : in_t -> 'u), + mu1 (dfun d) f = mu1 (d x0) (f x0) * mu1 (dfun d.[x0 <- dunit (f x0)]) f. + + lemma dlet_dfun_fupdate_ll ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t) (v : 'u), + dlet (dfun d.[x0 <- dunit v]) (fun (f : in_t -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + dfun d. + + lemma dfunE_dlet_fix1 ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t), + dfun d = dlet (d x0) (fun (v : 'u) => dfun d.[x0 <- dunit v]). + + lemma dlet_dfun_update ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t), + dlet (dfun d) (fun (f : in_t -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + (weight (d x0) \cdot dfun d). + + lemma dfun_projE ['u]: + forall (df : in_t -> 'u distr) (x : in_t), + dmap (dfun df) (fun (f : in_t -> 'u) => f x) = (weight (dfun df) / weight (df x) \cdot df x). + + lemma eq_from_dfun_proj_eq ['u]: + forall (df1 df2 : in_t -> 'u distr), + (forall (x : in_t), is_lossless (df1 x)) => + (forall (x : in_t), is_lossless (df2 x)) => + (forall (x : in_t), + dmap (dfun df1) (fun (f : in_t -> 'u) => f x) = dmap (dfun df2) (fun (f : in_t -> 'u) => f x)) => + df1 == df2. + + lemma dfun_prod1E ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr) (f : + in_t -> 'u) (g : in_t -> 'v), + mu1 (dfun (fun (x : in_t) => df x `*` dg x)) (fun (x : in_t) => (f x, g x)) = + mu1 (dfun df) f * mu1 (dfun dg) g. + + lemma dprod_funE ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr), + dfun df `*` dfun dg = + dmap (dfun (fun (x : in_t) => df x `*` dg x)) + (fun (fg : in_t -> 'u * 'v) => + ((fun (p : 'u * 'v) => p.`1) \o fg, (fun (p : 'u * 'v) => p.`2) \o fg)). + + lemma dfun_prodE ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr), + dfun (fun (x : in_t) => df x `*` dg x) = + dmap (dfun df `*` dfun dg) (fun (fg : (in_t -> 'u) * (in_t -> 'v)) (x : in_t) => (fg.`1 x, fg.`2 x)). + + lemma dfun_condE ['u]: + forall (X : in_t -> bool) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dmap (dfun dt `*` dfun df) + (fun (tf : (in_t -> 'u) * (in_t -> 'u)) (x : in_t) => if X x then tf.`1 x else tf.`2 x) = + dfun (fun (x : in_t) => if X x then dt x else df x). + + lemma dlet_dfun_cond ['u]: + forall (dX : in_t -> bool distr) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dX x)) => + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dlet (dfun dX) (fun (X : in_t -> bool) => dfun (fun (x : in_t) => if X x then dt x else df x)) = + dfun (fun (x : in_t) => dlet (dX x) (fun (b : bool) => if b then dt x else df x)). + + lemma dfun_dcondE ['u]: + forall (dX : in_t -> bool distr) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dX x)) => + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : in_t -> bool) => + dmap (dfun dt `*` dfun df) + (fun (tf : (in_t -> 'u) * (in_t -> 'u)) (x : in_t) => if X x then tf.`1 x else tf.`2 x)) = + dfun (fun (x : in_t) => (mu1 (dX x) true \cdot dt x) + (mu1 (dX x) false \cdot df x)). + end MUniFinFun. + + module FunRO = { + var f : in_t -> out_t + + proc init() : unit = { + FunRO.f <$ (dfun dout)%MUniFinFun; + } + + proc get(x : in_t) : out_t = { + return FunRO.f x; + } + + proc set(x : in_t, y : out_t) : unit = { + FunRO.f <- fun (z : in_t) => if z = x then y else FunRO.f z; + } + + proc sample(x : in_t) : unit = {} + + proc rem(x : in_t) : unit = {} + }. + + lemma FinRO_FunRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ), + equiv[ MainD(D, FinRO).distinguish ~ MainD(D, FunRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_FinRO_FunRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ) &m (x : d_in_t) + (p : d_out_t -> bool), + Pr[MainD(D, FinRO).distinguish(x) @ &m : p res] = Pr[MainD(D, FunRO).distinguish(x) @ &m : p res]. + end FinEager. + end RO_SMP. + + module type SMP_RO = { + proc get(x : RO_SMP.in_t) : RO_SMP.out_t {} + } + + module type Sampler(O : SMP_RO) = { + proc sampleA(sd : W8.t Array32.t) : polymat {O.get} + + proc sampleAT(sd : W8.t Array32.t) : polymat {O.get} + } + + module type PSampler = { + proc sampleA(sd : W8.t Array32.t) : polymat {} + + proc sampleAT(sd : W8.t Array32.t) : polymat {} + } + + module type SAdv_T(O : SMP_RO) = { + proc interact(sd : W8.t Array32.t, t : polyvec) : unit {O.get} + + proc guess(uv : polyvec * poly) : bool {O.get} + } + + module MLWE_SMP(Adv : SAdv_T, S : Sampler, O : RO_SMP.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var _A : polymat; + var u0 : polyvec; + var u1 : polyvec; + var t : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + O.init(); + sd <$ srand; + t <$ duni; + Adv(O).interact(sd, t); + if (tr) + _A <@ S(O).sampleAT(sd); + else + _A <@ S(O).sampleA(sd); + s <$ dshort; + e <$ dshort; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ Adv(O).guess(if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + end MLWE_SMP. + + module S(H : MLWE_SMP.SMP_RO) = { + proc sampleA(sd : W8.t Array32.t) : polymat = { + var _A : MLWE_SMP.RO_SMP.out_t; + + _A <@ H.get(sd); + + return _A; + } + + proc sampleAT(sd : W8.t Array32.t) : polymat = { + var _A : MLWE_SMP.RO_SMP.out_t; + + _A <@ H.get(sd); + + return (trmx _A)%Matrix_.Matrix; + } + }. + + module BS(Adv : MLWE_SMP.SAdv_T, S0 : MLWE_SMP.Sampler, O : MLWE_SMP.SMP_RO) = { + proc guess(sd : W8.t Array32.t, t : polyvec, uv : polyvec * poly) : bool = { + var b : bool; + + Adv(O).interact(sd, t); + b <@ Adv(O).guess(uv); + + return b; + } + }. + + module MLWE_SMPs(Adv : MLWE_SMP.SAdv_T, S0 : MLWE_SMP.Sampler, O : MLWE_ROM.RO_H.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var _A : polymat; + var u0 : polyvec; + var u1 : polyvec; + var t : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + O.init(); + sd <$ srand; + t <$ duni; + O.sample(sd); + Adv(O).interact(sd, t); + if (tr) + _A <@ S0(O).sampleAT(sd); + else + _A <@ S0(O).sampleA(sd); + s <$ dshort; + e <$ dshort; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ Adv(O).guess(if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + module MLWE_ROs(Adv : MLWE_ROM.ROAdv_T, O : MLWE_ROM.RO_H.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var _A : MLWE_ROM.RO_H.out_t; + var u0 : polyvec; + var u1 : polyvec; + var t : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + O.init(); + sd <$ srand; + s <$ dshort; + e <$ dshort; + O.sample(sd); + _A <@ O.get(sd); + _A <- if tr then (trmx _A)%Matrix_.Matrix else _A; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + t <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ Adv(O).guess(sd, t, if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + module DLeftAux(A : MLWE_SMP.SAdv_T, O : MLWE_ROM.RO_H.RO) = { + proc run(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var t : polyvec; + var _A : MLWE_ROM.RO_H.out_t; + var s : polyvec; + var e : polyvec; + var u0 : polyvec; + var u1 : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + sd <$ srand; + s <$ dshort; + e <$ dshort; + O.sample(sd); + _A <@ O.get(sd); + _A <- if tr then (trmx _A)%Matrix_.Matrix else _A; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + t <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ BS(A, S, O).guess(sd, t, if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + module DLeft(A : MLWE_SMP.SAdv_T, O : MLWE_ROM.RO_H.RO) = { + proc distinguish(b : bool) : bool = { + var b' : bool; + + b' <@ DLeftAux(A, O).run(false, b); + + return b'; + } + }. + + module DLeftT(A : MLWE_SMP.SAdv_T, O : MLWE_ROM.RO_H.RO) = { + proc distinguish(b : bool) : bool = { + var b' : bool; + + b' <@ DLeftAux(A, O).run(true, b); + + return b'; + } + }. + + module DRightAux(A : MLWE_SMP.SAdv_T, O : MLWE_ROM.RO_H.RO) = { + proc run(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var t : polyvec; + var _A : polymat; + var s : polyvec; + var e : polyvec; + var u0 : polyvec; + var u1 : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + sd <$ srand; + t <$ duni; + O.sample(sd); + A(O).interact(sd, t); + if (tr) + _A <@ S(O).sampleAT(sd); + else + _A <@ S(O).sampleA(sd); + s <$ dshort; + e <$ dshort; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ A(O).guess(if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + module DRight(A : MLWE_SMP.SAdv_T, O : MLWE_ROM.RO_H.RO) = { + proc distinguish(b : bool) : bool = { + var b' : bool; + + b' <@ DRightAux(A, O).run(false, b); + + return b'; + } + }. + + module DRightT(A : MLWE_SMP.SAdv_T, O : MLWE_ROM.RO_H.RO) = { + proc distinguish(b : bool) : bool = { + var b' : bool; + + b' <@ DRightAux(A, O).run(true, b); + + return b'; + } + }. + + lemma MLWE_SMP_equiv_lel: + forall (tr b : bool) &m + (A(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.SAdv_T{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS} + ), + Pr[MLWE_ROs(BS(A, S), MLWE_ROM.RO_H.LRO).main(tr, b) @ &m : res] = + Pr[MLWE_ROs(BS(A, S), MLWE_ROM.RO_H.RO).main(tr, b) @ &m : res]. + + lemma MLWE_SMP_equiv_ler: + forall (tr b : bool) &m + (A(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.SAdv_T{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS} + ), + Pr[MLWE_SMPs(A, S, MLWE_ROM.RO_H.LRO).main(tr, b) @ &m : res] = + Pr[MLWE_SMPs(A, S, MLWE_ROM.RO_H.RO).main(tr, b) @ &m : res]. + + lemma MLWE_SMP_equiv: + forall (b : bool) &m + (A(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.SAdv_T{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS} + ), + Pr[MLWE(MLWE_ROM.MLWE_vs_MLWE_ROM.B(BS(A, S), MLWE_ROM.RO_H.LRO)).main(b) @ &m : res] = + Pr[MLWE_SMP.MLWE_SMP(A, S, MLWE_ROM.RO_H.LRO).main(false, b) @ &m : res]. + + lemma MLWE_SMP_equiv_t: + forall (b : bool) &m + (A(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.SAdv_T{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS} + ), + Pr[MLWE(MLWE_ROM.MLWE_vs_MLWE_ROM.Bt(BS(A, S), MLWE_ROM.RO_H.LRO)).main(b) @ &m : res] = + Pr[MLWE_SMP.MLWE_SMP(A, S, MLWE_ROM.RO_H.LRO).main(true, b) @ &m : res]. + end SMP_vs_ROM. + + theory SMP_vs_ROM_IND. + module type Simulator_t(O : MLWE_ROM.Ideal_RO) = { + proc init() : unit {} + + proc get(x : MLWE_SMP.RO_SMP.in_t) : MLWE_SMP.RO_SMP.out_t {O.get} + } + + module type Distinguisher_t(S : MLWE_SMP.PSampler, H : MLWE_SMP.SMP_RO) = { + proc distinguish(tr : bool, b : bool, sd : W8.t Array32.t) : bool {H.get, S.sampleA, S.sampleAT} + } + + module WIndfReal(D0 : Distinguisher_t, S : MLWE_SMP.Sampler, O : MLWE_SMP.RO_SMP.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var b' : bool; + + O.init(); + sd <$ srand; + b' <@ D0(S(O), O).distinguish(tr, b, sd); + + return b'; + } + }. + + module WIndfIdeal(D0 : Distinguisher_t, Sim : Simulator_t, O : MLWE_ROM.RO_H.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var b' : bool; + + O.init(); + Sim(O).init(); + sd <$ srand; + b' <@ D0(SMP_vs_ROM.S(O), Sim(O)).distinguish(tr, b, sd); + + return b'; + } + }. + + module BS(Adv : MLWE_SMP.SAdv_T, Sim : Simulator_t, H : MLWE_ROM.Ideal_RO) = { + proc guess(sd : W8.t Array32.t, t : polyvec, uv : polyvec * poly) : bool = { + var b : bool; + + Sim(H).init(); + Adv(Sim(H)).interact(sd, t); + b <@ Adv(Sim(H)).guess(uv); + + return b; + } + }. + + module D(A : MLWE_SMP.SAdv_T, S : MLWE_SMP.PSampler, H : MLWE_SMP.SMP_RO) = { + proc distinguish(tr : bool, b : bool, sd : W8.t Array32.t) : bool = { + var _A : polymat; + var t : polyvec; + var s : polyvec; + var e : polyvec; + var u0 : polyvec; + var u1 : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + t <$ duni; + A(H).interact(sd, t); + if (tr) + _A <@ S.sampleAT(sd); + else + _A <@ S.sampleA(sd); + s <$ dshort; + e <$ dshort; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ A(H).guess(if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + module DLeftAux(A : MLWE_SMP.SAdv_T, Sim : Simulator_t, O : MLWE_ROM.RO_H.RO) = { + proc run(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var t : polyvec; + var _A : MLWE_ROM.RO_H.out_t; + var s : polyvec; + var e : polyvec; + var u0 : polyvec; + var u1 : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + sd <$ srand; + s <$ dshort; + e <$ dshort; + O.sample(sd); + _A <@ O.get(sd); + _A <- if tr then (trmx _A)%Matrix_.Matrix else _A; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + t <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ BS(A, Sim, O).guess(sd, t, if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + module DLeft(A : MLWE_SMP.SAdv_T, Sim : Simulator_t, O : MLWE_ROM.RO_H.RO) = { + proc distinguish(b : bool) : bool = { + var b' : bool; + + b' <@ DLeftAux(A, Sim, O).run(false, b); + + return b'; + } + }. + + module DLeftT(A : MLWE_SMP.SAdv_T, Sim : Simulator_t, O : MLWE_ROM.RO_H.RO) = { + proc distinguish(b : bool) : bool = { + var b' : bool; + + b' <@ DLeftAux(A, Sim, O).run(true, b); + + return b'; + } + }. + + module DRightAux(A : MLWE_SMP.SAdv_T, Sim : Simulator_t, O : MLWE_ROM.RO_H.RO) = { + proc run(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var t : polyvec; + var _A : MLWE_ROM.RO_H.out_t; + var s : polyvec; + var e : polyvec; + var u0 : polyvec; + var u1 : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + sd <$ srand; + t <$ duni; + O.sample(sd); + Sim(O).init(); + A(Sim(O)).interact(sd, t); + if (tr) { + _A <@ O.get(sd); + _A <- (trmx _A)%Matrix_.Matrix; + } + else + _A <@ O.get(sd); + s <$ dshort; + e <$ dshort; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ A(Sim(O)).guess(if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + module DRight(A : MLWE_SMP.SAdv_T, Sim : Simulator_t, O : MLWE_ROM.RO_H.RO) = { + proc distinguish(b : bool) : bool = { + var b' : bool; + + b' <@ DRightAux(A, Sim, O).run(false, b); + + return b'; + } + }. + + module DRightT(A : MLWE_SMP.SAdv_T, Sim : Simulator_t, O : MLWE_ROM.RO_H.RO) = { + proc distinguish(b : bool) : bool = { + var b' : bool; + + b' <@ DRightAux(A, Sim, O).run(true, b); + + return b'; + } + }. + + module MLWE_ROs(Adv : MLWE_ROM.ROAdv_T, O : MLWE_ROM.RO_H.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var _A : MLWE_ROM.RO_H.out_t; + var u0 : polyvec; + var u1 : polyvec; + var t : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + O.init(); + sd <$ srand; + s <$ dshort; + e <$ dshort; + O.sample(sd); + _A <@ O.get(sd); + _A <- if tr then (trmx _A)%Matrix_.Matrix else _A; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + t <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ Adv(O).guess(sd, t, if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + module WIndfIdeals(D0 : Distinguisher_t, Sim : Simulator_t, O : MLWE_ROM.RO_H.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var b' : bool; + + O.init(); + Sim(O).init(); + sd <$ srand; + O.sample(sd); + b' <@ D0(SMP_vs_ROM.S(O), Sim(O)).distinguish(tr, b, sd); + + return b'; + } + }. + + lemma MLWE_SMP_equiv_lel: + forall (tr b : bool) &m + (A(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.SAdv_T{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS} + ) + (Sim(O : MLWE_ROM.Ideal_RO) <: + Simulator_t{+all mem, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS, -A} + ), + Pr[MLWE_ROs(BS(A, Sim), MLWE_ROM.RO_H.LRO).main(tr, b) @ &m : res] = + Pr[MLWE_ROs(BS(A, Sim), MLWE_ROM.RO_H.RO).main(tr, b) @ &m : res]. + + lemma MLWE_SMP_equiv_ler: + forall (tr b : bool) &m + (A(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.SAdv_T{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS, -D} + ) + (Sim(O : MLWE_ROM.Ideal_RO) <: + Simulator_t{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS, -D, -A} + ), + Pr[WIndfIdeals(D(A), Sim, MLWE_ROM.RO_H.LRO).main(tr, b) @ &m : res] = + Pr[WIndfIdeals(D(A), Sim, MLWE_ROM.RO_H.RO).main(tr, b) @ &m : res]. + + lemma MLWE_SMP_equiv: + forall (_b : bool) &m + (S(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.Sampler{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_SMP.RO_SMP.LRO, -D} + ) + (A(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.SAdv_T{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_SMP.RO_SMP.LRO, -D, -S} + ) + (Sim(O : MLWE_ROM.Ideal_RO) <: + Simulator_t{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS, -D, -A} + ), + Pr[MLWE_SMP.MLWE_SMP(A, S, MLWE_SMP.RO_SMP.LRO).main(false, _b) @ &m : res] - + Pr[MLWE(MLWE_ROM.MLWE_vs_MLWE_ROM.B(BS(A, Sim), MLWE_ROM.RO_H.LRO)).main(_b) @ &m : res] = + Pr[WIndfReal(D(A), S, MLWE_SMP.RO_SMP.LRO).main(false, _b) @ &m : res] - + Pr[WIndfIdeal(D(A), Sim, MLWE_ROM.RO_H.LRO).main(false, _b) @ &m : res]. + + lemma MLWE_SMP_equiv_t: + forall (_b : bool) &m + (S(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.Sampler{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_SMP.RO_SMP.LRO, -D} + ) + (A(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.SAdv_T{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_SMP.RO_SMP.LRO, -D, -S} + ) + (Sim(O : MLWE_ROM.Ideal_RO) <: + Simulator_t{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS, -D, -A} + ), + Pr[MLWE_SMP.MLWE_SMP(A, S, MLWE_SMP.RO_SMP.LRO).main(true, _b) @ &m : res] - + Pr[MLWE(MLWE_ROM.MLWE_vs_MLWE_ROM.Bt(BS(A, Sim), MLWE_ROM.RO_H.LRO)).main(_b) @ &m : res] = + Pr[WIndfReal(D(A), S, MLWE_SMP.RO_SMP.LRO).main(true, _b) @ &m : res] - + Pr[WIndfIdeal(D(A), Sim, MLWE_ROM.RO_H.LRO).main(true, _b) @ &m : res]. + end SMP_vs_ROM_IND. + end MLWE_. + + type raw_ciphertext = polyvec * poly. + + type raw_pkey = W8.t Array32.t * polyvec. + + type raw_skey = polyvec. + + lemma pk_encodeK: cancel pk_encode pk_decode. + + lemma sk_encodeK: cancel sk_encode sk_decode. + + lemma drand_ll: is_lossless srand. + + hint solve 0 lossless : drand_ll. + + hint solve 0 random : drand_ll. + + lemma drand_uni: is_uniform srand. + + hint solve 0 random : drand_uni. + + lemma drand_fu: is_full srand. + + hint solve 0 random : drand_fu. + + op prg_kg_ideal : (W8.t Array32.t * polyvec * polyvec) distr = + dlet srand + (fun (sd : W8.t Array32.t) => + dlet MLWE_.dshort (fun (s : polyvec) => dmap MLWE_.dshort (fun (e : polyvec) => (sd, s, e)))). + + op prg_enc_ideal : (polyvec * polyvec * poly) distr = + dlet MLWE_.dshort + (fun (r : polyvec) => dlet MLWE_.dshort (fun (e1 : polyvec) => dmap dshort_R (fun (e2 : poly) => (r, e1, e2)))). + + op kg (r : W8.t Array32.t) : publickey * W8.t Array1152.t = + let (sd, s, e) = prg_kg_inner r in + let t = ((H sd *^ s)%MLWE_.Matrix_.Matrix + e)%Vector in (pk_encode (sd, t), sk_encode s). + + op enc (rr : W8.t Array32.t) (pk : publickey) (m : plaintext) : W8.t Array960.t * W8.t Array128.t = + let (sd, t) = pk_decode pk in + let (r, e1, e2) = prg_enc_inner rr in + let u = ((trmx (H sd) *^ r)%MLWE_.Matrix_.Matrix + e1)%Vector in + let v = ((t `<*>` r) &+ e2 &+ m_encode m)%MLWE_ in c_encode (u, v). + + op dec (sk : W8.t Array1152.t) (c : W8.t Array960.t * W8.t Array128.t) : plaintext option = + let (u, v) = c_decode c in Some (m_decode (v &- (sk_decode sk `<*>` u))%MLWE_). + + theory FO_MLKEM. + theory UU. + theory TT. + lemma perm_eq_set ['a, 'b, 'c]: + forall (m : ('a, 'b) SmtMap.fmap) (l : 'a list) (x : 'a) (y : 'b), + perm_eq l ((elems ((fdom m))%SmtMap))%FSet => + ! (x \in l) => perm_eq (l ++ [x]) ((elems ((fdom (m.[x <- y])%SmtMap))%SmtMap))%FSet. + + lemma card_set ['a, 'b]: + forall (m : ('a, 'b) SmtMap.fmap) (x : 'a) (y : 'b) (n : int), + (card ((fdom m))%SmtMap)%FSet = n => (card ((fdom (m.[x <- y])%SmtMap))%SmtMap)%FSet <= n + 1. + + lemma card_set_bnd ['a, 'b]: + forall (m : ('a, 'b) SmtMap.fmap) (x : 'a) (y : 'b), + (card ((fdom (m.[x <- y])%SmtMap))%SmtMap)%FSet <= (card ((fdom m))%SmtMap)%FSet + 1. + + op find ['a, 'b] (f : 'a -> 'b -> bool) (m : ('a, 'b) SmtMap.fmap) : ('a * 'b) option = + let bindings = map (fun (a : 'a) => (a, oget (m.[a])%SmtMap)) ((elems ((fdom m))%SmtMap))%FSet in + let n = find (fun (p : 'a * 'b) => f p.`1 p.`2) bindings in + if n < size bindings then Some (nth witness bindings n) else None. + + lemma find_map ['a, 'b]: + forall (l : 'a list) (f : 'a -> 'b) (P : 'b -> bool), find P (map f l) = find (fun (x : 'a) => P (f x)) l. + + lemma findP_Some ['a, 'b]: + forall (f : 'a -> 'b -> bool) (m : ('a, 'b) SmtMap.fmap) (a : 'a) (b : 'b), + find f m = Some (a, b) => (m.[a])%SmtMap = Some b /\ f a b. + + lemma findP_None ['a, 'b]: + forall (f : 'a -> 'b -> bool) (m : ('a, 'b) SmtMap.fmap), + find f m = None => forall (a : 'a) (b : 'b), (m.[a])%SmtMap = Some b => ! f a b. + + lemma dplaintext_ll: is_lossless srand. + + hint solve 0 lossless : dplaintext_ll. + + hint solve 0 random : dplaintext_ll. + + lemma dplaintext_uni: is_uniform srand. + + hint solve 0 random : dplaintext_uni. + + lemma dplaintext_fu: is_full srand. + + hint solve 0 random : dplaintext_fu. + + theory FinT. + op enum : plaintext list. + + op card : int = size enum. + + axiom enum_spec: forall (x : plaintext), count (pred1 x) enum = 1. + + lemma enumP: forall (x : plaintext), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : plaintext list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinT. + + lemma randd_ll: is_lossless srand. + + hint solve 0 lossless : randd_ll. + + hint solve 0 random : randd_ll. + + theory PKE. + module type Scheme = { + proc kg() : publickey * W8.t Array1152.t {} + + proc enc(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t {} + + proc dec(sk : W8.t Array1152.t, c : W8.t Array960.t * W8.t Array128.t) : plaintext option {} + } + + module type CORR_ADV = { + proc find(pk : publickey, sk : W8.t Array1152.t) : plaintext {} + } + + module Correctness_Adv(S : Scheme, A : CORR_ADV) = { + proc main() : bool = { + var pk : publickey; + var sk : W8.t Array1152.t; + var c : W8.t Array960.t * W8.t Array128.t; + var m : plaintext; + var m' : plaintext option; + + (pk, sk) <@ S.kg(); + m <@ A.find(pk, sk); + c <@ S.enc(pk, m); + m' <@ S.dec(sk, c); + + return m' <> Some m; + } + }. + + module type Adversary = { + proc choose(pk : publickey) : plaintext * plaintext {} + + proc guess(c : W8.t Array960.t * W8.t Array128.t) : bool {} + } + + module CPA(S : Scheme, A : Adversary) = { + proc main() : bool = { + var pk : publickey; + var sk : W8.t Array1152.t; + var m0 : plaintext; + var m1 : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var b : bool; + var b' : bool; + + (pk, sk) <@ S.kg(); + (m0, m1) <@ A.choose(pk); + b <$ {0,1}; + c <@ S.enc(pk, if b then m1 else m0); + b' <@ A.guess(c); + + return b' = b; + } + }. + + module CPA_L(S : Scheme, A : Adversary) = { + proc main() : bool = { + var pk : publickey; + var sk : W8.t Array1152.t; + var m0 : plaintext; + var m1 : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var b' : bool; + + (pk, sk) <@ S.kg(); + (m0, m1) <@ A.choose(pk); + c <@ S.enc(pk, m0); + b' <@ A.guess(c); + + return b'; + } + }. + + module CPA_R(S : Scheme, A : Adversary) = { + proc main() : bool = { + var pk : publickey; + var sk : W8.t Array1152.t; + var m0 : plaintext; + var m1 : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var b' : bool; + + (pk, sk) <@ S.kg(); + (m0, m1) <@ A.choose(pk); + c <@ S.enc(pk, m1); + b' <@ A.guess(c); + + return b'; + } + }. + + theory LorR. + module type A = { + proc main(x : unit) : bool {} + } + + module RandomLR(L : A, R : A) = { + proc main(x : unit) : bool = { + var b : bool; + var r : bool; + + b <$ {0,1}; + if (b) + r <@ L.main(x); + else + r <@ R.main(x); + + return b = r; + } + }. + + lemma pr_AdvLR_AdvRndLR: + forall (L R <: A) &m (x' : unit), + Pr[R.main(x') @ &m : true] = 1%r => + `|Pr[L.main(x') @ &m : res] - Pr[R.main(x') @ &m : res]| = + 2%r * `|Pr[RandomLR(L, R).main(x') @ &m : res] - 1%r / 2%r|. + end LorR. + + lemma pr_CPA_LR: + forall (S <: Scheme) (A <: Adversary{+all mem, -S} ) &m, + islossless S.kg => + islossless S.enc => + islossless A.choose => + islossless A.guess => + `|Pr[CPA_L(S, A).main() @ &m : res] - Pr[CPA_R(S, A).main() @ &m : res]| = + 2%r * `|Pr[CPA(S, A).main() @ &m : res] - 1%r / 2%r|. + + module type OW_CPA_ADV = { + proc find(pk : publickey, c : W8.t Array960.t * W8.t Array128.t) : plaintext option {} + } + + theory MFinT. + op card : int = size FinT.enum. + + lemma enum_spec: forall (x : plaintext), count (pred1 x) FinT.enum = 1. + + lemma enumP: forall (x : plaintext), x \in FinT.enum. + + lemma enum_uniq: uniq FinT.enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : plaintext list), uniq xs => count (mem xs) FinT.enum = size xs. + + lemma is_finite_for: (is_finite_for predT FinT.enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq FinT.enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end MFinT. + + lemma dplaintext_ll: is_lossless srand. + + hint solve 0 lossless : dplaintext_ll. + + hint solve 0 random : dplaintext_ll. + + lemma dplaintext_uni: is_uniform srand. + + hint solve 0 random : dplaintext_uni. + + lemma dplaintext_fu: is_full srand. + + hint solve 0 random : dplaintext_fu. + + op eps_msg : real = 1%r / MFinT.card%r. + + lemma eps_msgE: forall (x : plaintext), mu1 srand x = eps_msg. + + module OW_CPA(S : Scheme, A : OW_CPA_ADV) = { + var pk : publickey + + var sk : W8.t Array1152.t + + var m : plaintext + + var cc : W8.t Array960.t * W8.t Array128.t + + var m' : plaintext option + + proc main_perfect() : bool = { + (OW_CPA.pk, OW_CPA.sk) <@ S.kg(); + OW_CPA.m <$ srand; + OW_CPA.cc <@ S.enc(OW_CPA.pk, OW_CPA.m); + OW_CPA.m' <@ A.find(OW_CPA.pk, OW_CPA.cc); + + return OW_CPA.m' = Some OW_CPA.m; + } + + module O = { + proc pco(sk : W8.t Array1152.t, m : plaintext, c : W8.t Array960.t * W8.t Array128.t) : bool = { + var m'' : plaintext option; + + m'' <@ S.dec(sk, c); + + return m'' = Some m; + } + } + + proc main() : bool = { + var b : bool; + + (OW_CPA.pk, OW_CPA.sk) <@ S.kg(); + OW_CPA.m <$ srand; + OW_CPA.cc <@ S.enc(OW_CPA.pk, OW_CPA.m); + OW_CPA.m' <@ A.find(OW_CPA.pk, OW_CPA.cc); + b <@ OW_CPA(S, A).O.pco(OW_CPA.sk, oget OW_CPA.m', OW_CPA.cc); + + return if OW_CPA.m' = None then false else b; + } + }. + + module BOWp(S : Scheme, A : OW_CPA_ADV) = { + var m'' : plaintext option + + proc find(pk : publickey, sk : W8.t Array1152.t) : plaintext = { + OW_CPA.m <$ srand; + + return OW_CPA.m; + } + + proc main() : bool = { + var pk : publickey; + var sk : W8.t Array1152.t; + + (pk, sk) <@ S.kg(); + BOWp(S, A).find(pk, sk); + OW_CPA.cc <@ S.enc(pk, OW_CPA.m); + OW_CPA.m' <@ A.find(pk, OW_CPA.cc); + BOWp.m'' <@ S.dec(sk, OW_CPA.cc); + + return BOWp.m'' <> Some OW_CPA.m; + } + }. + + lemma ow_perfect: + forall (S <: Scheme{+all mem, -OW_CPA, -BOWp} ) (A <: OW_CPA_ADV{+all mem, -OW_CPA, -BOWp, -S} ) &m, + islossless A.find => + islossless S.enc => + islossless S.dec => + `|Pr[OW_CPA(S, A).main() @ &m : res] - Pr[OW_CPA(S, A).main_perfect() @ &m : res]| <= + Pr[Correctness_Adv(S, BOWp(S, A)).main() @ &m : res]. + + module type OWL_CPA_ADV = { + proc find(pk : publickey, c : W8.t Array960.t * W8.t Array128.t) : plaintext list {} + } + + module OWL_CPA(S : Scheme, A : OWL_CPA_ADV) = { + var pk : publickey + + var sk : W8.t Array1152.t + + var m : plaintext + + var cc : W8.t Array960.t * W8.t Array128.t + + var l : plaintext list + + proc main() : bool = { + (OWL_CPA.pk, OWL_CPA.sk) <@ S.kg(); + OWL_CPA.m <$ srand; + OWL_CPA.cc <@ S.enc(OWL_CPA.pk, OWL_CPA.m); + OWL_CPA.l <@ A.find(OWL_CPA.pk, OWL_CPA.cc); + + return OWL_CPA.m \in OWL_CPA.l; + } + }. + + theory OWvsIND. + module Bowl(A : OWL_CPA_ADV) = { + var m0 : plaintext + + var m1 : plaintext + + var pk : publickey + + var l : plaintext list + + proc choose(_pk : publickey) : plaintext * plaintext = { + Bowl.pk <- _pk; + Bowl.m0 <$ srand; + Bowl.m1 <$ srand; + + return (Bowl.m0, Bowl.m1); + } + + proc guess(c : W8.t Array960.t * W8.t Array128.t) : bool = { + var b : bool; + + b <$ {0,1}; + Bowl.l <@ A.find(Bowl.pk, c); + + return + if (Bowl.m0 \in Bowl.l) = (Bowl.m1 \in Bowl.l) then b else if Bowl.m0 \in Bowl.l then false else true; + } + }. + + lemma boundl: + forall (l : plaintext list) (MAX : int), + 0 <= MAX => mu srand (fun (x : plaintext) => size l <= MAX /\ (x \in l)) <= MAX%r * eps_msg. + + pred bad (gB : plaintext list * plaintext * plaintext * publickey) = (gB.`2 \in gB.`1) = (gB.`3 \in gB.`1). + + lemma ow_ind_l: + forall (S <: Scheme{+all mem, -BOWp, -OWL_CPA, -Bowl} ) + (A <: OWL_CPA_ADV{+all mem, -BOWp, -OWL_CPA, -Bowl, -S} ) &m (MAX : int), + 0 <= MAX => + islossless S.kg => + islossless S.enc => + islossless S.dec => + islossless A.find => + hoare[ A.find : true ==> size res <= MAX] => + Pr[OWL_CPA(S, A).main() @ &m : OWL_CPA.m \in OWL_CPA.l] <= + 2%r * (MAX%r * eps_msg + `|Pr[CPA(S, Bowl(A)).main() @ &m : res] - 1%r / 2%r|). + + module BL(A : OW_CPA_ADV) = { + proc find(pk : publickey, c : W8.t Array960.t * W8.t Array128.t) : plaintext list = { + var m' : plaintext option; + + m' <@ A.find(pk, c); + + return if m' = None then [] else [oget m']; + } + }. + + lemma ow_ind: + forall (S <: Scheme{+all mem, -OW_CPA, -BOWp, -OWL_CPA, -Bowl} ) + (A <: OW_CPA_ADV{+all mem, -OW_CPA, -BOWp, -OWL_CPA, -Bowl, -S} ) &m, + islossless S.kg => + islossless S.enc => + islossless S.dec => + islossless A.find => + Pr[OW_CPA(S, A).main() @ &m : res] <= + 2%r * (eps_msg + `|Pr[CPA(S, Bowl(BL(A))).main() @ &m : res] - 1%r / 2%r|) + + Pr[Correctness_Adv(S, BOWp(S, A)).main() @ &m : res]. + end OWvsIND. + end PKE. + + op kg : (publickey * W8.t Array1152.t) distr = dmap srand MLWEPKEHash.kg. + + lemma kg_ll: is_lossless UU.TT.kg. + + hint solve 0 lossless : kg_ll. + + hint solve 0 random : kg_ll. + + module BasePKE = { + proc kg() : publickey * W8.t Array1152.t = { + var kpair : publickey * W8.t Array1152.t; + + kpair <$ UU.TT.kg; + + return kpair; + } + + proc enc(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = { + var r : W8.t Array32.t; + var c : W8.t Array960.t * W8.t Array128.t; + + r <$ srand; + c <- enc r pk m; + + return c; + } + + proc dec(sk : W8.t Array1152.t, c : W8.t Array960.t * W8.t Array128.t) : plaintext option = { + return dec sk c; + } + }. + + theory PKEROM. + type skey = publickey * W8.t Array1152.t. + + theory RO. + module type RO = { + proc init() : unit {} + + proc get(x : plaintext) : W8.t Array32.t {} + + proc set(x : plaintext, y : W8.t Array32.t) : unit {} + + proc rem(x : plaintext) : unit {} + + proc sample(x : plaintext) : unit {} + } + + module type RO_Distinguisher(G : RO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.rem, G.sample} + } + + module MainD(D : RO_Distinguisher, RO0 : RO) = { + proc distinguish(x : unit) : bool = { + var r : bool; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + module type ROmap = { + proc init() : unit {} + + proc get(x : plaintext) : W8.t Array32.t {} + + proc set(x : plaintext, y : W8.t Array32.t) : unit {} + + proc rem(x : plaintext) : unit {} + + proc sample(x : plaintext) : unit {} + + proc restrK() : (plaintext, W8.t Array32.t) SmtMap.fmap {} + } + + module type FRO = { + proc init() : unit {} + + proc get(x : plaintext) : W8.t Array32.t {} + + proc set(x : plaintext, y : W8.t Array32.t) : unit {} + + proc rem(x : plaintext) : unit {} + + proc sample(x : plaintext) : unit {} + + proc queried(x : plaintext, f : PROM.flag) : bool {} + + proc allKnown() : (plaintext, W8.t Array32.t) SmtMap.fmap {} + } + + module type FRO_Distinguisher(G : FRO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.rem, G.sample, G.queried, G.allKnown} + } + + abstract theory MkRO. + module RO = { + var m : (plaintext, W8.t Array32.t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : plaintext) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : plaintext) : unit = { + RO.get(x); + } + + proc restrK() : (plaintext, W8.t Array32.t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : plaintext) : W8.t Array32.t = RO.get + + proc set(x : plaintext, y : W8.t Array32.t) : unit = RO.set + + proc rem(x : plaintext) : unit = RO.rem + + proc sample(x : plaintext) : unit = {} + }. + end MkRO. + + module RO = { + var m : (plaintext, W8.t Array32.t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : plaintext) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : plaintext) : unit = { + RO.get(x); + } + + proc restrK() : (plaintext, W8.t Array32.t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : plaintext) : W8.t Array32.t = RO.get + + proc set(x : plaintext, y : W8.t Array32.t) : unit = RO.set + + proc rem(x : plaintext) : unit = RO.rem + + proc sample(x : plaintext) : unit = {} + }. + + module FRO = { + var m : (plaintext, W8.t Array32.t * PROM.flag) SmtMap.fmap + + proc init() : unit = { + FRO.m <- SmtMap.empty; + } + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \in FRO.m)%SmtMap) + r <- (oget (FRO.m.[x])%SmtMap).`1; + FRO.m.[x] <- (r, PROM.Known); + + return r; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = { + FRO.m.[x] <- (y, PROM.Known); + } + + proc rem(x : plaintext) : unit = { + FRO.m <- (rem FRO.m x)%SmtMap; + } + + proc sample(x : plaintext) : unit = { + var c : W8.t Array32.t; + + c <$ srand; + if ((x \notin FRO.m)%SmtMap) + FRO.m.[x] <- (c, PROM.Unknown); + } + + proc queried(x : plaintext, f : PROM.flag) : bool = { + return (x \in FRO.m)%SmtMap /\ ((FRO.m.[x])%SmtMap \is f)%PROM; + } + + proc allKnown() : (plaintext, W8.t Array32.t) SmtMap.fmap = { + return (restr PROM.Known FRO.m)%SmtMap; + } + }. + + lemma RO_FRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_get: + equiv[ RO.get ~ FRO.get : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> ={res} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_sample: + equiv[ RO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(RO).distinguish ~ D(FRO).distinguish : + ={arg, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_init_ll: islossless RO.init. + + lemma FRO_init_ll: islossless FRO.init. + + lemma FRO_in_dom_ll: islossless FRO.queried. + + lemma FRO_restrK_ll: islossless FRO.allKnown. + + lemma RO_set_ll: islossless RO.set. + + lemma FRO_set_ll: islossless FRO.set. + + lemma RO_get_ll: (forall (_ : plaintext), is_lossless srand) => islossless RO.get. + + lemma FRO_get_ll: (forall (_ : plaintext), is_lossless srand) => islossless FRO.get. + + lemma RO_sample_ll: (forall (_ : plaintext), is_lossless srand) => islossless RO.sample. + + lemma FRO_sample_ll: (forall (_ : plaintext), is_lossless srand) => islossless FRO.sample. + + module type RM_Distinguisher(G : ROmap) = { + proc distinguish(_ : unit) : bool {G.get, G.sample, G.restrK} + } + + module MainRM(D : RM_Distinguisher, RO0 : ROmap) = { + proc distinguish(x : unit) : bool = { + var r : bool; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + lemma fcoll_bound ['rT]: + forall (f : W8.t Array32.t -> 'rT) (Pc : real), + 0%r <= Pc => + (forall (_ _ : plaintext) (y : 'rT), y \in dmap srand f => mu1 (dmap srand f) y <= Pc) => + forall (q : int), + 0 <= q => + forall (D(G : ROmap) <: RM_Distinguisher{+all mem, -RO} ) &m (z : unit), + Pr[MainRM(D, RO).distinguish(z) @ &m : (fcoll f RO.m)%SmtMap /\ (fsize RO.m)%SmtMap <= q] <= + (q * (q - 1))%r / 2%r * Pc. + + theory FullEager. + abstract theory EagerCore. + axiom dout_ll: forall (_ : plaintext), is_lossless srand. + + module type Orcl = { + proc f(x : plaintext) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : plaintext list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : plaintext list, l2 : plaintext list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : plaintext, l : plaintext list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : plaintext list) (i : plaintext) (s2 : plaintext list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : + ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : plaintext), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : plaintext), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = FRO.set + + proc rem(x : plaintext) : unit = FRO.rem + + proc sample(x : plaintext) : unit = FRO.sample + + proc queried(x : plaintext, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (plaintext, W8.t Array32.t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : plaintext) : unit = { + var c : W8.t Array32.t; + + c <$ srand; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: + eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : plaintext) (mx1 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : plaintext) (mx1 mx2 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : plaintext) (r1 : W8.t Array32.t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + end FullEager. + + abstract theory FinEager. + abstract theory EagerCore. + axiom dout_ll: forall (_ : plaintext), is_lossless srand. + + module type Orcl = { + proc f(x : plaintext) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : plaintext list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : plaintext list, l2 : plaintext list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : plaintext, l : plaintext list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : plaintext list) (i : plaintext) (s2 : plaintext list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : + ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : plaintext), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : plaintext), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = FRO.set + + proc rem(x : plaintext) : unit = FRO.rem + + proc sample(x : plaintext) : unit = FRO.sample + + proc queried(x : plaintext, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (plaintext, W8.t Array32.t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : plaintext) : unit = { + var c : W8.t Array32.t; + + c <$ srand; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: + eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : plaintext) (mx1 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : plaintext) (mx1 mx2 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : plaintext) (r1 : W8.t Array32.t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + theory FinFrom. + type t = plaintext. + + op enum : t list. + + op card : int = size enum. + + axiom enum_spec: forall (x : t), count (pred1 x) enum = 1. + + lemma enumP: forall (x : t), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : t list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinFrom. + + module FinRO = { + proc rem(x : plaintext) : unit = RO.rem + + proc set(x : plaintext, y : W8.t Array32.t) : unit = RO.set + + proc init() : unit = { + var l : FinFrom.t list; + + l <- FinFrom.enum; + RO.init(); + while (l <> []){ + RO.sample(head witness l); + l <- behead l; + } + } + + proc get(x : plaintext) : W8.t Array32.t = { + return oget (RO.m.[x])%SmtMap; + } + + proc sample(x : plaintext) : unit = {} + }. + + module type FinRO_Distinguisher(G : RO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.sample} + } + + lemma RO_FinRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ MainD(D, RO).distinguish ~ MainD(D, FinRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_RO_FinRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ) &m (x : unit) (p : + bool -> bool), + Pr[MainD(D, RO).distinguish(x) @ &m : p res] = Pr[MainD(D, FinRO).distinguish(x) @ &m : p res]. + + theory MUniFinFun. + op mfun ['u] (d : plaintext -> 'u distr) (f : plaintext -> 'u) : real = + (big predT (fun (x : plaintext) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma mfunE ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mfun d f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + lemma isdistr_dfun ['u]: forall (d : plaintext -> 'u distr), isdistr (mfun d). + + op dfun ['u] (d : plaintext -> 'u distr) : (plaintext -> 'u) distr = mk (mfun d). + + lemma dfun1E ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mu1 (dfun d) f = + (big predT (fun (x : plaintext) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun1E_djoin ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mu1 (dfun d) f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + op tofun ['u] (us : 'u list) (x : plaintext) : 'u = nth witness us (index x FinFrom.enum). + + op offun ['u] (f : plaintext -> 'u) : 'u list = map f FinFrom.enum. + + lemma offunK ['u]: cancel offun tofun. + + lemma tofunK ['u]: forall (us : 'u list), size us = FinFrom.card => offun (tofun us) = us. + + lemma dfun_dmap ['u]: + forall (d : plaintext -> 'u distr), dfun d = dmap (djoinmap d FinFrom.enum) tofun. + + lemma dfun_supp ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + f \in dfun d <=> forall (x : plaintext), f x \in d x. + + lemma dfun_fu ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_full (d x)) => is_full (dfun d). + + lemma dfun_uni ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_uniform (d x)) => is_uniform (dfun d). + + lemma dfun_funi ['u]: + forall (d : plaintext -> 'u distr), + (forall (x : plaintext), is_uniform (d x)) => + (forall (x : plaintext), is_full (d x)) => is_funiform (dfun d). + + lemma dfunE_djoin ['u]: + forall (du : plaintext -> 'u distr) (E : (plaintext -> 'u) -> bool), + mu (dfun du) E = mu (djoinmap du FinFrom.enum) (E \o tofun). + + lemma dfunE ['u]: + forall (du : plaintext -> 'u distr) (p : plaintext -> 'u -> bool), + mu (dfun du) (fun (f : plaintext -> 'u) => forall (x : plaintext), p x (f x)) = + (big predT (fun (x : plaintext) => mu (du x) (p x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma weight_dfun ['u]: + forall (df : plaintext -> 'u distr), + weight (dfun df) = + (big predT (fun (x : plaintext) => weight (df x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun_ll ['u]: + forall (d : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (d x)) => is_lossless (dfun d). + + lemma dlet_dfun ['u, 'v]: + forall (d1 : plaintext -> 'u distr) (d2 : plaintext -> 'u -> 'v distr), + dlet (dfun d1) (fun (f1 : plaintext -> 'u) => dfun (fun (x : plaintext) => d2 x (f1 x))) = + dfun (fun (x : plaintext) => dlet (d1 x) (fun (x1 : 'u) => d2 x x1)). + + lemma dfun_unit ['u]: + forall (f : plaintext -> 'u), dfun (fun (x : plaintext) => dunit (f x)) = dunit f. + + lemma dmap_dfun ['u, 'v]: + forall (d : plaintext -> 'u distr) (F : plaintext -> 'u -> 'v), + dmap (dfun d) (fun (f : plaintext -> 'u) (x : plaintext) => F x (f x)) = + dfun (fun (x : plaintext) => dmap (d x) (fun (fx : 'u) => F x fx)). + + lemma dfun1E_fix1 ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext) (f : + plaintext -> 'u), mu1 (dfun d) f = mu1 (d x0) (f x0) * mu1 (dfun d.[x0 <- dunit (f x0)]) f. + + lemma dlet_dfun_fupdate_ll ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext) (v : 'u), + dlet (dfun d.[x0 <- dunit v]) + (fun (f : plaintext -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + dfun d. + + lemma dfunE_dlet_fix1 ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext), + dfun d = dlet (d x0) (fun (v : 'u) => dfun d.[x0 <- dunit v]). + + lemma dlet_dfun_update ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext), + dlet (dfun d) (fun (f : plaintext -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + (weight (d x0) \cdot dfun d). + + lemma dfun_projE ['u]: + forall (df : plaintext -> 'u distr) (x : plaintext), + dmap (dfun df) (fun (f : plaintext -> 'u) => f x) = (weight (dfun df) / weight (df x) \cdot df x). + + lemma eq_from_dfun_proj_eq ['u]: + forall (df1 df2 : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (df1 x)) => + (forall (x : plaintext), is_lossless (df2 x)) => + (forall (x : plaintext), + dmap (dfun df1) (fun (f : plaintext -> 'u) => f x) = + dmap (dfun df2) (fun (f : plaintext -> 'u) => f x)) => + df1 == df2. + + lemma dfun_prod1E ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr) (f : + plaintext -> 'u) (g : plaintext -> 'v), + mu1 (dfun (fun (x : plaintext) => df x `*` dg x)) (fun (x : plaintext) => (f x, g x)) = + mu1 (dfun df) f * mu1 (dfun dg) g. + + lemma dprod_funE ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr), + dfun df `*` dfun dg = + dmap (dfun (fun (x : plaintext) => df x `*` dg x)) + (fun (fg : plaintext -> 'u * 'v) => + ((fun (p : 'u * 'v) => p.`1) \o fg, (fun (p : 'u * 'v) => p.`2) \o fg)). + + lemma dfun_prodE ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr), + dfun (fun (x : plaintext) => df x `*` dg x) = + dmap (dfun df `*` dfun dg) + (fun (fg : (plaintext -> 'u) * (plaintext -> 'v)) (x : plaintext) => (fg.`1 x, fg.`2 x)). + + lemma dfun_condE ['u]: + forall (X : plaintext -> bool) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext -> 'u) * (plaintext -> 'u)) (x : plaintext) => + if X x then tf.`1 x else tf.`2 x) = + dfun (fun (x : plaintext) => if X x then dt x else df x). + + lemma dlet_dfun_cond ['u]: + forall (dX : plaintext -> bool distr) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dX x)) => + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : plaintext -> bool) => dfun (fun (x : plaintext) => if X x then dt x else df x)) = + dfun (fun (x : plaintext) => dlet (dX x) (fun (b : bool) => if b then dt x else df x)). + + lemma dfun_dcondE ['u]: + forall (dX : plaintext -> bool distr) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dX x)) => + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : plaintext -> bool) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext -> 'u) * (plaintext -> 'u)) (x : plaintext) => + if X x then tf.`1 x else tf.`2 x)) = + dfun (fun (x : plaintext) => (mu1 (dX x) true \cdot dt x) + (mu1 (dX x) false \cdot df x)). + end MUniFinFun. + + module FunRO = { + var f : plaintext -> W8.t Array32.t + + proc init() : unit = { + FunRO.f <$ (dfun (fun (_ : plaintext) => srand))%MUniFinFun; + } + + proc get(x : plaintext) : W8.t Array32.t = { + return FunRO.f x; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = { + FunRO.f <- fun (z : plaintext) => if z = x then y else FunRO.f z; + } + + proc sample(x : plaintext) : unit = {} + + proc rem(x : plaintext) : unit = {} + }. + + lemma FinRO_FunRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ), + equiv[ MainD(D, FinRO).distinguish ~ MainD(D, FunRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_FinRO_FunRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ) &m (x : unit) + (p : bool -> bool), + Pr[MainD(D, FinRO).distinguish(x) @ &m : p res] = Pr[MainD(D, FunRO).distinguish(x) @ &m : p res]. + end FinEager. + end RO. + + module type Oracle = { + proc init() : unit {} + + proc get(x : plaintext) : W8.t Array32.t {} + } + + module type POracle = { + proc get(x : plaintext) : W8.t Array32.t {} + } + + module type Scheme(H : POracle) = { + proc kg() : publickey * skey {H.get} + + proc enc(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t {H.get} + + proc dec(sk : skey, c : W8.t Array960.t * W8.t Array128.t) : plaintext option {H.get} + } + + module type Adversary(H : POracle) = { + proc choose(pk : publickey) : plaintext * plaintext {H.get} + + proc guess(c : W8.t Array960.t * W8.t Array128.t) : bool {H.get} + } + + module CPA(H : Oracle, S : Scheme, A : Adversary) = { + module A = A(H) + + proc main() : bool = { + var pk : publickey; + var sk : skey; + var m0 : plaintext; + var m1 : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var b : bool; + var b' : bool; + + H.init(); + (pk, sk) <@ S(H).kg(); + (m0, m1) <@ CPA(H, S, A).A.choose(pk); + b <$ {0,1}; + c <@ S(H).enc(pk, if b then m1 else m0); + b' <@ CPA(H, S, A).A.guess(c); + + return b' = b; + } + }. + + module CPA_L(H : Oracle, S : Scheme, A : Adversary) = { + module A = A(H) + + proc main() : bool = { + var pk : publickey; + var sk : skey; + var m0 : plaintext; + var m1 : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var b' : bool; + + H.init(); + (pk, sk) <@ S(H).kg(); + (m0, m1) <@ CPA_L(H, S, A).A.choose(pk); + c <@ S(H).enc(pk, m0); + b' <@ CPA_L(H, S, A).A.guess(c); + + return b'; + } + }. + + module CPA_R(H : Oracle, S : Scheme, A : Adversary) = { + module A = A(H) + + proc main() : bool = { + var pk : publickey; + var sk : skey; + var m0 : plaintext; + var m1 : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var b' : bool; + + H.init(); + (pk, sk) <@ S(H).kg(); + (m0, m1) <@ CPA_R(H, S, A).A.choose(pk); + c <@ S(H).enc(pk, m1); + b' <@ CPA_R(H, S, A).A.guess(c); + + return b'; + } + }. + + theory LorR. + module type A = { + proc main(x : unit) : bool {} + } + + module RandomLR(L : A, R : A) = { + proc main(x : unit) : bool = { + var b : bool; + var r : bool; + + b <$ {0,1}; + if (b) + r <@ L.main(x); + else + r <@ R.main(x); + + return b = r; + } + }. + + lemma pr_AdvLR_AdvRndLR: + forall (L R <: A) &m (x' : unit), + Pr[R.main(x') @ &m : true] = 1%r => + `|Pr[L.main(x') @ &m : res] - Pr[R.main(x') @ &m : res]| = + 2%r * `|Pr[RandomLR(L, R).main(x') @ &m : res] - 1%r / 2%r|. + end LorR. + + lemma pr_CPA_LR: + forall (S(H : POracle) <: Scheme) (H <: Oracle{+all mem, -S} ) + (A(H0 : POracle) <: Adversary{+all mem, -S, -H} ) &m, + islossless S(H).kg => + islossless S(H).enc => + islossless A(H).choose => + islossless A(H).guess => + islossless H.init => + `|Pr[CPA_L(H, S, A).main() @ &m : res] - Pr[CPA_R(H, S, A).main() @ &m : res]| = + 2%r * `|Pr[CPA(H, S, A).main() @ &m : res] - 1%r / 2%r|. + + module type CCA_ORC = { + proc dec(c : W8.t Array960.t * W8.t Array128.t) : plaintext option {} + } + + module type CCA_ADV(H : POracle, O : CCA_ORC) = { + proc choose(pk : publickey) : plaintext * plaintext {O.dec, H.get} + + proc guess(c : W8.t Array960.t * W8.t Array128.t) : bool {O.dec} + } + + module CCA(H : Oracle, S : Scheme, A : CCA_ADV) = { + var cstar : (W8.t Array960.t * W8.t Array128.t) option + + var sk : skey + + module O = { + proc dec(c : W8.t Array960.t * W8.t Array128.t) : plaintext option = { + var m : plaintext option; + + m <- None; + if (Some c <> CCA.cstar) + m <@ S(H).dec(CCA.sk, c); + + return m; + } + } + + module A = A(H, CCA(H, S, A).O) + + proc main() : bool = { + var pk : publickey; + var m0 : plaintext; + var m1 : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var b : bool; + var b' : bool; + + H.init(); + CCA.cstar <- None; + (pk, CCA.sk) <@ S(H).kg(); + (m0, m1) <@ CCA(H, S, A).A.choose(pk); + b <$ {0,1}; + c <@ S(H).enc(pk, if b then m1 else m0); + CCA.cstar <- Some c; + b' <@ CCA(H, S, A).A.guess(c); + + return b' = b; + } + }. + + module type CORR_ADV(H : POracle) = { + proc find(pk : publickey, sk : skey) : plaintext {H.get} + } + + module Correctness_Adv(H : Oracle, S : Scheme, A : CORR_ADV) = { + module A = A(H) + + proc main() : bool = { + var pk : publickey; + var sk : skey; + var c : W8.t Array960.t * W8.t Array128.t; + var m : plaintext; + var m' : plaintext option; + + H.init(); + (pk, sk) <@ S(H).kg(); + m <@ Correctness_Adv(H, S, A).A.find(pk, sk); + c <@ S(H).enc(pk, m); + m' <@ S(H).dec(sk, c); + + return m' <> Some m; + } + }. + + module type VA_ORC = { + proc cvo(c : W8.t Array960.t * W8.t Array128.t) : bool {} + + proc pco(m : plaintext, c : W8.t Array960.t * W8.t Array128.t) : bool {} + } + + module type PCVA_ADV(H : POracle, O : VA_ORC) = { + proc find(pk : publickey, c : W8.t Array960.t * W8.t Array128.t) : plaintext option {O.cvo, O.pco, H.get} + } + + hint solve 0 lossless : . + + hint solve 0 random : . + + module OW_PCVA(H : Oracle, S : Scheme, A : PCVA_ADV) = { + var sk : skey + + var cc : W8.t Array960.t * W8.t Array128.t + + module O = { + proc cvo(c : W8.t Array960.t * W8.t Array128.t) : bool = { + var m : plaintext option; + + m <- None; + if (c <> OW_PCVA.cc) + m <@ S(H).dec(OW_PCVA.sk, c); + + return m <> None; + } + + proc pco(m : plaintext, c : W8.t Array960.t * W8.t Array128.t) : bool = { + var m' : plaintext option; + + m' <@ S(H).dec(OW_PCVA.sk, c); + + return m' = Some m; + } + } + + module A = A(H, OW_PCVA(H, S, A).O) + + proc main() : bool = { + var pk : publickey; + var m : plaintext; + var m' : plaintext option; + var b : bool; + + H.init(); + (pk, OW_PCVA.sk) <@ S(H).kg(); + m <$ srand; + OW_PCVA.cc <@ S(H).enc(pk, m); + m' <@ OW_PCVA(H, S, A).A.find(pk, OW_PCVA.cc); + b <@ OW_PCVA(H, S, A).O.pco(oget m', OW_PCVA.cc); + + return if m' = None then false else b; + } + }. + end PKEROM. + + op qH : int. + + axiom ge0_qH: 0 <= qH. + + op qV : int. + + axiom ge0_qV: 0 <= qV. + + op qP : int. + + axiom ge0_qP: 0 <= qP. + + op qHC : int. + + axiom ge0_qHC: 0 <= qHC. + + module TT(H0 : PKEROM.POracle) = { + proc kg() : publickey * PKEROM.skey = { + var kpair : publickey * W8.t Array1152.t; + + kpair <$ UU.TT.kg; + + return (kpair.`1, kpair); + } + + proc enc(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = { + var r : W8.t Array32.t; + var c : W8.t Array960.t * W8.t Array128.t; + + r <@ H0.get(m); + c <- enc r pk m; + + return c; + } + + proc dec(sk : PKEROM.skey, c : W8.t Array960.t * W8.t Array128.t) : plaintext option = { + var m' : plaintext option; + var r : W8.t Array32.t; + var c' : W8.t Array960.t * W8.t Array128.t; + var rv : plaintext option; + + rv <- None; + m' <- dec sk.`2 c; + if (m' <> None) { + r <@ H0.get(oget m'); + c' <- enc r sk.`1 (oget m'); + rv <- if c = c' then m' else None; + } + + return rv; + } + }. + + module CO1(O : PKEROM.RO.RO) = { + var pk : publickey + + var sk : W8.t Array1152.t + + var counter : int + + var i : int + + var queried : plaintext list + + var bad : bool + + proc get(x : plaintext) : W8.t Array32.t = { + var y : W8.t Array32.t; + + y <- witness; + if (! (x \in CO1.queried)) { + if (size CO1.queried = CO1.i) { + O.sample(x); + CO1.bad <- true; + } + else + y <@ O.get(x); + CO1.queried <- CO1.queried ++ [x]; + } + else + if (find (pred1 x) CO1.queried <> CO1.i) + y <@ O.get(x); + CO1.counter <- CO1.counter + 1; + + return y; + } + }. + + module Correctness_Adv1(O : PKEROM.RO.RO, A : PKEROM.CORR_ADV) = { + module A = A(CO1(O)) + + proc main'(pk : publickey, sk : W8.t Array1152.t, i : int) : plaintext = { + var m : plaintext; + + CO1.i <- i; + O.init(); + CO1.counter <- 0; + CO1.pk <- pk; + CO1.sk <- sk; + CO1.queried <- []; + CO1.bad <- false; + m <@ Correctness_Adv1(O, A).A.find(CO1.pk, (CO1.pk, CO1.sk)); + + return m; + } + + proc main() : unit = { + var m : plaintext; + + (CO1.pk, CO1.sk) <@ BasePKE.kg(); + m <@ Correctness_Adv1(O, A).main'(CO1.pk, CO1.sk, -1); + CO1(O).get(m); + } + }. + + module B(A : PKEROM.CORR_ADV, O : PKEROM.RO.RO) = { + proc find(pk : publickey, sk : W8.t Array1152.t) : plaintext = { + var m : plaintext; + + CO1.i <$ [0..qHC]; + m <@ Correctness_Adv1(O, A).main'(pk, sk, CO1.i); + CO1(O).get(m); + + return + if 0 <= CO1.i && CO1.i < size CO1.queried then nth witness CO1.queried CO1.i + else head witness (filter (fun (x : plaintext) => ! (x \in CO1.queried)) FinT.enum); + } + + proc main() : bool = { + var m : plaintext; + var r : W8.t Array32.t; + var c : W8.t Array960.t * W8.t Array128.t; + var m' : plaintext option; + + (CO1.pk, CO1.sk) <@ BasePKE.kg(); + m <@ B(A, O).find(CO1.pk, CO1.sk); + r <@ O.get(m); + c <- enc r CO1.pk m; + m' <@ BasePKE.dec(CO1.sk, c); + + return m' <> Some m; + } + }. + + module DC0(O : PKEROM.RO.RO) = { + proc distinguish() : bool = PKE.Correctness_Adv(BasePKE, B(A/206960, O)).main + }. + + module DC1(O : PKEROM.RO.RO) = { + proc distinguish() : bool = B(A/206960, O).main + }. + + lemma CO1_lossless: islossless CO1(PKEROM.RO.RO).get. + + lemma corr_pnp: + forall (A(H0 : PKEROM.POracle) <: + PKEROM.CORR_ADV{+all mem, -PKEROM.RO.RO, -PKEROM.RO.FRO, -Correctness_Adv1, -B} ) &m, + qHC < FinT.card - 1 => + (forall (RO0 <: PKEROM.RO.RO{+all mem, -CO1, -A} ), + hoare[ Correctness_Adv1(RO0, A).A.find : CO1.counter = 0 ==> CO1.counter <= qHC]) => + (forall (H0 <: PKEROM.POracle{+all mem, -A} ), islossless H0.get => islossless A(H0).find) => + Pr[Correctness_Adv1(PKEROM.RO.RO, A).main() @ &m : + has (fun (m : plaintext) => Some m <> dec CO1.sk (enc (oget (PKEROM.RO.RO.m.[m])%SmtMap) CO1.pk m)) + CO1.queried] <= + (qHC + 1)%r * Pr[PKE.Correctness_Adv(BasePKE, B(A, PKEROM.RO.RO)).main() @ &m : res]. + + lemma correctness: + forall (A(H0 : PKEROM.POracle) <: + PKEROM.CORR_ADV{+all mem, -PKEROM.RO.RO, -PKEROM.RO.FRO, -Correctness_Adv1, -B} ) &m, + qHC < FinT.card - 1 => + (forall (RO0 <: PKEROM.RO.RO{+all mem, -CO1, -A} ), + hoare[ Correctness_Adv1(RO0, A).A.find : CO1.counter = 0 ==> CO1.counter <= qHC]) => + (forall (H0 <: PKEROM.POracle{+all mem, -A} ), islossless H0.get => islossless A(H0).find) => + Pr[PKEROM.Correctness_Adv(PKEROM.RO.RO, TT, A).main() @ &m : res] <= + (qHC + 1)%r * Pr[PKE.Correctness_Adv(BasePKE, B(A, PKEROM.RO.RO)).main() @ &m : res]. + + module CountO(O : PKEROM.VA_ORC) = { + var c_cvo : int + + var c_pco : int + + var c_h : int + + proc init() : unit = { + CountO.c_h <- 0; + CountO.c_cvo <- 0; + CountO.c_pco <- 0; + } + + proc cvo(c : W8.t Array960.t * W8.t Array128.t) : bool = { + var r : bool; + + r <@ O.cvo(c); + CountO.c_cvo <- CountO.c_cvo + 1; + + return r; + } + + proc pco(m : plaintext, c : W8.t Array960.t * W8.t Array128.t) : bool = { + var r : bool; + + r <@ O.pco(m, c); + CountO.c_pco <- CountO.c_pco + 1; + + return r; + } + }. + + module CountH(H0 : PKEROM.POracle) = { + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <@ H0.get(x); + CountO.c_h <- CountO.c_h + 1; + + return r; + } + }. + + module Gm = { + var m : plaintext + + var r : W8.t Array32.t + + var log : (plaintext, W8.t Array32.t) SmtMap.fmap + + var bad_corr : plaintext option + }. + + module O_AdvOW = { + var pk : publickey + + proc pco(m : plaintext, c : W8.t Array960.t * W8.t Array128.t) : bool = { + var r : W8.t Array32.t; + var c' : W8.t Array960.t * W8.t Array128.t; + + r <@ PKEROM.RO.RO.get(m); + c' <- enc r O_AdvOW.pk m; + + return c = c'; + } + + proc cvo(c : W8.t Array960.t * W8.t Array128.t) : bool = { + var rv : bool; + + rv <- false; + if (c <> PKEROM.OW_PCVA.cc) + rv <- find (fun (m : plaintext) (r : W8.t Array32.t) => c = enc r O_AdvOW.pk m) PKEROM.RO.RO.m <> None; + + return rv; + } + }. + + module AdvOW(A : PKEROM.PCVA_ADV) = { + module A = A(CountH(PKEROM.RO.RO), CountO(O_AdvOW)) + + proc find(pk0 : publickey, c : W8.t Array960.t * W8.t Array128.t) : plaintext option = { + var m' : plaintext option; + + O_AdvOW.pk <- pk0; + PKEROM.RO.RO.init(); + PKEROM.OW_PCVA.cc <- c; + CountO(O_AdvOW).init(); + m' <@ AdvOW(A).A.find(O_AdvOW.pk, PKEROM.OW_PCVA.cc); + + return m'; + } + }. + + module AdvOW_query(A : PKEROM.PCVA_ADV) = { + module A = A(CountH(PKEROM.RO.RO), CountO(O_AdvOW)) + + proc main(pk0 : publickey, c : W8.t Array960.t * W8.t Array128.t) : unit = { + var m' : plaintext option; + + O_AdvOW.pk <- pk0; + PKEROM.RO.RO.init(); + PKEROM.OW_PCVA.cc <- c; + CountO(O_AdvOW).init(); + m' <@ AdvOW_query(A).A.find(O_AdvOW.pk, PKEROM.OW_PCVA.cc); + } + + proc find(pk0 : publickey, c : W8.t Array960.t * W8.t Array128.t) : plaintext option = { + var i : int; + + AdvOW_query(A).main(pk0, c); + i <$ [0..qH + qP - 1]; + + return Some (nth witness ((elems ((fdom PKEROM.RO.RO.m))%SmtMap))%FSet i); + } + }. + + module AdvOWL_query(A : PKEROM.PCVA_ADV) = { + proc find(pk0 : publickey, c : W8.t Array960.t * W8.t Array128.t) : plaintext list = { + AdvOW_query(A).find(pk0, c); + + return (elems ((fdom PKEROM.RO.RO.m))%SmtMap)%FSet; + } + }. + + module type PCOT = { + proc pco(m : plaintext, c : W8.t Array960.t * W8.t Array128.t) : bool {} + } + + module type GT(PCO : PCOT, RO0 : PKEROM.RO.RO) = { + proc distinguish() : bool {RO0.init, RO0.get, RO0.set, RO0.rem, RO0.sample, PCO.pco} + } + + module H(RO0 : PKEROM.POracle) = { + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + if ((x \notin Gm.log)%SmtMap) { + r <@ RO0.get(x); + Gm.log.[x] <- r; + } + + return oget (Gm.log.[x])%SmtMap; + } + }. + + op incl ['a, 'b] (m1 m2 : ('a, 'b) SmtMap.fmap) : bool = + forall (x : 'a), (x \in m1)%SmtMap => (m1.[x])%SmtMap = (m2.[x])%SmtMap. + + pred gamma_spread_ok (gamma_spread : real) = + forall (pk : publickey) (sk : W8.t Array1152.t) (m : plaintext) (c : W8.t Array960.t * W8.t Array128.t), + (pk, sk) \in UU.TT.kg => mu srand (fun (r : W8.t Array32.t) => enc r pk m = c) <= gamma_spread. + + module G2_O(RO0 : PKEROM.POracle) = { + proc pco(m : plaintext, c : W8.t Array960.t * W8.t Array128.t) : bool = { + var m' : plaintext option; + var r : W8.t Array32.t; + var c' : W8.t Array960.t * W8.t Array128.t; + + m' <- dec PKEROM.OW_PCVA.sk.`2 c; + r <@ H(RO0).get(m); + c' <- enc r PKEROM.OW_PCVA.sk.`1 m; + Gm.bad_corr <- if c = c' /\ m' <> Some m then Some m else Gm.bad_corr; + + return c = c'; + } + + proc cvo(c : W8.t Array960.t * W8.t Array128.t) : bool = { + var rv : bool; + var m' : plaintext option; + + rv <- false; + if (c <> PKEROM.OW_PCVA.cc) { + m' <- dec PKEROM.OW_PCVA.sk.`2 c; + rv <- find (fun (m : plaintext) (r : W8.t Array32.t) => c = enc r PKEROM.OW_PCVA.sk.`1 m) Gm.log <> None; + if (m' <> None) + if ((oget m' \in Gm.log)%SmtMap) + Gm.bad_corr <- + let f = find (fun (m : plaintext) (r : W8.t Array32.t) => c = enc r PKEROM.OW_PCVA.sk.`1 m) Gm.log + in if f <> None /\ (oget f).`1 <> oget m' then Some (oget f).`1 else Gm.bad_corr; + else + if (oget m' = Gm.m) + Gm.bad_corr <- + let f = + find (fun (m : plaintext) (r : W8.t Array32.t) => c = enc r PKEROM.OW_PCVA.sk.`1 m) Gm.log in + if f <> None /\ (oget f).`1 <> oget m' then Some (oget f).`1 else Gm.bad_corr; + else + Gm.bad_corr <- + let f = + find (fun (m : plaintext) (r : W8.t Array32.t) => c = enc r PKEROM.OW_PCVA.sk.`1 m) Gm.log in + if f <> None then Some (oget f).`1 else Gm.bad_corr; + else + Gm.bad_corr <- + let f = find (fun (m : plaintext) (r : W8.t Array32.t) => c = enc r PKEROM.OW_PCVA.sk.`1 m) Gm.log in + if f <> None then Some (oget f).`1 else Gm.bad_corr; + } + + return rv; + } + }. + + module AdvCorr(A : PKEROM.PCVA_ADV, RO0 : PKEROM.POracle) = { + module H = H(RO0) + + module O = G2_O(RO0) + + module A = A(CountH(AdvCorr(A, RO0).H), CountO(AdvCorr(A, RO0).O)) + + proc find(pk : publickey, sk : PKEROM.skey) : plaintext = { + var m' : plaintext option; + + PKEROM.OW_PCVA.sk <- sk; + Gm.log <- SmtMap.empty; + Gm.bad_corr <- None; + Gm.m <$ srand; + Gm.r <@ RO0.get(Gm.m); + PKEROM.OW_PCVA.cc <- enc Gm.r pk Gm.m; + CountO(AdvCorr(A, RO0).O).init(); + m' <@ AdvCorr(A, RO0).A.find(pk, PKEROM.OW_PCVA.cc); + Gm.bad_corr <- if dec sk.`2 PKEROM.OW_PCVA.cc = m' /\ Some Gm.m <> m' then Some Gm.m else Gm.bad_corr; + + return oget Gm.bad_corr; + } + }. + + op inv_G2_corr (log ro : (plaintext, W8.t Array32.t) SmtMap.fmap) (sk : PKEROM.skey) + (bad_corr : plaintext option) (gm : plaintext) (gr : W8.t Array32.t) : bool = + incl log ro /\ + (gm \in ro)%SmtMap /\ + gr = oget (ro.[gm])%SmtMap /\ + (bad_corr <> None => + let m = oget bad_corr in + let r = oget (ro.[m])%SmtMap in + let c = enc r sk.`1 m in let m' = dec sk.`2 c in (m \in ro)%SmtMap /\ m' <> Some m). + + lemma corr_red_count: + forall (A(H0 : PKEROM.POracle, O : PKEROM.VA_ORC) <: + PKEROM.PCVA_ADV{+all mem, -PKE.OW_CPA, -PKE.BOWp, -PKE.OWL_CPA, -PKE.OWvsIND.Bowl, -PKEROM.RO.RO, -PKEROM.RO.FRO, -PKEROM.OW_PCVA, -Correctness_Adv1, -B, -CountO, -Gm, -O_AdvOW} + ) (RO0 <: PKEROM.RO.RO{+all mem, -PKEROM.OW_PCVA, -CO1, -CountO, -Gm, -A} ), + (forall (RO1 <: PKEROM.POracle{+all mem, -CountO, -A} ) (O <: PKEROM.VA_ORC{+all mem, -CountO, -A} ), + hoare[ A(CountH(RO1), CountO(O)).find : + CountO.c_h = 0 /\ CountO.c_cvo = 0 /\ CountO.c_pco = 0 ==> + CountO.c_h <= qH /\ CountO.c_cvo <= qV /\ CountO.c_pco <= qP]) => + hoare[ AdvCorr(A, CO1(RO0)).A.find : + CO1.counter = 1 /\ CountO.c_h = 0 /\ CountO.c_cvo = 0 /\ CountO.c_pco = 0 ==> + CO1.counter <= 1 + qH + qP]. + + lemma pre_conclusion: + forall (A(H0 : PKEROM.POracle, O : PKEROM.VA_ORC) <: + PKEROM.PCVA_ADV{+all mem, -PKE.OW_CPA, -PKE.BOWp, -PKE.OWL_CPA, -PKE.OWvsIND.Bowl, -PKEROM.RO.RO, -PKEROM.RO.FRO, -PKEROM.OW_PCVA, -Correctness_Adv1, -B, -CountO, -Gm, -O_AdvOW} + ) &m (gamma_spread : real), + gamma_spread_ok gamma_spread => + (forall (RO0 <: PKEROM.POracle{+all mem, -CountO, -A} ) (O <: PKEROM.VA_ORC{+all mem, -CountO, -A} ), + hoare[ A(CountH(RO0), CountO(O)).find : + CountO.c_h = 0 /\ CountO.c_cvo = 0 /\ CountO.c_pco = 0 ==> + CountO.c_h <= qH /\ CountO.c_cvo <= qV /\ CountO.c_pco <= qP]) => + (forall (H0 <: PKEROM.POracle{+all mem, -A} ) (O <: PKEROM.VA_ORC{+all mem, -A} ), + islossless O.cvo => islossless O.pco => islossless H0.get => islossless A(H0, O).find) => + Pr[PKEROM.OW_PCVA(PKEROM.RO.LRO, TT, A).main() @ &m : res] <= + Pr[PKE.OW_CPA(BasePKE, AdvOW(A)).main() @ &m : res] + + (qH + qP)%r * Pr[PKE.OW_CPA(BasePKE, AdvOW_query(A)).main() @ &m : res] + + Pr[PKEROM.Correctness_Adv(PKEROM.RO.RO, TT, AdvCorr(A)).main() @ &m : res] + + qV%r * gamma_spread. + + lemma conclusion: + forall (A(H0 : PKEROM.POracle, O : PKEROM.VA_ORC) <: + PKEROM.PCVA_ADV{+all mem, -PKE.OW_CPA, -PKE.BOWp, -PKE.OWL_CPA, -PKE.OWvsIND.Bowl, -PKEROM.RO.RO, -PKEROM.RO.FRO, -PKEROM.OW_PCVA, -Correctness_Adv1, -B, -CountO, -Gm, -O_AdvOW} + ) &m (gamma_spread : real), + qH + qP + 1 = qHC => + qHC < FinT.card - 1 => + gamma_spread_ok gamma_spread => + (forall (RO0 <: PKEROM.POracle{+all mem, -CountO, -A} ) (O <: PKEROM.VA_ORC{+all mem, -CountO, -A} ), + hoare[ A(CountH(RO0), CountO(O)).find : + CountO.c_h = 0 /\ CountO.c_cvo = 0 /\ CountO.c_pco = 0 ==> + CountO.c_h <= qH /\ CountO.c_cvo <= qV /\ CountO.c_pco <= qP]) => + (forall (H0 <: PKEROM.POracle{+all mem, -A} ) (O <: PKEROM.VA_ORC{+all mem, -A} ), + islossless O.cvo => islossless O.pco => islossless H0.get => islossless A(H0, O).find) => + Pr[PKEROM.OW_PCVA(PKEROM.RO.LRO, TT, A).main() @ &m : res] <= + Pr[PKE.OW_CPA(BasePKE, AdvOW(A)).main() @ &m : res] + + (qH + qP)%r * Pr[PKE.OW_CPA(BasePKE, AdvOW_query(A)).main() @ &m : res] + + (qH + qP + 2)%r * Pr[PKE.Correctness_Adv(BasePKE, B(AdvCorr(A), PKEROM.RO.RO)).main() @ &m : res] + + qV%r * gamma_spread. + + lemma pre_conclusion_cpa: + forall (A(H0 : PKEROM.POracle, O : PKEROM.VA_ORC) <: + PKEROM.PCVA_ADV{+all mem, -PKE.OW_CPA, -PKE.BOWp, -PKE.OWL_CPA, -PKE.OWvsIND.Bowl, -PKEROM.RO.RO, -PKEROM.RO.FRO, -PKEROM.OW_PCVA, -Correctness_Adv1, -B, -CountO, -Gm, -O_AdvOW} + ) &m (gamma_spread : real), + gamma_spread_ok gamma_spread => + (forall (RO0 <: PKEROM.POracle{+all mem, -CountO, -A} ) (O <: PKEROM.VA_ORC{+all mem, -CountO, -A} ), + hoare[ A(CountH(RO0), CountO(O)).find : + CountO.c_h = 0 /\ CountO.c_cvo = 0 /\ CountO.c_pco = 0 ==> + CountO.c_h <= qH /\ CountO.c_cvo <= qV /\ CountO.c_pco <= qP]) => + (forall (H0 <: PKEROM.POracle{+all mem, -A} ) (O <: PKEROM.VA_ORC{+all mem, -A} ), + islossless O.cvo => islossless O.pco => islossless H0.get => islossless A(H0, O).find) => + Pr[PKEROM.OW_PCVA(PKEROM.RO.LRO, TT, A).main() @ &m : res] <= + 2%r * `|Pr[PKE.CPA(BasePKE, PKE.OWvsIND.Bowl(PKE.OWvsIND.BL(AdvOW(A)))).main() @ &m : res] - 1%r / 2%r| + + 2%r * `|Pr[PKE.CPA(BasePKE, PKE.OWvsIND.Bowl(AdvOWL_query(A))).main() @ &m : res] - 1%r / 2%r| + + Pr[PKE.Correctness_Adv(BasePKE, PKE.BOWp(BasePKE, AdvOW(A))).main() @ &m : res] + + Pr[PKEROM.Correctness_Adv(PKEROM.RO.RO, TT, AdvCorr(A)).main() @ &m : res] + + qV%r * gamma_spread + 2%r * (qH + qP + 1)%r * PKE.eps_msg. + + lemma conclusion_cpa: + forall (A(H0 : PKEROM.POracle, O : PKEROM.VA_ORC) <: + PKEROM.PCVA_ADV{+all mem, -PKE.OW_CPA, -PKE.BOWp, -PKE.OWL_CPA, -PKE.OWvsIND.Bowl, -PKEROM.RO.RO, -PKEROM.RO.FRO, -PKEROM.OW_PCVA, -Correctness_Adv1, -B, -CountO, -Gm, -O_AdvOW} + ) &m (gamma_spread : real), + qH + qP + 1 = qHC => + qHC < FinT.card - 1 => + gamma_spread_ok gamma_spread => + (forall (RO0 <: PKEROM.POracle{+all mem, -CountO, -A} ) (O <: PKEROM.VA_ORC{+all mem, -CountO, -A} ), + hoare[ A(CountH(RO0), CountO(O)).find : + CountO.c_h = 0 /\ CountO.c_cvo = 0 /\ CountO.c_pco = 0 ==> + CountO.c_h <= qH /\ CountO.c_cvo <= qV /\ CountO.c_pco <= qP]) => + (forall (H0 <: PKEROM.POracle{+all mem, -A} ) (O <: PKEROM.VA_ORC{+all mem, -A} ), + islossless O.cvo => islossless O.pco => islossless H0.get => islossless A(H0, O).find) => + Pr[PKEROM.OW_PCVA(PKEROM.RO.LRO, TT, A).main() @ &m : res] <= + 2%r * `|Pr[PKE.CPA(BasePKE, PKE.OWvsIND.Bowl(PKE.OWvsIND.BL(AdvOW(A)))).main() @ &m : res] - 1%r / 2%r| + + 2%r * `|Pr[PKE.CPA(BasePKE, PKE.OWvsIND.Bowl(AdvOWL_query(A))).main() @ &m : res] - 1%r / 2%r| + + Pr[PKE.Correctness_Adv(BasePKE, PKE.BOWp(BasePKE, AdvOW(A))).main() @ &m : res] + + (qH + qP + 2)%r * Pr[PKE.Correctness_Adv(BasePKE, B(AdvCorr(A), PKEROM.RO.RO)).main() @ &m : res] + + qV%r * gamma_spread + 2%r * (qH + qP + 1)%r * PKE.eps_msg. + end TT. + + lemma dkey_ll: is_lossless srand. + + hint solve 0 lossless : dkey_ll. + + hint solve 0 random : dkey_ll. + + lemma dkey_uni: is_uniform srand. + + hint solve 0 random : dkey_uni. + + lemma dkey_fu: is_full srand. + + hint solve 0 random : dkey_fu. + + theory J. + module type PRF = { + proc init() : unit {} + + proc f(_ : W8.t Array960.t * W8.t Array128.t) : sharedsecret {} + } + + module type PRF_Oracles = { + proc f(_ : W8.t Array960.t * W8.t Array128.t) : sharedsecret {} + } + + module type Distinguisher(F : PRF_Oracles) = { + proc distinguish() : bool {F.f} + } + + module IND(F : PRF, D : Distinguisher) = { + proc main() : bool = { + var b : bool; + + F.init(); + b <@ D(F).distinguish(); + + return b; + } + }. + + abstract theory RF. + op dR : W8.t Array960.t * W8.t Array128.t -> sharedsecret distr. + + axiom dR_ll: forall (x : W8.t Array960.t * W8.t Array128.t), is_lossless (dR x). + + module RF = { + var m : (W8.t Array960.t * W8.t Array128.t, sharedsecret) SmtMap.fmap + + proc init() : unit = { + RF.m <- SmtMap.empty; + } + + proc f(x : W8.t Array960.t * W8.t Array128.t) : sharedsecret = { + var r : sharedsecret; + + if ((x \notin RF.m)%SmtMap) { + r <$ dR x; + RF.m.[x] <- r; + } + + return oget (RF.m.[x])%SmtMap; + } + }. + end RF. + + abstract theory PseudoRF. + type K. + + op dK : K distr. + + axiom dK_ll: is_lossless dK. + + op F : K -> W8.t Array960.t * W8.t Array128.t -> sharedsecret. + + module type PseudoRF = { + proc keygen() : K {} + + proc f(_ : K * (W8.t Array960.t * W8.t Array128.t)) : sharedsecret {} + } + + module PseudoRF = { + proc keygen() : K = { + var k : K; + + k <$ dK; + + return k; + } + + proc f(k : K, x : W8.t Array960.t * W8.t Array128.t) : sharedsecret = { + return F k x; + } + }. + + module PRF = { + var k : K + + proc init() : unit = { + PRF.k <$ dK; + } + + proc f(x : W8.t Array960.t * W8.t Array128.t) : sharedsecret = { + return F PRF.k x; + } + }. + end PseudoRF. + end J. + + theory RF. + lemma dR_ll: forall (_ : W8.t Array960.t * W8.t Array128.t), is_lossless srand. + + module RF = { + var m : (W8.t Array960.t * W8.t Array128.t, sharedsecret) SmtMap.fmap + + proc init() : unit = { + RF.m <- SmtMap.empty; + } + + proc f(x : W8.t Array960.t * W8.t Array128.t) : sharedsecret = { + var r : sharedsecret; + + if ((x \notin RF.m)%SmtMap) { + r <$ srand; + RF.m.[x] <- r; + } + + return oget (RF.m.[x])%SmtMap; + } + }. + end RF. + + theory PseudoRF. + lemma dK_ll: is_lossless srand. + + module type PseudoRF = { + proc keygen() : sharedsecret {} + + proc f(_ : sharedsecret * (W8.t Array960.t * W8.t Array128.t)) : sharedsecret {} + } + + module PseudoRF = { + proc keygen() : sharedsecret = { + var k : sharedsecret; + + k <$ srand; + + return k; + } + + proc f(k : sharedsecret, x : W8.t Array960.t * W8.t Array128.t) : sharedsecret = { + return J k x; + } + }. + + module PRF = { + var k : sharedsecret + + proc init() : unit = { + PRF.k <$ srand; + } + + proc f(x : W8.t Array960.t * W8.t Array128.t) : sharedsecret = { + return J PRF.k x; + } + }. + end PseudoRF. + + theory KEMROMx2. + type skey = (publickey * W8.t Array1152.t) * sharedsecret. + + lemma dkey_ll: is_lossless srand. + + hint solve 0 lossless : dkey_ll. + + hint solve 0 random : dkey_ll. + + lemma dkey_uni: is_uniform srand. + + hint solve 0 random : dkey_uni. + + lemma dkey_fu: is_full srand. + + hint solve 0 random : dkey_fu. + + theory RO1. + module type RO = { + proc init() : unit {} + + proc get(x : plaintext) : W8.t Array32.t {} + + proc set(x : plaintext, y : W8.t Array32.t) : unit {} + + proc rem(x : plaintext) : unit {} + + proc sample(x : plaintext) : unit {} + } + + module type RO_Distinguisher(G : RO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.rem, G.sample} + } + + module MainD(D : RO_Distinguisher, RO0 : RO) = { + proc distinguish(x : unit) : bool = { + var r : bool; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + module type ROmap = { + proc init() : unit {} + + proc get(x : plaintext) : W8.t Array32.t {} + + proc set(x : plaintext, y : W8.t Array32.t) : unit {} + + proc rem(x : plaintext) : unit {} + + proc sample(x : plaintext) : unit {} + + proc restrK() : (plaintext, W8.t Array32.t) SmtMap.fmap {} + } + + module type FRO = { + proc init() : unit {} + + proc get(x : plaintext) : W8.t Array32.t {} + + proc set(x : plaintext, y : W8.t Array32.t) : unit {} + + proc rem(x : plaintext) : unit {} + + proc sample(x : plaintext) : unit {} + + proc queried(x : plaintext, f : PROM.flag) : bool {} + + proc allKnown() : (plaintext, W8.t Array32.t) SmtMap.fmap {} + } + + module type FRO_Distinguisher(G : FRO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.rem, G.sample, G.queried, G.allKnown} + } + + abstract theory MkRO. + module RO = { + var m : (plaintext, W8.t Array32.t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : plaintext) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : plaintext) : unit = { + RO.get(x); + } + + proc restrK() : (plaintext, W8.t Array32.t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : plaintext) : W8.t Array32.t = RO.get + + proc set(x : plaintext, y : W8.t Array32.t) : unit = RO.set + + proc rem(x : plaintext) : unit = RO.rem + + proc sample(x : plaintext) : unit = {} + }. + end MkRO. + + module RO = { + var m : (plaintext, W8.t Array32.t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : plaintext) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : plaintext) : unit = { + RO.get(x); + } + + proc restrK() : (plaintext, W8.t Array32.t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : plaintext) : W8.t Array32.t = RO.get + + proc set(x : plaintext, y : W8.t Array32.t) : unit = RO.set + + proc rem(x : plaintext) : unit = RO.rem + + proc sample(x : plaintext) : unit = {} + }. + + module FRO = { + var m : (plaintext, W8.t Array32.t * PROM.flag) SmtMap.fmap + + proc init() : unit = { + FRO.m <- SmtMap.empty; + } + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \in FRO.m)%SmtMap) + r <- (oget (FRO.m.[x])%SmtMap).`1; + FRO.m.[x] <- (r, PROM.Known); + + return r; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = { + FRO.m.[x] <- (y, PROM.Known); + } + + proc rem(x : plaintext) : unit = { + FRO.m <- (rem FRO.m x)%SmtMap; + } + + proc sample(x : plaintext) : unit = { + var c : W8.t Array32.t; + + c <$ srand; + if ((x \notin FRO.m)%SmtMap) + FRO.m.[x] <- (c, PROM.Unknown); + } + + proc queried(x : plaintext, f : PROM.flag) : bool = { + return (x \in FRO.m)%SmtMap /\ ((FRO.m.[x])%SmtMap \is f)%PROM; + } + + proc allKnown() : (plaintext, W8.t Array32.t) SmtMap.fmap = { + return (restr PROM.Known FRO.m)%SmtMap; + } + }. + + lemma RO_FRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_get: + equiv[ RO.get ~ FRO.get : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> ={res} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_sample: + equiv[ RO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(RO).distinguish ~ D(FRO).distinguish : + ={arg, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_init_ll: islossless RO.init. + + lemma FRO_init_ll: islossless FRO.init. + + lemma FRO_in_dom_ll: islossless FRO.queried. + + lemma FRO_restrK_ll: islossless FRO.allKnown. + + lemma RO_set_ll: islossless RO.set. + + lemma FRO_set_ll: islossless FRO.set. + + lemma RO_get_ll: (forall (_ : plaintext), is_lossless srand) => islossless RO.get. + + lemma FRO_get_ll: (forall (_ : plaintext), is_lossless srand) => islossless FRO.get. + + lemma RO_sample_ll: (forall (_ : plaintext), is_lossless srand) => islossless RO.sample. + + lemma FRO_sample_ll: (forall (_ : plaintext), is_lossless srand) => islossless FRO.sample. + + module type RM_Distinguisher(G : ROmap) = { + proc distinguish(_ : unit) : bool {G.get, G.sample, G.restrK} + } + + module MainRM(D : RM_Distinguisher, RO0 : ROmap) = { + proc distinguish(x : unit) : bool = { + var r : bool; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + lemma fcoll_bound ['rT]: + forall (f : W8.t Array32.t -> 'rT) (Pc : real), + 0%r <= Pc => + (forall (_ _ : plaintext) (y : 'rT), y \in dmap srand f => mu1 (dmap srand f) y <= Pc) => + forall (q : int), + 0 <= q => + forall (D(G : ROmap) <: RM_Distinguisher{+all mem, -RO} ) &m (z : unit), + Pr[MainRM(D, RO).distinguish(z) @ &m : (fcoll f RO.m)%SmtMap /\ (fsize RO.m)%SmtMap <= q] <= + (q * (q - 1))%r / 2%r * Pc. + + theory FullEager. + abstract theory EagerCore. + axiom dout_ll: forall (_ : plaintext), is_lossless srand. + + module type Orcl = { + proc f(x : plaintext) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : plaintext list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : plaintext list, l2 : plaintext list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : plaintext, l : plaintext list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : plaintext list) (i : plaintext) (s2 : plaintext list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : plaintext), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : plaintext), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = FRO.set + + proc rem(x : plaintext) : unit = FRO.rem + + proc sample(x : plaintext) : unit = FRO.sample + + proc queried(x : plaintext, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (plaintext, W8.t Array32.t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : plaintext) : unit = { + var c : W8.t Array32.t; + + c <$ srand; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : plaintext) (mx1 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : plaintext) (mx1 mx2 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : plaintext) (r1 : W8.t Array32.t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + end FullEager. + + abstract theory FinEager. + abstract theory EagerCore. + axiom dout_ll: forall (_ : plaintext), is_lossless srand. + + module type Orcl = { + proc f(x : plaintext) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : plaintext list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : plaintext list, l2 : plaintext list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : plaintext, l : plaintext list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : plaintext list) (i : plaintext) (s2 : plaintext list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : plaintext), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : plaintext), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = FRO.set + + proc rem(x : plaintext) : unit = FRO.rem + + proc sample(x : plaintext) : unit = FRO.sample + + proc queried(x : plaintext, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (plaintext, W8.t Array32.t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : plaintext) : unit = { + var c : W8.t Array32.t; + + c <$ srand; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : plaintext) (mx1 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : plaintext) (mx1 mx2 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : plaintext) (r1 : W8.t Array32.t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + theory FinFrom. + type t = plaintext. + + op enum : t list. + + op card : int = size enum. + + axiom enum_spec: forall (x : t), count (pred1 x) enum = 1. + + lemma enumP: forall (x : t), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : t list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinFrom. + + module FinRO = { + proc rem(x : plaintext) : unit = RO.rem + + proc set(x : plaintext, y : W8.t Array32.t) : unit = RO.set + + proc init() : unit = { + var l : FinFrom.t list; + + l <- FinFrom.enum; + RO.init(); + while (l <> []){ + RO.sample(head witness l); + l <- behead l; + } + } + + proc get(x : plaintext) : W8.t Array32.t = { + return oget (RO.m.[x])%SmtMap; + } + + proc sample(x : plaintext) : unit = {} + }. + + module type FinRO_Distinguisher(G : RO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.sample} + } + + lemma RO_FinRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ MainD(D, RO).distinguish ~ MainD(D, FinRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_RO_FinRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ) &m (x : unit) (p : + bool -> bool), + Pr[MainD(D, RO).distinguish(x) @ &m : p res] = Pr[MainD(D, FinRO).distinguish(x) @ &m : p res]. + + theory MUniFinFun. + op mfun ['u] (d : plaintext -> 'u distr) (f : plaintext -> 'u) : real = + (big predT (fun (x : plaintext) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma mfunE ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mfun d f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + lemma isdistr_dfun ['u]: forall (d : plaintext -> 'u distr), isdistr (mfun d). + + op dfun ['u] (d : plaintext -> 'u distr) : (plaintext -> 'u) distr = mk (mfun d). + + lemma dfun1E ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mu1 (dfun d) f = + (big predT (fun (x : plaintext) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun1E_djoin ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mu1 (dfun d) f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + op tofun ['u] (us : 'u list) (x : plaintext) : 'u = nth witness us (index x FinFrom.enum). + + op offun ['u] (f : plaintext -> 'u) : 'u list = map f FinFrom.enum. + + lemma offunK ['u]: cancel offun tofun. + + lemma tofunK ['u]: forall (us : 'u list), size us = FinFrom.card => offun (tofun us) = us. + + lemma dfun_dmap ['u]: forall (d : plaintext -> 'u distr), dfun d = dmap (djoinmap d FinFrom.enum) tofun. + + lemma dfun_supp ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + f \in dfun d <=> forall (x : plaintext), f x \in d x. + + lemma dfun_fu ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_full (d x)) => is_full (dfun d). + + lemma dfun_uni ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_uniform (d x)) => is_uniform (dfun d). + + lemma dfun_funi ['u]: + forall (d : plaintext -> 'u distr), + (forall (x : plaintext), is_uniform (d x)) => + (forall (x : plaintext), is_full (d x)) => is_funiform (dfun d). + + lemma dfunE_djoin ['u]: + forall (du : plaintext -> 'u distr) (E : (plaintext -> 'u) -> bool), + mu (dfun du) E = mu (djoinmap du FinFrom.enum) (E \o tofun). + + lemma dfunE ['u]: + forall (du : plaintext -> 'u distr) (p : plaintext -> 'u -> bool), + mu (dfun du) (fun (f : plaintext -> 'u) => forall (x : plaintext), p x (f x)) = + (big predT (fun (x : plaintext) => mu (du x) (p x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma weight_dfun ['u]: + forall (df : plaintext -> 'u distr), + weight (dfun df) = + (big predT (fun (x : plaintext) => weight (df x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun_ll ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_lossless (d x)) => is_lossless (dfun d). + + lemma dlet_dfun ['u, 'v]: + forall (d1 : plaintext -> 'u distr) (d2 : plaintext -> 'u -> 'v distr), + dlet (dfun d1) (fun (f1 : plaintext -> 'u) => dfun (fun (x : plaintext) => d2 x (f1 x))) = + dfun (fun (x : plaintext) => dlet (d1 x) (fun (x1 : 'u) => d2 x x1)). + + lemma dfun_unit ['u]: forall (f : plaintext -> 'u), dfun (fun (x : plaintext) => dunit (f x)) = dunit f. + + lemma dmap_dfun ['u, 'v]: + forall (d : plaintext -> 'u distr) (F : plaintext -> 'u -> 'v), + dmap (dfun d) (fun (f : plaintext -> 'u) (x : plaintext) => F x (f x)) = + dfun (fun (x : plaintext) => dmap (d x) (fun (fx : 'u) => F x fx)). + + lemma dfun1E_fix1 ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext) (f : + plaintext -> 'u), mu1 (dfun d) f = mu1 (d x0) (f x0) * mu1 (dfun d.[x0 <- dunit (f x0)]) f. + + lemma dlet_dfun_fupdate_ll ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext) (v : 'u), + dlet (dfun d.[x0 <- dunit v]) + (fun (f : plaintext -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + dfun d. + + lemma dfunE_dlet_fix1 ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext), + dfun d = dlet (d x0) (fun (v : 'u) => dfun d.[x0 <- dunit v]). + + lemma dlet_dfun_update ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext), + dlet (dfun d) (fun (f : plaintext -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + (weight (d x0) \cdot dfun d). + + lemma dfun_projE ['u]: + forall (df : plaintext -> 'u distr) (x : plaintext), + dmap (dfun df) (fun (f : plaintext -> 'u) => f x) = (weight (dfun df) / weight (df x) \cdot df x). + + lemma eq_from_dfun_proj_eq ['u]: + forall (df1 df2 : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (df1 x)) => + (forall (x : plaintext), is_lossless (df2 x)) => + (forall (x : plaintext), + dmap (dfun df1) (fun (f : plaintext -> 'u) => f x) = + dmap (dfun df2) (fun (f : plaintext -> 'u) => f x)) => + df1 == df2. + + lemma dfun_prod1E ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr) (f : + plaintext -> 'u) (g : plaintext -> 'v), + mu1 (dfun (fun (x : plaintext) => df x `*` dg x)) (fun (x : plaintext) => (f x, g x)) = + mu1 (dfun df) f * mu1 (dfun dg) g. + + lemma dprod_funE ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr), + dfun df `*` dfun dg = + dmap (dfun (fun (x : plaintext) => df x `*` dg x)) + (fun (fg : plaintext -> 'u * 'v) => + ((fun (p : 'u * 'v) => p.`1) \o fg, (fun (p : 'u * 'v) => p.`2) \o fg)). + + lemma dfun_prodE ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr), + dfun (fun (x : plaintext) => df x `*` dg x) = + dmap (dfun df `*` dfun dg) + (fun (fg : (plaintext -> 'u) * (plaintext -> 'v)) (x : plaintext) => (fg.`1 x, fg.`2 x)). + + lemma dfun_condE ['u]: + forall (X : plaintext -> bool) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext -> 'u) * (plaintext -> 'u)) (x : plaintext) => + if X x then tf.`1 x else tf.`2 x) = + dfun (fun (x : plaintext) => if X x then dt x else df x). + + lemma dlet_dfun_cond ['u]: + forall (dX : plaintext -> bool distr) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dX x)) => + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : plaintext -> bool) => dfun (fun (x : plaintext) => if X x then dt x else df x)) = + dfun (fun (x : plaintext) => dlet (dX x) (fun (b : bool) => if b then dt x else df x)). + + lemma dfun_dcondE ['u]: + forall (dX : plaintext -> bool distr) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dX x)) => + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : plaintext -> bool) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext -> 'u) * (plaintext -> 'u)) (x : plaintext) => + if X x then tf.`1 x else tf.`2 x)) = + dfun (fun (x : plaintext) => (mu1 (dX x) true \cdot dt x) + (mu1 (dX x) false \cdot df x)). + end MUniFinFun. + + module FunRO = { + var f : plaintext -> W8.t Array32.t + + proc init() : unit = { + FunRO.f <$ (dfun (fun (_ : plaintext) => srand))%MUniFinFun; + } + + proc get(x : plaintext) : W8.t Array32.t = { + return FunRO.f x; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = { + FunRO.f <- fun (z : plaintext) => if z = x then y else FunRO.f z; + } + + proc sample(x : plaintext) : unit = {} + + proc rem(x : plaintext) : unit = {} + }. + + lemma FinRO_FunRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ), + equiv[ MainD(D, FinRO).distinguish ~ MainD(D, FunRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_FinRO_FunRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ) &m (x : unit) (p : + bool -> bool), + Pr[MainD(D, FinRO).distinguish(x) @ &m : p res] = Pr[MainD(D, FunRO).distinguish(x) @ &m : p res]. + end FinEager. + end RO1. + + theory RO2. + module type RO = { + proc init() : unit {} + + proc get(x : plaintext) : sharedsecret {} + + proc set(x : plaintext, y : sharedsecret) : unit {} + + proc rem(x : plaintext) : unit {} + + proc sample(x : plaintext) : unit {} + } + + module type RO_Distinguisher(G : RO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.rem, G.sample} + } + + module MainD(D : RO_Distinguisher, RO0 : RO) = { + proc distinguish(x : unit) : bool = { + var r : bool; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + module type ROmap = { + proc init() : unit {} + + proc get(x : plaintext) : sharedsecret {} + + proc set(x : plaintext, y : sharedsecret) : unit {} + + proc rem(x : plaintext) : unit {} + + proc sample(x : plaintext) : unit {} + + proc restrK() : (plaintext, sharedsecret) SmtMap.fmap {} + } + + module type FRO = { + proc init() : unit {} + + proc get(x : plaintext) : sharedsecret {} + + proc set(x : plaintext, y : sharedsecret) : unit {} + + proc rem(x : plaintext) : unit {} + + proc sample(x : plaintext) : unit {} + + proc queried(x : plaintext, f : PROM.flag) : bool {} + + proc allKnown() : (plaintext, sharedsecret) SmtMap.fmap {} + } + + module type FRO_Distinguisher(G : FRO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.rem, G.sample, G.queried, G.allKnown} + } + + abstract theory MkRO. + module RO = { + var m : (plaintext, sharedsecret) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : plaintext) : sharedsecret = { + var r : sharedsecret; + + r <$ srand; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : plaintext, y : sharedsecret) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : plaintext) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : plaintext) : unit = { + RO.get(x); + } + + proc restrK() : (plaintext, sharedsecret) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : plaintext) : sharedsecret = RO.get + + proc set(x : plaintext, y : sharedsecret) : unit = RO.set + + proc rem(x : plaintext) : unit = RO.rem + + proc sample(x : plaintext) : unit = {} + }. + end MkRO. + + module RO = { + var m : (plaintext, sharedsecret) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : plaintext) : sharedsecret = { + var r : sharedsecret; + + r <$ srand; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : plaintext, y : sharedsecret) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : plaintext) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : plaintext) : unit = { + RO.get(x); + } + + proc restrK() : (plaintext, sharedsecret) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : plaintext) : sharedsecret = RO.get + + proc set(x : plaintext, y : sharedsecret) : unit = RO.set + + proc rem(x : plaintext) : unit = RO.rem + + proc sample(x : plaintext) : unit = {} + }. + + module FRO = { + var m : (plaintext, sharedsecret * PROM.flag) SmtMap.fmap + + proc init() : unit = { + FRO.m <- SmtMap.empty; + } + + proc get(x : plaintext) : sharedsecret = { + var r : sharedsecret; + + r <$ srand; + if ((x \in FRO.m)%SmtMap) + r <- (oget (FRO.m.[x])%SmtMap).`1; + FRO.m.[x] <- (r, PROM.Known); + + return r; + } + + proc set(x : plaintext, y : sharedsecret) : unit = { + FRO.m.[x] <- (y, PROM.Known); + } + + proc rem(x : plaintext) : unit = { + FRO.m <- (rem FRO.m x)%SmtMap; + } + + proc sample(x : plaintext) : unit = { + var c : sharedsecret; + + c <$ srand; + if ((x \notin FRO.m)%SmtMap) + FRO.m.[x] <- (c, PROM.Unknown); + } + + proc queried(x : plaintext, f : PROM.flag) : bool = { + return (x \in FRO.m)%SmtMap /\ ((FRO.m.[x])%SmtMap \is f)%PROM; + } + + proc allKnown() : (plaintext, sharedsecret) SmtMap.fmap = { + return (restr PROM.Known FRO.m)%SmtMap; + } + }. + + lemma RO_FRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_get: + equiv[ RO.get ~ FRO.get : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> ={res} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_sample: + equiv[ RO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(RO).distinguish ~ D(FRO).distinguish : + ={arg, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_init_ll: islossless RO.init. + + lemma FRO_init_ll: islossless FRO.init. + + lemma FRO_in_dom_ll: islossless FRO.queried. + + lemma FRO_restrK_ll: islossless FRO.allKnown. + + lemma RO_set_ll: islossless RO.set. + + lemma FRO_set_ll: islossless FRO.set. + + lemma RO_get_ll: (forall (_ : plaintext), is_lossless srand) => islossless RO.get. + + lemma FRO_get_ll: (forall (_ : plaintext), is_lossless srand) => islossless FRO.get. + + lemma RO_sample_ll: (forall (_ : plaintext), is_lossless srand) => islossless RO.sample. + + lemma FRO_sample_ll: (forall (_ : plaintext), is_lossless srand) => islossless FRO.sample. + + module type RM_Distinguisher(G : ROmap) = { + proc distinguish(_ : unit) : bool {G.get, G.sample, G.restrK} + } + + module MainRM(D : RM_Distinguisher, RO0 : ROmap) = { + proc distinguish(x : unit) : bool = { + var r : bool; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + lemma fcoll_bound ['rT]: + forall (f : sharedsecret -> 'rT) (Pc : real), + 0%r <= Pc => + (forall (_ _ : plaintext) (y : 'rT), y \in dmap srand f => mu1 (dmap srand f) y <= Pc) => + forall (q : int), + 0 <= q => + forall (D(G : ROmap) <: RM_Distinguisher{+all mem, -RO} ) &m (z : unit), + Pr[MainRM(D, RO).distinguish(z) @ &m : (fcoll f RO.m)%SmtMap /\ (fsize RO.m)%SmtMap <= q] <= + (q * (q - 1))%r / 2%r * Pc. + + theory FullEager. + abstract theory EagerCore. + axiom dout_ll: forall (_ : plaintext), is_lossless srand. + + module type Orcl = { + proc f(x : plaintext) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : plaintext list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : plaintext list, l2 : plaintext list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : plaintext, l : plaintext list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : plaintext list) (i : plaintext) (s2 : plaintext list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : plaintext), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : plaintext), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : plaintext) : sharedsecret = { + var r : sharedsecret; + + r <$ srand; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : plaintext, y : sharedsecret) : unit = FRO.set + + proc rem(x : plaintext) : unit = FRO.rem + + proc sample(x : plaintext) : unit = FRO.sample + + proc queried(x : plaintext, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (plaintext, sharedsecret) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : plaintext) : unit = { + var c : sharedsecret; + + c <$ srand; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : plaintext) (mx1 : (sharedsecret * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : plaintext) (mx1 mx2 : (sharedsecret * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : plaintext) (r1 : sharedsecret), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + end FullEager. + + abstract theory FinEager. + abstract theory EagerCore. + axiom dout_ll: forall (_ : plaintext), is_lossless srand. + + module type Orcl = { + proc f(x : plaintext) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : plaintext list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : plaintext list, l2 : plaintext list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : plaintext, l : plaintext list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : plaintext list) (i : plaintext) (s2 : plaintext list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : plaintext), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : plaintext), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : plaintext) : sharedsecret = { + var r : sharedsecret; + + r <$ srand; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : plaintext, y : sharedsecret) : unit = FRO.set + + proc rem(x : plaintext) : unit = FRO.rem + + proc sample(x : plaintext) : unit = FRO.sample + + proc queried(x : plaintext, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (plaintext, sharedsecret) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : plaintext) : unit = { + var c : sharedsecret; + + c <$ srand; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : plaintext) (mx1 : (sharedsecret * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : plaintext) (mx1 mx2 : (sharedsecret * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : plaintext) (r1 : sharedsecret), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + theory FinFrom. + type t = plaintext. + + op enum : t list. + + op card : int = size enum. + + axiom enum_spec: forall (x : t), count (pred1 x) enum = 1. + + lemma enumP: forall (x : t), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : t list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinFrom. + + module FinRO = { + proc rem(x : plaintext) : unit = RO.rem + + proc set(x : plaintext, y : sharedsecret) : unit = RO.set + + proc init() : unit = { + var l : FinFrom.t list; + + l <- FinFrom.enum; + RO.init(); + while (l <> []){ + RO.sample(head witness l); + l <- behead l; + } + } + + proc get(x : plaintext) : sharedsecret = { + return oget (RO.m.[x])%SmtMap; + } + + proc sample(x : plaintext) : unit = {} + }. + + module type FinRO_Distinguisher(G : RO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.sample} + } + + lemma RO_FinRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ MainD(D, RO).distinguish ~ MainD(D, FinRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_RO_FinRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ) &m (x : unit) (p : + bool -> bool), + Pr[MainD(D, RO).distinguish(x) @ &m : p res] = Pr[MainD(D, FinRO).distinguish(x) @ &m : p res]. + + theory MUniFinFun. + op mfun ['u] (d : plaintext -> 'u distr) (f : plaintext -> 'u) : real = + (big predT (fun (x : plaintext) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma mfunE ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mfun d f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + lemma isdistr_dfun ['u]: forall (d : plaintext -> 'u distr), isdistr (mfun d). + + op dfun ['u] (d : plaintext -> 'u distr) : (plaintext -> 'u) distr = mk (mfun d). + + lemma dfun1E ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mu1 (dfun d) f = + (big predT (fun (x : plaintext) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun1E_djoin ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mu1 (dfun d) f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + op tofun ['u] (us : 'u list) (x : plaintext) : 'u = nth witness us (index x FinFrom.enum). + + op offun ['u] (f : plaintext -> 'u) : 'u list = map f FinFrom.enum. + + lemma offunK ['u]: cancel offun tofun. + + lemma tofunK ['u]: forall (us : 'u list), size us = FinFrom.card => offun (tofun us) = us. + + lemma dfun_dmap ['u]: forall (d : plaintext -> 'u distr), dfun d = dmap (djoinmap d FinFrom.enum) tofun. + + lemma dfun_supp ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + f \in dfun d <=> forall (x : plaintext), f x \in d x. + + lemma dfun_fu ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_full (d x)) => is_full (dfun d). + + lemma dfun_uni ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_uniform (d x)) => is_uniform (dfun d). + + lemma dfun_funi ['u]: + forall (d : plaintext -> 'u distr), + (forall (x : plaintext), is_uniform (d x)) => + (forall (x : plaintext), is_full (d x)) => is_funiform (dfun d). + + lemma dfunE_djoin ['u]: + forall (du : plaintext -> 'u distr) (E : (plaintext -> 'u) -> bool), + mu (dfun du) E = mu (djoinmap du FinFrom.enum) (E \o tofun). + + lemma dfunE ['u]: + forall (du : plaintext -> 'u distr) (p : plaintext -> 'u -> bool), + mu (dfun du) (fun (f : plaintext -> 'u) => forall (x : plaintext), p x (f x)) = + (big predT (fun (x : plaintext) => mu (du x) (p x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma weight_dfun ['u]: + forall (df : plaintext -> 'u distr), + weight (dfun df) = + (big predT (fun (x : plaintext) => weight (df x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun_ll ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_lossless (d x)) => is_lossless (dfun d). + + lemma dlet_dfun ['u, 'v]: + forall (d1 : plaintext -> 'u distr) (d2 : plaintext -> 'u -> 'v distr), + dlet (dfun d1) (fun (f1 : plaintext -> 'u) => dfun (fun (x : plaintext) => d2 x (f1 x))) = + dfun (fun (x : plaintext) => dlet (d1 x) (fun (x1 : 'u) => d2 x x1)). + + lemma dfun_unit ['u]: forall (f : plaintext -> 'u), dfun (fun (x : plaintext) => dunit (f x)) = dunit f. + + lemma dmap_dfun ['u, 'v]: + forall (d : plaintext -> 'u distr) (F : plaintext -> 'u -> 'v), + dmap (dfun d) (fun (f : plaintext -> 'u) (x : plaintext) => F x (f x)) = + dfun (fun (x : plaintext) => dmap (d x) (fun (fx : 'u) => F x fx)). + + lemma dfun1E_fix1 ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext) (f : + plaintext -> 'u), mu1 (dfun d) f = mu1 (d x0) (f x0) * mu1 (dfun d.[x0 <- dunit (f x0)]) f. + + lemma dlet_dfun_fupdate_ll ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext) (v : 'u), + dlet (dfun d.[x0 <- dunit v]) + (fun (f : plaintext -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + dfun d. + + lemma dfunE_dlet_fix1 ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext), + dfun d = dlet (d x0) (fun (v : 'u) => dfun d.[x0 <- dunit v]). + + lemma dlet_dfun_update ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext), + dlet (dfun d) (fun (f : plaintext -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + (weight (d x0) \cdot dfun d). + + lemma dfun_projE ['u]: + forall (df : plaintext -> 'u distr) (x : plaintext), + dmap (dfun df) (fun (f : plaintext -> 'u) => f x) = (weight (dfun df) / weight (df x) \cdot df x). + + lemma eq_from_dfun_proj_eq ['u]: + forall (df1 df2 : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (df1 x)) => + (forall (x : plaintext), is_lossless (df2 x)) => + (forall (x : plaintext), + dmap (dfun df1) (fun (f : plaintext -> 'u) => f x) = + dmap (dfun df2) (fun (f : plaintext -> 'u) => f x)) => + df1 == df2. + + lemma dfun_prod1E ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr) (f : + plaintext -> 'u) (g : plaintext -> 'v), + mu1 (dfun (fun (x : plaintext) => df x `*` dg x)) (fun (x : plaintext) => (f x, g x)) = + mu1 (dfun df) f * mu1 (dfun dg) g. + + lemma dprod_funE ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr), + dfun df `*` dfun dg = + dmap (dfun (fun (x : plaintext) => df x `*` dg x)) + (fun (fg : plaintext -> 'u * 'v) => + ((fun (p : 'u * 'v) => p.`1) \o fg, (fun (p : 'u * 'v) => p.`2) \o fg)). + + lemma dfun_prodE ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr), + dfun (fun (x : plaintext) => df x `*` dg x) = + dmap (dfun df `*` dfun dg) + (fun (fg : (plaintext -> 'u) * (plaintext -> 'v)) (x : plaintext) => (fg.`1 x, fg.`2 x)). + + lemma dfun_condE ['u]: + forall (X : plaintext -> bool) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext -> 'u) * (plaintext -> 'u)) (x : plaintext) => + if X x then tf.`1 x else tf.`2 x) = + dfun (fun (x : plaintext) => if X x then dt x else df x). + + lemma dlet_dfun_cond ['u]: + forall (dX : plaintext -> bool distr) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dX x)) => + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : plaintext -> bool) => dfun (fun (x : plaintext) => if X x then dt x else df x)) = + dfun (fun (x : plaintext) => dlet (dX x) (fun (b : bool) => if b then dt x else df x)). + + lemma dfun_dcondE ['u]: + forall (dX : plaintext -> bool distr) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dX x)) => + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : plaintext -> bool) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext -> 'u) * (plaintext -> 'u)) (x : plaintext) => + if X x then tf.`1 x else tf.`2 x)) = + dfun (fun (x : plaintext) => (mu1 (dX x) true \cdot dt x) + (mu1 (dX x) false \cdot df x)). + end MUniFinFun. + + module FunRO = { + var f : plaintext -> sharedsecret + + proc init() : unit = { + FunRO.f <$ (dfun (fun (_ : plaintext) => srand))%MUniFinFun; + } + + proc get(x : plaintext) : sharedsecret = { + return FunRO.f x; + } + + proc set(x : plaintext, y : sharedsecret) : unit = { + FunRO.f <- fun (z : plaintext) => if z = x then y else FunRO.f z; + } + + proc sample(x : plaintext) : unit = {} + + proc rem(x : plaintext) : unit = {} + }. + + lemma FinRO_FunRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ), + equiv[ MainD(D, FinRO).distinguish ~ MainD(D, FunRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_FinRO_FunRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ) &m (x : unit) (p : + bool -> bool), + Pr[MainD(D, FinRO).distinguish(x) @ &m : p res] = Pr[MainD(D, FunRO).distinguish(x) @ &m : p res]. + end FinEager. + end RO2. + + module type Oracle_x2 = { + proc init() : unit {} + + proc get1(_ : plaintext) : W8.t Array32.t {} + + proc get2(_ : plaintext) : sharedsecret {} + } + + module type POracle_x2 = { + proc get1(_ : plaintext) : W8.t Array32.t {} + + proc get2(_ : plaintext) : sharedsecret {} + } + + module RO_x2(H1 : RO1.RO, H2 : RO2.RO) = { + proc init() : unit = { + H1.init(); + H2.init(); + } + + proc get1(x : plaintext) : W8.t Array32.t = H1.get + + proc get2(x : plaintext) : sharedsecret = H2.get + }. + + module type Scheme(O : POracle_x2) = { + proc kg() : publickey * skey {O.get1, O.get2} + + proc enc(pk : publickey) : (W8.t Array960.t * W8.t Array128.t) * sharedsecret {O.get1, O.get2} + + proc dec(sk : skey, c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option {O.get1, O.get2} + } + + module Correctness(H : Oracle_x2, S : Scheme) = { + proc main() : bool = { + var pk : publickey; + var sk : skey; + var c : W8.t Array960.t * W8.t Array128.t; + var k : sharedsecret; + var k' : sharedsecret option; + + H.init(); + (pk, sk) <@ S(H).kg(); + (c, k) <@ S(H).enc(pk); + k' <@ S(H).dec(sk, c); + + return k' <> Some k; + } + }. + + module type CCA_ORC = { + proc dec(c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option {} + } + + module type CCA_ADV(H : POracle_x2, O : CCA_ORC) = { + proc guess(pk : publickey, c : W8.t Array960.t * W8.t Array128.t, k : sharedsecret) : bool + {O.dec, H.get1, H.get2} + } + + module CCA(H : Oracle_x2, S : Scheme, A : CCA_ADV) = { + var cstar : (W8.t Array960.t * W8.t Array128.t) option + + var sk : skey + + module O = { + proc dec(c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var k : sharedsecret option; + + k <- None; + if (Some c <> CCA.cstar) + k <@ S(H).dec(CCA.sk, c); + + return k; + } + } + + module A = A(H, CCA(H, S, A).O) + + proc main() : bool = { + var pk : publickey; + var k1 : sharedsecret; + var ck0 : (W8.t Array960.t * W8.t Array128.t) * sharedsecret; + var b : bool; + var b' : bool; + + H.init(); + CCA.cstar <- None; + (pk, CCA.sk) <@ S(H).kg(); + k1 <$ srand; + b <$ {0,1}; + ck0 <@ S(H).enc(pk); + CCA.cstar <- Some ck0.`1; + b' <@ CCA(H, S, A).A.guess(pk, ck0.`1, if b then k1 else ck0.`2); + + return b' = b; + } + }. + end KEMROMx2. + + op qHT : int. + + axiom ge0_qHT: 0 <= qHT. + + op qHU : int. + + axiom ge0_qHU: 0 <= qHU. + + op qD : int. + + axiom ge0_qD: 0 <= qD. + + module UU(H : KEMROMx2.POracle_x2) = { + module HT = { + proc get(_ : plaintext) : W8.t Array32.t = H.get1 + } + + module HU = { + proc get(_ : plaintext) : sharedsecret = H.get2 + } + + proc kg() : publickey * KEMROMx2.skey = { + var pk : publickey; + var sk : W8.t Array1152.t; + var k : sharedsecret; + + (pk, sk) <$ TT.kg; + k <$ srand; + + return (pk, ((pk, sk), k)); + } + + proc enc(pk : publickey) : (W8.t Array960.t * W8.t Array128.t) * sharedsecret = { + var m : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var k : sharedsecret; + + m <$ srand; + c <@ TT.TT(UU(H).HT).enc(pk, m); + k <@ UU(H).HU.get(m); + + return (c, k); + } + + proc dec(sk : KEMROMx2.skey, c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var m' : plaintext option; + var k : sharedsecret; + + k <- witness; + m' <@ TT.TT(UU(H).HT).dec(sk.`1, c); + if (m' = None) + k <- J sk.`2 c; + else + k <@ UU(H).HU.get(oget m'); + + return Some k; + } + }. + + module B_UC(HT : TT.PKEROM.POracle) = { + proc find(pk : publickey, sk : TT.PKEROM.skey) : plaintext = { + var m : plaintext; + + m <$ srand; + + return m; + } + }. + + lemma correctness_rel: + forall &m, + Pr[KEMROMx2.Correctness(KEMROMx2.RO_x2(KEMROMx2.RO1.RO, KEMROMx2.RO2.RO), UU).main() @ &m : res] <= + Pr[TT.PKEROM.Correctness_Adv(TT.PKEROM.RO.RO, TT.TT, B_UC).main() @ &m : res]. + + lemma correctness: + forall &m, + TT.qHC = 0 => + 1 < TT.FinT.card => + Pr[KEMROMx2.Correctness(KEMROMx2.RO_x2(KEMROMx2.RO1.RO, KEMROMx2.RO2.RO), UU).main() @ &m : res] <= + Pr[TT.PKE.Correctness_Adv(TT.BasePKE, TT.B(B_UC, TT.PKEROM.RO.RO)).main() @ &m : res]. + + module CountHx2(H : KEMROMx2.POracle_x2) = { + var c_hu : int + + var c_ht : int + + proc init() : unit = { + CountHx2.c_ht <- 0; + CountHx2.c_hu <- 0; + } + + proc get1(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <@ H.get1(x); + CountHx2.c_ht <- CountHx2.c_ht + 1; + + return r; + } + + proc get2(x : plaintext) : sharedsecret = { + var r : sharedsecret; + + r <@ H.get2(x); + CountHx2.c_hu <- CountHx2.c_hu + 1; + + return r; + } + }. + + module UU1(PRFO : J.PRF_Oracles, H : KEMROMx2.POracle_x2) = { + proc enc(pk : publickey) : (W8.t Array960.t * W8.t Array128.t) * sharedsecret = UU(H).enc + + proc kg() : publickey * KEMROMx2.skey = { + var pk : publickey; + var sk : W8.t Array1152.t; + + (pk, sk) <$ TT.kg; + + return (pk, ((pk, sk), witness)); + } + + proc dec(sk : KEMROMx2.skey, c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var m' : plaintext option; + var k : sharedsecret; + + k <- witness; + m' <@ TT.TT(UU(H).HT).dec(sk.`1, c); + if (m' = None) + k <@ PRFO.f(c); + else + k <@ UU(H).HU.get(oget m'); + + return Some k; + } + }. + + module Gm1P(H : KEMROMx2.Oracle_x2, A : KEMROMx2.CCA_ADV, PRFO : J.PRF_Oracles) = { + proc main'() : bool = { + var pk : publickey; + var k1 : sharedsecret; + var ck0 : (W8.t Array960.t * W8.t Array128.t) * sharedsecret; + var b : bool; + var b' : bool; + + H.init(); + KEMROMx2.CCA.cstar <- None; + (pk, KEMROMx2.CCA.sk) <@ UU1(PRFO, H).kg(); + k1 <$ srand; + b <$ {0,1}; + ck0 <@ UU1(PRFO, H).enc(pk); + KEMROMx2.CCA.cstar <- Some ck0.`1; + b' <@ KEMROMx2.CCA(H, UU1(PRFO), A).A.guess(pk, ck0.`1, if b then k1 else ck0.`2); + + return b' = b; + } + }. + + module Gm1(H : KEMROMx2.Oracle_x2, A : KEMROMx2.CCA_ADV) = { + proc main() : bool = { + var b : bool; + + RF.RF.init(); + b <@ Gm1P(H, A, RF.RF).main'(); + + return b; + } + }. + + module D(A : KEMROMx2.CCA_ADV, PRFO : J.PRF_Oracles) = { + proc distinguish() : bool = Gm1P(KEMROMx2.RO_x2(KEMROMx2.RO1.RO, KEMROMx2.RO2.RO), A, PRFO).main' + }. + + theory RO1E. + abstract theory EagerCore. + axiom dout_ll: forall (_ : plaintext), is_lossless srand. + + module type Orcl = { + proc f(x : plaintext) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : plaintext list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : plaintext list, l2 : plaintext list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : plaintext, l : plaintext list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : plaintext list) (i : plaintext) (s2 : plaintext list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : plaintext), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : plaintext), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = KEMROMx2.RO1.FRO.init + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \notin KEMROMx2.RO1.FRO.m)%SmtMap \/ ((KEMROMx2.RO1.FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + KEMROMx2.RO1.FRO.m.[x] <- (r, PROM.Known); + + return (oget (KEMROMx2.RO1.FRO.m.[x])%SmtMap).`1; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = KEMROMx2.RO1.FRO.set + + proc rem(x : plaintext) : unit = KEMROMx2.RO1.FRO.rem + + proc sample(x : plaintext) : unit = KEMROMx2.RO1.FRO.sample + + proc queried(x : plaintext, f : PROM.flag) : bool = KEMROMx2.RO1.FRO.queried + + proc allKnown() : (plaintext, W8.t Array32.t) SmtMap.fmap = KEMROMx2.RO1.FRO.allKnown + + module I = { + proc f(x : plaintext) : unit = { + var c : W8.t Array32.t; + + c <$ srand; + KEMROMx2.RO1.FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown KEMROMx2.RO1.FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: + eager[ RRO.resample();, KEMROMx2.RO1.FRO.init ~ RRO.init, RRO.resample( + ); : ={KEMROMx2.RO1.FRO.m} ==> ={KEMROMx2.RO1.FRO.m}]. + + lemma iter_perm2: + equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={KEMROMx2.RO1.FRO.m, t1, t2} ==> ={KEMROMx2.RO1.FRO.m}]. + + lemma I_f_neq: + forall (x1 : plaintext) (mx1 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, KEMROMx2.RO1.FRO.m} /\ x1 <> arg{1} /\ (KEMROMx2.RO1.FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={KEMROMx2.RO1.FRO.m} /\ (KEMROMx2.RO1.FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : plaintext) (mx1 mx2 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) KEMROMx2.RO1.FRO.m{1} KEMROMx2.RO1.FRO.m{2})%SmtMap /\ + (KEMROMx2.RO1.FRO.m{1}.[x1])%SmtMap = mx1 /\ (KEMROMx2.RO1.FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) KEMROMx2.RO1.FRO.m{1} KEMROMx2.RO1.FRO.m{2})%SmtMap /\ + (KEMROMx2.RO1.FRO.m{1}.[x1])%SmtMap = mx1 /\ (KEMROMx2.RO1.FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : plaintext) (r1 : W8.t Array32.t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (KEMROMx2.RO1.FRO.m{1}.[x1])%SmtMap = None /\ + KEMROMx2.RO1.FRO.m{2} = (KEMROMx2.RO1.FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (KEMROMx2.RO1.FRO.m{1}.[x1])%SmtMap = None /\ + KEMROMx2.RO1.FRO.m{2} = (KEMROMx2.RO1.FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, KEMROMx2.RO1.FRO.get ~ RRO.get, RRO.resample( + ); : ={arg, KEMROMx2.RO1.FRO.m} ==> ={res, KEMROMx2.RO1.FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, KEMROMx2.RO1.FRO.set ~ RRO.set, RRO.resample( + ); : ={x, y} /\ ={KEMROMx2.RO1.FRO.m} ==> ={res, KEMROMx2.RO1.FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, KEMROMx2.RO1.FRO.rem ~ RRO.rem, RRO.resample( + ); : ={arg, KEMROMx2.RO1.FRO.m} ==> ={res, KEMROMx2.RO1.FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, KEMROMx2.RO1.FRO.sample ~ RRO.sample, RRO.resample( + ); : ={arg, KEMROMx2.RO1.FRO.m} ==> ={res, KEMROMx2.RO1.FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, KEMROMx2.RO1.FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={KEMROMx2.RO1.FRO.m} ==> ={res, KEMROMx2.RO1.FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, KEMROMx2.RO1.FRO.allKnown ~ RRO.allKnown, RRO.resample( + ); : ={KEMROMx2.RO1.FRO.m} ==> ={res, KEMROMx2.RO1.FRO.m}]. + + lemma eager_D: + forall (D0(G : KEMROMx2.RO1.FRO) <: KEMROMx2.RO1.FRO_Distinguisher{+all mem, -KEMROMx2.RO1.FRO} ), + eager[ RRO.resample();, D0(KEMROMx2.RO1.FRO).distinguish ~ D0(RRO).distinguish, RRO.resample( + ); : ={glob D0, KEMROMx2.RO1.FRO.m, arg} ==> ={KEMROMx2.RO1.FRO.m, glob D0} /\ ={res}]. + + module Eager(D0 : KEMROMx2.RO1.FRO_Distinguisher) = { + proc main1(x : unit) : bool = { + var b : bool; + + KEMROMx2.RO1.FRO.init(); + b <@ D0(KEMROMx2.RO1.FRO).distinguish(x); + + return b; + } + + proc main2(x : unit) : bool = { + var b : bool; + + KEMROMx2.RO1.FRO.init(); + b <@ D0(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D0(G : KEMROMx2.RO1.FRO) <: KEMROMx2.RO1.FRO_Distinguisher{+all mem, -KEMROMx2.RO1.FRO} ), + equiv[ Eager(D0).main1 ~ Eager(D0).main2 : ={glob D0, arg} ==> ={res, KEMROMx2.RO1.FRO.m, glob D0}]. + + lemma LRO_RRO_init: + equiv[ KEMROMx2.RO1.RO.init ~ KEMROMx2.RO1.FRO.init : + true ==> KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ KEMROMx2.RO1.RO.get ~ RRO.get : + ={arg} /\ KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap ==> + ={res} /\ KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ KEMROMx2.RO1.RO.set ~ KEMROMx2.RO1.FRO.set : + ={x, y} /\ KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap ==> + KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ KEMROMx2.RO1.RO.rem ~ KEMROMx2.RO1.FRO.rem : + ={arg} /\ KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap ==> + KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ KEMROMx2.RO1.LRO.sample ~ KEMROMx2.RO1.FRO.sample : + ={arg} /\ KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap ==> + KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D0(G : KEMROMx2.RO1.RO) <: + KEMROMx2.RO1.RO_Distinguisher{+all mem, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO} ), + equiv[ D0(KEMROMx2.RO1.LRO).distinguish ~ D0(RRO).distinguish : + ={glob D0, arg} /\ KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap ==> + ={res, glob D0} /\ KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D0(G : KEMROMx2.RO1.RO) <: + KEMROMx2.RO1.RO_Distinguisher{+all mem, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ D0(KEMROMx2.RO1.RO).distinguish ~ D0(KEMROMx2.RO1.LRO).distinguish : + ={glob D0, KEMROMx2.RO1.RO.m, arg} ==> ={res, glob D0}]. + + lemma RO_LRO: + forall (D0(G : KEMROMx2.RO1.RO) <: + KEMROMx2.RO1.RO_Distinguisher{+all mem, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ KEMROMx2.RO1.MainD(D0, KEMROMx2.RO1.RO).distinguish ~ + KEMROMx2.RO1.MainD(D0, KEMROMx2.RO1.LRO).distinguish : + ={glob D0, arg} ==> ={res, glob D0}]. + + theory FinFrom. + type t = plaintext. + + op enum : plaintext list = TT.FinT.enum. + + op card : int = size enum. + + lemma enum_spec: forall (x : t), count (pred1 x) enum = 1. + + lemma enumP: forall (x : t), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : t list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinFrom. + + module FinRO = { + proc rem(x : plaintext) : unit = KEMROMx2.RO1.RO.rem + + proc set(x : plaintext, y : W8.t Array32.t) : unit = KEMROMx2.RO1.RO.set + + proc init() : unit = { + var l : FinFrom.t list; + + l <- FinFrom.enum; + KEMROMx2.RO1.RO.init(); + while (l <> []){ + KEMROMx2.RO1.RO.sample(head witness l); + l <- behead l; + } + } + + proc get(x : plaintext) : W8.t Array32.t = { + return oget (KEMROMx2.RO1.RO.m.[x])%SmtMap; + } + + proc sample(x : plaintext) : unit = {} + }. + + module type FinRO_Distinguisher(G : KEMROMx2.RO1.RO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.sample} + } + + lemma RO_FinRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D0(G : KEMROMx2.RO1.RO) <: FinRO_Distinguisher{+all mem, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO} ), + equiv[ KEMROMx2.RO1.MainD(D0, KEMROMx2.RO1.RO).distinguish ~ KEMROMx2.RO1.MainD(D0, FinRO).distinguish : + ={glob D0, arg} ==> ={res, glob D0}]. + + lemma pr_RO_FinRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D0(G : KEMROMx2.RO1.RO) <: FinRO_Distinguisher{+all mem, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO} ) &m + (x : unit) (p : bool -> bool), + Pr[KEMROMx2.RO1.MainD(D0, KEMROMx2.RO1.RO).distinguish(x) @ &m : p res] = + Pr[KEMROMx2.RO1.MainD(D0, FinRO).distinguish(x) @ &m : p res]. + + theory MUniFinFun. + op mfun ['u] (d : plaintext -> 'u distr) (f : plaintext -> 'u) : real = + (big predT (fun (x : plaintext) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma mfunE ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mfun d f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + lemma isdistr_dfun ['u]: forall (d : plaintext -> 'u distr), isdistr (mfun d). + + op dfun ['u] (d : plaintext -> 'u distr) : (plaintext -> 'u) distr = mk (mfun d). + + lemma dfun1E ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mu1 (dfun d) f = (big predT (fun (x : plaintext) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun1E_djoin ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mu1 (dfun d) f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + op tofun ['u] (us : 'u list) (x : plaintext) : 'u = nth witness us (index x FinFrom.enum). + + op offun ['u] (f : plaintext -> 'u) : 'u list = map f FinFrom.enum. + + lemma offunK ['u]: cancel offun tofun. + + lemma tofunK ['u]: forall (us : 'u list), size us = FinFrom.card => offun (tofun us) = us. + + lemma dfun_dmap ['u]: forall (d : plaintext -> 'u distr), dfun d = dmap (djoinmap d FinFrom.enum) tofun. + + lemma dfun_supp ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + f \in dfun d <=> forall (x : plaintext), f x \in d x. + + lemma dfun_fu ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_full (d x)) => is_full (dfun d). + + lemma dfun_uni ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_uniform (d x)) => is_uniform (dfun d). + + lemma dfun_funi ['u]: + forall (d : plaintext -> 'u distr), + (forall (x : plaintext), is_uniform (d x)) => + (forall (x : plaintext), is_full (d x)) => is_funiform (dfun d). + + lemma dfunE_djoin ['u]: + forall (du : plaintext -> 'u distr) (E : (plaintext -> 'u) -> bool), + mu (dfun du) E = mu (djoinmap du FinFrom.enum) (E \o tofun). + + lemma dfunE ['u]: + forall (du : plaintext -> 'u distr) (p : plaintext -> 'u -> bool), + mu (dfun du) (fun (f : plaintext -> 'u) => forall (x : plaintext), p x (f x)) = + (big predT (fun (x : plaintext) => mu (du x) (p x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma weight_dfun ['u]: + forall (df : plaintext -> 'u distr), + weight (dfun df) = (big predT (fun (x : plaintext) => weight (df x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun_ll ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_lossless (d x)) => is_lossless (dfun d). + + lemma dlet_dfun ['u, 'v]: + forall (d1 : plaintext -> 'u distr) (d2 : plaintext -> 'u -> 'v distr), + dlet (dfun d1) (fun (f1 : plaintext -> 'u) => dfun (fun (x : plaintext) => d2 x (f1 x))) = + dfun (fun (x : plaintext) => dlet (d1 x) (fun (x1 : 'u) => d2 x x1)). + + lemma dfun_unit ['u]: forall (f : plaintext -> 'u), dfun (fun (x : plaintext) => dunit (f x)) = dunit f. + + lemma dmap_dfun ['u, 'v]: + forall (d : plaintext -> 'u distr) (F : plaintext -> 'u -> 'v), + dmap (dfun d) (fun (f : plaintext -> 'u) (x : plaintext) => F x (f x)) = + dfun (fun (x : plaintext) => dmap (d x) (fun (fx : 'u) => F x fx)). + + lemma dfun1E_fix1 ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext) (f : + plaintext -> 'u), mu1 (dfun d) f = mu1 (d x0) (f x0) * mu1 (dfun d.[x0 <- dunit (f x0)]) f. + + lemma dlet_dfun_fupdate_ll ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext) (v : 'u), + dlet (dfun d.[x0 <- dunit v]) (fun (f : plaintext -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + dfun d. + + lemma dfunE_dlet_fix1 ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext), + dfun d = dlet (d x0) (fun (v : 'u) => dfun d.[x0 <- dunit v]). + + lemma dlet_dfun_update ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext), + dlet (dfun d) (fun (f : plaintext -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + (weight (d x0) \cdot dfun d). + + lemma dfun_projE ['u]: + forall (df : plaintext -> 'u distr) (x : plaintext), + dmap (dfun df) (fun (f : plaintext -> 'u) => f x) = (weight (dfun df) / weight (df x) \cdot df x). + + lemma eq_from_dfun_proj_eq ['u]: + forall (df1 df2 : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (df1 x)) => + (forall (x : plaintext), is_lossless (df2 x)) => + (forall (x : plaintext), + dmap (dfun df1) (fun (f : plaintext -> 'u) => f x) = + dmap (dfun df2) (fun (f : plaintext -> 'u) => f x)) => + df1 == df2. + + lemma dfun_prod1E ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr) (f : + plaintext -> 'u) (g : plaintext -> 'v), + mu1 (dfun (fun (x : plaintext) => df x `*` dg x)) (fun (x : plaintext) => (f x, g x)) = + mu1 (dfun df) f * mu1 (dfun dg) g. + + lemma dprod_funE ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr), + dfun df `*` dfun dg = + dmap (dfun (fun (x : plaintext) => df x `*` dg x)) + (fun (fg : plaintext -> 'u * 'v) => + ((fun (p : 'u * 'v) => p.`1) \o fg, (fun (p : 'u * 'v) => p.`2) \o fg)). + + lemma dfun_prodE ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr), + dfun (fun (x : plaintext) => df x `*` dg x) = + dmap (dfun df `*` dfun dg) + (fun (fg : (plaintext -> 'u) * (plaintext -> 'v)) (x : plaintext) => (fg.`1 x, fg.`2 x)). + + lemma dfun_condE ['u]: + forall (X : plaintext -> bool) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext -> 'u) * (plaintext -> 'u)) (x : plaintext) => if X x then tf.`1 x else tf.`2 x) = + dfun (fun (x : plaintext) => if X x then dt x else df x). + + lemma dlet_dfun_cond ['u]: + forall (dX : plaintext -> bool distr) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dX x)) => + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dlet (dfun dX) (fun (X : plaintext -> bool) => dfun (fun (x : plaintext) => if X x then dt x else df x)) = + dfun (fun (x : plaintext) => dlet (dX x) (fun (b : bool) => if b then dt x else df x)). + + lemma dfun_dcondE ['u]: + forall (dX : plaintext -> bool distr) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dX x)) => + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : plaintext -> bool) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext -> 'u) * (plaintext -> 'u)) (x : plaintext) => + if X x then tf.`1 x else tf.`2 x)) = + dfun (fun (x : plaintext) => (mu1 (dX x) true \cdot dt x) + (mu1 (dX x) false \cdot df x)). + end MUniFinFun. + + module FunRO = { + var f : plaintext -> W8.t Array32.t + + proc init() : unit = { + FunRO.f <$ (dfun (fun (_ : plaintext) => srand))%MUniFinFun; + } + + proc get(x : plaintext) : W8.t Array32.t = { + return FunRO.f x; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = { + FunRO.f <- fun (z : plaintext) => if z = x then y else FunRO.f z; + } + + proc sample(x : plaintext) : unit = {} + + proc rem(x : plaintext) : unit = {} + }. + + lemma FinRO_FunRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D0(G : KEMROMx2.RO1.RO) <: + FinRO_Distinguisher{+all mem, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -FunRO} ), + equiv[ KEMROMx2.RO1.MainD(D0, FinRO).distinguish ~ KEMROMx2.RO1.MainD(D0, FunRO).distinguish : + ={glob D0, arg} ==> ={res, glob D0}]. + + lemma pr_FinRO_FunRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D0(G : KEMROMx2.RO1.RO) <: + FinRO_Distinguisher{+all mem, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -FunRO} ) &m (x : unit) + (p : bool -> bool), + Pr[KEMROMx2.RO1.MainD(D0, FinRO).distinguish(x) @ &m : p res] = + Pr[KEMROMx2.RO1.MainD(D0, FunRO).distinguish(x) @ &m : p res]. + end RO1E. + + module RO_x2E = KEMROMx2.RO_x2(RO1E.FunRO, KEMROMx2.RO2.RO). + + module UU2(H : KEMROMx2.POracle_x2) = { + proc kg() : publickey * KEMROMx2.skey = UU1(RF.RF, H).kg + + proc enc(pk : publickey) : (W8.t Array960.t * W8.t Array128.t) * sharedsecret = UU1(RF.RF, H).enc + + var lD : ((W8.t Array960.t * W8.t Array128.t) * sharedsecret) list + + proc dec(sk : KEMROMx2.skey, c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var k : sharedsecret; + var ko : sharedsecret option; + + ko <- None; + if (assoc UU2.lD c <> None) + ko <- assoc UU2.lD c; + else { + k <$ srand; + ko <- Some k; + UU2.lD <- (c, k) :: UU2.lD; + } + + return ko; + } + }. + + module H1 = { + var bad : bool + + proc init() : unit = {} + + proc get1(x : plaintext) : W8.t Array32.t = RO_x2E.get1 + + proc get2(m : plaintext) : sharedsecret = { + var k : sharedsecret; + var cm : W8.t Array960.t * W8.t Array128.t; + + cm <- enc (RO1E.FunRO.f m) KEMROMx2.CCA.sk.`1.`1 m; + H1.bad <- if dec KEMROMx2.CCA.sk.`1.`2 cm <> Some m then true else H1.bad; + k <$ srand; + if ((m \notin KEMROMx2.RO2.RO.m)%SmtMap) + KEMROMx2.RO2.RO.m.[m] <- k; + + return oget (KEMROMx2.RO2.RO.m.[m])%SmtMap; + } + }. + + module H2(O1 : TT.PKEROM.POracle) = { + var merr : plaintext option + + var invert : bool + + var mtgt : plaintext + + proc init() : unit = {} + + proc get1(x : plaintext) : W8.t Array32.t = RO_x2E.get1 + + proc get2(m : plaintext) : sharedsecret = { + var k : sharedsecret; + var cm : W8.t Array960.t * W8.t Array128.t; + var r : W8.t Array32.t; + + H2.mtgt <- if KEMROMx2.CCA.cstar = None then m else H2.mtgt; + r <@ O1.get(m); + cm <- enc r KEMROMx2.CCA.sk.`1.`1 m; + H1.bad <- if dec KEMROMx2.CCA.sk.`1.`2 cm <> Some m then true else H1.bad; + H2.merr <- if H2.merr = None && H1.bad then Some m else H2.merr; + H2.invert <- + if KEMROMx2.CCA.cstar <> None && + m = H2.mtgt && dec KEMROMx2.CCA.sk.`1.`2 (oget KEMROMx2.CCA.cstar) = Some H2.mtgt then true + else H2.invert; + k <$ srand; + if ((m \notin KEMROMx2.RO2.RO.m)%SmtMap) { + if (assoc UU2.lD cm <> None) + k <- oget (assoc UU2.lD cm); + else + UU2.lD <- if H1.bad then UU2.lD else (cm, k) :: UU2.lD; + KEMROMx2.RO2.RO.m <- if H1.bad then KEMROMx2.RO2.RO.m else (KEMROMx2.RO2.RO.m.[m <- k])%SmtMap; + } + + return if H1.bad then witness else oget (KEMROMx2.RO2.RO.m.[m])%SmtMap; + } + }. + + module Gm2(H : KEMROMx2.Oracle_x2, S : KEMROMx2.Scheme, A : KEMROMx2.CCA_ADV) = { + module O = { + proc dec(c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var k : sharedsecret option; + + k <- None; + if (Some c <> KEMROMx2.CCA.cstar) + k <@ S(H).dec(KEMROMx2.CCA.sk, c); + + return k; + } + } + + proc main2() : bool = { + var pk : publickey; + var k1 : sharedsecret; + var ck0 : (W8.t Array960.t * W8.t Array128.t) * sharedsecret; + var cstar : (W8.t Array960.t * W8.t Array128.t) option; + var b : bool; + var b' : bool; + + H1.bad <- false; + H2.merr <- None; + H2.invert <- false; + RF.RF.init(); + RO_x2E.init(); + UU2.lD <- []; + KEMROMx2.CCA.cstar <- None; + (pk, KEMROMx2.CCA.sk) <@ S(H).kg(); + k1 <$ srand; + b <$ {0,1}; + ck0 <@ UU2(H).enc(pk); + KEMROMx2.CCA.cstar <- Some ck0.`1; + b' <@ KEMROMx2.CCA(H, S, A).A.guess(pk, ck0.`1, if b then k1 else ck0.`2); + + return b' = b; + } + + proc main() : bool = { + var win : bool; + var nobias : bool; + + win <@ Gm2(H, S, A).main2(); + nobias <$ {0,1}; + + return if H1.bad then nobias else win; + } + }. + + module BUUC(A : KEMROMx2.CCA_ADV, H : TT.PKEROM.POracle) = { + module H2B = { + proc get2(m : plaintext) : sharedsecret = H2(H).get2 + + proc init() : unit = H2(H).init + + proc get1(x : plaintext) : W8.t Array32.t = H.get + } + + proc find(pk : publickey, sk : TT.PKEROM.skey) : plaintext = { + var k1 : sharedsecret; + var ck0 : (W8.t Array960.t * W8.t Array128.t) * sharedsecret; + var cstar : (W8.t Array960.t * W8.t Array128.t) option; + var b : bool; + var b' : bool; + var z : sharedsecret; + + H1.bad <- false; + H2.merr <- None; + H2.invert <- false; + RF.RF.init(); + KEMROMx2.RO2.RO.init(); + UU2.lD <- []; + KEMROMx2.CCA.cstar <- None; + KEMROMx2.CCA.sk <- (sk, witness); + k1 <$ srand; + b <$ {0,1}; + ck0 <@ UU2(BUUC(A, H).H2B).enc(pk); + KEMROMx2.CCA.cstar <- Some ck0.`1; + CountHx2(BUUC(A, H).H2B).init(); + b' <@ KEMROMx2.CCA(CountHx2(BUUC(A, H).H2B), UU2, A).A.guess(pk, ck0.`1, if b then k1 else ck0.`2); + + return oget H2.merr; + } + }. + + module Gm3(H : KEMROMx2.Oracle_x2, S : KEMROMx2.Scheme, A : KEMROMx2.CCA_ADV) = { + module O = { + proc dec(c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var k : sharedsecret option; + + k <- None; + if (Some c <> KEMROMx2.CCA.cstar) + k <@ S(H).dec(KEMROMx2.CCA.sk, c); + + return k; + } + } + + proc main() : bool = { + var pk : publickey; + var k1 : sharedsecret; + var k2 : sharedsecret; + var b : bool; + var b' : bool; + var r : W8.t Array32.t; + var cm : W8.t Array960.t * W8.t Array128.t; + var nobias : bool; + + H1.bad <- false; + H2.merr <- None; + H2.invert <- false; + RF.RF.init(); + RO_x2E.init(); + UU2.lD <- []; + KEMROMx2.CCA.cstar <- None; + (pk, KEMROMx2.CCA.sk) <@ S(H).kg(); + k1 <$ srand; + k2 <$ srand; + b <$ {0,1}; + H2.mtgt <$ srand; + r <@ H.get1(H2.mtgt); + cm <- enc r pk H2.mtgt; + H1.bad <- if dec KEMROMx2.CCA.sk.`1.`2 cm <> Some H2.mtgt then true else H1.bad; + H2.merr <- if H2.merr = None && H1.bad then Some H2.mtgt else H2.merr; + UU2.lD <- (cm, witness) :: UU2.lD; + KEMROMx2.CCA.cstar <- Some cm; + b' <@ KEMROMx2.CCA(H, S, A).A.guess(pk, cm, if b then k1 else k2); + nobias <$ {0,1}; + + return if H1.bad then nobias else b' = b; + } + + proc main_0adv() : bool = { + var pk : publickey; + var k : sharedsecret; + var b : bool; + var b' : bool; + var r : W8.t Array32.t; + var cm : W8.t Array960.t * W8.t Array128.t; + var nobias : bool; + + H1.bad <- false; + H2.merr <- None; + H2.invert <- false; + RF.RF.init(); + RO_x2E.init(); + UU2.lD <- []; + KEMROMx2.CCA.cstar <- None; + (pk, KEMROMx2.CCA.sk) <@ S(H).kg(); + k <$ srand; + H2.mtgt <$ srand; + r <@ H.get1(H2.mtgt); + cm <- enc r pk H2.mtgt; + H1.bad <- if dec KEMROMx2.CCA.sk.`1.`2 cm <> Some H2.mtgt then true else H1.bad; + H2.merr <- if H2.merr = None && H1.bad then Some H2.mtgt else H2.merr; + UU2.lD <- (cm, witness) :: UU2.lD; + KEMROMx2.CCA.cstar <- Some cm; + b' <@ KEMROMx2.CCA(H, S, A).A.guess(pk, cm, k); + nobias <$ {0,1}; + b <$ {0,1}; + + return if H1.bad then nobias else b' = b; + } + }. + + module H2BOW(OO1 : TT.PKEROM.POracle) = { + proc init() : unit = {} + + proc get1(x : plaintext) : W8.t Array32.t = RO_x2E.get1 + + proc get2(m : plaintext) : sharedsecret = { + var k : sharedsecret; + var cm : W8.t Array960.t * W8.t Array128.t; + var r : W8.t Array32.t; + + r <@ OO1.get(m); + cm <- enc r KEMROMx2.CCA.sk.`1.`1 m; + H1.bad <- if dec KEMROMx2.CCA.sk.`1.`2 cm <> Some m then true else H1.bad; + H2.merr <- if H2.merr = None && H1.bad then Some m else H2.merr; + H2.invert <- + if KEMROMx2.CCA.cstar <> None && + m = H2.mtgt && dec KEMROMx2.CCA.sk.`1.`2 (oget KEMROMx2.CCA.cstar) = Some H2.mtgt then true + else H2.invert; + k <$ srand; + if ((m \notin KEMROMx2.RO2.RO.m)%SmtMap) { + if (assoc UU2.lD cm <> None) + k <- oget (assoc UU2.lD cm); + else + UU2.lD <- (cm, k) :: UU2.lD; + KEMROMx2.RO2.RO.m.[m] <- k; + } + + return oget (KEMROMx2.RO2.RO.m.[m])%SmtMap; + } + }. + + module BUUOW(A : KEMROMx2.CCA_ADV, H : TT.PKEROM.POracle, O : TT.PKEROM.VA_ORC) = { + module H2B = { + proc get2(m : plaintext) : sharedsecret = H2BOW(H).get2 + + proc init() : unit = H2BOW(H).init + + proc get1(x : plaintext) : W8.t Array32.t = H.get + } + + proc find(pk : publickey, cm : W8.t Array960.t * W8.t Array128.t) : plaintext option = { + var k1 : sharedsecret; + var k2 : sharedsecret; + var b : bool; + var b' : bool; + var r : W8.t Array32.t; + + H1.bad <- false; + H2.merr <- None; + H2.invert <- false; + RF.RF.init(); + KEMROMx2.RO2.RO.init(); + UU2.lD <- []; + KEMROMx2.CCA.cstar <- None; + KEMROMx2.CCA.sk <- ((pk, witness), witness); + k1 <$ srand; + k2 <$ srand; + b <$ {0,1}; + UU2.lD <- (cm, witness) :: UU2.lD; + KEMROMx2.CCA.cstar <- Some cm; + b' <@ KEMROMx2.CCA(BUUOW(A, H, O).H2B, UU2, A).A.guess(pk, cm, if b then k1 else k2); + + return + if (card + ((filter (fun (m0 : plaintext) => enc (RO1E.FunRO.f m0) pk m0 = oget KEMROMx2.CCA.cstar) + ((fdom KEMROMx2.RO2.RO.m))%SmtMap))%FSet)%FSet = + 1 then + Some + (head witness + ((elems + ((filter (fun (m0 : plaintext) => enc (RO1E.FunRO.f m0) pk m0 = oget KEMROMx2.CCA.cstar) + ((fdom KEMROMx2.RO2.RO.m))%SmtMap))%FSet))%FSet) + else None; + } + }. + + module H2BOWMod(OO1 : TT.PKEROM.POracle) = { + var crd : int + + var mf : plaintext option + + proc init() : unit = {} + + proc get1(x : plaintext) : W8.t Array32.t = RO_x2E.get1 + + proc get2(m : plaintext) : sharedsecret = { + var k : sharedsecret; + var cm : W8.t Array960.t * W8.t Array128.t; + var r : W8.t Array32.t; + + r <@ OO1.get(m); + cm <- enc r KEMROMx2.CCA.sk.`1.`1 m; + H1.bad <- if dec KEMROMx2.CCA.sk.`1.`2 cm <> Some m then true else H1.bad; + H2.merr <- if H2.merr = None && H1.bad then Some m else H2.merr; + H2.invert <- + if KEMROMx2.CCA.cstar <> None && + m = H2.mtgt && dec KEMROMx2.CCA.sk.`1.`2 (oget KEMROMx2.CCA.cstar) = Some H2.mtgt then true + else H2.invert; + k <$ srand; + if ((m \notin KEMROMx2.RO2.RO.m)%SmtMap) { + H2BOWMod.crd <- H2BOWMod.crd + b2i (Some cm = KEMROMx2.CCA.cstar); + H2BOWMod.mf <- if Some cm = KEMROMx2.CCA.cstar then Some m else H2BOWMod.mf; + if (assoc UU2.lD cm <> None) + k <- oget (assoc UU2.lD cm); + else + UU2.lD <- (cm, k) :: UU2.lD; + KEMROMx2.RO2.RO.m.[m] <- k; + } + + return oget (KEMROMx2.RO2.RO.m.[m])%SmtMap; + } + }. + + module BUUOWMod(A : KEMROMx2.CCA_ADV, H : TT.PKEROM.POracle, O : TT.PKEROM.VA_ORC) = { + module H2B = { + proc get2(m : plaintext) : sharedsecret = H2BOWMod(H).get2 + + proc init() : unit = H2BOWMod(H).init + + proc get1(x : plaintext) : W8.t Array32.t = H.get + } + + proc find(pk : publickey, cm : W8.t Array960.t * W8.t Array128.t) : plaintext option = { + var k1 : sharedsecret; + var k2 : sharedsecret; + var b : bool; + var b' : bool; + + H1.bad <- false; + H2.merr <- None; + H2.invert <- false; + RF.RF.init(); + KEMROMx2.RO2.RO.init(); + UU2.lD <- []; + KEMROMx2.CCA.cstar <- None; + KEMROMx2.CCA.sk <- ((pk, witness), witness); + k1 <$ srand; + k2 <$ srand; + b <$ {0,1}; + UU2.lD <- (cm, witness) :: UU2.lD; + KEMROMx2.CCA.cstar <- Some cm; + H2BOWMod.crd <- 0; + H2BOWMod.mf <- None; + CountHx2(BUUOWMod(A, H, O).H2B).init(); + b' <@ KEMROMx2.CCA(CountHx2(BUUOWMod(A, H, O).H2B), UU2, A).A.guess(pk, cm, if b then k1 else k2); + + return if H2BOWMod.crd = 1 then H2BOWMod.mf else None; + } + }. + + module BUUCI(A : KEMROMx2.CCA_ADV, H : TT.PKEROM.POracle) = { + module H2B = { + proc get2(m : plaintext) : sharedsecret = H2(H).get2 + + proc init() : unit = H2(H).init + + proc get1(x : plaintext) : W8.t Array32.t = H.get + } + + proc find(pk : publickey, sk : TT.PKEROM.skey) : plaintext = { + var k1 : sharedsecret; + var k2 : sharedsecret; + var b : bool; + var b' : bool; + var r : W8.t Array32.t; + var cm : W8.t Array960.t * W8.t Array128.t; + var nobias : bool; + + H1.bad <- false; + H2.merr <- None; + H2.invert <- false; + RF.RF.init(); + KEMROMx2.RO2.RO.init(); + UU2.lD <- []; + KEMROMx2.CCA.cstar <- None; + KEMROMx2.CCA.sk <- (sk, witness); + k1 <$ srand; + k2 <$ srand; + b <$ {0,1}; + H2.mtgt <$ srand; + r <@ BUUCI(A, H).H2B.get1(H2.mtgt); + cm <- enc r pk H2.mtgt; + H1.bad <- if dec KEMROMx2.CCA.sk.`1.`2 cm <> Some H2.mtgt then true else H1.bad; + H2.merr <- if H2.merr = None && H1.bad then Some H2.mtgt else H2.merr; + UU2.lD <- (cm, witness) :: UU2.lD; + KEMROMx2.CCA.cstar <- Some cm; + CountHx2(BUUCI(A, H).H2B).init(); + b' <@ KEMROMx2.CCA(CountHx2(BUUCI(A, H).H2B), UU2, A).A.guess(pk, cm, if b then k1 else k2); + + return oget H2.merr; + } + }. + + lemma Gm0_Gm1: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + Pr[KEMROMx2.CCA(KEMROMx2.RO_x2(KEMROMx2.RO1.RO, KEMROMx2.RO2.RO), UU, A).main() @ &m : res] - + Pr[Gm1(KEMROMx2.RO_x2(KEMROMx2.RO1.RO, KEMROMx2.RO2.RO), A).main() @ &m : res] = + Pr[J.IND(PseudoRF.PRF, D(A)).main() @ &m : res] - Pr[J.IND(RF.RF, D(A)).main() @ &m : res]. + + lemma uu_goal_eager: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + Pr[Gm1(KEMROMx2.RO_x2(KEMROMx2.RO1.RO, KEMROMx2.RO2.RO), A).main() @ &m : res] = + Pr[Gm1(RO_x2E, A).main() @ &m : res]. + + op c2m (c : W8.t Array960.t * W8.t Array128.t) (sk : TT.PKEROM.skey) : plaintext option = dec sk.`2 c. + + op oc2m (c : W8.t Array960.t * W8.t Array128.t) (sk : TT.PKEROM.skey) : plaintext = oget (dec sk.`2 c). + + op m2c (m : plaintext) (sk : TT.PKEROM.skey) (f : plaintext -> W8.t Array32.t) : W8.t Array960.t * + W8.t Array128.t = enc (f m) sk.`1 m. + + op goodc (c : W8.t Array960.t * W8.t Array128.t) (sk : TT.PKEROM.skey) (f : + plaintext -> W8.t Array32.t) : bool = c2m c sk <> None /\ m2c (oc2m c sk) sk f = c. + + lemma bound_bad: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + Pr[Gm2(H2(RO1E.FunRO), UU2, A).main() @ &m : H1.bad] <= + Pr[TT.PKEROM.Correctness_Adv(RO1E.FunRO, TT.TT, BUUC(A)).main() @ &m : res]. + + lemma bound_invert: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + (forall (H0 <: KEMROMx2.POracle_x2{+all mem, -A} ) (O <: KEMROMx2.CCA_ORC{+all mem, -A} ), + islossless O.dec => islossless H0.get1 => islossless H0.get2 => islossless A(H0, O).guess) => + `|Pr[TT.PKEROM.OW_PCVA(RO1E.FunRO, TT.TT, BUUOW(A)).main() @ &m : res] - + Pr[Gm3(H2(RO1E.FunRO), UU2, A).main() @ &m : H2.invert]| <= + Pr[Gm3(H2(RO1E.FunRO), UU2, A).main() @ &m : H1.bad]. + + lemma bound_bad2: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + Pr[Gm3(H2(RO1E.FunRO), UU2, A).main() @ &m : H1.bad] <= + Pr[TT.PKEROM.Correctness_Adv(RO1E.FunRO, TT.TT, BUUCI(A)).main() @ &m : res]. + + lemma G3adv: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + (forall (H0 <: KEMROMx2.POracle_x2{+all mem, -A} ) (O <: KEMROMx2.CCA_ORC{+all mem, -A} ), + islossless O.dec => islossless H0.get1 => islossless H0.get2 => islossless A(H0, O).guess) => + Pr[Gm3(H2(RO1E.FunRO), UU2, A).main() @ &m : res] = 1%r / 2%r. + + lemma corr_goal_eagerC: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + Pr[TT.PKEROM.Correctness_Adv(RO1E.FunRO, TT.TT, BUUC(A)).main() @ &m : res] = + Pr[TT.PKEROM.Correctness_Adv(KEMROMx2.RO1.RO, TT.TT, BUUC(A)).main() @ &m : res]. + + lemma corr_goal_eagerCI: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + Pr[TT.PKEROM.Correctness_Adv(RO1E.FunRO, TT.TT, BUUCI(A)).main() @ &m : res] = + Pr[TT.PKEROM.Correctness_Adv(KEMROMx2.RO1.RO, TT.TT, BUUCI(A)).main() @ &m : res]. + + lemma owmod: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + Pr[TT.PKEROM.OW_PCVA(RO1E.FunRO, TT.TT, BUUOW(A)).main() @ &m : res] = + Pr[TT.PKEROM.OW_PCVA(RO1E.FunRO, TT.TT, BUUOWMod(A)).main() @ &m : res]. + + lemma corr_goal_eagerOW: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + Pr[TT.PKEROM.OW_PCVA(KEMROMx2.RO1.RO, TT.TT, BUUOWMod(A)).main() @ &m : res] = + Pr[TT.PKEROM.OW_PCVA(RO1E.FunRO, TT.TT, BUUOW(A)).main() @ &m : res]. + + lemma count_buuc: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) (H1_0 <: TT.PKEROM.RO.RO{+all mem, -TT.CO1, -BUUC(A)} ), + qHT + qHU + 2 <= TT.qHC => + (forall (RO0 <: KEMROMx2.POracle_x2{+all mem, -CountHx2, -A} ) + (O <: KEMROMx2.CCA_ORC{+all mem, -CountHx2, -A} ), + hoare[ A(CountHx2(RO0), O).guess : + CountHx2.c_ht = 0 /\ CountHx2.c_hu = 0 ==> CountHx2.c_ht <= qHT /\ CountHx2.c_hu <= qHU]) => + hoare[ TT.Correctness_Adv1(H1_0, BUUC(A)).A.find : TT.CO1.counter = 0 ==> TT.CO1.counter <= TT.qHC]. + + lemma count_buuci: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) (H1_0 <: TT.PKEROM.RO.RO{+all mem, -TT.CO1, -BUUCI(A)} ), + qHT + qHU + 1 <= TT.qHC => + (forall (RO0 <: KEMROMx2.POracle_x2{+all mem, -CountHx2, -A} ) + (O <: KEMROMx2.CCA_ORC{+all mem, -CountHx2, -A} ), + hoare[ A(CountHx2(RO0), O).guess : + CountHx2.c_ht = 0 /\ CountHx2.c_hu = 0 ==> CountHx2.c_ht <= qHT /\ CountHx2.c_hu <= qHU]) => + hoare[ TT.Correctness_Adv1(H1_0, BUUCI(A)).A.find : TT.CO1.counter = 0 ==> TT.CO1.counter <= TT.qHC]. + + lemma count_buuowmod: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) (RO0 <: TT.PKEROM.POracle{+all mem, -TT.CountO, -BUUOWMod(A)} ) + (O <: TT.PKEROM.VA_ORC{+all mem, -TT.CountO, -BUUOWMod(A)} ), + TT.qP = 0 => + TT.qV = 0 => + qHT + qHU + 1 <= TT.qH => + (forall (RO1 <: KEMROMx2.POracle_x2{+all mem, -CountHx2, -A} ) + (O0 <: KEMROMx2.CCA_ORC{+all mem, -CountHx2, -A} ), + hoare[ A(CountHx2(RO1), O0).guess : + CountHx2.c_ht = 0 /\ CountHx2.c_hu = 0 ==> CountHx2.c_ht <= qHT /\ CountHx2.c_hu <= qHU]) => + hoare[ BUUOWMod(A, TT.CountH(RO0), TT.CountO(O)).find : + TT.CountO.c_h = 0 /\ TT.CountO.c_cvo = 0 /\ TT.CountO.c_pco = 0 ==> + TT.CountO.c_h <= TT.qH /\ TT.CountO.c_cvo <= TT.qV /\ TT.CountO.c_pco <= TT.qP]. + + lemma conclusion_cca_pre: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + (forall (H0 <: KEMROMx2.POracle_x2{+all mem, -A} ) (O <: KEMROMx2.CCA_ORC{+all mem, -A} ), + islossless O.dec => islossless H0.get1 => islossless H0.get2 => islossless A(H0, O).guess) => + `|Pr[KEMROMx2.CCA(KEMROMx2.RO_x2(KEMROMx2.RO1.RO, KEMROMx2.RO2.RO), UU, A).main() @ &m : res] - 1%r / 2%r| <= + `|Pr[J.IND(PseudoRF.PRF, D(A)).main() @ &m : res] - Pr[J.IND(RF.RF, D(A)).main() @ &m : res]| + + Pr[TT.PKEROM.Correctness_Adv(KEMROMx2.RO1.RO, TT.TT, BUUC(A)).main() @ &m : res] + + Pr[TT.PKEROM.Correctness_Adv(KEMROMx2.RO1.RO, TT.TT, BUUCI(A)).main() @ &m : res] + + Pr[TT.PKEROM.OW_PCVA(KEMROMx2.RO1.RO, TT.TT, BUUOWMod(A)).main() @ &m : res]. + + lemma conclusion: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKE.OW_CPA, -TT.PKE.BOWp, -TT.PKE.OWL_CPA, -TT.PKE.OWvsIND.Bowl, -TT.BasePKE, -TT.PKEROM.RO.RO, -TT.PKEROM.RO.FRO, -TT.PKEROM.OW_PCVA, -TT.Correctness_Adv1, -TT.B, -TT.CountO, -TT.Gm, -TT.O_AdvOW, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + TT.qH = qHT + qHU + 1 => + TT.qV = 0 => + TT.qP = 0 => + TT.qH + 1 = TT.qHC => + TT.qHC < TT.FinT.card - 1 => + (forall (RO0 <: KEMROMx2.POracle_x2{+all mem, -CountHx2, -A} ) + (O <: KEMROMx2.CCA_ORC{+all mem, -CountHx2, -A} ), + hoare[ A(CountHx2(RO0), O).guess : + CountHx2.c_ht = 0 /\ CountHx2.c_hu = 0 ==> CountHx2.c_ht <= qHT /\ CountHx2.c_hu <= qHU]) => + (forall (H0 <: KEMROMx2.POracle_x2{+all mem, -A} ) (O <: KEMROMx2.CCA_ORC{+all mem, -A} ), + islossless O.dec => islossless H0.get1 => islossless H0.get2 => islossless A(H0, O).guess) => + `|Pr[KEMROMx2.CCA(KEMROMx2.RO_x2(KEMROMx2.RO1.RO, KEMROMx2.RO2.RO), UU, A).main() @ &m : res] - 1%r / 2%r| <= + `|Pr[J.IND(PseudoRF.PRF, D(A)).main() @ &m : res] - Pr[J.IND(RF.RF, D(A)).main() @ &m : res]| + + (qHT + qHU + 3)%r * Pr[TT.PKE.Correctness_Adv(TT.BasePKE, TT.B(BUUC(A), TT.PKEROM.RO.RO)).main() @ &m : res] + + (qHT + qHU + 3)%r * Pr[TT.PKE.Correctness_Adv(TT.BasePKE, TT.B(BUUCI(A), TT.PKEROM.RO.RO)).main() @ &m : res] + + (qHT + qHU + 3)%r * + Pr[TT.PKE.Correctness_Adv(TT.BasePKE, TT.B(TT.AdvCorr(BUUOWMod(A)), TT.PKEROM.RO.RO)).main() @ &m : res] + + Pr[TT.PKE.Correctness_Adv(TT.BasePKE, TT.PKE.BOWp(TT.BasePKE, TT.AdvOW(BUUOWMod(A)))).main() @ &m : res] + + 2%r * + `|Pr[TT.PKE.CPA(TT.BasePKE, TT.PKE.OWvsIND.Bowl(TT.PKE.OWvsIND.BL(TT.AdvOW(BUUOWMod(A))))).main() @ &m : res] - + 1%r / 2%r| + + (qHT + qHU + 1)%r * Pr[TT.PKE.OW_CPA(TT.BasePKE, TT.AdvOW_query(BUUOWMod(A))).main() @ &m : res] + + 2%r * TT.PKE.eps_msg. + + module X(O : TT.PKEROM.POracle) = BUUC(A/218906, O).H2B. + + module XI(O : TT.PKEROM.POracle) = BUUCI(A/218906, O).H2B. + + lemma conclusion_cpa: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKE.OW_CPA, -TT.PKE.BOWp, -TT.PKE.OWL_CPA, -TT.PKE.OWvsIND.Bowl, -TT.BasePKE, -TT.PKEROM.RO.RO, -TT.PKEROM.RO.FRO, -TT.PKEROM.OW_PCVA, -TT.Correctness_Adv1, -TT.B, -TT.CountO, -TT.Gm, -TT.O_AdvOW, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + TT.qH = qHT + qHU + 1 => + TT.qV = 0 => + TT.qP = 0 => + TT.qH + 1 = TT.qHC => + TT.qHC < TT.FinT.card - 1 => + (forall (RO0 <: KEMROMx2.POracle_x2{+all mem, -CountHx2, -A} ) + (O <: KEMROMx2.CCA_ORC{+all mem, -CountHx2, -A} ), + hoare[ A(CountHx2(RO0), O).guess : + CountHx2.c_ht = 0 /\ CountHx2.c_hu = 0 ==> CountHx2.c_ht <= qHT /\ CountHx2.c_hu <= qHU]) => + (forall (H0 <: KEMROMx2.POracle_x2{+all mem, -A} ) (O <: KEMROMx2.CCA_ORC{+all mem, -A} ), + islossless O.dec => islossless H0.get1 => islossless H0.get2 => islossless A(H0, O).guess) => + `|Pr[KEMROMx2.CCA(KEMROMx2.RO_x2(KEMROMx2.RO1.RO, KEMROMx2.RO2.RO), UU, A).main() @ &m : res] - 1%r / 2%r| <= + 2%r * + `|Pr[TT.PKE.CPA(TT.BasePKE, TT.PKE.OWvsIND.Bowl(TT.PKE.OWvsIND.BL(TT.AdvOW(BUUOWMod(A))))).main() @ &m : res] - + 1%r / 2%r| + + 2%r * + `|Pr[TT.PKE.CPA(TT.BasePKE, TT.PKE.OWvsIND.Bowl(TT.AdvOWL_query(BUUOWMod(A)))).main() @ &m : res] - 1%r / 2%r| + + (qHT + qHU + 3)%r * Pr[TT.PKE.Correctness_Adv(TT.BasePKE, TT.B(BUUC(A), TT.PKEROM.RO.RO)).main() @ &m : res] + + (qHT + qHU + 3)%r * Pr[TT.PKE.Correctness_Adv(TT.BasePKE, TT.B(BUUCI(A), TT.PKEROM.RO.RO)).main() @ &m : res] + + (qHT + qHU + 3)%r * + Pr[TT.PKE.Correctness_Adv(TT.BasePKE, TT.B(TT.AdvCorr(BUUOWMod(A)), TT.PKEROM.RO.RO)).main() @ &m : res] + + Pr[TT.PKE.Correctness_Adv(TT.BasePKE, TT.PKE.BOWp(TT.BasePKE, TT.AdvOW(BUUOWMod(A)))).main() @ &m : res] + + `|Pr[J.IND(PseudoRF.PRF, D(A)).main() @ &m : res] - Pr[J.IND(RF.RF, D(A)).main() @ &m : res]| + + 2%r * (qHT + qHU + 2)%r * TT.PKE.eps_msg. + end UU. + + theory KEMROM. + type skey = (publickey * W8.t Array1152.t) * sharedsecret. + + lemma dkey_ll: is_lossless srand. + + hint solve 0 lossless : dkey_ll. + + hint solve 0 random : dkey_ll. + + lemma dkey_uni: is_uniform srand. + + hint solve 0 random : dkey_uni. + + lemma dkey_fu: is_full srand. + + hint solve 0 random : dkey_fu. + + theory RO. + module type RO = { + proc init() : unit {} + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t {} + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit {} + + proc rem(x : plaintext * W8.t Array32.t) : unit {} + + proc sample(x : plaintext * W8.t Array32.t) : unit {} + } + + module type RO_Distinguisher(G : RO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.rem, G.sample} + } + + module MainD(D : RO_Distinguisher, RO0 : RO) = { + proc distinguish(x : unit) : bool = { + var r : bool; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + module type ROmap = { + proc init() : unit {} + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t {} + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit {} + + proc rem(x : plaintext * W8.t Array32.t) : unit {} + + proc sample(x : plaintext * W8.t Array32.t) : unit {} + + proc restrK() : (plaintext * W8.t Array32.t, sharedsecret * W8.t Array32.t) SmtMap.fmap {} + } + + module type FRO = { + proc init() : unit {} + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t {} + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit {} + + proc rem(x : plaintext * W8.t Array32.t) : unit {} + + proc sample(x : plaintext * W8.t Array32.t) : unit {} + + proc queried(x : plaintext * W8.t Array32.t, f : PROM.flag) : bool {} + + proc allKnown() : (plaintext * W8.t Array32.t, sharedsecret * W8.t Array32.t) SmtMap.fmap {} + } + + module type FRO_Distinguisher(G : FRO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.rem, G.sample, G.queried, G.allKnown} + } + + abstract theory MkRO. + module RO = { + var m : (plaintext * W8.t Array32.t, sharedsecret * W8.t Array32.t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = { + var r : sharedsecret * W8.t Array32.t; + + r <$ dRO; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : plaintext * W8.t Array32.t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : plaintext * W8.t Array32.t) : unit = { + RO.get(x); + } + + proc restrK() : (plaintext * W8.t Array32.t, sharedsecret * W8.t Array32.t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = RO.get + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit = RO.set + + proc rem(x : plaintext * W8.t Array32.t) : unit = RO.rem + + proc sample(x : plaintext * W8.t Array32.t) : unit = {} + }. + end MkRO. + + module RO = { + var m : (plaintext * W8.t Array32.t, sharedsecret * W8.t Array32.t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = { + var r : sharedsecret * W8.t Array32.t; + + r <$ dRO; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : plaintext * W8.t Array32.t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : plaintext * W8.t Array32.t) : unit = { + RO.get(x); + } + + proc restrK() : (plaintext * W8.t Array32.t, sharedsecret * W8.t Array32.t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = RO.get + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit = RO.set + + proc rem(x : plaintext * W8.t Array32.t) : unit = RO.rem + + proc sample(x : plaintext * W8.t Array32.t) : unit = {} + }. + + module FRO = { + var m : (plaintext * W8.t Array32.t, (sharedsecret * W8.t Array32.t) * PROM.flag) SmtMap.fmap + + proc init() : unit = { + FRO.m <- SmtMap.empty; + } + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = { + var r : sharedsecret * W8.t Array32.t; + + r <$ dRO; + if ((x \in FRO.m)%SmtMap) + r <- (oget (FRO.m.[x])%SmtMap).`1; + FRO.m.[x] <- (r, PROM.Known); + + return r; + } + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit = { + FRO.m.[x] <- (y, PROM.Known); + } + + proc rem(x : plaintext * W8.t Array32.t) : unit = { + FRO.m <- (rem FRO.m x)%SmtMap; + } + + proc sample(x : plaintext * W8.t Array32.t) : unit = { + var c : sharedsecret * W8.t Array32.t; + + c <$ dRO; + if ((x \notin FRO.m)%SmtMap) + FRO.m.[x] <- (c, PROM.Unknown); + } + + proc queried(x : plaintext * W8.t Array32.t, f : PROM.flag) : bool = { + return (x \in FRO.m)%SmtMap /\ ((FRO.m.[x])%SmtMap \is f)%PROM; + } + + proc allKnown() : (plaintext * W8.t Array32.t, sharedsecret * W8.t Array32.t) SmtMap.fmap = { + return (restr PROM.Known FRO.m)%SmtMap; + } + }. + + lemma RO_FRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_get: + equiv[ RO.get ~ FRO.get : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> ={res} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_sample: + equiv[ RO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(RO).distinguish ~ D(FRO).distinguish : + ={arg, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_init_ll: islossless RO.init. + + lemma FRO_init_ll: islossless FRO.init. + + lemma FRO_in_dom_ll: islossless FRO.queried. + + lemma FRO_restrK_ll: islossless FRO.allKnown. + + lemma RO_set_ll: islossless RO.set. + + lemma FRO_set_ll: islossless FRO.set. + + lemma RO_get_ll: (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => islossless RO.get. + + lemma FRO_get_ll: (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => islossless FRO.get. + + lemma RO_sample_ll: (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => islossless RO.sample. + + lemma FRO_sample_ll: (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => islossless FRO.sample. + + module type RM_Distinguisher(G : ROmap) = { + proc distinguish(_ : unit) : bool {G.get, G.sample, G.restrK} + } + + module MainRM(D : RM_Distinguisher, RO0 : ROmap) = { + proc distinguish(x : unit) : bool = { + var r : bool; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + lemma fcoll_bound ['rT]: + forall (f : sharedsecret * W8.t Array32.t -> 'rT) (Pc : real), + 0%r <= Pc => + (forall (_ _ : plaintext * W8.t Array32.t) (y : 'rT), y \in dmap dRO f => mu1 (dmap dRO f) y <= Pc) => + forall (q : int), + 0 <= q => + forall (D(G : ROmap) <: RM_Distinguisher{+all mem, -RO} ) &m (z : unit), + Pr[MainRM(D, RO).distinguish(z) @ &m : (fcoll f RO.m)%SmtMap /\ (fsize RO.m)%SmtMap <= q] <= + (q * (q - 1))%r / 2%r * Pc. + + theory FullEager. + abstract theory EagerCore. + axiom dout_ll: forall (_ : plaintext * W8.t Array32.t), is_lossless dRO. + + module type Orcl = { + proc f(x : plaintext * W8.t Array32.t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : (plaintext * W8.t Array32.t) list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : (plaintext * W8.t Array32.t) list, l2 : (plaintext * W8.t Array32.t) list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : plaintext * W8.t Array32.t, l : (plaintext * W8.t Array32.t) list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : plaintext * W8.t Array32.t, t2 : plaintext * W8.t Array32.t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : plaintext * W8.t Array32.t, t2 : plaintext * W8.t Array32.t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : (plaintext * W8.t Array32.t) list) (i : plaintext * W8.t Array32.t) (s2 : (plaintext * + W8.t Array32.t) list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : plaintext * W8.t Array32.t -> bool) (Inv : + (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ + (forall (x : plaintext * W8.t Array32.t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : plaintext * W8.t Array32.t -> bool) (Inv : + (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : + (forall (x : plaintext * W8.t Array32.t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = { + var r : sharedsecret * W8.t Array32.t; + + r <$ dRO; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit = FRO.set + + proc rem(x : plaintext * W8.t Array32.t) : unit = FRO.rem + + proc sample(x : plaintext * W8.t Array32.t) : unit = FRO.sample + + proc queried(x : plaintext * W8.t Array32.t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (plaintext * W8.t Array32.t, sharedsecret * W8.t Array32.t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : plaintext * W8.t Array32.t) : unit = { + var c : sharedsecret * W8.t Array32.t; + + c <$ dRO; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : plaintext * W8.t Array32.t) (mx1 : ((sharedsecret * W8.t Array32.t) * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : plaintext * W8.t Array32.t) (mx1 mx2 : ((sharedsecret * W8.t Array32.t) * + PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : plaintext * W8.t Array32.t) (r1 : sharedsecret * W8.t Array32.t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + end FullEager. + + abstract theory FinEager. + abstract theory EagerCore. + axiom dout_ll: forall (_ : plaintext * W8.t Array32.t), is_lossless dRO. + + module type Orcl = { + proc f(x : plaintext * W8.t Array32.t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : (plaintext * W8.t Array32.t) list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : (plaintext * W8.t Array32.t) list, l2 : (plaintext * W8.t Array32.t) list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : plaintext * W8.t Array32.t, l : (plaintext * W8.t Array32.t) list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : plaintext * W8.t Array32.t, t2 : plaintext * W8.t Array32.t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : plaintext * W8.t Array32.t, t2 : plaintext * W8.t Array32.t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : (plaintext * W8.t Array32.t) list) (i : plaintext * W8.t Array32.t) (s2 : (plaintext * + W8.t Array32.t) list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : plaintext * W8.t Array32.t -> bool) (Inv : + (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ + (forall (x : plaintext * W8.t Array32.t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : plaintext * W8.t Array32.t -> bool) (Inv : + (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : + (forall (x : plaintext * W8.t Array32.t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = { + var r : sharedsecret * W8.t Array32.t; + + r <$ dRO; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit = FRO.set + + proc rem(x : plaintext * W8.t Array32.t) : unit = FRO.rem + + proc sample(x : plaintext * W8.t Array32.t) : unit = FRO.sample + + proc queried(x : plaintext * W8.t Array32.t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (plaintext * W8.t Array32.t, sharedsecret * W8.t Array32.t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : plaintext * W8.t Array32.t) : unit = { + var c : sharedsecret * W8.t Array32.t; + + c <$ dRO; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : plaintext * W8.t Array32.t) (mx1 : ((sharedsecret * W8.t Array32.t) * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : plaintext * W8.t Array32.t) (mx1 mx2 : ((sharedsecret * W8.t Array32.t) * + PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : plaintext * W8.t Array32.t) (r1 : sharedsecret * W8.t Array32.t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + theory FinFrom. + type t = plaintext * W8.t Array32.t. + + op enum : t list. + + op card : int = size enum. + + axiom enum_spec: forall (x : t), count (pred1 x) enum = 1. + + lemma enumP: forall (x : t), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : t list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinFrom. + + module FinRO = { + proc rem(x : plaintext * W8.t Array32.t) : unit = RO.rem + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit = RO.set + + proc init() : unit = { + var l : FinFrom.t list; + + l <- FinFrom.enum; + RO.init(); + while (l <> []){ + RO.sample(head witness l); + l <- behead l; + } + } + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = { + return oget (RO.m.[x])%SmtMap; + } + + proc sample(x : plaintext * W8.t Array32.t) : unit = {} + }. + + module type FinRO_Distinguisher(G : RO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.sample} + } + + lemma RO_FinRO_D: + (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ MainD(D, RO).distinguish ~ MainD(D, FinRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_RO_FinRO_D: + (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ) &m (x : unit) (p : + bool -> bool), + Pr[MainD(D, RO).distinguish(x) @ &m : p res] = Pr[MainD(D, FinRO).distinguish(x) @ &m : p res]. + + theory MUniFinFun. + op mfun ['u] (d : plaintext * W8.t Array32.t -> 'u distr) (f : + plaintext * W8.t Array32.t -> 'u) : real = + (big predT (fun (x : plaintext * W8.t Array32.t) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma mfunE ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr) (f : + plaintext * W8.t Array32.t -> 'u), mfun d f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + lemma isdistr_dfun ['u]: forall (d : plaintext * W8.t Array32.t -> 'u distr), isdistr (mfun d). + + op dfun ['u] (d : plaintext * W8.t Array32.t -> 'u distr) : ( + plaintext * W8.t Array32.t -> 'u) distr = mk (mfun d). + + lemma dfun1E ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr) (f : + plaintext * W8.t Array32.t -> 'u), + mu1 (dfun d) f = + (big predT (fun (x : plaintext * W8.t Array32.t) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun1E_djoin ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr) (f : + plaintext * W8.t Array32.t -> 'u), mu1 (dfun d) f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + op tofun ['u] (us : 'u list) (x : plaintext * W8.t Array32.t) : 'u = nth witness us (index x FinFrom.enum). + + op offun ['u] (f : plaintext * W8.t Array32.t -> 'u) : 'u list = map f FinFrom.enum. + + lemma offunK ['u]: cancel offun tofun. + + lemma tofunK ['u]: forall (us : 'u list), size us = FinFrom.card => offun (tofun us) = us. + + lemma dfun_dmap ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr), dfun d = dmap (djoinmap d FinFrom.enum) tofun. + + lemma dfun_supp ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr) (f : + plaintext * W8.t Array32.t -> 'u), + f \in dfun d <=> forall (x : plaintext * W8.t Array32.t), f x \in d x. + + lemma dfun_fu ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr), + (forall (x : plaintext * W8.t Array32.t), is_full (d x)) => is_full (dfun d). + + lemma dfun_uni ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr), + (forall (x : plaintext * W8.t Array32.t), is_uniform (d x)) => is_uniform (dfun d). + + lemma dfun_funi ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr), + (forall (x : plaintext * W8.t Array32.t), is_uniform (d x)) => + (forall (x : plaintext * W8.t Array32.t), is_full (d x)) => is_funiform (dfun d). + + lemma dfunE_djoin ['u]: + forall (du : plaintext * W8.t Array32.t -> 'u distr) (E : + (plaintext * W8.t Array32.t -> 'u) -> bool), + mu (dfun du) E = mu (djoinmap du FinFrom.enum) (E \o tofun). + + lemma dfunE ['u]: + forall (du : plaintext * W8.t Array32.t -> 'u distr) (p : + plaintext * W8.t Array32.t -> 'u -> bool), + mu (dfun du) + (fun (f : plaintext * W8.t Array32.t -> 'u) => forall (x : plaintext * W8.t Array32.t), p x (f x)) = + (big predT (fun (x : plaintext * W8.t Array32.t) => mu (du x) (p x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma weight_dfun ['u]: + forall (df : plaintext * W8.t Array32.t -> 'u distr), + weight (dfun df) = + (big predT (fun (x : plaintext * W8.t Array32.t) => weight (df x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun_ll ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr), + (forall (x : plaintext * W8.t Array32.t), is_lossless (d x)) => is_lossless (dfun d). + + lemma dlet_dfun ['u, 'v]: + forall (d1 : plaintext * W8.t Array32.t -> 'u distr) (d2 : + plaintext * W8.t Array32.t -> 'u -> 'v distr), + dlet (dfun d1) + (fun (f1 : plaintext * W8.t Array32.t -> 'u) => + dfun (fun (x : plaintext * W8.t Array32.t) => d2 x (f1 x))) = + dfun (fun (x : plaintext * W8.t Array32.t) => dlet (d1 x) (fun (x1 : 'u) => d2 x x1)). + + lemma dfun_unit ['u]: + forall (f : plaintext * W8.t Array32.t -> 'u), + dfun (fun (x : plaintext * W8.t Array32.t) => dunit (f x)) = dunit f. + + lemma dmap_dfun ['u, 'v]: + forall (d : plaintext * W8.t Array32.t -> 'u distr) (F : + plaintext * W8.t Array32.t -> 'u -> 'v), + dmap (dfun d) + (fun (f : plaintext * W8.t Array32.t -> 'u) (x : plaintext * W8.t Array32.t) => F x (f x)) = + dfun (fun (x : plaintext * W8.t Array32.t) => dmap (d x) (fun (fx : 'u) => F x fx)). + + lemma dfun1E_fix1 ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr) (x0 : plaintext * W8.t Array32.t) + (f : plaintext * W8.t Array32.t -> 'u), + mu1 (dfun d) f = mu1 (d x0) (f x0) * mu1 (dfun d.[x0 <- dunit (f x0)]) f. + + lemma dlet_dfun_fupdate_ll ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr) (x0 : plaintext * W8.t Array32.t) (v : 'u), + dlet (dfun d.[x0 <- dunit v]) + (fun (f : plaintext * W8.t Array32.t -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + dfun d. + + lemma dfunE_dlet_fix1 ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr) (x0 : plaintext * W8.t Array32.t), + dfun d = dlet (d x0) (fun (v : 'u) => dfun d.[x0 <- dunit v]). + + lemma dlet_dfun_update ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr) (x0 : plaintext * W8.t Array32.t), + dlet (dfun d) (fun (f : plaintext * W8.t Array32.t -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + (weight (d x0) \cdot dfun d). + + lemma dfun_projE ['u]: + forall (df : plaintext * W8.t Array32.t -> 'u distr) (x : plaintext * W8.t Array32.t), + dmap (dfun df) (fun (f : plaintext * W8.t Array32.t -> 'u) => f x) = + (weight (dfun df) / weight (df x) \cdot df x). + + lemma eq_from_dfun_proj_eq ['u]: + forall (df1 df2 : plaintext * W8.t Array32.t -> 'u distr), + (forall (x : plaintext * W8.t Array32.t), is_lossless (df1 x)) => + (forall (x : plaintext * W8.t Array32.t), is_lossless (df2 x)) => + (forall (x : plaintext * W8.t Array32.t), + dmap (dfun df1) (fun (f : plaintext * W8.t Array32.t -> 'u) => f x) = + dmap (dfun df2) (fun (f : plaintext * W8.t Array32.t -> 'u) => f x)) => + df1 == df2. + + lemma dfun_prod1E ['u, 'v]: + forall (df : plaintext * W8.t Array32.t -> 'u distr) (dg : + plaintext * W8.t Array32.t -> 'v distr) (f : plaintext * W8.t Array32.t -> 'u) + (g : plaintext * W8.t Array32.t -> 'v), + mu1 (dfun (fun (x : plaintext * W8.t Array32.t) => df x `*` dg x)) + (fun (x : plaintext * W8.t Array32.t) => (f x, g x)) = + mu1 (dfun df) f * mu1 (dfun dg) g. + + lemma dprod_funE ['u, 'v]: + forall (df : plaintext * W8.t Array32.t -> 'u distr) (dg : + plaintext * W8.t Array32.t -> 'v distr), + dfun df `*` dfun dg = + dmap (dfun (fun (x : plaintext * W8.t Array32.t) => df x `*` dg x)) + (fun (fg : plaintext * W8.t Array32.t -> 'u * 'v) => + ((fun (p : 'u * 'v) => p.`1) \o fg, (fun (p : 'u * 'v) => p.`2) \o fg)). + + lemma dfun_prodE ['u, 'v]: + forall (df : plaintext * W8.t Array32.t -> 'u distr) (dg : + plaintext * W8.t Array32.t -> 'v distr), + dfun (fun (x : plaintext * W8.t Array32.t) => df x `*` dg x) = + dmap (dfun df `*` dfun dg) + (fun (fg : (plaintext * W8.t Array32.t -> 'u) * (plaintext * W8.t Array32.t -> 'v)) (x : plaintext * + W8.t Array32.t) => (fg.`1 x, fg.`2 x)). + + lemma dfun_condE ['u]: + forall (X : plaintext * W8.t Array32.t -> bool) (dt df : + plaintext * W8.t Array32.t -> 'u distr), + (forall (x : plaintext * W8.t Array32.t), is_lossless (dt x)) => + (forall (x : plaintext * W8.t Array32.t), is_lossless (df x)) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext * W8.t Array32.t -> 'u) * (plaintext * W8.t Array32.t -> 'u)) (x : plaintext * + W8.t Array32.t) => if X x then tf.`1 x else tf.`2 x) = + dfun (fun (x : plaintext * W8.t Array32.t) => if X x then dt x else df x). + + lemma dlet_dfun_cond ['u]: + forall (dX : plaintext * W8.t Array32.t -> bool distr) (dt df : + plaintext * W8.t Array32.t -> 'u distr), + (forall (x : plaintext * W8.t Array32.t), is_lossless (dX x)) => + (forall (x : plaintext * W8.t Array32.t), is_lossless (dt x)) => + (forall (x : plaintext * W8.t Array32.t), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : plaintext * W8.t Array32.t -> bool) => + dfun (fun (x : plaintext * W8.t Array32.t) => if X x then dt x else df x)) = + dfun (fun (x : plaintext * W8.t Array32.t) => dlet (dX x) (fun (b : bool) => if b then dt x else df x)). + + lemma dfun_dcondE ['u]: + forall (dX : plaintext * W8.t Array32.t -> bool distr) (dt df : + plaintext * W8.t Array32.t -> 'u distr), + (forall (x : plaintext * W8.t Array32.t), is_lossless (dX x)) => + (forall (x : plaintext * W8.t Array32.t), is_lossless (dt x)) => + (forall (x : plaintext * W8.t Array32.t), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : plaintext * W8.t Array32.t -> bool) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext * W8.t Array32.t -> 'u) * ( + plaintext * W8.t Array32.t -> 'u)) (x : plaintext * W8.t Array32.t) => + if X x then tf.`1 x else tf.`2 x)) = + dfun + (fun (x : plaintext * W8.t Array32.t) => (mu1 (dX x) true \cdot dt x) + (mu1 (dX x) false \cdot df x)). + end MUniFinFun. + + module FunRO = { + var f : plaintext * W8.t Array32.t -> sharedsecret * W8.t Array32.t + + proc init() : unit = { + FunRO.f <$ (dfun (fun (_ : plaintext * W8.t Array32.t) => dRO))%MUniFinFun; + } + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = { + return FunRO.f x; + } + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit = { + FunRO.f <- fun (z : plaintext * W8.t Array32.t) => if z = x then y else FunRO.f z; + } + + proc sample(x : plaintext * W8.t Array32.t) : unit = {} + + proc rem(x : plaintext * W8.t Array32.t) : unit = {} + }. + + lemma FinRO_FunRO_D: + (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ), + equiv[ MainD(D, FinRO).distinguish ~ MainD(D, FunRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_FinRO_FunRO_D: + (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ) &m (x : unit) (p : + bool -> bool), + Pr[MainD(D, FinRO).distinguish(x) @ &m : p res] = Pr[MainD(D, FunRO).distinguish(x) @ &m : p res]. + end FinEager. + end RO. + + module type Oracle = { + proc init() : unit {} + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t {} + } + + module type POracle = { + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t {} + } + + module type Scheme(O : POracle) = { + proc kg() : publickey * skey {O.get} + + proc enc(pk : publickey) : (W8.t Array960.t * W8.t Array128.t) * sharedsecret {O.get} + + proc dec(sk : skey, c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option {O.get} + } + + module Correctness(H : Oracle, S : Scheme) = { + proc main() : bool = { + var pk : publickey; + var sk : skey; + var c : W8.t Array960.t * W8.t Array128.t; + var k : sharedsecret; + var k' : sharedsecret option; + + H.init(); + (pk, sk) <@ S(H).kg(); + (c, k) <@ S(H).enc(pk); + k' <@ S(H).dec(sk, c); + + return k' <> Some k; + } + }. + + module type CCA_ORC = { + proc dec(c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option {} + } + + module type CCA_ADV(H : POracle, O : CCA_ORC) = { + proc guess(pk : publickey, c : W8.t Array960.t * W8.t Array128.t, k : sharedsecret) : bool {O.dec, H.get} + } + + module CCA(H : Oracle, S : Scheme, A : CCA_ADV) = { + var cstar : (W8.t Array960.t * W8.t Array128.t) option + + var sk : skey + + module O = { + proc dec(c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var k : sharedsecret option; + + k <- None; + if (Some c <> CCA.cstar) + k <@ S(H).dec(CCA.sk, c); + + return k; + } + } + + module A = A(H, CCA(H, S, A).O) + + proc main() : bool = { + var pk : publickey; + var k1 : sharedsecret; + var ck0 : (W8.t Array960.t * W8.t Array128.t) * sharedsecret; + var b : bool; + var b' : bool; + + H.init(); + CCA.cstar <- None; + (pk, CCA.sk) <@ S(H).kg(); + k1 <$ srand; + b <$ {0,1}; + ck0 <@ S(H).enc(pk); + CCA.cstar <- Some ck0.`1; + b' <@ CCA(H, S, A).A.guess(pk, ck0.`1, if b then k1 else ck0.`2); + + return b' = b; + } + }. + end KEMROM. + + op qHK : int. + + axiom ge0_qHK: 0 <= qHK. + + module CountH(H : KEMROM.POracle) = { + var c_h : int + + proc init() : unit = { + CountH.c_h <- 0; + } + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = { + var r : sharedsecret * W8.t Array32.t; + + r <@ H.get(x); + CountH.c_h <- CountH.c_h + 1; + + return r; + } + }. + + module FO_K(H : KEMROM.POracle) = { + proc kg() : publickey * KEMROM.skey = { + var pk : publickey; + var sk : W8.t Array1152.t; + var k : sharedsecret; + + (pk, sk) <$ UU.TT.kg; + k <$ srand; + + return (pk, ((pk, sk), k)); + } + + proc enc(pk : publickey) : (W8.t Array960.t * W8.t Array128.t) * sharedsecret = { + var m : plaintext; + var r : W8.t Array32.t; + var c : W8.t Array960.t * W8.t Array128.t; + var k : sharedsecret; + + m <$ srand; + (k, r) <@ H.get(m, H_pk pk); + c <- enc r pk m; + + return (c, k); + } + + proc dec(sk : KEMROM.skey, c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var m' : plaintext option; + var c' : W8.t Array960.t * W8.t Array128.t; + var ks : sharedsecret; + var r : W8.t Array32.t; + var kn : sharedsecret; + + m' <- dec sk.`1.`2 c; + (ks, r) <@ H.get(oget m', H_pk sk.`1.`1); + kn <- J sk.`2 c; + c' <- enc r sk.`1.`1 (oget m'); + + return if m' <> None /\ c' = c then Some ks else Some kn; + } + }. + + module UU_L(H1 : UU.KEMROMx2.RO1.RO, H2 : UU.KEMROMx2.RO2.RO) = { + proc enc(pk : publickey) : (W8.t Array960.t * W8.t Array128.t) * sharedsecret = UU.UU(UU.KEMROMx2.RO_x2(H1, H2)).enc + + proc kg() : publickey * UU.KEMROMx2.skey = UU.UU(UU.KEMROMx2.RO_x2(H1, H2)).kg + + proc dec(sk : KEMROM.skey, c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var m' : plaintext option; + var r : W8.t Array32.t; + var c' : W8.t Array960.t * W8.t Array128.t; + var rv : plaintext option; + var k : sharedsecret; + + rv <- None; + m' <- dec sk.`1.`2 c; + H1.sample(oget m'); + H2.sample(oget m'); + if (m' <> None) { + r <@ H1.get(oget m'); + c' <- enc r sk.`1.`1 (oget m'); + rv <- if c = c' then m' else None; + } + if (rv = None) + k <- J sk.`2 c; + else + k <@ H2.get(oget m'); + + return Some k; + } + }. + + module Correctness_L(H1 : UU.KEMROMx2.RO1.RO, H2 : UU.KEMROMx2.RO2.RO) = { + proc main() : bool = { + var pk : publickey; + var sk : UU.KEMROMx2.skey; + var c : W8.t Array960.t * W8.t Array128.t; + var k : sharedsecret; + var k' : sharedsecret option; + + H1.init(); + H2.init(); + (pk, sk) <@ UU_L(H1, H2).kg(); + (c, k) <@ UU_L(H1, H2).enc(pk); + k' <@ UU_L(H1, H2).dec(sk, c); + + return k' <> Some k; + } + }. + + lemma go_parametric_corr_lro: + forall &m, + Pr[UU.KEMROMx2.Correctness(UU.KEMROMx2.RO_x2(UU.KEMROMx2.RO1.RO, UU.KEMROMx2.RO2.RO), UU.UU).main() @ &m : res] = + Pr[Correctness_L(UU.KEMROMx2.RO1.LRO, UU.KEMROMx2.RO2.LRO).main() @ &m : res]. + + module DC1(G1 : UU.KEMROMx2.RO1.RO) = { + proc distinguish() : bool = Correctness_L(G1, UU.KEMROMx2.RO2.LRO).main + }. + + module DC2(G2 : UU.KEMROMx2.RO2.RO) = { + proc distinguish() : bool = Correctness_L(UU.KEMROMx2.RO1.RO, G2).main + }. + + lemma go_parametric_corr: + forall &m, + Pr[UU.KEMROMx2.Correctness(UU.KEMROMx2.RO_x2(UU.KEMROMx2.RO1.RO, UU.KEMROMx2.RO2.RO), UU.UU).main() @ &m : res] = + Pr[Correctness_L(UU.KEMROMx2.RO1.RO, UU.KEMROMx2.RO2.RO).main() @ &m : res]. + + lemma correctness_fo_k: + forall &m, + UU.TT.qHC = 0 => + 1 < UU.TT.FinT.card => + Pr[KEMROM.Correctness(KEMROM.RO.RO, FO_K).main() @ &m : res] <= + Pr[UU.TT.PKE.Correctness_Adv(UU.TT.BasePKE, UU.TT.B(UU.B_UC, UU.TT.PKEROM.RO.RO)).main() @ &m : res]. + + module CCAL(H1 : UU.KEMROMx2.RO1.RO, H2 : UU.KEMROMx2.RO2.RO, A : UU.KEMROMx2.CCA_ADV) = { + module O = { + proc dec(c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var k : sharedsecret option; + + k <- None; + if (Some c <> KEMROM.CCA.cstar) + k <@ UU_L(H1, H2).dec(KEMROM.CCA.sk, c); + + return k; + } + } + + module A = A(UU.KEMROMx2.RO_x2(H1, H2), CCAL(H1, H2, A).O) + + proc main() : bool = { + var pk : publickey; + var k1 : sharedsecret; + var ck0 : (W8.t Array960.t * W8.t Array128.t) * sharedsecret; + var b : bool; + var b' : bool; + + H1.init(); + H2.init(); + KEMROM.CCA.cstar <- None; + (pk, KEMROM.CCA.sk) <@ UU_L(H1, H2).kg(); + k1 <$ srand; + b <$ {0,1}; + ck0 <@ UU_L(H1, H2).enc(pk); + KEMROM.CCA.cstar <- Some ck0.`1; + b' <@ CCAL(H1, H2, A).A.guess(pk, ck0.`1, if b then k1 else ck0.`2); + + return b' = b; + } + }. + + module B1x2(A : KEMROM.CCA_ADV, H2x : UU.KEMROMx2.POracle_x2, DO : KEMROM.CCA_ORC) = { + var _pk : publickey + + module BH = { + proc get(m : plaintext, hpk : W8.t Array32.t) : sharedsecret * W8.t Array32.t = { + var r : W8.t Array32.t; + var k : sharedsecret; + + (k, r) <@ KEMROM.RO.RO.get(m, hpk); + if (hpk = H_pk B1x2._pk) { + r <@ H2x.get1(m); + k <@ H2x.get2(m); + } + + return (k, r); + } + } + + proc guess(pk : publickey, c : W8.t Array960.t * W8.t Array128.t, k : sharedsecret) : bool = { + var b : bool; + + B1x2._pk <- pk; + CountH(KEMROM.RO.RO).init(); + KEMROM.RO.RO.init(); + b <@ A(CountH(B1x2(A, H2x, DO).BH), DO).guess(pk, c, k); + + return b; + } + }. + + module DKK1(A : UU.KEMROMx2.CCA_ADV, G1 : UU.KEMROMx2.RO1.RO) = { + proc distinguish() : bool = CCAL(G1, UU.KEMROMx2.RO2.LRO, A).main + }. + + module DKK2(A : UU.KEMROMx2.CCA_ADV, G2 : UU.KEMROMx2.RO2.RO) = { + proc distinguish() : bool = CCAL(UU.KEMROMx2.RO1.RO, G2, A).main + }. + + lemma go_parametric: + forall (A(H : UU.KEMROMx2.POracle_x2, O : UU.KEMROMx2.CCA_ORC) <: + UU.KEMROMx2.CCA_ADV{+all mem, -UU.KEMROMx2.RO1.RO, -UU.KEMROMx2.RO1.FRO, -UU.KEMROMx2.RO2.RO, -UU.KEMROMx2.RO2.FRO, -UU.KEMROMx2.CCA, -KEMROM.CCA} + ) &m, + Pr[UU.KEMROMx2.CCA(UU.KEMROMx2.RO_x2(UU.KEMROMx2.RO1.RO, UU.KEMROMx2.RO2.RO), UU.UU, A).main() @ &m : res] = + Pr[CCAL(UU.KEMROMx2.RO1.RO, UU.KEMROMx2.RO2.RO, A).main() @ &m : res]. + + lemma same_scheme: + forall (A(H : KEMROM.POracle, O : KEMROM.CCA_ORC) <: + KEMROM.CCA_ADV{+all mem, -KEMROM.RO.RO.m, -UU.TT.PKE.OW_CPA, -UU.TT.PKE.BOWp, -UU.TT.PKE.OWL_CPA, -UU.TT.PKE.OWvsIND.Bowl, -UU.TT.BasePKE, -UU.TT.PKEROM.RO.RO, -UU.TT.PKEROM.RO.FRO, -UU.TT.PKEROM.OW_PCVA, -UU.TT.Correctness_Adv1, -UU.TT.B, -UU.TT.CountO, -UU.TT.Gm, -UU.TT.O_AdvOW, -UU.RF.RF, -UU.PseudoRF.PRF, -UU.KEMROMx2.RO1.RO, -UU.KEMROMx2.RO1.FRO, -UU.KEMROMx2.RO2.RO, -UU.KEMROMx2.RO2.FRO, -UU.KEMROMx2.CCA, -UU.CountHx2, -UU.RO1E.FunRO, -UU.UU2, -UU.H2, -UU.Gm2, -UU.Gm3, -UU.H2BOWMod, -KEMROM.CCA, -B1x2} + ) &m, + Pr[UU.KEMROMx2.CCA(UU.KEMROMx2.RO_x2(UU.KEMROMx2.RO1.RO, UU.KEMROMx2.RO2.RO), UU.UU, B1x2(A)).main() @ &m : res] = + Pr[KEMROM.CCA(KEMROM.RO.RO, FO_K, A).main() @ &m : res]. + + lemma countB1x2: + forall (A(H : KEMROM.POracle, O : KEMROM.CCA_ORC) <: + KEMROM.CCA_ADV{+all mem, -KEMROM.RO.RO.m, -UU.TT.PKE.OW_CPA, -UU.TT.PKE.BOWp, -UU.TT.PKE.OWL_CPA, -UU.TT.PKE.OWvsIND.Bowl, -UU.TT.BasePKE, -UU.TT.PKEROM.RO.RO, -UU.TT.PKEROM.RO.FRO, -UU.TT.PKEROM.OW_PCVA, -UU.TT.Correctness_Adv1, -UU.TT.B, -UU.TT.CountO, -UU.TT.Gm, -UU.TT.O_AdvOW, -UU.RF.RF, -UU.PseudoRF.PRF, -UU.KEMROMx2.RO1.RO, -UU.KEMROMx2.RO1.FRO, -UU.KEMROMx2.RO2.RO, -UU.KEMROMx2.RO2.FRO, -UU.KEMROMx2.CCA, -UU.CountHx2, -UU.RO1E.FunRO, -UU.UU2, -UU.H2, -UU.Gm2, -UU.Gm3, -UU.H2BOWMod, -KEMROM.CCA, -B1x2} + ) (RO0 <: UU.KEMROMx2.POracle_x2{+all mem, -UU.CountHx2, -B1x2(A)} ) + (O <: UU.KEMROMx2.CCA_ORC{+all mem, -UU.CountHx2, -B1x2(A)} ), + UU.qHT = qHK => + UU.qHU = qHK => + (forall (RO1 <: KEMROM.POracle{+all mem, -CountH, -A} ) (O0 <: KEMROM.CCA_ORC{+all mem, -CountH, -A} ), + hoare[ A(CountH(RO1), O0).guess : CountH.c_h = 0 ==> CountH.c_h <= qHK]) => + hoare[ B1x2(A, UU.CountHx2(RO0), O).guess : + UU.CountHx2.c_ht = 0 /\ UU.CountHx2.c_hu = 0 ==> + UU.CountHx2.c_ht <= UU.qHT /\ UU.CountHx2.c_hu <= UU.qHU]. + + lemma conclusion_fo_mlkem: + forall (A(H : KEMROM.POracle, O : KEMROM.CCA_ORC) <: + KEMROM.CCA_ADV{+all mem, -KEMROM.RO.RO.m, -UU.TT.PKE.OW_CPA, -UU.TT.PKE.BOWp, -UU.TT.PKE.OWL_CPA, -UU.TT.PKE.OWvsIND.Bowl, -UU.TT.BasePKE, -UU.TT.PKEROM.RO.RO, -UU.TT.PKEROM.RO.FRO, -UU.TT.PKEROM.OW_PCVA, -UU.TT.Correctness_Adv1, -UU.TT.B, -UU.TT.CountO, -UU.TT.Gm, -UU.TT.O_AdvOW, -UU.RF.RF, -UU.PseudoRF.PRF, -UU.KEMROMx2.RO1.RO, -UU.KEMROMx2.RO1.FRO, -UU.KEMROMx2.RO2.RO, -UU.KEMROMx2.RO2.FRO, -UU.KEMROMx2.CCA, -UU.CountHx2, -UU.RO1E.FunRO, -UU.UU2, -UU.H2, -UU.Gm2, -UU.Gm3, -UU.H2BOWMod, -KEMROM.CCA, -B1x2} + ) &m, + UU.qHT = qHK => + UU.qHU = qHK => + UU.TT.qH = UU.qHT + UU.qHU + 1 => + UU.TT.qV = 0 => + UU.TT.qP = 0 => + UU.TT.qH + 1 = UU.TT.qHC => + UU.TT.qHC < UU.TT.FinT.card - 1 => + (forall (RO0 <: KEMROM.POracle{+all mem, -CountH, -A} ) (O0 <: KEMROM.CCA_ORC{+all mem, -CountH, -A} ), + hoare[ A(CountH(RO0), O0).guess : CountH.c_h = 0 ==> CountH.c_h <= qHK]) => + (forall (H0 <: KEMROM.POracle{+all mem, -A} ) (O <: UU.KEMROMx2.CCA_ORC{+all mem, -A} ), + islossless O.dec => islossless H0.get => islossless A(H0, O).guess) => + `|Pr[KEMROM.CCA(KEMROM.RO.RO, FO_K, A).main() @ &m : res] - 1%r / 2%r| <= + 2%r * + `|Pr[UU.TT.PKE.CPA(UU.TT.BasePKE, UU.TT.PKE.OWvsIND.Bowl(UU.TT.PKE.OWvsIND.BL(UU.TT.AdvOW(UU.BUUOWMod(B1x2(A)))))).main + () @ &m : res] - + 1%r / 2%r| + + 2%r * + `|Pr[UU.TT.PKE.CPA(UU.TT.BasePKE, UU.TT.PKE.OWvsIND.Bowl(UU.TT.AdvOWL_query(UU.BUUOWMod(B1x2(A))))).main + () @ &m : res] - + 1%r / 2%r| + + (2 * qHK + 3)%r * + Pr[UU.TT.PKE.Correctness_Adv(UU.TT.BasePKE, UU.TT.B(UU.BUUC(B1x2(A)), UU.TT.PKEROM.RO.RO)).main() @ &m : res] + + (2 * qHK + 3)%r * + Pr[UU.TT.PKE.Correctness_Adv(UU.TT.BasePKE, UU.TT.B(UU.BUUCI(B1x2(A)), UU.TT.PKEROM.RO.RO)).main() @ &m : res] + + (2 * qHK + 3)%r * + Pr[UU.TT.PKE.Correctness_Adv(UU.TT.BasePKE, UU.TT.B(UU.TT.AdvCorr(UU.BUUOWMod(B1x2(A))), UU.TT.PKEROM.RO.RO)).main + () @ &m : res] + + Pr[UU.TT.PKE.Correctness_Adv(UU.TT.BasePKE, UU.TT.PKE.BOWp(UU.TT.BasePKE, UU.TT.AdvOW(UU.BUUOWMod(B1x2(A))))).main + () @ &m : res] + + `|Pr[UU.J.IND(UU.PseudoRF.PRF, UU.D(B1x2(A))).main() @ &m : res] - + Pr[UU.J.IND(UU.RF.RF, UU.D(B1x2(A))).main() @ &m : res]| + + 2%r * (2 * qHK + 2)%r * UU.TT.PKE.eps_msg. + end FO_MLKEM. + + module MLWE_PKE_HASH = { + proc kg() : publickey * W8.t Array1152.t = { + var r : W8.t Array32.t; + var pk : publickey; + var sk : W8.t Array1152.t; + + r <$ srand; + (pk, sk) <- kg r; + + return (pk, sk); + } + + proc enc(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = { + var rr : W8.t Array32.t; + var c : W8.t Array960.t * W8.t Array128.t; + + rr <$ srand; + c <- enc rr pk m; + + return c; + } + + proc dec(sk : W8.t Array1152.t, c : W8.t Array960.t * W8.t Array128.t) : plaintext option = { + var mo : plaintext option; + + mo <- dec sk c; + + return mo; + } + }. + + module MLWE_PKE_HASH_PROC = { + proc kg_bridge() : publickey * W8.t Array1152.t = { + var r : W8.t Array32.t; + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var t : polyvec; + + r <$ srand; + (sd, s, e) <- prg_kg_inner r; + t <- ((H sd *^ s)%MLWE_.Matrix_.Matrix + e)%Vector; + + return (pk_encode (sd, t), sk_encode s); + } + + proc kg() : publickey * W8.t Array1152.t = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var t : polyvec; + + sd <$ srand; + s <$ MLWE_.dshort; + e <$ MLWE_.dshort; + t <- ((H sd *^ s)%MLWE_.Matrix_.Matrix + e)%Vector; + + return (pk_encode (sd, t), sk_encode s); + } + + proc enc_bridge(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = { + var sd : W8.t Array32.t; + var t : polyvec; + var rr : W8.t Array32.t; + var r : polyvec; + var e1 : polyvec; + var e2 : poly; + var u : polyvec; + var v : poly; + + (sd, t) <- pk_decode pk; + rr <$ srand; + (r, e1, e2) <- prg_enc_inner rr; + u <- ((trmx (H sd) *^ r)%MLWE_.Matrix_.Matrix + e1)%Vector; + v <- ((t `<*>` r) &+ e2 &+ m_encode m)%MLWE_; + + return c_encode (u, v); + } + + proc enc(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = { + var sd : W8.t Array32.t; + var t : polyvec; + var r : polyvec; + var e1 : polyvec; + var e2 : poly; + var u : polyvec; + var v : poly; + + (sd, t) <- pk_decode pk; + r <$ MLWE_.dshort; + e1 <$ MLWE_.dshort; + e2 <$ dshort_R; + u <- ((trmx (H sd) *^ r)%MLWE_.Matrix_.Matrix + e1)%Vector; + v <- ((t `<*>` r) &+ e2 &+ m_encode m)%MLWE_; + + return c_encode (u, v); + } + + proc dec(sk : W8.t Array1152.t, c : W8.t Array960.t * W8.t Array128.t) : plaintext option = { + var u : polyvec; + var v : poly; + + (u, v) <- c_decode c; + + return Some (m_decode (v &- (sk_decode sk `<*>` u))%MLWE_); + } + }. + + theory PRG_KG. + module type RG = { + proc get(sd : W8.t Array32.t) : W8.t Array32.t * polyvec * polyvec {} + } + + module type Distinguisher = { + proc distinguish(x : W8.t Array32.t * polyvec * polyvec) : bool {} + } + + module IND(PRG : RG, D : Distinguisher) = { + proc main() : bool = { + var b : bool; + var sd : W8.t Array32.t; + var x : W8.t Array32.t * polyvec * polyvec; + + sd <$ srand; + x <@ PRG.get(sd); + b <@ D.distinguish(x); + + return b; + } + }. + + module PRGr = { + proc get(sd : W8.t Array32.t) : W8.t Array32.t * polyvec * polyvec = { + var r : W8.t Array32.t * polyvec * polyvec; + + r <- prg_kg_inner sd; + + return r; + } + }. + + module PRGi = { + proc get(sd : W8.t Array32.t) : W8.t Array32.t * polyvec * polyvec = { + var r : W8.t Array32.t * polyvec * polyvec; + + r <$ prg_kg_ideal; + + return r; + } + }. + end PRG_KG. + + theory PRG_ENC. + module type RG = { + proc get(sd : W8.t Array32.t) : polyvec * polyvec * poly {} + } + + module type Distinguisher = { + proc distinguish(x : polyvec * polyvec * poly) : bool {} + } + + module IND(PRG : RG, D : Distinguisher) = { + proc main() : bool = { + var b : bool; + var sd : W8.t Array32.t; + var x : polyvec * polyvec * poly; + + sd <$ srand; + x <@ PRG.get(sd); + b <@ D.distinguish(x); + + return b; + } + }. + + module PRGr = { + proc get(sd : W8.t Array32.t) : polyvec * polyvec * poly = { + var r : polyvec * polyvec * poly; + + r <- prg_enc_inner sd; + + return r; + } + }. + + module PRGi = { + proc get(sd : W8.t Array32.t) : polyvec * polyvec * poly = { + var r : polyvec * polyvec * poly; + + r <$ prg_enc_ideal; + + return r; + } + }. + end PRG_ENC. + + module MLWE_PKE_HASH_PRG = { + var sd : W8.t Array32.t + + var s : polyvec + + var e : polyvec + + var r : polyvec + + var e1 : polyvec + + var e2 : poly + + proc kg() : publickey * W8.t Array1152.t = { + var t : polyvec; + + t <- ((H MLWE_PKE_HASH_PRG.sd *^ MLWE_PKE_HASH_PRG.s)%MLWE_.Matrix_.Matrix + MLWE_PKE_HASH_PRG.e)%Vector; + + return (pk_encode (MLWE_PKE_HASH_PRG.sd, t), sk_encode MLWE_PKE_HASH_PRG.s); + } + + proc enc(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = { + var sd : W8.t Array32.t; + var t : polyvec; + var u : polyvec; + var v : poly; + + (sd, t) <- pk_decode pk; + u <- ((trmx (H sd) *^ MLWE_PKE_HASH_PRG.r)%MLWE_.Matrix_.Matrix + MLWE_PKE_HASH_PRG.e1)%Vector; + v <- ((t `<*>` MLWE_PKE_HASH_PRG.r) &+ MLWE_PKE_HASH_PRG.e2 &+ m_encode m)%MLWE_; + + return c_encode (u, v); + } + + proc dec(sk : W8.t Array1152.t, c : W8.t Array960.t * W8.t Array128.t) : plaintext option = MLWE_PKE_HASH.dec + }. + + module D_KG(A : FO_MLKEM.UU.TT.PKE.Adversary) = { + proc distinguish(sd : W8.t Array32.t, s : polyvec, e : polyvec) : bool = { + var coins : W8.t Array32.t; + var b : bool; + + MLWE_PKE_HASH_PRG.sd <- sd; + MLWE_PKE_HASH_PRG.s <- s; + MLWE_PKE_HASH_PRG.e <- e; + coins <$ srand; + (MLWE_PKE_HASH_PRG.r, MLWE_PKE_HASH_PRG.e1, MLWE_PKE_HASH_PRG.e2) <- prg_enc_inner coins; + b <@ FO_MLKEM.UU.TT.PKE.CPA(MLWE_PKE_HASH_PRG, A).main(); + + return b; + } + }. + + module D_ENC(A : FO_MLKEM.UU.TT.PKE.Adversary) = { + proc distinguish(r : polyvec, e1 : polyvec, e2 : poly) : bool = { + var b : bool; + + (MLWE_PKE_HASH_PRG.sd, MLWE_PKE_HASH_PRG.s, MLWE_PKE_HASH_PRG.e) <$ prg_kg_ideal; + MLWE_PKE_HASH_PRG.r <- r; + MLWE_PKE_HASH_PRG.e1 <- e1; + MLWE_PKE_HASH_PRG.e2 <- e2; + b <@ FO_MLKEM.UU.TT.PKE.CPA(MLWE_PKE_HASH_PRG, A).main(); + + return b; + } + }. + + lemma cpa_proc: + forall (A <: FO_MLKEM.UU.TT.PKE.Adversary{+all mem, -MLWE_PKE_HASH_PRG} ) &m, + Pr[FO_MLKEM.UU.TT.PKE.CPA(MLWE_PKE_HASH, A).main() @ &m : res] - + Pr[FO_MLKEM.UU.TT.PKE.CPA(MLWE_PKE_HASH_PROC, A).main() @ &m : res] = + Pr[PRG_KG.IND(PRG_KG.PRGr, D_KG(A)).main() @ &m : res] - Pr[PRG_KG.IND(PRG_KG.PRGi, D_KG(A)).main() @ &m : res] + + Pr[PRG_ENC.IND(PRG_ENC.PRGr, D_ENC(A)).main() @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, D_ENC(A)).main() @ &m : res]. + + module DC_KG(A : FO_MLKEM.UU.TT.PKE.CORR_ADV) = { + proc distinguish(sd : W8.t Array32.t, s : polyvec, e : polyvec) : bool = { + var coins : W8.t Array32.t; + var b : bool; + + MLWE_PKE_HASH_PRG.sd <- sd; + MLWE_PKE_HASH_PRG.s <- s; + MLWE_PKE_HASH_PRG.e <- e; + coins <$ srand; + (MLWE_PKE_HASH_PRG.r, MLWE_PKE_HASH_PRG.e1, MLWE_PKE_HASH_PRG.e2) <- prg_enc_inner coins; + b <@ FO_MLKEM.UU.TT.PKE.Correctness_Adv(MLWE_PKE_HASH_PRG, A).main(); + + return b; + } + }. + + module DC_ENC(A : FO_MLKEM.UU.TT.PKE.CORR_ADV) = { + proc distinguish(r : polyvec, e1 : polyvec, e2 : poly) : bool = { + var b : bool; + + (MLWE_PKE_HASH_PRG.sd, MLWE_PKE_HASH_PRG.s, MLWE_PKE_HASH_PRG.e) <$ prg_kg_ideal; + MLWE_PKE_HASH_PRG.r <- r; + MLWE_PKE_HASH_PRG.e1 <- e1; + MLWE_PKE_HASH_PRG.e2 <- e2; + b <@ FO_MLKEM.UU.TT.PKE.Correctness_Adv(MLWE_PKE_HASH_PRG, A).main(); + + return b; + } + }. + + lemma corr_proc: + forall (A <: FO_MLKEM.UU.TT.PKE.CORR_ADV{+all mem, -MLWE_PKE_HASH_PRG} ) &m, + Pr[FO_MLKEM.UU.TT.PKE.Correctness_Adv(MLWE_PKE_HASH, A).main() @ &m : res] - + Pr[FO_MLKEM.UU.TT.PKE.Correctness_Adv(MLWE_PKE_HASH_PROC, A).main() @ &m : res] = + Pr[PRG_KG.IND(PRG_KG.PRGr, DC_KG(A)).main() @ &m : res] - Pr[PRG_KG.IND(PRG_KG.PRGi, DC_KG(A)).main() @ &m : res] + + Pr[PRG_ENC.IND(PRG_ENC.PRGr, DC_ENC(A)).main() @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, DC_ENC(A)).main() @ &m : res]. + + module MLWE_PKE_HASH1 = { + proc kg() : publickey * W8.t Array1152.t = { + var sd : W8.t Array32.t; + var s : polyvec; + var t : polyvec; + + sd <$ srand; + s <$ MLWE_.dshort; + t <$ MLWE_.duni; + + return (pk_encode (sd, t), sk_encode s); + } + + proc dec(sk : W8.t Array1152.t, c : W8.t Array960.t * W8.t Array128.t) : plaintext option = MLWE_PKE_HASH_PROC.dec + + proc enc(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = MLWE_PKE_HASH_PROC.enc + + proc enc_bridge(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = MLWE_PKE_HASH_PROC.enc_bridge + + proc kg_bridge() : publickey * W8.t Array1152.t = MLWE_PKE_HASH_PROC.kg_bridge + }. + + module B1(A : FO_MLKEM.UU.TT.PKE.Adversary) = { + proc kg(sd : W8.t Array32.t, t : polyvec) : publickey * W8.t Array1152.t = { + return (pk_encode (sd, t), witness); + } + + proc guess(sd : W8.t Array32.t, t : polyvec, uv : polyvec * poly) : bool = { + var pk : publickey; + var sk : W8.t Array1152.t; + var m0 : plaintext; + var m1 : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var b : bool; + var b' : bool; + + (pk, sk) <@ B1(A).kg(sd, uv.`1); + (m0, m1) <@ A.choose(pk); + b <$ {0,1}; + c <@ MLWE_PKE_HASH1.enc(pk, if b then m1 else m0); + b' <@ A.guess(c); + + return b' = b; + } + }. + + lemma hop1_left: + forall (A <: FO_MLKEM.UU.TT.PKE.Adversary) &m, + Pr[FO_MLKEM.UU.TT.PKE.CPA(MLWE_PKE_HASH_PROC, A).main() @ &m : res] = + Pr[MLWE_.MLWE_H(B1(A)).main(false, false) @ &m : res]. + + lemma hop1_right: + forall (A <: FO_MLKEM.UU.TT.PKE.Adversary) &m, + Pr[MLWE_.MLWE_H(B1(A)).main(false, true) @ &m : res] = + Pr[FO_MLKEM.UU.TT.PKE.CPA(MLWE_PKE_HASH1, A).main() @ &m : res]. + + module MLWE_PKE_HASH2 = { + proc enc(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = { + var _A : polymat; + var u : polyvec; + var v : poly; + + _A <- (trmx (H (pk_decode pk).`1))%MLWE_.Matrix_.Matrix; + u <$ MLWE_.duni; + v <$ duni_R; + + return c_encode (u, (v &+ m_encode m)%MLWE_); + } + + proc kg_bridge() : publickey * W8.t Array1152.t = MLWE_PKE_HASH1.kg_bridge + + proc enc_bridge(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = MLWE_PKE_HASH1.enc_bridge + + proc dec(sk : W8.t Array1152.t, c : W8.t Array960.t * W8.t Array128.t) : plaintext option = MLWE_PKE_HASH1.dec + + proc kg() : publickey * W8.t Array1152.t = MLWE_PKE_HASH1.kg + }. + + module B2(A : FO_MLKEM.UU.TT.PKE.Adversary) = { + proc kg(sd : W8.t Array32.t, t : polyvec) : publickey * W8.t Array1152.t = { + return (pk_encode (sd, t), witness); + } + + proc enc(pk : publickey, m : plaintext, uv : polyvec * poly) : W8.t Array960.t * W8.t Array128.t = { + return c_encode (uv.`1, (uv.`2 &+ m_encode m)%MLWE_); + } + + proc guess(sd : W8.t Array32.t, t : polyvec, uv : polyvec * poly) : bool = { + var pk : publickey; + var sk : W8.t Array1152.t; + var m0 : plaintext; + var m1 : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var b : bool; + var b' : bool; + + (pk, sk) <@ B2(A).kg(sd, t); + (m0, m1) <@ A.choose(pk); + b <$ {0,1}; + c <@ B2(A).enc(pk, if b then m1 else m0, uv); + b' <@ A.guess(c); + + return b' = b; + } + }. + + lemma hop2_left: + forall (A <: FO_MLKEM.UU.TT.PKE.Adversary) &m, + Pr[FO_MLKEM.UU.TT.PKE.CPA(MLWE_PKE_HASH1, A).main() @ &m : res] = + Pr[MLWE_.MLWE_H(B2(A)).main(true, false) @ &m : res]. + + lemma hop2_right: + forall (A <: FO_MLKEM.UU.TT.PKE.Adversary) &m, + Pr[MLWE_.MLWE_H(B2(A)).main(true, true) @ &m : res] = + Pr[FO_MLKEM.UU.TT.PKE.CPA(MLWE_PKE_HASH2, A).main() @ &m : res]. + + lemma main_theorem: + forall (A <: FO_MLKEM.UU.TT.PKE.Adversary{+all mem, -MLWE_PKE_HASH_PRG} ) &m, + islossless A.guess => + islossless A.choose => + `|Pr[FO_MLKEM.UU.TT.PKE.CPA(MLWE_PKE_HASH, A).main() @ &m : res] - 1%r / 2%r| <= + `|Pr[MLWE_.MLWE_H(B1(A)).main(false, false) @ &m : res] - Pr[MLWE_.MLWE_H(B1(A)).main(false, true) @ &m : res]| + + `|Pr[MLWE_.MLWE_H(B2(A)).main(true, false) @ &m : res] - Pr[MLWE_.MLWE_H(B2(A)).main(true, true) @ &m : res]| + + `|Pr[PRG_KG.IND(PRG_KG.PRGr, D_KG(A)).main() @ &m : res] - Pr[PRG_KG.IND(PRG_KG.PRGi, D_KG(A)).main() @ &m : res]| + + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, D_ENC(A)).main() @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, D_ENC(A)).main() @ &m : res]|. + + op noise_exp (_A : polymat) (s e r e1 : polyvec) (e2 : poly) (m : plaintext) : poly = + let t = ((_A *^ s)%MLWE_.Matrix_.Matrix + e)%Vector in + let u = ((trmx _A *^ r)%MLWE_.Matrix_.Matrix + e1)%Vector in + let v = ((t `<*>` r) &+ e2 &+ m_encode m)%MLWE_ in + let (u', v') = c_decode (c_encode (u, v)) in (v' &- (s `<*>` u') &- m_encode m)%MLWE_. + + lemma encode_noise: + forall (u : polyvec) (v : poly), c_decode (c_encode (u, v)) = ((u + rnd_err_u u)%Vector, (v &+ rnd_err_v v)%MLWE_). + + lemma matrix_props1: + forall (_A : polymat) (s e r : polyvec), + (((_A *^ s)%MLWE_.Matrix_.Matrix + e)%Vector `<*>` r)%MLWE_ = + (((s ^* trmx _A)%MLWE_.Matrix_.Matrix `<*>` r) &+ (e `<*>` r))%MLWE_. + + lemma matrix_props2: + forall (s : polyvec) (_A : polymat) (r e1 cu : polyvec), + (s `<*>` ((trmx _A *^ r)%MLWE_.Matrix_.Matrix + e1 + cu)%Vector)%MLWE_ = + (((s ^* trmx _A)%MLWE_.Matrix_.Matrix `<*>` r) &+ (s `<*>` e1) &+ (s `<*>` cu))%MLWE_. + + lemma noise_exp_val: + forall (_A : polymat) (s e r e1 : polyvec) (e2 : poly) (m : plaintext), + noise_exp _A s e r e1 e2 m = + let t = ((_A *^ s)%MLWE_.Matrix_.Matrix + e)%Vector in + let u = ((trmx _A *^ r)%MLWE_.Matrix_.Matrix + e1)%Vector in + let v = ((t `<*>` r) &+ e2 &+ m_encode m)%MLWE_ in + let cu = rnd_err_u u in let cv = rnd_err_v v in ((e `<*>` r) &- (s `<*>` e1) &- (s `<*>` cu) &+ e2 &+ cv)%MLWE_. + + lemma good_decode: + forall (m : plaintext) (n : poly), under_noise_bound n max_noise => m_decode (m_encode m &+ n)%MLWE_ = m. + + module CorrectnessAdvNoise(A : FO_MLKEM.UU.TT.PKE.CORR_ADV) = { + proc main() : bool = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var _A : polymat; + var r : polyvec; + var e1 : polyvec; + var e2 : poly; + var m : plaintext; + var n : poly; + + sd <$ srand; + _A <- H sd; + r <$ MLWE_.dshort; + s <$ MLWE_.dshort; + e <$ MLWE_.dshort; + e1 <$ MLWE_.dshort; + e2 <$ dshort_R; + m <@ A.find(pk_encode (sd, ((_A *^ s)%MLWE_.Matrix_.Matrix + e)%Vector), sk_encode s); + n <- noise_exp _A s e r e1 e2 m; + + return ! under_noise_bound n max_noise; + } + }. + + lemma correctness_noise: + forall (A <: FO_MLKEM.UU.TT.PKE.CORR_ADV{+all mem, -MLWE_PKE_HASH_PRG} ) &m, + Pr[FO_MLKEM.UU.TT.PKE.Correctness_Adv(MLWE_PKE_HASH, A).main() @ &m : res] <= + Pr[CorrectnessAdvNoise(A).main() @ &m : res] + Pr[PRG_KG.IND(PRG_KG.PRGr, DC_KG(A)).main() @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, DC_KG(A)).main() @ &m : res] + + Pr[PRG_ENC.IND(PRG_ENC.PRGr, DC_ENC(A)).main() @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, DC_ENC(A)).main() @ &m : res]. + + lemma noise_commutes: + forall (n n' : poly) (maxn b : int), + under_noise_bound n' b => under_noise_bound n (maxn - b) => under_noise_bound (n &+ n')%MLWE_ maxn. + + lemma noise_preserved: forall (n : poly) (maxn : int), under_noise_bound n maxn = under_noise_bound ((&-) n) maxn. + + op noise_exp_part1 (_A : polymat) (s e r e1 : polyvec) (e2 : poly) : poly = + let u = ((trmx _A *^ r)%MLWE_.Matrix_.Matrix + e1)%Vector in + let cu = rnd_err_u u in ((e `<*>` r) &- (s `<*>` e1) &+ e2 &- (s `<*>` cu))%MLWE_. + + op noise_exp_part2 (_A : polymat) (s e r : polyvec) (e2 : poly) (m : plaintext) : poly = + let t = ((_A *^ s)%MLWE_.Matrix_.Matrix + e)%Vector in + let v = ((t `<*>` r) &+ e2 &+ m_encode m)%MLWE_ in let cv = rnd_err_v v in cv. + + lemma parts_work: + forall (_A : polymat) (s e r e1 : polyvec) (e2 : poly) (m : plaintext), + noise_exp _A s e r e1 e2 m = (noise_exp_part1 _A s e r e1 e2 &+ noise_exp_part2 _A s e r e2 m)%MLWE_. + + module CB(A : FO_MLKEM.UU.TT.PKE.CORR_ADV) = { + var s : polyvec + + var e : polyvec + + var _A : polymat + + var r : polyvec + + var e1 : polyvec + + var e2 : poly + + var n1 : poly + + var n2 : poly + + var u : polyvec + + var cu : polyvec + + var m : plaintext + + proc main() : unit = { + var sd : W8.t Array32.t; + + sd <$ srand; + CB._A <- H sd; + CB.r <$ MLWE_.dshort; + CB.s <$ MLWE_.dshort; + CB.e <$ MLWE_.dshort; + CB.e1 <$ MLWE_.dshort; + CB.e2 <$ dshort_R; + CB.m <@ A.find(pk_encode (sd, ((CB._A *^ CB.s)%MLWE_.Matrix_.Matrix + CB.e)%Vector), sk_encode CB.s); + CB.n1 <- noise_exp_part1 CB._A CB.s CB.e CB.r CB.e1 CB.e2; + CB.n2 <- noise_exp_part2 CB._A CB.s CB.e CB.r CB.e2 CB.m; + } + }. + + lemma cv_bound_valid: + forall (_A : polymat) (s e r : polyvec) (e2 : poly) (m : plaintext), + s \in MLWE_.dshort => + e \in MLWE_.dshort => + r \in MLWE_.dshort => + e2 \in dshort_R => + let t = ((_A *^ s)%MLWE_.Matrix_.Matrix + e)%Vector in + let v = ((t `<*>` r) &+ e2 &+ m_encode m)%MLWE_ in under_noise_bound (rnd_err_v v) cv_bound_max. + + module CorrectnessBound = { + proc main() : bool = { + var sd : W8.t Array32.t; + var _A : polymat; + var r : polyvec; + var s : polyvec; + var e : polyvec; + var e1 : polyvec; + var e2 : poly; + var n : poly; + + sd <$ srand; + _A <- H sd; + r <$ MLWE_.dshort; + s <$ MLWE_.dshort; + e <$ MLWE_.dshort; + e1 <$ MLWE_.dshort; + e2 <$ dshort_R; + n <- noise_exp_part1 _A s e r e1 e2; + + return ! under_noise_bound n (max_noise - cv_bound_max); + } + }. + + lemma correctness_split: + forall (A <: FO_MLKEM.UU.TT.PKE.CORR_ADV{+all mem, -MLWE_PKE_HASH_PRG, -CB} ) &m (cv_bound : int) (failprob1 + failprob2 : real), + Pr[CB(A).main() @ &m : ! under_noise_bound CB.n1 (max_noise - cv_bound)] <= failprob1 => + Pr[CB(A).main() @ &m : ! under_noise_bound CB.n2 cv_bound] <= failprob2 => + Pr[CorrectnessAdvNoise(A).main() @ &m : res] <= failprob1 + failprob2. + + lemma correctness_bound_aux: + forall (A <: FO_MLKEM.UU.TT.PKE.CORR_ADV{+all mem, -MLWE_PKE_HASH_PRG, -CB} ) &m, + islossless A.find => + Pr[CB(A).main() @ &m : ! under_noise_bound CB.n1 (max_noise - cv_bound_max)] = + Pr[CorrectnessBound.main() @ &m : res]. + + lemma cv_max: + forall (A <: FO_MLKEM.UU.TT.PKE.CORR_ADV{+all mem, -MLWE_PKE_HASH_PRG, -CB} ) &m, + Pr[CB(A).main() @ &m : ! under_noise_bound CB.n2 cv_bound_max] = 0%r. + + lemma correctness_theorem: + forall (A <: FO_MLKEM.UU.TT.PKE.CORR_ADV{+all mem, -MLWE_PKE_HASH_PRG, -CB} ) &m, + islossless A.find => + Pr[FO_MLKEM.UU.TT.PKE.Correctness_Adv(MLWE_PKE_HASH, A).main() @ &m : res] <= + Pr[CorrectnessBound.main() @ &m : res] + + `|Pr[PRG_KG.IND(PRG_KG.PRGr, DC_KG(A)).main() @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, DC_KG(A)).main() @ &m : res]| + + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, DC_ENC(A)).main() @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, DC_ENC(A)).main() @ &m : res]|. + + lemma kg_same: equiv[ FO_MLKEM.UU.TT.BasePKE.kg ~ MLWE_PKE_HASH.kg : true ==> ={res}]. + + lemma correctness: + forall &m (fail_prob : real), + Pr[CorrectnessBound.main() @ &m : res] <= fail_prob => + FO_MLKEM.UU.TT.qHC = 0 => + 1 < FO_MLKEM.UU.TT.FinT.card => + Pr[FO_MLKEM.KEMROM.Correctness(FO_MLKEM.KEMROM.RO.RO, FO_MLKEM.FO_K).main() @ &m : res] <= + fail_prob + + `|Pr[PRG_KG.IND(PRG_KG.PRGr, DC_KG(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.B_UC, FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, DC_KG(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.B_UC, FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res]| + + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, DC_ENC(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.B_UC, FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, DC_ENC(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.B_UC, FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res]|. + + module BUOOOWMod_Hx2 = FO_MLKEM.CountH(FO_MLKEM.B1x2(A/227860, FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CountH(FO_MLKEM.UU.TT.H(FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO))), FO_MLKEM.UU.TT.CountO(FO_MLKEM.UU.TT.G2_O(FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO)))).H2B), FO_MLKEM.UU.KEMROMx2.CCA(FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CountH(FO_MLKEM.UU.TT.H(FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO))), FO_MLKEM.UU.TT.CountO(FO_MLKEM.UU.TT.G2_O(FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO)))).H2B), FO_MLKEM.UU.UU2, FO_MLKEM.B1x2(A/227860)).O).BH). + + module BUOOOWMod_dec = FO_MLKEM.UU.KEMROMx2.CCA(FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CountH(FO_MLKEM.UU.TT.H(FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO))), FO_MLKEM.UU.TT.CountO(FO_MLKEM.UU.TT.G2_O(FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO)))).H2B), FO_MLKEM.UU.UU2, FO_MLKEM.B1x2(A/227860)).O. + + module BUOOOWModCPA_Hx2 = FO_MLKEM.CountH(FO_MLKEM.B1x2(A/227860, FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CountH(FO_MLKEM.UU.TT.PKEROM.RO.RO), FO_MLKEM.UU.TT.CountO(FO_MLKEM.UU.TT.O_AdvOW)).H2B), FO_MLKEM.UU.KEMROMx2.CCA(FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CountH(FO_MLKEM.UU.TT.PKEROM.RO.RO), FO_MLKEM.UU.TT.CountO(FO_MLKEM.UU.TT.O_AdvOW)).H2B), FO_MLKEM.UU.UU2, FO_MLKEM.B1x2(A/227860)).O).BH). + + module BUOOOWModCPA_dec = FO_MLKEM.UU.KEMROMx2.CCA(FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CountH(FO_MLKEM.UU.TT.PKEROM.RO.RO), FO_MLKEM.UU.TT.CountO(FO_MLKEM.UU.TT.O_AdvOW)).H2B), FO_MLKEM.UU.UU2, FO_MLKEM.B1x2(A/227860)).O. + + module BUUCI_Hx2 = FO_MLKEM.CountH(FO_MLKEM.B1x2(A/227860, FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUCI(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO)).H2B), FO_MLKEM.UU.KEMROMx2.CCA(FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUCI(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO)).H2B), FO_MLKEM.UU.UU2, FO_MLKEM.B1x2(A/227860)).O).BH). + + module BUUCI_dec = FO_MLKEM.UU.KEMROMx2.CCA(FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUCI(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO)).H2B), FO_MLKEM.UU.UU2, FO_MLKEM.B1x2(A/227860)).O. + + module BUUC_Hx2 = FO_MLKEM.CountH(FO_MLKEM.B1x2(A/227860, FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUC(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO)).H2B), FO_MLKEM.UU.KEMROMx2.CCA(FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUC(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO)).H2B), FO_MLKEM.UU.UU2, FO_MLKEM.B1x2(A/227860)).O).BH). + + module BUUC_dec = FO_MLKEM.UU.KEMROMx2.CCA(FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUC(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO)).H2B), FO_MLKEM.UU.UU2, FO_MLKEM.B1x2(A/227860)).O. + + lemma conclusion: + forall (A(H : FO_MLKEM.KEMROM.POracle, O : FO_MLKEM.KEMROM.CCA_ORC) <: + FO_MLKEM.KEMROM.CCA_ADV{+all mem, -FO_MLKEM.KEMROM.RO.RO.m, -FO_MLKEM.UU.TT.PKE.OW_CPA, -FO_MLKEM.UU.TT.PKE.BOWp, -FO_MLKEM.UU.TT.PKE.OWL_CPA, -FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl, -FO_MLKEM.UU.TT.BasePKE, -FO_MLKEM.UU.TT.PKEROM.RO.RO, -FO_MLKEM.UU.TT.PKEROM.RO.FRO, -FO_MLKEM.UU.TT.PKEROM.OW_PCVA, -FO_MLKEM.UU.TT.Correctness_Adv1, -FO_MLKEM.UU.TT.B, -FO_MLKEM.UU.TT.CountO, -FO_MLKEM.UU.TT.Gm, -FO_MLKEM.UU.TT.O_AdvOW, -FO_MLKEM.UU.RF.RF, -FO_MLKEM.UU.PseudoRF.PRF, -FO_MLKEM.UU.KEMROMx2.RO1.RO, -FO_MLKEM.UU.KEMROMx2.RO1.FRO, -FO_MLKEM.UU.KEMROMx2.RO2.RO, -FO_MLKEM.UU.KEMROMx2.RO2.FRO, -FO_MLKEM.UU.KEMROMx2.CCA, -FO_MLKEM.UU.CountHx2, -FO_MLKEM.UU.RO1E.FunRO, -FO_MLKEM.UU.UU2, -FO_MLKEM.UU.H2, -FO_MLKEM.UU.Gm2, -FO_MLKEM.UU.Gm3, -FO_MLKEM.UU.H2BOWMod, -FO_MLKEM.KEMROM.CCA, -FO_MLKEM.B1x2, -MLWE_PKE_HASH_PRG, -CB} + ) &m (fail_prob prg_kg_bound prg_enc_bound : real), + Pr[CorrectnessBound.main() @ &m : res] <= fail_prob => + `|Pr[PRG_KG.IND(PRG_KG.PRGr, DC_KG(FO_MLKEM.UU.TT.PKE.BOWp(FO_MLKEM.UU.TT.BasePKE, FO_MLKEM.UU.TT.AdvOW_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, DC_KG(FO_MLKEM.UU.TT.PKE.BOWp(FO_MLKEM.UU.TT.BasePKE, FO_MLKEM.UU.TT.AdvOW_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res]| <= + prg_kg_bound => + `|Pr[PRG_KG.IND(PRG_KG.PRGr, DC_KG(FO_MLKEM.UU.TT.PKE.BOWp(FO_MLKEM.UU.TT.BasePKE, FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, DC_KG(FO_MLKEM.UU.TT.PKE.BOWp(FO_MLKEM.UU.TT.BasePKE, FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res]| <= + prg_kg_bound => + `|Pr[PRG_KG.IND(PRG_KG.PRGr, DC_KG(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.TT.AdvCorr(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, DC_KG(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.TT.AdvCorr(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res]| <= + prg_kg_bound => + `|Pr[PRG_KG.IND(PRG_KG.PRGr, DC_KG(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.BUUCI(FO_MLKEM.B1x2(A)), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, DC_KG(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.BUUCI(FO_MLKEM.B1x2(A)), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res]| <= + prg_kg_bound => + `|Pr[PRG_KG.IND(PRG_KG.PRGr, DC_KG(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.BUUC(FO_MLKEM.B1x2(A)), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, DC_KG(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.BUUC(FO_MLKEM.B1x2(A)), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res]| <= + prg_kg_bound => + `|Pr[PRG_KG.IND(PRG_KG.PRGr, D_KG(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.PKE.OWvsIND.BL(FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))))))).main + () @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, D_KG(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.PKE.OWvsIND.BL(FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))))))).main + () @ &m : res]| <= + prg_kg_bound => + `|Pr[PRG_KG.IND(PRG_KG.PRGr, D_KG(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.AdvOWL_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, D_KG(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.AdvOWL_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res]| <= + prg_kg_bound => + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, DC_ENC(FO_MLKEM.UU.TT.PKE.BOWp(FO_MLKEM.UU.TT.BasePKE, FO_MLKEM.UU.TT.AdvOW_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, DC_ENC(FO_MLKEM.UU.TT.PKE.BOWp(FO_MLKEM.UU.TT.BasePKE, FO_MLKEM.UU.TT.AdvOW_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res]| <= + prg_enc_bound => + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, DC_ENC(FO_MLKEM.UU.TT.PKE.BOWp(FO_MLKEM.UU.TT.BasePKE, FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, DC_ENC(FO_MLKEM.UU.TT.PKE.BOWp(FO_MLKEM.UU.TT.BasePKE, FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res]| <= + prg_enc_bound => + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, DC_ENC(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.TT.AdvCorr(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, DC_ENC(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.TT.AdvCorr(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res]| <= + prg_enc_bound => + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, DC_ENC(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.BUUCI(FO_MLKEM.B1x2(A)), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, DC_ENC(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.BUUCI(FO_MLKEM.B1x2(A)), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res]| <= + prg_enc_bound => + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, DC_ENC(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.BUUC(FO_MLKEM.B1x2(A)), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, DC_ENC(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.BUUC(FO_MLKEM.B1x2(A)), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res]| <= + prg_enc_bound => + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, D_ENC(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.PKE.OWvsIND.BL(FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))))))).main + () @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, D_ENC(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.PKE.OWvsIND.BL(FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))))))).main + () @ &m : res]| <= + prg_enc_bound => + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, D_ENC(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.AdvOWL_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, D_ENC(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.AdvOWL_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res]| <= + prg_enc_bound => + FO_MLKEM.UU.qHT = FO_MLKEM.qHK => + FO_MLKEM.UU.qHU = FO_MLKEM.qHK => + FO_MLKEM.UU.TT.qH = FO_MLKEM.UU.qHT + FO_MLKEM.UU.qHU + 1 => + FO_MLKEM.UU.TT.qV = 0 => + FO_MLKEM.UU.TT.qP = 0 => + FO_MLKEM.UU.TT.qH + 1 = FO_MLKEM.UU.TT.qHC => + FO_MLKEM.UU.TT.qHC < FO_MLKEM.UU.TT.FinT.card - 1 => + (forall (RO0 <: FO_MLKEM.KEMROM.POracle{+all mem, -FO_MLKEM.CountH, -A} ) + (O0 <: FO_MLKEM.KEMROM.CCA_ORC{+all mem, -FO_MLKEM.CountH, -A} ), + hoare[ A(FO_MLKEM.CountH(RO0), O0).guess : FO_MLKEM.CountH.c_h = 0 ==> FO_MLKEM.CountH.c_h <= FO_MLKEM.qHK]) => + (forall (H0 <: FO_MLKEM.KEMROM.POracle{+all mem, -A} ) (O <: FO_MLKEM.UU.KEMROMx2.CCA_ORC{+all mem, -A} ), + islossless O.dec => islossless H0.get => islossless A(H0, O).guess) => + `|Pr[FO_MLKEM.KEMROM.CCA(FO_MLKEM.KEMROM.RO.RO, FO_MLKEM.FO_K, A).main() @ &m : res] - 1%r / 2%r| <= + 2%r * + (`|Pr[MLWE_.MLWE_H(B1(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.AdvOWL_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + (false, false) @ &m : res] - + Pr[MLWE_.MLWE_H(B1(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.AdvOWL_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + (false, true) @ &m : res]| + + `|Pr[MLWE_.MLWE_H(B2(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.AdvOWL_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + (true, false) @ &m : res] - + Pr[MLWE_.MLWE_H(B2(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.AdvOWL_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + (true, true) @ &m : res]| + + prg_kg_bound + prg_enc_bound) + + 2%r * + (`|Pr[MLWE_.MLWE_H(B1(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.PKE.OWvsIND.BL(FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))))))).main + (false, false) @ &m : res] - + Pr[MLWE_.MLWE_H(B1(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.PKE.OWvsIND.BL(FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))))))).main + (false, true) @ &m : res]| + + `|Pr[MLWE_.MLWE_H(B2(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.PKE.OWvsIND.BL(FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))))))).main + (true, false) @ &m : res] - + Pr[MLWE_.MLWE_H(B2(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.PKE.OWvsIND.BL(FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))))))).main + (true, true) @ &m : res]| + + prg_kg_bound + prg_enc_bound) + + (3%r * (2 * FO_MLKEM.qHK + 3)%r + 1%r) * (fail_prob + prg_kg_bound + prg_enc_bound) + + `|Pr[FO_MLKEM.UU.J.IND(FO_MLKEM.UU.PseudoRF.PRF, FO_MLKEM.UU.D(FO_MLKEM.B1x2(A))).main() @ &m : res] - + Pr[FO_MLKEM.UU.J.IND(FO_MLKEM.UU.RF.RF, FO_MLKEM.UU.D(FO_MLKEM.B1x2(A))).main() @ &m : res]| + + 2%r * (2 * FO_MLKEM.qHK + 2)%r * FO_MLKEM.UU.TT.PKE.eps_msg. +end MLWEPKEHash. + +[\] [0919] 39.8% (-1.0B / [frag -1.0B]) * In [theories]: + +theory MLWEPKEHash. + theory MLWE_. + theory Matrix_. + theory ZR. + lemma nosmt addrA: associative (&+). + + lemma nosmt addrC: commutative (&+). + + lemma nosmt add0r: left_id Rq.zero (&+). + + lemma nosmt addNr: left_inverse Rq.zero (&-) (&+). + + theory AddMonoid. + lemma addmA: associative (&+). + + lemma addmC: commutative (&+). + + lemma add0m: left_id Rq.zero (&+). + + lemma addm0: right_id Rq.zero (&+). + + lemma addmCA: left_commutative (&+). + + lemma addmAC: right_commutative (&+). + + lemma addmACA: interchange (&+) (&+). + + lemma iteropE: forall (n : int) (x : poly), iterop n (&+) x Rq.zero = iter n ((&+) x) Rq.zero. + end AddMonoid. + + abbrev (-) (x y : poly) : poly = (x - y)%KMatrix.ZR. + + lemma nosmt addr0: right_id Rq.zero (&+). + + lemma nosmt addrN: right_inverse Rq.zero (&-) (&+). + + lemma nosmt addrCA: left_commutative (&+). + + lemma nosmt addrAC: right_commutative (&+). + + lemma nosmt addrACA: interchange (&+) (&+). + + lemma nosmt subrr: forall (x : poly), x - x = Rq.zero. + + lemma nosmt addKr: left_loop (&-) (&+). + + lemma nosmt addNKr: rev_left_loop (&-) (&+). + + lemma nosmt addrK: right_loop (&-) (&+). + + lemma nosmt addrNK: rev_right_loop (&-) (&+). + + lemma nosmt subrK: forall (x y : poly), (x - y) &+ y = x. + + lemma nosmt addrI: right_injective (&+). + + lemma nosmt addIr: left_injective (&+). + + lemma nosmt opprK: involutive (&-). + + lemma oppr_inj: injective (&-). + + lemma nosmt oppr0: (&-) Rq.zero = Rq.zero. + + lemma oppr_eq0: forall (x : poly), (&-) x = Rq.zero <=> x = Rq.zero. + + lemma nosmt subr0: forall (x : poly), x - Rq.zero = x. + + lemma nosmt sub0r: forall (x : poly), Rq.zero - x = (&-) x. + + lemma nosmt opprD: forall (x y : poly), (&-) (x &+ y) = (&-) x - y. + + lemma nosmt opprB: forall (x y : poly), (&-) (x - y) = y - x. + + lemma nosmt subrACA: interchange (fun (x y : poly) => x - y) (&+). + + lemma nosmt subr_eq: forall (x y z : poly), x - z = y <=> x = y &+ z. + + lemma nosmt subr_eq0: forall (x y : poly), x - y = Rq.zero <=> x = y. + + lemma nosmt addr_eq0: forall (x y : poly), x &+ y = Rq.zero <=> x = (&-) y. + + lemma nosmt eqr_opp: forall (x y : poly), (&-) x = (&-) y <=> x = y. + + lemma eqr_oppLR: forall (x y : poly), (&-) x = y <=> x = (&-) y. + + lemma nosmt eqr_sub: forall (x y z t : poly), x - y = z - t <=> x &+ t = z &+ y. + + lemma subr_add2r: forall (z x y : poly), x &+ z - y &+ z = x - y. + + op intmul (x : poly) (n : int) : poly = + if n < 0 then (&-) (iterop (-n) (&+) x Rq.zero) else iterop n (&+) x Rq.zero. + + lemma intmulpE: forall (z : poly) (c : int), 0 <= c => intmul z c = iterop c (&+) z Rq.zero. + + lemma mulr0z: forall (x : poly), intmul x 0 = Rq.zero. + + lemma mulr1z: forall (x : poly), intmul x 1 = x. + + lemma mulr2z: forall (x : poly), intmul x 2 = x &+ x. + + lemma mulrNz: forall (x : poly) (n : int), intmul x (-n) = (&-) (intmul x n). + + lemma mulrS: forall (x : poly) (n : int), 0 <= n => intmul x (n + 1) = x &+ intmul x n. + + lemma mulNrz: forall (x : poly) (n : int), intmul ((&-) x) n = (&-) (intmul x n). + + lemma mulNrNz: forall (x : poly) (n : int), intmul ((&-) x) (-n) = intmul x n. + + lemma mulrSz: forall (x : poly) (n : int), intmul x (n + 1) = x &+ intmul x n. + + lemma mulrDz: forall (x : poly) (n m : int), intmul x (n + m) = intmul x n &+ intmul x m. + + abbrev (/) (x y : poly) : poly = (x / y)%KMatrix.ZR. + + lemma nosmt oner_neq0: Rq.one <> Rq.zero. + + lemma nosmt mulrA: associative ( &* ). + + lemma nosmt mulrC: commutative ( &* ). + + lemma nosmt mul1r: left_id Rq.one ( &* ). + + lemma nosmt mulrDl: left_distributive ( &* ) (&+). + + lemma nosmt mulVr: left_inverse_in KMatrix.ZR.unit Rq.one Top.Correctness.invr ( &* ). + + lemma nosmt unitP: forall (x y : poly), y &* x = Rq.one => (unit x)%KMatrix.ZR. + + lemma nosmt unitout: forall (x : poly), ! (unit x)%KMatrix.ZR => invr x = x. + + theory MulMonoid. + lemma addmA: associative ( &* ). + + lemma addmC: commutative ( &* ). + + lemma add0m: left_id Rq.one ( &* ). + + lemma addm0: right_id Rq.one ( &* ). + + lemma addmCA: left_commutative ( &* ). + + lemma addmAC: right_commutative ( &* ). + + lemma addmACA: interchange ( &* ) ( &* ). + + lemma iteropE: forall (n : int) (x : poly), iterop n ( &* ) x Rq.one = iter n (( &* ) x) Rq.one. + end MulMonoid. + + lemma nosmt mulr1: right_id Rq.one ( &* ). + + lemma nosmt mulrCA: left_commutative ( &* ). + + lemma nosmt mulrAC: right_commutative ( &* ). + + lemma nosmt mulrACA: interchange ( &* ) ( &* ). + + lemma nosmt mulrSl: forall (x y : poly), x &+ Rq.one &* y = x &* y &+ y. + + lemma nosmt mulrDr: right_distributive ( &* ) (&+). + + lemma nosmt mul0r: left_zero Rq.zero ( &* ). + + lemma nosmt mulr0: right_zero Rq.zero ( &* ). + + lemma nosmt mulrN: forall (x y : poly), x &* (&-) y = (&-) (x &* y). + + lemma nosmt mulNr: forall (x y : poly), (&-) x &* y = (&-) (x &* y). + + lemma nosmt mulrNN: forall (x y : poly), (&-) x &* (&-) y = x &* y. + + lemma nosmt mulN1r: forall (x : poly), (&-) Rq.one &* x = (&-) x. + + lemma nosmt mulrN1: forall (x : poly), x &* (&-) Rq.one = (&-) x. + + lemma nosmt mulrBl: left_distributive ( &* ) (fun (x y : poly) => x - y). + + lemma nosmt mulrBr: right_distributive ( &* ) (fun (x y : poly) => x - y). + + lemma mulrnAl: forall (x y : poly) (n : int), 0 <= n => intmul x n &* y = intmul (x &* y) n. + + lemma mulrnAr: forall (x y : poly) (n : int), 0 <= n => x &* intmul y n = intmul (x &* y) n. + + lemma mulrzAl: forall (x y : poly) (z : int), intmul x z &* y = intmul (x &* y) z. + + lemma mulrzAr: forall (x y : poly) (z : int), x &* intmul y z = intmul (x &* y) z. + + lemma nosmt mulrV: right_inverse_in KMatrix.ZR.unit Rq.one Top.Correctness.invr ( &* ). + + lemma nosmt divrr: forall (x : poly), (unit x)%KMatrix.ZR => x / x = Rq.one. + + lemma nosmt invr_out: forall (x : poly), ! (unit x)%KMatrix.ZR => invr x = x. + + lemma nosmt unitrP: forall (x : poly), (unit x)%KMatrix.ZR <=> exists (y : poly), y &* x = Rq.one. + + lemma nosmt mulKr: left_loop_in KMatrix.ZR.unit Top.Correctness.invr ( &* ). + + lemma nosmt mulrK: right_loop_in KMatrix.ZR.unit Top.Correctness.invr ( &* ). + + lemma nosmt mulVKr: rev_left_loop_in KMatrix.ZR.unit Top.Correctness.invr ( &* ). + + lemma nosmt mulrVK: rev_right_loop_in KMatrix.ZR.unit Top.Correctness.invr ( &* ). + + lemma nosmt mulrI: right_injective_in KMatrix.ZR.unit ( &* ). + + lemma nosmt mulIr: left_injective_in KMatrix.ZR.unit ( &* ). + + lemma nosmt unitrE: forall (x : poly), (unit x)%KMatrix.ZR <=> x / x = Rq.one. + + lemma nosmt invrK: involutive Top.Correctness.invr. + + lemma nosmt invr_inj: injective Top.Correctness.invr. + + lemma nosmt unitrV: forall (x : poly), (unit (invr x))%KMatrix.ZR <=> (unit x)%KMatrix.ZR. + + lemma nosmt unitr1: (unit Rq.one)%KMatrix.ZR. + + lemma nosmt invr1: invr Rq.one = Rq.one. + + lemma nosmt div1r: forall (x : poly), Rq.one / x = invr x. + + lemma nosmt divr1: forall (x : poly), x / Rq.one = x. + + lemma nosmt unitr0: ! (unit Rq.zero)%KMatrix.ZR. + + lemma nosmt invr0: invr Rq.zero = Rq.zero. + + lemma nosmt unitrN1: (unit ((&-) Rq.one))%KMatrix.ZR. + + lemma nosmt invrN1: invr ((&-) Rq.one) = (&-) Rq.one. + + lemma nosmt unitrMl: + forall (x y : poly), (unit y)%KMatrix.ZR => (unit (x &* y))%KMatrix.ZR <=> (unit x)%KMatrix.ZR. + + lemma nosmt unitrMr: + forall (x y : poly), (unit x)%KMatrix.ZR => (unit (x &* y))%KMatrix.ZR <=> (unit y)%KMatrix.ZR. + + lemma nosmt unitrM: + forall (x y : poly), (unit (x &* y))%KMatrix.ZR <=> (unit x)%KMatrix.ZR /\ (unit y)%KMatrix.ZR. + + lemma nosmt unitrN: forall (x : poly), (unit ((&-) x))%KMatrix.ZR <=> (unit x)%KMatrix.ZR. + + lemma nosmt invrM: + forall (x y : poly), (unit x)%KMatrix.ZR => (unit y)%KMatrix.ZR => invr (x &* y) = invr y / x. + + lemma nosmt invrN: forall (x : poly), invr ((&-) x) = (&-) (invr x). + + lemma nosmt invr_neq0: forall (x : poly), x <> Rq.zero => invr x <> Rq.zero. + + lemma nosmt invr_eq0: forall (x : poly), invr x = Rq.zero <=> x = Rq.zero. + + lemma nosmt invr_eq1: forall (x : poly), invr x = Rq.one <=> x = Rq.one. + + op ofint (n : int) : poly = intmul Rq.one n. + + lemma ofint0: (ofint 0)%ZR = Rq.zero. + + lemma ofint1: (ofint 1)%ZR = Rq.one. + + lemma ofintS: forall (i : int), 0 <= i => (ofint (i + 1))%ZR = Rq.one &+ (ofint i)%ZR. + + lemma ofintN: forall (i : int), (ofint (-i))%ZR = (&-) ((ofint i))%ZR. + + lemma mul1r0z: forall (x : poly), x &* (ofint 0)%ZR = Rq.zero. + + lemma mul1r1z: forall (x : poly), x &* (ofint 1)%ZR = x. + + lemma mul1r2z: forall (x : poly), x &* (ofint 2)%ZR = x &+ x. + + lemma mulr_intl: forall (x : poly) (z : int), (ofint z)%ZR &* x = intmul x z. + + lemma mulr_intr: forall (x : poly) (z : int), x &* (ofint z)%ZR = intmul x z. + + lemma fracrDE: + forall (n1 n2 d1 d2 : poly), + (unit d1)%KMatrix.ZR => (unit d2)%KMatrix.ZR => n1 / d1 &+ (n2 / d2) = n1 &* d2 &+ (n2 &* d1) / (d1 &* d2). + + op exp (x : poly) (n : int) : poly = + if n < 0 then invr (iterop (-n) ( &* ) x Rq.one) else iterop n ( &* ) x Rq.one. + + lemma expr0: forall (x : poly), exp x 0 = Rq.one. + + lemma expr1: forall (x : poly), exp x 1 = x. + + lemma exprS: forall (x : poly) (i : int), 0 <= i => exp x (i + 1) = x &* exp x i. + + lemma expr_pred: forall (x : poly) (i : int), 0 < i => exp x i = x &* exp x (i - 1). + + lemma exprSr: forall (x : poly) (i : int), 0 <= i => exp x (i + 1) = exp x i &* x. + + lemma expr2: forall (x : poly), exp x 2 = x &* x. + + lemma exprN: forall (x : poly) (i : int), exp x (-i) = invr (exp x i). + + lemma exprN1: forall (x : poly), exp x (-1) = invr x. + + lemma unitrX: forall (x : poly) (m : int), (unit x)%KMatrix.ZR => (unit (exp x m))%KMatrix.ZR. + + lemma unitrX_neq0: forall (x : poly) (m : int), m <> 0 => (unit (exp x m))%KMatrix.ZR => (unit x)%KMatrix.ZR. + + lemma exprV: forall (x : poly) (i : int), exp (invr x) i = exp x (-i). + + lemma exprVn: forall (x : poly) (n : int), 0 <= n => exp (invr x) n = invr (exp x n). + + lemma exprMn: forall (x y : poly) (n : int), 0 <= n => exp (x &* y) n = exp x n &* exp y n. + + lemma exprD_nneg: forall (x : poly) (m n : int), 0 <= m => 0 <= n => exp x (m + n) = exp x m &* exp x n. + + lemma exprD: forall (x : poly) (m n : int), (unit x)%KMatrix.ZR => exp x (m + n) = exp x m &* exp x n. + + lemma exprM: forall (x : poly) (m n : int), exp x (m * n) = exp (exp x m) n. + + lemma expr0n: forall (n : int), 0 <= n => exp Rq.zero n = if n = 0 then Rq.one else Rq.zero. + + lemma expr0z: forall (z : int), exp Rq.zero z = if z = 0 then Rq.one else Rq.zero. + + lemma expr1z: forall (z : int), exp Rq.one z = Rq.one. + + lemma sqrrD: forall (x y : poly), exp (x &+ y) 2 = exp x 2 &+ intmul (x &* y) 2 &+ exp y 2. + + lemma sqrrN: forall (x : poly), exp ((&-) x) 2 = exp x 2. + + lemma sqrrB: forall (x y : poly), exp (x - y) 2 = (exp x 2 - intmul (x &* y) 2) &+ exp y 2. + + lemma signr_odd: forall (n : int), 0 <= n => exp ((&-) Rq.one) (b2i (odd n)) = exp ((&-) Rq.one) n. + + lemma subr_sqr_1: forall (x : poly), exp x 2 - Rq.one = (x - Rq.one) &* (x &+ Rq.one). + + op lreg (x : poly) : bool = injective (fun (y : poly) => x &* y). + + lemma mulrI_eq0: forall (x y : poly), lreg x => x &* y = Rq.zero <=> y = Rq.zero. + + lemma lreg_neq0: forall (x : poly), lreg x => x <> Rq.zero. + + lemma mulrI0_lreg: forall (x : poly), (forall (y : poly), x &* y = Rq.zero => y = Rq.zero) => lreg x. + + lemma lregN: forall (x : poly), lreg x => lreg ((&-) x). + + lemma lreg1: lreg Rq.one. + + lemma lregM: forall (x y : poly), lreg x => lreg y => lreg (x &* y). + + lemma lregXn: forall (x : poly) (n : int), 0 <= n => lreg x => lreg (exp x n). + + instance ring with [()] poly + op rzero = Top.Rq.zero + op rone = Top.Rq.one + op add = Top.Rq.&+ + op opp = Top.Rq.&- + op mul = Top.Rq.&* + op expr = Top.MLWEPKEHash.MLWE_.Matrix_.ZR.exp + op ofint = Top.MLWEPKEHash.MLWE_.Matrix_.ZR.ofint + + instance poly with Top.Ring.ZModule.zmodule. + + instance poly with Top.MLWEPKEHash.MLWE_.Matrix_.ZR.ring. + + instance poly with Top.Ring.IDomain.idomain. + end ZR. + + theory Big. + theory CR. + instance ring with [()] poly + op rzero = Top.Rq.zero + op rone = Top.Rq.one + op add = Top.Rq.&+ + op opp = Top.Rq.&- + op mul = Top.Rq.&* + op expr = Top.MLWEPKEHash.MLWE_.Matrix_.ZR.exp + op ofint = Top.MLWEPKEHash.MLWE_.Matrix_.ZR.ofint + + instance poly with Top.Ring.ZModule.zmodule. + + instance poly with Top.MLWEPKEHash.MLWE_.Matrix_.Big.CR.ring. + + instance poly with Top.Ring.IDomain.idomain. + end CR. + + theory BAdd. + op big ['a] (P : 'a -> bool) (F : 'a -> poly) (r : 'a list) : poly = foldr (&+) Rq.zero (map F (filter P r)). + + abbrev bigi (P : int -> bool) (F : int -> poly) (i j : int) : poly = big P F (range i j). + + lemma big_nil ['a]: forall (P : 'a -> bool) (F : 'a -> poly), big P F [] = Rq.zero. + + lemma big_cons ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (x : 'a) (s : 'a list), + big P F (x :: s) = if P x then F x &+ big P F s else big P F s. + + lemma big_consT ['a]: + forall (F : 'a -> poly) (x : 'a) (s : 'a list), big predT F (x :: s) = F x &+ big predT F s. + + lemma nosmt big_rec ['a]: + forall (K : poly -> bool) (r : 'a list) (P : 'a -> bool) (F : + 'a -> poly), K Rq.zero => (forall (i : 'a) (x : poly), P i => K x => K (F i &+ x)) => K (big P F r). + + lemma nosmt big_ind ['a]: + forall (K : poly -> bool) (r : 'a list) (P : 'a -> bool) (F : + 'a -> poly), + (forall (x y : poly), K x => K y => K (x &+ y)) => + K Rq.zero => (forall (i : 'a), P i => K (F i)) => K (big P F r). + + lemma nosmt big_rec2 ['a]: + forall (K : poly -> poly -> bool) (r : 'a list) (P : 'a -> bool) (F1 F2 : + 'a -> poly), + K Rq.zero Rq.zero => + (forall (i : 'a) (y1 y2 : poly), P i => K y1 y2 => K (F1 i &+ y1) (F2 i &+ y2)) => + K (big P F1 r) (big P F2 r). + + lemma nosmt big_ind2 ['a]: + forall (K : poly -> poly -> bool) (r : 'a list) (P : 'a -> bool) (F1 F2 : + 'a -> poly), + (forall (x1 x2 y1 y2 : poly), K x1 x2 => K y1 y2 => K (x1 &+ y1) (x2 &+ y2)) => + K Rq.zero Rq.zero => (forall (i : 'a), P i => K (F1 i) (F2 i)) => K (big P F1 r) (big P F2 r). + + lemma nosmt big_endo ['a]: + forall (f : poly -> poly), + f Rq.zero = Rq.zero => + (forall (x y : poly), f (x &+ y) = f x &+ f y) => + forall (r : 'a list) (P : 'a -> bool) (F : 'a -> poly), f (big P F r) = big P (f \o F) r. + + lemma nosmt big_map ['a, 'b]: + forall (h : 'b -> 'a) (P : 'a -> bool) (F : 'a -> poly) (s : 'b list), + big P F (map h s) = big (P \o h) (F \o h) s. + + lemma nosmt big_mapT ['a, 'b]: + forall (h : 'b -> 'a) (F : 'a -> poly) (s : 'b list), big predT F (map h s) = big predT (F \o h) s. + + lemma nosmt big_comp ['a]: + forall (h : poly -> poly) (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + h Rq.zero = Rq.zero => morphism_2 h (&+) (&+) => h (big P F s) = big P (h \o F) s. + + lemma nosmt big_nth ['a]: + forall (x0 : 'a) (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = bigi (P \o nth x0 s) (F \o nth x0 s) 0 (size s). + + lemma nosmt big_const ['a]: + forall (P : 'a -> bool) (x : poly) (s : 'a list), + big P (fun (_ : 'a) => x) s = iter (count P s) ((&+) x) Rq.zero. + + lemma nosmt big_seq1 ['a]: forall (F : 'a -> poly) (x : 'a), big predT F [x] = F x. + + lemma nosmt big_mkcond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = big predT (fun (i : 'a) => if P i then F i else Rq.zero) s. + + lemma nosmt big_filter ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), big predT F (filter P s) = big P F s. + + lemma big_filter_cond ['a]: + forall (P1 P2 : 'a -> bool) (F : 'a -> poly) (s : 'a list), big P2 F (filter P1 s) = big (predI P1 P2) F s. + + lemma nosmt eq_bigl ['a]: + forall (P1 P2 : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P1 i <=> P2 i) => big P1 F s = big P2 F s. + + lemma nosmt eq_bigr ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i => F1 i = F2 i) => big P F1 s = big P F2 s. + + lemma nosmt big_distrl ['a]: + forall (op_ : poly -> poly -> poly) (P : 'a -> bool) (F : + 'a -> poly) (s : 'a list) (t : poly), + left_zero Rq.zero op_ => + left_distributive op_ (&+) => op_ (big P F s) t = big P (fun (a : 'a) => op_ (F a) t) s. + + lemma nosmt big_distrr ['a]: + forall (op_ : poly -> poly -> poly) (P : 'a -> bool) (F : + 'a -> poly) (s : 'a list) (t : poly), + right_zero Rq.zero op_ => + right_distributive op_ (&+) => op_ t (big P F s) = big P (fun (a : 'a) => op_ t (F a)) s. + + lemma nosmt big_distr ['a, 'b]: + forall (op_ : poly -> poly -> poly) (P1 : 'a -> bool) (P2 : + 'b -> bool) (F1 : 'a -> poly) (s1 : 'a list) (F2 : 'b -> poly) (s2 : 'b list), + commutative op_ => + left_zero Rq.zero op_ => + left_distributive op_ (&+) => + op_ (big P1 F1 s1) (big P2 F2 s2) = + big P1 (fun (a1 : 'a) => big P2 (fun (a2 : 'b) => op_ (F1 a1) (F2 a2)) s2) s1. + + lemma big_andbC ['a]: + forall (P Q : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big (fun (x : 'a) => P x /\ Q x) F s = big (fun (x : 'a) => Q x /\ P x) F s. + + lemma nosmt eq_big ['a]: + forall (P1 P2 : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + (forall (i : 'a), P1 i <=> P2 i) => (forall (i : 'a), P1 i => F1 i = F2 i) => big P1 F1 s = big P2 F2 s. + + lemma nosmt congr_big ['a]: + forall (r1 r2 : 'a list) (P1 P2 : 'a -> bool) (F1 F2 : 'a -> poly), + r1 = r2 => + (forall (x : 'a), P1 x <=> P2 x) => (forall (i : 'a), P1 i => F1 i = F2 i) => big P1 F1 r1 = big P2 F2 r2. + + lemma big_hasC ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), ! has P s => big P F s = Rq.zero. + + lemma big_pred0_eq ['a]: forall (F : 'a -> poly) (s : 'a list), big pred0 F s = Rq.zero. + + lemma big_pred0 ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i <=> false) => big P F s = Rq.zero. + + lemma big_cat ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), big P F (s1 ++ s2) = big P F s1 &+ big P F s2. + + lemma nosmt big_catl ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), ! has P s2 => big P F (s1 ++ s2) = big P F s1. + + lemma nosmt big_catr ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), ! has P s1 => big P F (s1 ++ s2) = big P F s2. + + lemma nosmt big_rcons ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + big P F (rcons s x) = if P x then big P F s &+ F x else big P F s. + + lemma nosmt eq_big_perm ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), perm_eq s1 s2 => big P F s1 = big P F s2. + + lemma nosmt eq_big_perm_map ['a]: + forall (F : 'a -> poly) (s1 s2 : 'a list), perm_eq (map F s1) (map F s2) => big predT F s1 = big predT F s2. + + lemma nosmt big_seq_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = big (fun (i : 'a) => (i \in s) /\ P i) F s. + + lemma nosmt big_seq ['a]: + forall (F : 'a -> poly) (s : 'a list), big predT F s = big (fun (i : 'a) => i \in s) F s. + + lemma nosmt big_rem ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + x \in s => big P F s = (if P x then F x else Rq.zero) &+ big P F (rem x s). + + lemma nosmt bigD1 ['a]: + forall (F : 'a -> poly) (s : 'a list) (x : 'a), + x \in s => uniq s => big predT F s = F x &+ big (predC1 x) F s. + + lemma nosmt bigD1_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + P x => x \in s => uniq s => big P F s = F x &+ big (predI P (predC1 x)) F s. + + lemma nosmt bigD1_cond_if ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + uniq s => big P F s = (if (x \in s) /\ P x then F x else Rq.zero) &+ big (predI P (predC1 x)) F s. + + lemma big_split ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + big P (fun (i : 'a) => F1 i &+ F2 i) s = big P F1 s &+ big P F2 s. + + lemma nosmt bigID ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (a : 'a -> bool) (s : 'a list), + big P F s = big (predI P a) F s &+ big (predI P (predC a)) F s. + + lemma bigU ['a]: + forall (P Q : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (x : 'a), ! (P x /\ Q x)) => big (predU P Q) F s = big P F s &+ big Q F s. + + lemma bigEM ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), big predT F s = big P F s &+ big (predC P) F s. + + lemma nosmt big_reindex ['a, 'b]: + forall (P : 'a -> bool) (F : 'a -> poly) (f : 'b -> 'a) (f' : + 'a -> 'b) (s : 'a list), + (forall (x : 'a), x \in s => f (f' x) = x) => big P F s = big (P \o f) (F \o f) (map f' s). + + lemma nosmt big_pair_pswap ['a, 'b]: + forall (p : 'a * 'b -> bool) (f : 'a * 'b -> poly) (s : ('a * 'b) list), + big p f s = big (p \o pswap) (f \o pswap) (map pswap s). + + lemma nosmt eq_big_seq ['a]: + forall (F1 F2 : 'a -> poly) (s : 'a list), + (forall (x : 'a), x \in s => F1 x = F2 x) => big predT F1 s = big predT F2 s. + + lemma nosmt congr_big_seq ['a]: + forall (P1 P2 : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + (forall (x : 'a), x \in s => P1 x = P2 x) => + (forall (x : 'a), x \in s => P1 x => P2 x => F1 x = F2 x) => big P1 F1 s = big P2 F2 s. + + lemma big1_eq ['a]: forall (P : 'a -> bool) (s : 'a list), big P (fun (_ : 'a) => Rq.zero) s = Rq.zero. + + lemma big1 ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i => F i = Rq.zero) => big P F s = Rq.zero. + + lemma big1_seq ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i /\ (i \in s) => F i = Rq.zero) => big P F s = Rq.zero. + + lemma big_eq_idm_filter ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (x : 'a), ! P x => F x = Rq.zero) => big predT F s = big P F s. + + lemma big_flatten ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (rr : 'a list list), + big P F (flatten rr) = big predT (fun (s : 'a list) => big P F s) rr. + + lemma nosmt big_pair ['a, 'b]: + forall (F : 'a * 'b -> poly) (s : ('a * 'b) list), + uniq s => + big predT F s = + big predT (fun (a : 'a) => big predT F (filter (fun (xy : 'a * 'b) => xy.`1 = a) s)) (undup (unzip1 s)). + + lemma big_nseq_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (n : int) (x : 'a), + big P F (nseq n x) = if P x then iter n ((&+) (F x)) Rq.zero else Rq.zero. + + lemma big_nseq ['a]: + forall (F : 'a -> poly) (n : int) (x : 'a), big predT F (nseq n x) = iter n ((&+) (F x)) Rq.zero. + + lemma big_undup ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = big P (fun (a : 'a) => iter (count (pred1 a) s) ((&+) (F a)) Rq.zero) (undup s). + + lemma nosmt exchange_big ['a, 'b]: + forall (P1 : 'a -> bool) (P2 : 'b -> bool) (F : 'a -> 'b -> poly) (s1 : 'a list) (s2 : 'b list), + big P1 (fun (a : 'a) => big P2 (F a) s2) s1 = + big P2 (fun (b : 'b) => big P1 (fun (a : 'a) => F a b) s1) s2. + + lemma partition_big ['a, 'b]: + forall (px : 'a -> 'b) (P : 'a -> bool) (Q : 'b -> bool) (F : + 'a -> poly) (s : 'a list) (s' : 'b list), + uniq s' => + (forall (x : 'a), x \in s => P x => (px x \in s') /\ Q (px x)) => + big P F s = big Q (fun (x : 'b) => big (fun (y : 'a) => P y /\ px y = x) F s) s'. + + lemma nosmt big_allpairs ['a, 'b, 'c]: + forall (f : 'a -> 'b -> 'c) (F : 'c -> poly) (s : 'a list) (t : 'b list), + big predT F (allpairs f s t) = big predT (fun (x : 'a) => big predT (fun (y : 'b) => F (f x y)) t) s. + + lemma nosmt big_int_cond: + forall (m n : int) (P : int -> bool) (F : int -> poly), + bigi P F m n = bigi (fun (i : int) => (m <= i && i < n) /\ P i) F m n. + + lemma nosmt big_int: + forall (m n : int) (F : int -> poly), bigi predT F m n = bigi (fun (i : int) => m <= i && i < n) F m n. + + lemma nosmt congr_big_int: + forall (m1 n1 m2 n2 : int) (P1 P2 : int -> bool) (F1 F2 : + int -> poly), + m1 = m2 => + n1 = n2 => + (forall (i : int), m1 <= i && i < n2 => P1 i = P2 i) => + (forall (i : int), P1 i /\ m1 <= i && i < n2 => F1 i = F2 i) => bigi P1 F1 m1 n1 = bigi P2 F2 m2 n2. + + lemma nosmt eq_big_int: + forall (m n : int) (F1 F2 : int -> poly), + (forall (i : int), m <= i && i < n => F1 i = F2 i) => bigi predT F1 m n = bigi predT F2 m n. + + lemma nosmt big_ltn_cond: + forall (m n : int) (P : int -> bool) (F : int -> poly), + m < n => let x = bigi P F (m + 1) n in bigi P F m n = if P m then F m &+ x else x. + + lemma nosmt big_ltn: + forall (m n : int) (F : int -> poly), m < n => bigi predT F m n = F m &+ bigi predT F (m + 1) n. + + lemma nosmt big_geq: + forall (m n : int) (P : int -> bool) (F : int -> poly), n <= m => bigi P F m n = Rq.zero. + + lemma nosmt big_addn: + forall (m n a : int) (P : int -> bool) (F : int -> poly), + bigi P F (m + a) n = bigi (fun (i : int) => P (i + a)) (fun (i : int) => F (i + a)) m (n - a). + + lemma nosmt big_int1: forall (n : int) (F : int -> poly), bigi predT F n (n + 1) = F n. + + lemma nosmt big_cat_int: + forall (n m p : int) (P : int -> bool) (F : int -> poly), + m <= n => n <= p => bigi P F m p = bigi P F m n &+ bigi P F n p. + + lemma nosmt big_int_recl: + forall (n m : int) (F : int -> poly), + m <= n => bigi predT F m (n + 1) = F m &+ bigi predT (fun (i : int) => F (i + 1)) m n. + + lemma nosmt big_int_recr: + forall (n m : int) (F : int -> poly), m <= n => bigi predT F m (n + 1) = bigi predT F m n &+ F n. + + lemma nosmt big_int_recl_cond: + forall (n m : int) (P : int -> bool) (F : int -> poly), + m <= n => + bigi P F m (n + 1) = + (if P m then F m else Rq.zero) &+ bigi (fun (i : int) => P (i + 1)) (fun (i : int) => F (i + 1)) m n. + + lemma nosmt big_int_recr_cond: + forall (n m : int) (P : int -> bool) (F : int -> poly), + m <= n => bigi P F m (n + 1) = bigi P F m n &+ if P n then F n else Rq.zero. + + lemma nosmt bigi_split_odd_even: + forall (n : int) (F : int -> poly), + 0 <= n => bigi predT (fun (i : int) => F (2 * i) &+ F (2 * i + 1)) 0 n = bigi predT F 0 (2 * n). + + lemma sumrD ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> poly) (r : 'a list), + big P F1 r &+ big P F2 r = big P (fun (x : 'a) => F1 x &+ F2 x) r. + + lemma sumrN ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (r : 'a list), + (&-) (big P F r) = big P (fun (x : 'a) => (&-) (F x)) r. + + lemma sumrB ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> poly) (r : 'a list), + (big P F1 r - big P F2 r)%ZR = big P (fun (x : 'a) => (F1 x - F2 x)%ZR) r. + + lemma nosmt sumr_const ['a]: + forall (P : 'a -> bool) (x : poly) (s : 'a list), big P (fun (_ : 'a) => x) s = (intmul x (count P s))%ZR. + + lemma sumri_const: + forall (k : poly) (n m : int), n <= m => bigi predT (fun (_ : int) => k) n m = (intmul k (m - n))%ZR. + + lemma sumr_undup ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = big P (fun (a : 'a) => (intmul (F a) (count (pred1 a) s))%ZR) (undup s). + + lemma telescoping_sum: + forall (F : int -> poly) (m n : int), + m <= n => (F m - F n)%ZR = bigi predT (fun (i : int) => (F i - F (i + 1))%ZR) m n. + + lemma telescoping_sum_down: + forall (F : int -> poly) (m n : int), + m <= n => (F n - F m)%ZR = bigi predT (fun (i : int) => (F (i + 1) - F i)%ZR) m n. + + lemma nosmt sumr_1 ['a]: + forall (P : 'a -> bool) (s : 'a list), big P (fun (_ : 'a) => Rq.one) s = (ofint (count P s))%ZR. + + lemma mulr_suml ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : poly), + big P F s &* x = big P (fun (i : 'a) => F i &* x) s. + + lemma mulr_sumr ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : poly), + x &* big P F s = big P (fun (i : 'a) => x &* F i) s. + + lemma divr_suml ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : poly), + (big P F s / x)%ZR = big P (fun (i : 'a) => (F i / x)%ZR) s. + + lemma nosmt sum_pair_dep ['a, 'b]: + forall (u : 'a -> poly) (v : 'a -> 'b -> poly) (J : ('a * 'b) list), + uniq J => + big predT (fun (ij : 'a * 'b) => u ij.`1 &* v ij.`1 ij.`2) J = + big predT + (fun (i : 'a) => + u i &* big predT (fun (ij : 'a * 'b) => v ij.`1 ij.`2) (filter (fun (ij : 'a * 'b) => ij.`1 = i) J)) + (undup (unzip1 J)). + + lemma nosmt sum_pair ['a, 'b]: + forall (u : 'a -> poly) (v : 'b -> poly) (J : ('a * 'b) list), + uniq J => + big predT (fun (ij : 'a * 'b) => u ij.`1 &* v ij.`2) J = + big predT (fun (i : 'a) => u i &* big predT v (unzip2 (filter (fun (ij : 'a * 'b) => ij.`1 = i) J))) + (undup (unzip1 J)). + end BAdd. + + theory BMul. + op big ['a] (P : 'a -> bool) (F : 'a -> poly) (r : 'a list) : poly = + foldr ( &* ) Rq.one (map F (filter P r)). + + abbrev bigi (P : int -> bool) (F : int -> poly) (i j : int) : poly = big P F (range i j). + + lemma big_nil ['a]: forall (P : 'a -> bool) (F : 'a -> poly), big P F [] = Rq.one. + + lemma big_cons ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (x : 'a) (s : 'a list), + big P F (x :: s) = if P x then F x &* big P F s else big P F s. + + lemma big_consT ['a]: + forall (F : 'a -> poly) (x : 'a) (s : 'a list), big predT F (x :: s) = F x &* big predT F s. + + lemma nosmt big_rec ['a]: + forall (K : poly -> bool) (r : 'a list) (P : 'a -> bool) (F : + 'a -> poly), K Rq.one => (forall (i : 'a) (x : poly), P i => K x => K (F i &* x)) => K (big P F r). + + lemma nosmt big_ind ['a]: + forall (K : poly -> bool) (r : 'a list) (P : 'a -> bool) (F : + 'a -> poly), + (forall (x y : poly), K x => K y => K (x &* y)) => + K Rq.one => (forall (i : 'a), P i => K (F i)) => K (big P F r). + + lemma nosmt big_rec2 ['a]: + forall (K : poly -> poly -> bool) (r : 'a list) (P : 'a -> bool) (F1 F2 : + 'a -> poly), + K Rq.one Rq.one => + (forall (i : 'a) (y1 y2 : poly), P i => K y1 y2 => K (F1 i &* y1) (F2 i &* y2)) => + K (big P F1 r) (big P F2 r). + + lemma nosmt big_ind2 ['a]: + forall (K : poly -> poly -> bool) (r : 'a list) (P : 'a -> bool) (F1 F2 : + 'a -> poly), + (forall (x1 x2 y1 y2 : poly), K x1 x2 => K y1 y2 => K (x1 &* y1) (x2 &* y2)) => + K Rq.one Rq.one => (forall (i : 'a), P i => K (F1 i) (F2 i)) => K (big P F1 r) (big P F2 r). + + lemma nosmt big_endo ['a]: + forall (f : poly -> poly), + f Rq.one = Rq.one => + (forall (x y : poly), f (x &* y) = f x &* f y) => + forall (r : 'a list) (P : 'a -> bool) (F : 'a -> poly), f (big P F r) = big P (f \o F) r. + + lemma nosmt big_map ['a, 'b]: + forall (h : 'b -> 'a) (P : 'a -> bool) (F : 'a -> poly) (s : 'b list), + big P F (map h s) = big (P \o h) (F \o h) s. + + lemma nosmt big_mapT ['a, 'b]: + forall (h : 'b -> 'a) (F : 'a -> poly) (s : 'b list), big predT F (map h s) = big predT (F \o h) s. + + lemma nosmt big_comp ['a]: + forall (h : poly -> poly) (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + h Rq.one = Rq.one => morphism_2 h ( &* ) ( &* ) => h (big P F s) = big P (h \o F) s. + + lemma nosmt big_nth ['a]: + forall (x0 : 'a) (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = bigi (P \o nth x0 s) (F \o nth x0 s) 0 (size s). + + lemma nosmt big_const ['a]: + forall (P : 'a -> bool) (x : poly) (s : 'a list), + big P (fun (_ : 'a) => x) s = iter (count P s) (( &* ) x) Rq.one. + + lemma nosmt big_seq1 ['a]: forall (F : 'a -> poly) (x : 'a), big predT F [x] = F x. + + lemma nosmt big_mkcond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = big predT (fun (i : 'a) => if P i then F i else Rq.one) s. + + lemma nosmt big_filter ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), big predT F (filter P s) = big P F s. + + lemma big_filter_cond ['a]: + forall (P1 P2 : 'a -> bool) (F : 'a -> poly) (s : 'a list), big P2 F (filter P1 s) = big (predI P1 P2) F s. + + lemma nosmt eq_bigl ['a]: + forall (P1 P2 : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P1 i <=> P2 i) => big P1 F s = big P2 F s. + + lemma nosmt eq_bigr ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i => F1 i = F2 i) => big P F1 s = big P F2 s. + + lemma nosmt big_distrl ['a]: + forall (op_ : poly -> poly -> poly) (P : 'a -> bool) (F : + 'a -> poly) (s : 'a list) (t : poly), + left_zero Rq.one op_ => + left_distributive op_ ( &* ) => op_ (big P F s) t = big P (fun (a : 'a) => op_ (F a) t) s. + + lemma nosmt big_distrr ['a]: + forall (op_ : poly -> poly -> poly) (P : 'a -> bool) (F : + 'a -> poly) (s : 'a list) (t : poly), + right_zero Rq.one op_ => + right_distributive op_ ( &* ) => op_ t (big P F s) = big P (fun (a : 'a) => op_ t (F a)) s. + + lemma nosmt big_distr ['a, 'b]: + forall (op_ : poly -> poly -> poly) (P1 : 'a -> bool) (P2 : + 'b -> bool) (F1 : 'a -> poly) (s1 : 'a list) (F2 : 'b -> poly) (s2 : 'b list), + commutative op_ => + left_zero Rq.one op_ => + left_distributive op_ ( &* ) => + op_ (big P1 F1 s1) (big P2 F2 s2) = + big P1 (fun (a1 : 'a) => big P2 (fun (a2 : 'b) => op_ (F1 a1) (F2 a2)) s2) s1. + + lemma big_andbC ['a]: + forall (P Q : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big (fun (x : 'a) => P x /\ Q x) F s = big (fun (x : 'a) => Q x /\ P x) F s. + + lemma nosmt eq_big ['a]: + forall (P1 P2 : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + (forall (i : 'a), P1 i <=> P2 i) => (forall (i : 'a), P1 i => F1 i = F2 i) => big P1 F1 s = big P2 F2 s. + + lemma nosmt congr_big ['a]: + forall (r1 r2 : 'a list) (P1 P2 : 'a -> bool) (F1 F2 : 'a -> poly), + r1 = r2 => + (forall (x : 'a), P1 x <=> P2 x) => (forall (i : 'a), P1 i => F1 i = F2 i) => big P1 F1 r1 = big P2 F2 r2. + + lemma big_hasC ['a]: forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), ! has P s => big P F s = Rq.one. + + lemma big_pred0_eq ['a]: forall (F : 'a -> poly) (s : 'a list), big pred0 F s = Rq.one. + + lemma big_pred0 ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i <=> false) => big P F s = Rq.one. + + lemma big_cat ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), big P F (s1 ++ s2) = big P F s1 &* big P F s2. + + lemma nosmt big_catl ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), ! has P s2 => big P F (s1 ++ s2) = big P F s1. + + lemma nosmt big_catr ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), ! has P s1 => big P F (s1 ++ s2) = big P F s2. + + lemma nosmt big_rcons ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + big P F (rcons s x) = if P x then big P F s &* F x else big P F s. + + lemma nosmt eq_big_perm ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), perm_eq s1 s2 => big P F s1 = big P F s2. + + lemma nosmt eq_big_perm_map ['a]: + forall (F : 'a -> poly) (s1 s2 : 'a list), perm_eq (map F s1) (map F s2) => big predT F s1 = big predT F s2. + + lemma nosmt big_seq_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = big (fun (i : 'a) => (i \in s) /\ P i) F s. + + lemma nosmt big_seq ['a]: + forall (F : 'a -> poly) (s : 'a list), big predT F s = big (fun (i : 'a) => i \in s) F s. + + lemma nosmt big_rem ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + x \in s => big P F s = (if P x then F x else Rq.one) &* big P F (rem x s). + + lemma nosmt bigD1 ['a]: + forall (F : 'a -> poly) (s : 'a list) (x : 'a), + x \in s => uniq s => big predT F s = F x &* big (predC1 x) F s. + + lemma nosmt bigD1_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + P x => x \in s => uniq s => big P F s = F x &* big (predI P (predC1 x)) F s. + + lemma nosmt bigD1_cond_if ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + uniq s => big P F s = (if (x \in s) /\ P x then F x else Rq.one) &* big (predI P (predC1 x)) F s. + + lemma big_split ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + big P (fun (i : 'a) => F1 i &* F2 i) s = big P F1 s &* big P F2 s. + + lemma nosmt bigID ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (a : 'a -> bool) (s : 'a list), + big P F s = big (predI P a) F s &* big (predI P (predC a)) F s. + + lemma bigU ['a]: + forall (P Q : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (x : 'a), ! (P x /\ Q x)) => big (predU P Q) F s = big P F s &* big Q F s. + + lemma bigEM ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), big predT F s = big P F s &* big (predC P) F s. + + lemma nosmt big_reindex ['a, 'b]: + forall (P : 'a -> bool) (F : 'a -> poly) (f : 'b -> 'a) (f' : + 'a -> 'b) (s : 'a list), + (forall (x : 'a), x \in s => f (f' x) = x) => big P F s = big (P \o f) (F \o f) (map f' s). + + lemma nosmt big_pair_pswap ['a, 'b]: + forall (p : 'a * 'b -> bool) (f : 'a * 'b -> poly) (s : ('a * 'b) list), + big p f s = big (p \o pswap) (f \o pswap) (map pswap s). + + lemma nosmt eq_big_seq ['a]: + forall (F1 F2 : 'a -> poly) (s : 'a list), + (forall (x : 'a), x \in s => F1 x = F2 x) => big predT F1 s = big predT F2 s. + + lemma nosmt congr_big_seq ['a]: + forall (P1 P2 : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + (forall (x : 'a), x \in s => P1 x = P2 x) => + (forall (x : 'a), x \in s => P1 x => P2 x => F1 x = F2 x) => big P1 F1 s = big P2 F2 s. + + lemma big1_eq ['a]: forall (P : 'a -> bool) (s : 'a list), big P (fun (_ : 'a) => Rq.one) s = Rq.one. + + lemma big1 ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i => F i = Rq.one) => big P F s = Rq.one. + + lemma big1_seq ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i /\ (i \in s) => F i = Rq.one) => big P F s = Rq.one. + + lemma big_eq_idm_filter ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (x : 'a), ! P x => F x = Rq.one) => big predT F s = big P F s. + + lemma big_flatten ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (rr : 'a list list), + big P F (flatten rr) = big predT (fun (s : 'a list) => big P F s) rr. + + lemma nosmt big_pair ['a, 'b]: + forall (F : 'a * 'b -> poly) (s : ('a * 'b) list), + uniq s => + big predT F s = + big predT (fun (a : 'a) => big predT F (filter (fun (xy : 'a * 'b) => xy.`1 = a) s)) (undup (unzip1 s)). + + lemma big_nseq_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (n : int) (x : 'a), + big P F (nseq n x) = if P x then iter n (( &* ) (F x)) Rq.one else Rq.one. + + lemma big_nseq ['a]: + forall (F : 'a -> poly) (n : int) (x : 'a), big predT F (nseq n x) = iter n (( &* ) (F x)) Rq.one. + + lemma big_undup ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = big P (fun (a : 'a) => iter (count (pred1 a) s) (( &* ) (F a)) Rq.one) (undup s). + + lemma nosmt exchange_big ['a, 'b]: + forall (P1 : 'a -> bool) (P2 : 'b -> bool) (F : 'a -> 'b -> poly) (s1 : 'a list) (s2 : 'b list), + big P1 (fun (a : 'a) => big P2 (F a) s2) s1 = + big P2 (fun (b : 'b) => big P1 (fun (a : 'a) => F a b) s1) s2. + + lemma partition_big ['a, 'b]: + forall (px : 'a -> 'b) (P : 'a -> bool) (Q : 'b -> bool) (F : + 'a -> poly) (s : 'a list) (s' : 'b list), + uniq s' => + (forall (x : 'a), x \in s => P x => (px x \in s') /\ Q (px x)) => + big P F s = big Q (fun (x : 'b) => big (fun (y : 'a) => P y /\ px y = x) F s) s'. + + lemma nosmt big_allpairs ['a, 'b, 'c]: + forall (f : 'a -> 'b -> 'c) (F : 'c -> poly) (s : 'a list) (t : 'b list), + big predT F (allpairs f s t) = big predT (fun (x : 'a) => big predT (fun (y : 'b) => F (f x y)) t) s. + + lemma nosmt big_int_cond: + forall (m n : int) (P : int -> bool) (F : int -> poly), + bigi P F m n = bigi (fun (i : int) => (m <= i && i < n) /\ P i) F m n. + + lemma nosmt big_int: + forall (m n : int) (F : int -> poly), bigi predT F m n = bigi (fun (i : int) => m <= i && i < n) F m n. + + lemma nosmt congr_big_int: + forall (m1 n1 m2 n2 : int) (P1 P2 : int -> bool) (F1 F2 : + int -> poly), + m1 = m2 => + n1 = n2 => + (forall (i : int), m1 <= i && i < n2 => P1 i = P2 i) => + (forall (i : int), P1 i /\ m1 <= i && i < n2 => F1 i = F2 i) => bigi P1 F1 m1 n1 = bigi P2 F2 m2 n2. + + lemma nosmt eq_big_int: + forall (m n : int) (F1 F2 : int -> poly), + (forall (i : int), m <= i && i < n => F1 i = F2 i) => bigi predT F1 m n = bigi predT F2 m n. + + lemma nosmt big_ltn_cond: + forall (m n : int) (P : int -> bool) (F : int -> poly), + m < n => let x = bigi P F (m + 1) n in bigi P F m n = if P m then F m &* x else x. + + lemma nosmt big_ltn: + forall (m n : int) (F : int -> poly), m < n => bigi predT F m n = F m &* bigi predT F (m + 1) n. + + lemma nosmt big_geq: forall (m n : int) (P : int -> bool) (F : int -> poly), n <= m => bigi P F m n = Rq.one. + + lemma nosmt big_addn: + forall (m n a : int) (P : int -> bool) (F : int -> poly), + bigi P F (m + a) n = bigi (fun (i : int) => P (i + a)) (fun (i : int) => F (i + a)) m (n - a). + + lemma nosmt big_int1: forall (n : int) (F : int -> poly), bigi predT F n (n + 1) = F n. + + lemma nosmt big_cat_int: + forall (n m p : int) (P : int -> bool) (F : int -> poly), + m <= n => n <= p => bigi P F m p = bigi P F m n &* bigi P F n p. + + lemma nosmt big_int_recl: + forall (n m : int) (F : int -> poly), + m <= n => bigi predT F m (n + 1) = F m &* bigi predT (fun (i : int) => F (i + 1)) m n. + + lemma nosmt big_int_recr: + forall (n m : int) (F : int -> poly), m <= n => bigi predT F m (n + 1) = bigi predT F m n &* F n. + + lemma nosmt big_int_recl_cond: + forall (n m : int) (P : int -> bool) (F : int -> poly), + m <= n => + bigi P F m (n + 1) = + (if P m then F m else Rq.one) &* bigi (fun (i : int) => P (i + 1)) (fun (i : int) => F (i + 1)) m n. + + lemma nosmt big_int_recr_cond: + forall (n m : int) (P : int -> bool) (F : int -> poly), + m <= n => bigi P F m (n + 1) = bigi P F m n &* if P n then F n else Rq.one. + + lemma nosmt bigi_split_odd_even: + forall (n : int) (F : int -> poly), + 0 <= n => bigi predT (fun (i : int) => F (2 * i) &* F (2 * i + 1)) 0 n = bigi predT F 0 (2 * n). + end BMul. + + lemma mulr_big ['a]: + forall (P Q : 'a -> bool) (f g : 'a -> poly) (r s : 'a list), + (big P f r)%BAdd &* (big Q g s)%BAdd = + (big P (fun (x : 'a) => (big Q (fun (y : 'a) => f x &* g y) s)%BAdd) r)%BAdd. + + lemma subrXX: + forall (x y : poly) (n : int), + 0 <= n => + (exp x n - exp y n)%ZR = + (x - y)%ZR &* (bigi predT (fun (i : int) => (exp x (n - 1 - i))%ZR &* (exp y i)%ZR) 0 n)%BAdd. + + lemma nosmt mulr_const_cond ['a]: + forall (p : 'a -> bool) (s : 'a list) (c : poly), (big p (fun (_ : 'a) => c) s)%BMul = (exp c (count p s))%ZR. + + lemma nosmt mulr_const ['a]: + forall (s : 'a list) (c : poly), (big predT (fun (_ : 'a) => c) s)%BMul = (exp c (size s))%ZR. + end Big. + + lemma ge0_size: 0 <= kvec. + + hint solve 0 : ge0_size. + + theory Vector. + lemma tofunv_prevector: forall (v : polyvec), prevector (tofunv v). + + lemma tofunvK: cancel tofunv offunv. + + lemma offunvK: forall (v : int -> poly), tofunv (offunv v) = vclamp v. + + op "_.[_]" (v : polyvec) (i : int) : poly = tofunv v i. + + lemma offunvE: forall (v : int -> poly) (i : int), 0 <= i && i < kvec => ((offunv v).[i])%Vector = v i. + + lemma getv_out: forall (v : polyvec) (i : int), ! (0 <= i && i < kvec) => (v.[i])%Vector = Rq.zero. + + lemma eq_vectorP: + forall (v1 v2 : polyvec), + v1 = v2 <=> forall (i : int), 0 <= i && i < kvec => (v1.[i])%Vector = (v2.[i])%Vector. + + lemma vectorW: + forall (P : polyvec -> bool), + (forall (f : int -> poly), prevector f => P (offunv f)) => forall (v : polyvec), P v. + + op vectc (c : poly) : polyvec = offunv (fun (_ : int) => c). + + abbrev zerov : polyvec = (vectc Rq.zero)%Vector. + + lemma offunCE: forall (c : poly) (i : int), 0 <= i && i < kvec => ((vectc c).[i])%Vector = c. + + lemma offun0E: forall (i : int), (Vector.zerov.[i])%Vector = Rq.zero. + + hint simplify. + + op [-] (v : polyvec) : polyvec = offunv (fun (i : int) => (&-) (v.[i])%Vector). + + lemma offunD: + forall (v1 v2 : polyvec) (i : int), + ((v1 + v2)%KMatrix.Vector.[i])%Vector = (v1.[i])%Vector &+ (v2.[i])%Vector. + + hint simplify. + + lemma offunN: forall (v : polyvec) (i : int), ((-v).[i])%Vector = (&-) (v.[i])%Vector. + + hint simplify. + + theory ZModule. + lemma nosmt addrA: associative KMatrix.Vector.(+). + + lemma nosmt addrC: commutative KMatrix.Vector.(+). + + lemma nosmt add0r: left_id Vector.zerov KMatrix.Vector.(+). + + lemma nosmt addNr: left_inverse Vector.zerov Vector.[-] KMatrix.Vector.(+). + + theory AddMonoid. + lemma addmA: associative KMatrix.Vector.(+). + + lemma addmC: commutative KMatrix.Vector.(+). + + lemma add0m: left_id Vector.zerov KMatrix.Vector.(+). + + lemma addm0: right_id Vector.zerov KMatrix.Vector.(+). + + lemma addmCA: left_commutative KMatrix.Vector.(+). + + lemma addmAC: right_commutative KMatrix.Vector.(+). + + lemma addmACA: interchange KMatrix.Vector.(+) KMatrix.Vector.(+). + + lemma iteropE: + forall (n : int) (x : polyvec), + iterop n KMatrix.Vector.(+) x Vector.zerov = iter n (((+) x))%KMatrix.Vector Vector.zerov. + end AddMonoid. + + abbrev (-) (x y : polyvec) : polyvec = (x + (-y)%Vector)%KMatrix.Vector. + + lemma nosmt addr0: right_id Vector.zerov KMatrix.Vector.(+). + + lemma nosmt addrN: right_inverse Vector.zerov Vector.[-] KMatrix.Vector.(+). + + lemma nosmt addrCA: left_commutative KMatrix.Vector.(+). + + lemma nosmt addrAC: right_commutative KMatrix.Vector.(+). + + lemma nosmt addrACA: interchange KMatrix.Vector.(+) KMatrix.Vector.(+). + + lemma nosmt subrr: forall (x : polyvec), x - x = Vector.zerov. + + lemma nosmt addKr: left_loop Vector.[-] KMatrix.Vector.(+). + + lemma nosmt addNKr: rev_left_loop Vector.[-] KMatrix.Vector.(+). + + lemma nosmt addrK: right_loop Vector.[-] KMatrix.Vector.(+). + + lemma nosmt addrNK: rev_right_loop Vector.[-] KMatrix.Vector.(+). + + lemma nosmt subrK: forall (x y : polyvec), (x - y + y)%KMatrix.Vector = x. + + lemma nosmt addrI: right_injective KMatrix.Vector.(+). + + lemma nosmt addIr: left_injective KMatrix.Vector.(+). + + lemma nosmt opprK: involutive Vector.[-]. + + lemma oppr_inj: injective Vector.[-]. + + lemma nosmt oppr0: (- Vector.zerov)%Vector = Vector.zerov. + + lemma oppr_eq0: forall (x : polyvec), (-x)%Vector = Vector.zerov <=> x = Vector.zerov. + + lemma nosmt subr0: forall (x : polyvec), x - Vector.zerov = x. + + lemma nosmt sub0r: forall (x : polyvec), Vector.zerov - x = (-x)%Vector. + + lemma nosmt opprD: forall (x y : polyvec), (- (x + y)%KMatrix.Vector)%Vector = (-x)%Vector - y. + + lemma nosmt opprB: forall (x y : polyvec), (- (x - y))%Vector = y - x. + + lemma nosmt subrACA: interchange (fun (x y : polyvec) => x - y) KMatrix.Vector.(+). + + lemma nosmt subr_eq: forall (x y z : polyvec), x - z = y <=> x = (y + z)%KMatrix.Vector. + + lemma nosmt subr_eq0: forall (x y : polyvec), x - y = Vector.zerov <=> x = y. + + lemma nosmt addr_eq0: forall (x y : polyvec), (x + y)%KMatrix.Vector = Vector.zerov <=> x = (-y)%Vector. + + lemma nosmt eqr_opp: forall (x y : polyvec), (-x)%Vector = (-y)%Vector <=> x = y. + + lemma eqr_oppLR: forall (x y : polyvec), (-x)%Vector = y <=> x = (-y)%Vector. + + lemma nosmt eqr_sub: + forall (x y z t : polyvec), x - y = z - t <=> (x + t)%KMatrix.Vector = (z + y)%KMatrix.Vector. + + lemma subr_add2r: forall (z x y : polyvec), (x + z)%KMatrix.Vector - (y + z)%KMatrix.Vector = x - y. + + op intmul (x : polyvec) (n : int) : polyvec = + if n < 0 then (- iterop (-n) KMatrix.Vector.(+) x Vector.zerov)%Vector + else iterop n KMatrix.Vector.(+) x Vector.zerov. + + lemma intmulpE: + forall (z : polyvec) (c : int), 0 <= c => intmul z c = iterop c KMatrix.Vector.(+) z Vector.zerov. + + lemma mulr0z: forall (x : polyvec), intmul x 0 = Vector.zerov. + + lemma mulr1z: forall (x : polyvec), intmul x 1 = x. + + lemma mulr2z: forall (x : polyvec), intmul x 2 = (x + x)%KMatrix.Vector. + + lemma mulrNz: forall (x : polyvec) (n : int), intmul x (-n) = (- intmul x n)%Vector. + + lemma mulrS: forall (x : polyvec) (n : int), 0 <= n => intmul x (n + 1) = (x + intmul x n)%KMatrix.Vector. + + lemma mulNrz: forall (x : polyvec) (n : int), intmul (-x)%Vector n = (- intmul x n)%Vector. + + lemma mulNrNz: forall (x : polyvec) (n : int), intmul (-x)%Vector (-n) = intmul x n. + + lemma mulrSz: forall (x : polyvec) (n : int), intmul x (n + 1) = (x + intmul x n)%KMatrix.Vector. + + lemma mulrDz: forall (x : polyvec) (n m : int), intmul x (n + m) = (intmul x n + intmul x m)%KMatrix.Vector. + end ZModule. + + lemma offunB: + forall (v1 v2 : polyvec) (i : int), ((v1 - v2)%ZModule.[i])%Vector = ((v1.[i])%Vector - (v2.[i])%Vector)%ZR. + + lemma dotpC: commutative dotp. + + lemma dotpDr: forall (v1 v2 v3 : polyvec), dotp v1 (v2 + v3)%KMatrix.Vector = dotp v1 v2 &+ dotp v1 v3. + + lemma dotpDl: forall (v1 v2 v3 : polyvec), dotp (v1 + v2)%KMatrix.Vector v3 = dotp v1 v3 &+ dotp v2 v3. + + op scalev (a : poly) (v : polyvec) : polyvec = offunv (fun (i : int) => a &* (v.[i])%Vector). + + abbrev ( ** ) : poly -> polyvec -> polyvec = Vector.scalev. + + lemma scalevE: forall (a : poly) (v : polyvec) (i : int), ((a ** v).[i])%Vector = a &* (v.[i])%Vector. + + lemma scalevDl: + forall (a b : poly) (v : polyvec), (a &+ b ** v)%Vector = ((a ** v)%Vector + (b ** v)%Vector)%KMatrix.Vector. + + lemma scalevDr: + forall (a : poly) (v w : polyvec), + (a ** (v + w)%KMatrix.Vector)%Vector = ((a ** v)%Vector + (a ** w)%Vector)%KMatrix.Vector. + + lemma scalevA: forall (a b : poly) (v : polyvec), (a &* b ** v)%Vector = (a ** (b ** v))%Vector. + + lemma scalevAC: forall (a b : poly) (v : polyvec), (a ** (b ** v))%Vector = (b ** (a ** v))%Vector. + end Vector. + + export Vector. + + theory Matrix. + abbrev mrange (i j : int) : bool = (mrange i j)%KMatrix.Matrix. + + lemma nosmt mrangeL: forall (i j : int), (mrange i j)%Matrix => 0 <= i && i < kvec. + + lemma nosmt mrangeR: forall (i j : int), (mrange i j)%Matrix => 0 <= j && j < kvec. + + lemma nosmt mrangeC: forall (i j : int), (mrange i j)%Matrix = (mrange j i)%Matrix. + + lemma tofunm_prematrix: forall (m : polymat), prematrix (tofunm m). + + lemma tofunmK: cancel tofunm offunm. + + lemma offunmK: forall (m : int -> int -> poly), tofunm (offunm m) = mclamp m. + + op "_.[_]" (m : polymat) (ij : int * int) : poly = tofunm m ij.`1 ij.`2. + + lemma offunmE: + forall (m : int -> int -> poly) (i j : int), (mrange i j)%Matrix => ((offunm m).[i, j])%Matrix = m i j. + + lemma getm_out: forall (m : polymat) (i j : int), ! (mrange i j)%Matrix => (m.[i, j])%Matrix = Rq.zero. + + lemma getm_outL: forall (m : polymat) (i j : int), ! (0 <= i && i < kvec) => (m.[i, j])%Matrix = Rq.zero. + + lemma getm_outR: forall (m : polymat) (i j : int), ! (0 <= j && j < kvec) => (m.[i, j])%Matrix = Rq.zero. + + lemma eq_matrixP: + forall (m1 m2 : polymat), + m1 = m2 <=> forall (i j : int), (mrange i j)%Matrix => (m1.[i, j])%Matrix = (m2.[i, j])%Matrix. + + lemma matrixW: + forall (P : polymat -> bool), + (forall (f : int -> int -> poly), prematrix f => P (offunm f)) => forall (v : polymat), P v. + + op matrixc (c : poly) : polymat = offunm (fun (_ _ : int) => c). + + op diagmx (v : polyvec) : polymat = offunm (fun (i j : int) => if i = j then (v.[i])%Vector else Rq.zero). + + abbrev diagc (c : poly) : polymat = (diagmx ((vectc c))%Vector)%Matrix. + + abbrev zerom : polymat = (matrixc Rq.zero)%Matrix. + + abbrev onem : polymat = (diagc Rq.one)%Matrix. + + lemma offunCE: forall (c : poly) (i j : int), (mrange i j)%Matrix => ((matrixc c).[i, j])%Matrix = c. + + lemma diagmxE: + forall (v : polyvec) (i j : int), ((diagmx v).[i, j])%Matrix = if i = j then (v.[i])%Vector else Rq.zero. + + lemma offun0E: forall (i j : int), (Matrix.zerom.[i, j])%Matrix = Rq.zero. + + lemma offun1E: + forall (i j : int), (mrange i j)%Matrix => (Matrix.onem.[i, j])%Matrix = if i = j then Rq.one else Rq.zero. + + lemma offun1_neqE: forall (i j : int), i <> j => (Matrix.onem.[i, j])%Matrix = Rq.zero. + + hint simplify. + + op (+) (m1 m2 : polymat) : polymat = offunm (fun (i j : int) => (m1.[i, j])%Matrix &+ (m2.[i, j])%Matrix). + + op [-] (m : polymat) : polymat = offunm (fun (i j : int) => (&-) (m.[i, j])%Matrix). + + lemma offunD: + forall (m1 m2 : polymat) (i j : int), ((m1 + m2).[i, j])%Matrix = (m1.[i, j])%Matrix &+ (m2.[i, j])%Matrix. + + hint simplify. + + lemma offunN: forall (m : polymat) (i j : int), ((-m).[i, j])%Matrix = (&-) (m.[i, j])%Matrix. + + hint simplify. + + theory ZModule. + lemma nosmt addrA: associative Matrix.(+). + + lemma nosmt addrC: commutative Matrix.(+). + + lemma nosmt add0r: left_id Matrix.zerom Matrix.(+). + + lemma nosmt addNr: left_inverse Matrix.zerom Matrix.[-] Matrix.(+). + + theory AddMonoid. + lemma addmA: associative Matrix.(+). + + lemma addmC: commutative Matrix.(+). + + lemma add0m: left_id Matrix.zerom Matrix.(+). + + lemma addm0: right_id Matrix.zerom Matrix.(+). + + lemma addmCA: left_commutative Matrix.(+). + + lemma addmAC: right_commutative Matrix.(+). + + lemma addmACA: interchange Matrix.(+) Matrix.(+). + + lemma iteropE: + forall (n : int) (x : polymat), iterop n Matrix.(+) x Matrix.zerom = iter n (((+) x))%Matrix Matrix.zerom. + end AddMonoid. + + abbrev (-) (x y : polymat) : polymat = (x + -y)%Matrix. + + lemma nosmt addr0: right_id Matrix.zerom Matrix.(+). + + lemma nosmt addrN: right_inverse Matrix.zerom Matrix.[-] Matrix.(+). + + lemma nosmt addrCA: left_commutative Matrix.(+). + + lemma nosmt addrAC: right_commutative Matrix.(+). + + lemma nosmt addrACA: interchange Matrix.(+) Matrix.(+). + + lemma nosmt subrr: forall (x : polymat), x - x = Matrix.zerom. + + lemma nosmt addKr: left_loop Matrix.[-] Matrix.(+). + + lemma nosmt addNKr: rev_left_loop Matrix.[-] Matrix.(+). + + lemma nosmt addrK: right_loop Matrix.[-] Matrix.(+). + + lemma nosmt addrNK: rev_right_loop Matrix.[-] Matrix.(+). + + lemma nosmt subrK: forall (x y : polymat), (x - y + y)%Matrix = x. + + lemma nosmt addrI: right_injective Matrix.(+). + + lemma nosmt addIr: left_injective Matrix.(+). + + lemma nosmt opprK: involutive Matrix.[-]. + + lemma oppr_inj: injective Matrix.[-]. + + lemma nosmt oppr0: (- Matrix.zerom)%Matrix = Matrix.zerom. + + lemma oppr_eq0: forall (x : polymat), (-x)%Matrix = Matrix.zerom <=> x = Matrix.zerom. + + lemma nosmt subr0: forall (x : polymat), x - Matrix.zerom = x. + + lemma nosmt sub0r: forall (x : polymat), Matrix.zerom - x = (-x)%Matrix. + + lemma nosmt opprD: forall (x y : polymat), (- (x + y))%Matrix = (-x)%Matrix - y. + + lemma nosmt opprB: forall (x y : polymat), (- (x - y))%Matrix = y - x. + + lemma nosmt subrACA: interchange (fun (x y : polymat) => x - y) Matrix.(+). + + lemma nosmt subr_eq: forall (x y z : polymat), x - z = y <=> x = (y + z)%Matrix. + + lemma nosmt subr_eq0: forall (x y : polymat), x - y = Matrix.zerom <=> x = y. + + lemma nosmt addr_eq0: forall (x y : polymat), (x + y)%Matrix = Matrix.zerom <=> x = (-y)%Matrix. + + lemma nosmt eqr_opp: forall (x y : polymat), (-x)%Matrix = (-y)%Matrix <=> x = y. + + lemma eqr_oppLR: forall (x y : polymat), (-x)%Matrix = y <=> x = (-y)%Matrix. + + lemma nosmt eqr_sub: forall (x y z t : polymat), x - y = z - t <=> (x + t)%Matrix = (z + y)%Matrix. + + lemma subr_add2r: forall (z x y : polymat), (x + z)%Matrix - (y + z)%Matrix = x - y. + + op intmul (x : polymat) (n : int) : polymat = + if n < 0 then (- iterop (-n) Matrix.(+) x Matrix.zerom)%Matrix else iterop n Matrix.(+) x Matrix.zerom. + + lemma intmulpE: forall (z : polymat) (c : int), 0 <= c => intmul z c = iterop c Matrix.(+) z Matrix.zerom. + + lemma mulr0z: forall (x : polymat), intmul x 0 = Matrix.zerom. + + lemma mulr1z: forall (x : polymat), intmul x 1 = x. + + lemma mulr2z: forall (x : polymat), intmul x 2 = (x + x)%Matrix. + + lemma mulrNz: forall (x : polymat) (n : int), intmul x (-n) = (- intmul x n)%Matrix. + + lemma mulrS: forall (x : polymat) (n : int), 0 <= n => intmul x (n + 1) = (x + intmul x n)%Matrix. + + lemma mulNrz: forall (x : polymat) (n : int), intmul (-x)%Matrix n = (- intmul x n)%Matrix. + + lemma mulNrNz: forall (x : polymat) (n : int), intmul (-x)%Matrix (-n) = intmul x n. + + lemma mulrSz: forall (x : polymat) (n : int), intmul x (n + 1) = (x + intmul x n)%Matrix. + + lemma mulrDz: forall (x : polymat) (n m : int), intmul x (n + m) = (intmul x n + intmul x m)%Matrix. + end ZModule. + + lemma offunB: + forall (m1 m2 : polymat) (i j : int), + ((m1 - m2)%ZModule.[i, j])%Matrix = ((m1.[i, j])%Matrix - (m2.[i, j])%Matrix)%ZR. + + op trace (m : polymat) : poly = (bigi predT (fun (i : int) => (m.[i, i])%Matrix) 0 kvec)%Big.BAdd. + + op trmx (m : polymat) : polymat = offunm (fun (i j : int) => (m.[j, i])%Matrix). + + lemma trmxE: forall (m : polymat) (i j : int), ((trmx m).[i, j])%Matrix = (m.[j, i])%Matrix. + + lemma trmxK: forall (m : polymat), (trmx ((trmx m))%Matrix)%Matrix = m. + + lemma trmx1: (trmx Matrix.onem)%Matrix = Matrix.onem. + + lemma trmxD: forall (m1 m2 : polymat), (trmx (m1 + m2)%Matrix)%Matrix = (trmx m1 + trmx m2)%Matrix. + + lemma trace_trmx: forall (m : polymat), (trace ((trmx m))%Matrix)%Matrix = (trace m)%Matrix. + + op ( * ) (m1 m2 : polymat) : polymat = + offunm + (fun (i j : int) => + (bigi predT (fun (k : int) => (m1.[i, k])%Matrix &* (m2.[k, j])%Matrix) 0 kvec)%Big.BAdd). + + lemma offunM: + forall (m1 m2 : polymat) (i j : int), + ((m1 * m2).[i, j])%Matrix = + (bigi predT (fun (k : int) => (m1.[i, k])%Matrix &* (m2.[k, j])%Matrix) 0 kvec)%Big.BAdd. + + hint simplify. + + lemma mulmx1: right_id Matrix.onem Matrix.( * ). + + lemma mul1mx: left_id Matrix.onem Matrix.( * ). + + lemma mulmxDl: forall (m1 m2 m : polymat), ((m1 + m2) * m)%Matrix = (m1 * m + m2 * m)%Matrix. + + lemma mulmxDr: forall (m1 m2 m : polymat), (m * (m1 + m2))%Matrix = (m * m1 + m * m2)%Matrix. + + lemma mulmxA: associative Matrix.( * ). + + lemma trmxM: forall (m1 m2 : polymat), (trmx (m1 * m2)%Matrix)%Matrix = (trmx m2 * trmx m1)%Matrix. + + op ( *^ ) (m : polymat) (v : polyvec) : polyvec = + offunv (fun (i : int) => (bigi predT (fun (j : int) => (m.[i, j])%Matrix &* (v.[j])%Vector) 0 kvec)%Big.BAdd). + + op ( ^* ) (v : polyvec) (m : polymat) : polyvec = + offunv (fun (j : int) => (bigi predT (fun (i : int) => (v.[i])%Vector &* (m.[i, j])%Matrix) 0 kvec)%Big.BAdd). + + lemma mulmxTv: forall (m : polymat) (v : polyvec), (trmx m *^ v)%Matrix = (v ^* m)%Matrix. + + lemma mulmxv0: forall (m : polymat), (m *^ Vector.zerov)%Matrix = Vector.zerov. + + lemma mulmx1v: forall (v : polyvec), (Matrix.onem *^ v)%Matrix = v. + + lemma mulmxvDl: + forall (m1 m2 : polymat) (v : polyvec), + ((m1 + m2) *^ v)%Matrix = ((m1 *^ v)%Matrix + (m2 *^ v)%Matrix)%KMatrix.Vector. + + lemma mulmxvDr: + forall (m : polymat) (v1 v2 : polyvec), + (m *^ (v1 + v2)%KMatrix.Vector)%Matrix = ((m *^ v1)%Matrix + (m *^ v2)%Matrix)%KMatrix.Vector. + + lemma mulmxvA: forall (m1 m2 : polymat) (v : polyvec), (m1 * m2 *^ v)%Matrix = (m1 *^ (m2 *^ v))%Matrix. + + lemma mulvmxT: forall (v : polyvec) (m : polymat), (v ^* trmx m)%Matrix = (m *^ v)%Matrix. + + lemma mulv0mx: forall (m : polymat), (Vector.zerov ^* m)%Matrix = Vector.zerov. + + lemma mulvmx1: forall (v : polyvec), (v ^* Matrix.onem)%Matrix = v. + + lemma mulvmxDr: + forall (v : polyvec) (m1 m2 : polymat), + (v ^* (m1 + m2))%Matrix = ((v ^* m1)%Matrix + (v ^* m2)%Matrix)%KMatrix.Vector. + + lemma mulvmxDl: + forall (v1 v2 : polyvec) (m : polymat), + ((v1 + v2)%KMatrix.Vector ^* m)%Matrix = ((v1 ^* m)%Matrix + (v2 ^* m)%Matrix)%KMatrix.Vector. + + lemma mulvmxA: forall (v : polyvec) (m1 m2 : polymat), (v ^* m1 ^* m2)%Matrix = (v ^* (m1 * m2))%Matrix. + + lemma mulmxvE: + forall (m : polymat) (v : polyvec) (i : int), + ((m *^ v)%Matrix.[i])%Vector = + (bigi predT (fun (j : int) => (m.[i, j])%Matrix &* (v.[j])%Vector) 0 kvec)%Big.BAdd. + + lemma mulvmxE: + forall (m : polymat) (v : polyvec) (j : int), + ((v ^* m)%Matrix.[j])%Vector = + (bigi predT (fun (i : int) => (v.[i])%Vector &* (m.[i, j])%Matrix) 0 kvec)%Big.BAdd. + + op colmx (v : polyvec) : polymat = offunm (fun (i _ : int) => (v.[i])%Vector). + + op rowmx (v : polyvec) : polymat = offunm (fun (_ j : int) => (v.[j])%Vector). + + lemma colmxT: forall (v : polyvec), (trmx ((colmx v))%Matrix)%Matrix = (rowmx v)%Matrix. + + lemma rowmxT: forall (v : polyvec), (trmx ((rowmx v))%Matrix)%Matrix = (colmx v)%Matrix. + + lemma colmxE: + forall (v : polyvec) (i j : int), 0 <= j && j < kvec => ((colmx v).[i, j])%Matrix = (v.[i])%Vector. + + lemma rowmxE: + forall (v : polyvec) (i j : int), 0 <= i && i < kvec => ((rowmx v).[i, j])%Matrix = (v.[j])%Vector. + + lemma colmx_mulmxv: forall (m : polymat) (v : polyvec), (colmx (m *^ v)%Matrix)%Matrix = (m * colmx v)%Matrix. + + lemma rowmx_mulvmx: forall (v : polyvec) (m : polymat), (rowmx (v ^* m)%Matrix)%Matrix = (rowmx v * m)%Matrix. + + lemma mulmx_diag: + forall (v1 v2 : polyvec), + (diagmx v1 * diagmx v2)%Matrix = + (diagmx (offunv (fun (i : int) => (v1.[i])%Vector &* (v2.[i])%Vector)))%Matrix. + + lemma dotp_tr: forall (v1 v2 : polyvec), dotp v1 v2 = (trace (diagmx v1 * diagmx v2)%Matrix)%Matrix. + + lemma dotp_mulmxv: forall (m : polymat) (v1 v2 : polyvec), dotp (m *^ v1)%Matrix v2 = dotp v1 (v2 ^* m)%Matrix. + + op dvector (d : poly distr) : polyvec distr = + dmap (djoin (nseq kvec d)) (fun (xs : poly list) => offunv (nth witness xs)). + + lemma dvector1E: + forall (d : poly distr) (v : polyvec), + mu1 ((dvector d))%Matrix v = + (bigi predT (fun (i : int) => mu1 d (v.[i])%Vector) 0 kvec)%StdBigop.Bigreal.BRM. + + lemma dvector_uni: forall (d : poly distr), is_uniform d => is_uniform ((dvector d))%Matrix. + + lemma dvector_ll: forall (d : poly distr), is_lossless d => is_lossless ((dvector d))%Matrix. + + lemma dvector_fu: forall (d : poly distr), is_full d => is_full ((dvector d))%Matrix. + + lemma dvector_funi: forall (d : poly distr), is_full d => is_uniform d => is_funiform ((dvector d))%Matrix. + + op dmatrix (d : poly distr) : polymat distr = + dmap (djoin (nseq kvec (djoin (nseq kvec d)))) + (fun (xs : poly list list) => offunm (fun (i j : int) => nth witness (nth witness xs j) i)). + + lemma dmatrix1E: + forall (d : poly distr) (m : polymat), + mu1 ((dmatrix d))%Matrix m = + (bigi predT + (fun (i : int) => (bigi predT (fun (j : int) => mu1 d (m.[i, j])%Matrix) 0 kvec)%StdBigop.Bigreal.BRM) 0 + kvec)%StdBigop.Bigreal.BRM. + + lemma dmatrix_dvector: + forall (d : poly distr), + (dmatrix d)%Matrix = + dmap (djoin (nseq kvec ((dvector d))%Matrix)) + (fun (vs : polyvec list) => offunm (fun (i j : int) => ((nth witness vs j).[i])%Vector)). + + lemma dmatrix_uni: forall (d : poly distr), is_uniform d => is_uniform ((dmatrix d))%Matrix. + + lemma dmatrix_ll: forall (d : poly distr), is_lossless d => is_lossless ((dmatrix d))%Matrix. + + lemma dmatrix_fu: forall (d : poly distr), is_full d => is_full ((dmatrix d))%Matrix. + + lemma dmatrix_funi: forall (d : poly distr), is_full d => is_uniform d => is_funiform ((dmatrix d))%Matrix. + end Matrix. + + export Matrix. + end Matrix_. + + instance ring with [()] poly + op rzero = Top.Rq.zero + op rone = Top.Rq.one + op add = Top.Rq.&+ + op opp = Top.Rq.&- + op mul = Top.Rq.&* + op expr = Top.MLWEPKEHash.MLWE_.Matrix_.ZR.exp + op ofint = Top.MLWEPKEHash.MLWE_.Matrix_.ZR.ofint + + instance poly with Top.Ring.ZModule.zmodule. + + instance poly with Top.Ring.ComRing.ring. + + instance poly with Top.Ring.IDomain.idomain. + + lemma duni_R_ll: is_lossless duni_R. + + hint solve 0 lossless : duni_R_ll. + + hint solve 0 random : duni_R_ll. + + lemma duni_R_uni: is_uniform duni_R. + + hint solve 0 random : duni_R_uni. + + lemma duni_R_fu: is_full duni_R. + + hint solve 0 random : duni_R_fu. + + lemma duni_R_funi: is_funiform duni_R. + + lemma dshort_R_ll: is_lossless dshort_R. + + hint solve 0 lossless : dshort_R_ll. + + hint solve 0 random : dshort_R_ll. + + op duni : polyvec distr = (dvector duni_R)%Matrix_.Matrix. + + op dshort : polyvec distr = (dvector dshort_R)%Matrix_.Matrix. + + lemma duni_ll: is_lossless duni. + + lemma duni_fu: is_full duni. + + lemma duni_uni: is_uniform duni. + + lemma duni_funi: is_funiform duni. + + lemma dshort_ll: is_lossless dshort. + + op duni_matrix : polymat distr = (dmatrix duni_R)%Matrix_.Matrix. + + lemma duni_matrix_ll: is_lossless duni_matrix. + + lemma duni_matrix_fu: is_full duni_matrix. + + lemma duni_matrix_uni: is_uniform duni_matrix. + + lemma duni_matrix_funi: is_funiform duni_matrix. + + module type Adv_T = { + proc guess(A : polymat, t : polyvec, uv : polyvec * poly) : bool {} + } + + abbrev m_transpose : polymat -> polymat = Matrix_.Matrix.trmx. + + abbrev (`<*>`) : polyvec -> polyvec -> poly = dotp. + + abbrev (&+) : poly -> poly -> poly = Rq.(&+). + + abbrev (&-) : poly -> poly -> poly = fun (x y : poly) => (x &+ ((&-) y)%Rq)%MLWE_. + + module MLWE(Adv : Adv_T) = { + proc main(b : bool) : bool = { + var s : polyvec; + var e : polyvec; + var _A : polymat; + var u0 : polyvec; + var u1 : polyvec; + var t : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + _A <$ duni_matrix; + s <$ dshort; + e <$ dshort; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + t <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ Adv.guess(_A, t, if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + lemma dseed_ll: is_lossless srand. + + hint solve 0 lossless : dseed_ll. + + hint solve 0 random : dseed_ll. + + module type HAdv_T = { + proc guess(sd : W8.t Array32.t, t : polyvec, uv : polyvec * poly) : bool {} + } + + module MLWE_H(Adv : HAdv_T) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var _A : polymat; + var u0 : polyvec; + var u1 : polyvec; + var t : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + sd <$ srand; + s <$ dshort; + e <$ dshort; + _A <- if tr then (trmx (H sd))%Matrix_.Matrix else H sd; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + t <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ Adv.guess(sd, t, if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + theory MLWE_ROM. + theory RO_H. + type in_t = W8.t Array32.t. + + type out_t = polymat. + + op dout (_ : W8.t Array32.t) : polymat distr = duni_matrix. + + type d_in_t = bool. + + type d_out_t = bool. + + module type RO = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + } + + module type RO_Distinguisher(G : RO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.rem, G.sample} + } + + module MainD(D : RO_Distinguisher, RO0 : RO) = { + proc distinguish(x : d_in_t) : d_out_t = { + var r : d_out_t; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + module type ROmap = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + + proc restrK() : (in_t, out_t) SmtMap.fmap {} + } + + module type FRO = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + + proc queried(x : in_t, f : PROM.flag) : bool {} + + proc allKnown() : (in_t, out_t) SmtMap.fmap {} + } + + module type FRO_Distinguisher(G : FRO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.rem, G.sample, G.queried, G.allKnown} + } + + abstract theory MkRO. + module RO = { + var m : (in_t, out_t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : in_t, y : out_t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : in_t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + RO.get(x); + } + + proc restrK() : (in_t, out_t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : in_t) : out_t = RO.get + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc rem(x : in_t) : unit = RO.rem + + proc sample(x : in_t) : unit = {} + }. + end MkRO. + + module RO = { + var m : (in_t, out_t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : in_t, y : out_t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : in_t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + RO.get(x); + } + + proc restrK() : (in_t, out_t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : in_t) : out_t = RO.get + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc rem(x : in_t) : unit = RO.rem + + proc sample(x : in_t) : unit = {} + }. + + module FRO = { + var m : (in_t, out_t * PROM.flag) SmtMap.fmap + + proc init() : unit = { + FRO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \in FRO.m)%SmtMap) + r <- (oget (FRO.m.[x])%SmtMap).`1; + FRO.m.[x] <- (r, PROM.Known); + + return r; + } + + proc set(x : in_t, y : out_t) : unit = { + FRO.m.[x] <- (y, PROM.Known); + } + + proc rem(x : in_t) : unit = { + FRO.m <- (rem FRO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + if ((x \notin FRO.m)%SmtMap) + FRO.m.[x] <- (c, PROM.Unknown); + } + + proc queried(x : in_t, f : PROM.flag) : bool = { + return (x \in FRO.m)%SmtMap /\ ((FRO.m.[x])%SmtMap \is f)%PROM; + } + + proc allKnown() : (in_t, out_t) SmtMap.fmap = { + return (restr PROM.Known FRO.m)%SmtMap; + } + }. + + lemma RO_FRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_get: + equiv[ RO.get ~ FRO.get : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> ={res} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_sample: + equiv[ RO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(RO).distinguish ~ D(FRO).distinguish : + ={arg, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_init_ll: islossless RO.init. + + lemma FRO_init_ll: islossless FRO.init. + + lemma FRO_in_dom_ll: islossless FRO.queried. + + lemma FRO_restrK_ll: islossless FRO.allKnown. + + lemma RO_set_ll: islossless RO.set. + + lemma FRO_set_ll: islossless FRO.set. + + lemma RO_get_ll: (forall (x : in_t), is_lossless (dout x)) => islossless RO.get. + + lemma FRO_get_ll: (forall (x : in_t), is_lossless (dout x)) => islossless FRO.get. + + lemma RO_sample_ll: (forall (x : in_t), is_lossless (dout x)) => islossless RO.sample. + + lemma FRO_sample_ll: (forall (x : in_t), is_lossless (dout x)) => islossless FRO.sample. + + module type RM_Distinguisher(G : ROmap) = { + proc distinguish(_ : d_in_t) : d_out_t {G.get, G.sample, G.restrK} + } + + module MainRM(D : RM_Distinguisher, RO0 : ROmap) = { + proc distinguish(x : d_in_t) : d_out_t = { + var r : d_out_t; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + lemma fcoll_bound ['rT]: + forall (f : out_t -> 'rT) (Pc : real), + 0%r <= Pc => + (forall (x1 x2 : in_t) (y : 'rT), y \in dmap (dout x1) f => mu1 (dmap (dout x2) f) y <= Pc) => + forall (q : int), + 0 <= q => + forall (D(G : ROmap) <: RM_Distinguisher{+all mem, -RO} ) &m (z : d_in_t), + Pr[MainRM(D, RO).distinguish(z) @ &m : (fcoll f RO.m)%SmtMap /\ (fsize RO.m)%SmtMap <= q] <= + (q * (q - 1))%r / 2%r * Pc. + + theory FullEager. + abstract theory EagerCore. + axiom dout_ll: forall (x : in_t), is_lossless (dout x). + + module type Orcl = { + proc f(x : in_t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : in_t list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : in_t list, l2 : in_t list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : in_t, l : in_t list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : in_t, t2 : in_t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : in_t, t2 : in_t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : in_t list) (i : in_t) (s2 : in_t list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : in_t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : in_t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : in_t, y : out_t) : unit = FRO.set + + proc rem(x : in_t) : unit = FRO.rem + + proc sample(x : in_t) : unit = FRO.sample + + proc queried(x : in_t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (in_t, out_t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : in_t) (mx1 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : in_t) (mx1 mx2 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : in_t) (r1 : out_t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + end FullEager. + + abstract theory FinEager. + abstract theory EagerCore. + axiom dout_ll: forall (x : in_t), is_lossless (dout x). + + module type Orcl = { + proc f(x : in_t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : in_t list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : in_t list, l2 : in_t list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : in_t, l : in_t list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : in_t, t2 : in_t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : in_t, t2 : in_t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : in_t list) (i : in_t) (s2 : in_t list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : in_t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : in_t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : in_t, y : out_t) : unit = FRO.set + + proc rem(x : in_t) : unit = FRO.rem + + proc sample(x : in_t) : unit = FRO.sample + + proc queried(x : in_t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (in_t, out_t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : in_t) (mx1 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : in_t) (mx1 mx2 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : in_t) (r1 : out_t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + theory FinFrom. + type t = in_t. + + op enum : t list. + + op card : int = size enum. + + axiom enum_spec: forall (x : t), count (pred1 x) enum = 1. + + lemma enumP: forall (x : t), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : t list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinFrom. + + module FinRO = { + proc rem(x : in_t) : unit = RO.rem + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc init() : unit = { + var l : FinFrom.t list; + + l <- FinFrom.enum; + RO.init(); + while (l <> []){ + RO.sample(head witness l); + l <- behead l; + } + } + + proc get(x : in_t) : out_t = { + return oget (RO.m.[x])%SmtMap; + } + + proc sample(x : in_t) : unit = {} + }. + + module type FinRO_Distinguisher(G : RO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.sample} + } + + lemma RO_FinRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ MainD(D, RO).distinguish ~ MainD(D, FinRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_RO_FinRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ) &m (x : d_in_t) (p : + d_out_t -> bool), + Pr[MainD(D, RO).distinguish(x) @ &m : p res] = Pr[MainD(D, FinRO).distinguish(x) @ &m : p res]. + + theory MUniFinFun. + op mfun ['u] (d : in_t -> 'u distr) (f : in_t -> 'u) : real = + (big predT (fun (x : in_t) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma mfunE ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mfun d f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + lemma isdistr_dfun ['u]: forall (d : in_t -> 'u distr), isdistr (mfun d). + + op dfun ['u] (d : in_t -> 'u distr) : (in_t -> 'u) distr = mk (mfun d). + + lemma dfun1E ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mu1 (dfun d) f = (big predT (fun (x : in_t) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun1E_djoin ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mu1 (dfun d) f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + op tofun ['u] (us : 'u list) (x : in_t) : 'u = nth witness us (index x FinFrom.enum). + + op offun ['u] (f : in_t -> 'u) : 'u list = map f FinFrom.enum. + + lemma offunK ['u]: cancel offun tofun. + + lemma tofunK ['u]: forall (us : 'u list), size us = FinFrom.card => offun (tofun us) = us. + + lemma dfun_dmap ['u]: forall (d : in_t -> 'u distr), dfun d = dmap (djoinmap d FinFrom.enum) tofun. + + lemma dfun_supp ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), f \in dfun d <=> forall (x : in_t), f x \in d x. + + lemma dfun_fu ['u]: forall (d : in_t -> 'u distr), (forall (x : in_t), is_full (d x)) => is_full (dfun d). + + lemma dfun_uni ['u]: + forall (d : in_t -> 'u distr), (forall (x : in_t), is_uniform (d x)) => is_uniform (dfun d). + + lemma dfun_funi ['u]: + forall (d : in_t -> 'u distr), + (forall (x : in_t), is_uniform (d x)) => (forall (x : in_t), is_full (d x)) => is_funiform (dfun d). + + lemma dfunE_djoin ['u]: + forall (du : in_t -> 'u distr) (E : (in_t -> 'u) -> bool), + mu (dfun du) E = mu (djoinmap du FinFrom.enum) (E \o tofun). + + lemma dfunE ['u]: + forall (du : in_t -> 'u distr) (p : in_t -> 'u -> bool), + mu (dfun du) (fun (f : in_t -> 'u) => forall (x : in_t), p x (f x)) = + (big predT (fun (x : in_t) => mu (du x) (p x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma weight_dfun ['u]: + forall (df : in_t -> 'u distr), + weight (dfun df) = (big predT (fun (x : in_t) => weight (df x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun_ll ['u]: + forall (d : in_t -> 'u distr), (forall (x : in_t), is_lossless (d x)) => is_lossless (dfun d). + + lemma dlet_dfun ['u, 'v]: + forall (d1 : in_t -> 'u distr) (d2 : in_t -> 'u -> 'v distr), + dlet (dfun d1) (fun (f1 : in_t -> 'u) => dfun (fun (x : in_t) => d2 x (f1 x))) = + dfun (fun (x : in_t) => dlet (d1 x) (fun (x1 : 'u) => d2 x x1)). + + lemma dfun_unit ['u]: forall (f : in_t -> 'u), dfun (fun (x : in_t) => dunit (f x)) = dunit f. + + lemma dmap_dfun ['u, 'v]: + forall (d : in_t -> 'u distr) (F : in_t -> 'u -> 'v), + dmap (dfun d) (fun (f : in_t -> 'u) (x : in_t) => F x (f x)) = + dfun (fun (x : in_t) => dmap (d x) (fun (fx : 'u) => F x fx)). + + lemma dfun1E_fix1 ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t) (f : in_t -> 'u), + mu1 (dfun d) f = mu1 (d x0) (f x0) * mu1 (dfun d.[x0 <- dunit (f x0)]) f. + + lemma dlet_dfun_fupdate_ll ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t) (v : 'u), + dlet (dfun d.[x0 <- dunit v]) (fun (f : in_t -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + dfun d. + + lemma dfunE_dlet_fix1 ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t), dfun d = dlet (d x0) (fun (v : 'u) => dfun d.[x0 <- dunit v]). + + lemma dlet_dfun_update ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t), + dlet (dfun d) (fun (f : in_t -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + (weight (d x0) \cdot dfun d). + + lemma dfun_projE ['u]: + forall (df : in_t -> 'u distr) (x : in_t), + dmap (dfun df) (fun (f : in_t -> 'u) => f x) = (weight (dfun df) / weight (df x) \cdot df x). + + lemma eq_from_dfun_proj_eq ['u]: + forall (df1 df2 : in_t -> 'u distr), + (forall (x : in_t), is_lossless (df1 x)) => + (forall (x : in_t), is_lossless (df2 x)) => + (forall (x : in_t), + dmap (dfun df1) (fun (f : in_t -> 'u) => f x) = dmap (dfun df2) (fun (f : in_t -> 'u) => f x)) => + df1 == df2. + + lemma dfun_prod1E ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr) (f : + in_t -> 'u) (g : in_t -> 'v), + mu1 (dfun (fun (x : in_t) => df x `*` dg x)) (fun (x : in_t) => (f x, g x)) = + mu1 (dfun df) f * mu1 (dfun dg) g. + + lemma dprod_funE ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr), + dfun df `*` dfun dg = + dmap (dfun (fun (x : in_t) => df x `*` dg x)) + (fun (fg : in_t -> 'u * 'v) => ((fun (p : 'u * 'v) => p.`1) \o fg, (fun (p : 'u * 'v) => p.`2) \o fg)). + + lemma dfun_prodE ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr), + dfun (fun (x : in_t) => df x `*` dg x) = + dmap (dfun df `*` dfun dg) (fun (fg : (in_t -> 'u) * (in_t -> 'v)) (x : in_t) => (fg.`1 x, fg.`2 x)). + + lemma dfun_condE ['u]: + forall (X : in_t -> bool) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dmap (dfun dt `*` dfun df) + (fun (tf : (in_t -> 'u) * (in_t -> 'u)) (x : in_t) => if X x then tf.`1 x else tf.`2 x) = + dfun (fun (x : in_t) => if X x then dt x else df x). + + lemma dlet_dfun_cond ['u]: + forall (dX : in_t -> bool distr) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dX x)) => + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dlet (dfun dX) (fun (X : in_t -> bool) => dfun (fun (x : in_t) => if X x then dt x else df x)) = + dfun (fun (x : in_t) => dlet (dX x) (fun (b : bool) => if b then dt x else df x)). + + lemma dfun_dcondE ['u]: + forall (dX : in_t -> bool distr) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dX x)) => + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : in_t -> bool) => + dmap (dfun dt `*` dfun df) + (fun (tf : (in_t -> 'u) * (in_t -> 'u)) (x : in_t) => if X x then tf.`1 x else tf.`2 x)) = + dfun (fun (x : in_t) => (mu1 (dX x) true \cdot dt x) + (mu1 (dX x) false \cdot df x)). + end MUniFinFun. + + module FunRO = { + var f : in_t -> out_t + + proc init() : unit = { + FunRO.f <$ (dfun dout)%MUniFinFun; + } + + proc get(x : in_t) : out_t = { + return FunRO.f x; + } + + proc set(x : in_t, y : out_t) : unit = { + FunRO.f <- fun (z : in_t) => if z = x then y else FunRO.f z; + } + + proc sample(x : in_t) : unit = {} + + proc rem(x : in_t) : unit = {} + }. + + lemma FinRO_FunRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ), + equiv[ MainD(D, FinRO).distinguish ~ MainD(D, FunRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_FinRO_FunRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ) &m (x : d_in_t) + (p : d_out_t -> bool), + Pr[MainD(D, FinRO).distinguish(x) @ &m : p res] = Pr[MainD(D, FunRO).distinguish(x) @ &m : p res]. + end FinEager. + end RO_H. + + module type Ideal_RO = { + proc get(x : RO_H.in_t) : RO_H.out_t {} + } + + module type ROAdv_T(O : Ideal_RO) = { + proc guess(sd : W8.t Array32.t, t : polyvec, uv : polyvec * poly) : bool {O.get} + } + + module MLWE_RO(Adv : ROAdv_T, O : RO_H.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var _A : RO_H.out_t; + var u0 : polyvec; + var u1 : polyvec; + var t : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + O.init(); + sd <$ srand; + s <$ dshort; + e <$ dshort; + _A <@ O.get(sd); + _A <- if tr then (trmx _A)%Matrix_.Matrix else _A; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + t <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ Adv(O).guess(sd, t, if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + theory MLWE_vs_MLWE_ROM. + module B(A : ROAdv_T, O : RO_H.RO) = { + var _sd : W8.t Array32.t + + var __A : polymat + + module FakeRO = { + proc get(sd : W8.t Array32.t) : polymat = { + var _Ares : polymat; + + _Ares <- B.__A; + if (sd <> B._sd) { + O.sample(sd); + _Ares <@ O.get(sd); + } + + return _Ares; + } + } + + proc guess(_A : polymat, t : polyvec, uv : polyvec * poly) : bool = { + var sd : W8.t Array32.t; + var b : bool; + + sd <$ srand; + B._sd <- sd; + B.__A <- _A; + O.init(); + b <@ A(B(A, O).FakeRO).guess(sd, t, uv); + + return b; + } + }. + + module Bt(A : ROAdv_T, O : RO_H.RO) = { + var _sd : W8.t Array32.t + + var __A : polymat + + module FakeRO = { + proc get(sd : W8.t Array32.t) : polymat = { + var _Ares : polymat; + + _Ares <- Bt.__A; + if (sd <> Bt._sd) { + O.sample(sd); + _Ares <@ O.get(sd); + } + + return _Ares; + } + } + + proc guess(_A : polymat, t : polyvec, uv : polyvec * poly) : bool = { + var sd : W8.t Array32.t; + var b : bool; + + sd <$ srand; + Bt._sd <- sd; + Bt.__A <- (trmx _A)%Matrix_.Matrix; + O.init(); + b <@ A(Bt(A, O).FakeRO).guess(sd, t, uv); + + return b; + } + }. + + lemma MLWE_RO_equiv: + forall (b : bool) &m (A(O : Ideal_RO) <: ROAdv_T{+all mem, -RO_H.LRO, -B} ), + Pr[MLWE_RO(A, RO_H.LRO).main(false, b) @ &m : res] = Pr[MLWE(B(A, RO_H.LRO)).main(b) @ &m : res]. + + lemma MLWE_RO_equiv_t: + forall (b : bool) &m (A(O : Ideal_RO) <: ROAdv_T{+all mem, -RO_H.LRO, -Bt} ), + Pr[MLWE_RO(A, RO_H.LRO).main(true, b) @ &m : res] = Pr[MLWE(Bt(A, RO_H.LRO)).main(b) @ &m : res]. + end MLWE_vs_MLWE_ROM. + end MLWE_ROM. + + theory MLWE_SMP. + theory RO_SMP. + type in_t. + + type out_t. + + op dout : in_t -> out_t distr. + + type d_in_t. + + type d_out_t. + + module type RO = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + } + + module type RO_Distinguisher(G : RO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.rem, G.sample} + } + + module MainD(D : RO_Distinguisher, RO0 : RO) = { + proc distinguish(x : d_in_t) : d_out_t = { + var r : d_out_t; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + module type ROmap = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + + proc restrK() : (in_t, out_t) SmtMap.fmap {} + } + + module type FRO = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + + proc queried(x : in_t, f : PROM.flag) : bool {} + + proc allKnown() : (in_t, out_t) SmtMap.fmap {} + } + + module type FRO_Distinguisher(G : FRO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.rem, G.sample, G.queried, G.allKnown} + } + + abstract theory MkRO. + module RO = { + var m : (in_t, out_t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : in_t, y : out_t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : in_t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + RO.get(x); + } + + proc restrK() : (in_t, out_t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : in_t) : out_t = RO.get + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc rem(x : in_t) : unit = RO.rem + + proc sample(x : in_t) : unit = {} + }. + end MkRO. + + module RO = { + var m : (in_t, out_t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : in_t, y : out_t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : in_t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + RO.get(x); + } + + proc restrK() : (in_t, out_t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : in_t) : out_t = RO.get + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc rem(x : in_t) : unit = RO.rem + + proc sample(x : in_t) : unit = {} + }. + + module FRO = { + var m : (in_t, out_t * PROM.flag) SmtMap.fmap + + proc init() : unit = { + FRO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \in FRO.m)%SmtMap) + r <- (oget (FRO.m.[x])%SmtMap).`1; + FRO.m.[x] <- (r, PROM.Known); + + return r; + } + + proc set(x : in_t, y : out_t) : unit = { + FRO.m.[x] <- (y, PROM.Known); + } + + proc rem(x : in_t) : unit = { + FRO.m <- (rem FRO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + if ((x \notin FRO.m)%SmtMap) + FRO.m.[x] <- (c, PROM.Unknown); + } + + proc queried(x : in_t, f : PROM.flag) : bool = { + return (x \in FRO.m)%SmtMap /\ ((FRO.m.[x])%SmtMap \is f)%PROM; + } + + proc allKnown() : (in_t, out_t) SmtMap.fmap = { + return (restr PROM.Known FRO.m)%SmtMap; + } + }. + + lemma RO_FRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_get: + equiv[ RO.get ~ FRO.get : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> ={res} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_sample: + equiv[ RO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(RO).distinguish ~ D(FRO).distinguish : + ={arg, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_init_ll: islossless RO.init. + + lemma FRO_init_ll: islossless FRO.init. + + lemma FRO_in_dom_ll: islossless FRO.queried. + + lemma FRO_restrK_ll: islossless FRO.allKnown. + + lemma RO_set_ll: islossless RO.set. + + lemma FRO_set_ll: islossless FRO.set. + + lemma RO_get_ll: (forall (x : in_t), is_lossless (dout x)) => islossless RO.get. + + lemma FRO_get_ll: (forall (x : in_t), is_lossless (dout x)) => islossless FRO.get. + + lemma RO_sample_ll: (forall (x : in_t), is_lossless (dout x)) => islossless RO.sample. + + lemma FRO_sample_ll: (forall (x : in_t), is_lossless (dout x)) => islossless FRO.sample. + + module type RM_Distinguisher(G : ROmap) = { + proc distinguish(_ : d_in_t) : d_out_t {G.get, G.sample, G.restrK} + } + + module MainRM(D : RM_Distinguisher, RO0 : ROmap) = { + proc distinguish(x : d_in_t) : d_out_t = { + var r : d_out_t; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + lemma fcoll_bound ['rT]: + forall (f : out_t -> 'rT) (Pc : real), + 0%r <= Pc => + (forall (x1 x2 : in_t) (y : 'rT), y \in dmap (dout x1) f => mu1 (dmap (dout x2) f) y <= Pc) => + forall (q : int), + 0 <= q => + forall (D(G : ROmap) <: RM_Distinguisher{+all mem, -RO} ) &m (z : d_in_t), + Pr[MainRM(D, RO).distinguish(z) @ &m : (fcoll f RO.m)%SmtMap /\ (fsize RO.m)%SmtMap <= q] <= + (q * (q - 1))%r / 2%r * Pc. + + theory FullEager. + abstract theory EagerCore. + axiom dout_ll: forall (x : in_t), is_lossless (dout x). + + module type Orcl = { + proc f(x : in_t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : in_t list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : in_t list, l2 : in_t list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : in_t, l : in_t list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : in_t, t2 : in_t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : in_t, t2 : in_t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : in_t list) (i : in_t) (s2 : in_t list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : in_t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : in_t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : in_t, y : out_t) : unit = FRO.set + + proc rem(x : in_t) : unit = FRO.rem + + proc sample(x : in_t) : unit = FRO.sample + + proc queried(x : in_t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (in_t, out_t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : in_t) (mx1 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : in_t) (mx1 mx2 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : in_t) (r1 : out_t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + end FullEager. + + abstract theory FinEager. + abstract theory EagerCore. + axiom dout_ll: forall (x : in_t), is_lossless (dout x). + + module type Orcl = { + proc f(x : in_t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : in_t list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : in_t list, l2 : in_t list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : in_t, l : in_t list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : in_t, t2 : in_t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : in_t, t2 : in_t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : in_t list) (i : in_t) (s2 : in_t list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : in_t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : in_t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : in_t, y : out_t) : unit = FRO.set + + proc rem(x : in_t) : unit = FRO.rem + + proc sample(x : in_t) : unit = FRO.sample + + proc queried(x : in_t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (in_t, out_t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : in_t) (mx1 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : in_t) (mx1 mx2 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : in_t) (r1 : out_t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + theory FinFrom. + type t = in_t. + + op enum : t list. + + op card : int = size enum. + + axiom enum_spec: forall (x : t), count (pred1 x) enum = 1. + + lemma enumP: forall (x : t), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : t list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinFrom. + + module FinRO = { + proc rem(x : in_t) : unit = RO.rem + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc init() : unit = { + var l : FinFrom.t list; + + l <- FinFrom.enum; + RO.init(); + while (l <> []){ + RO.sample(head witness l); + l <- behead l; + } + } + + proc get(x : in_t) : out_t = { + return oget (RO.m.[x])%SmtMap; + } + + proc sample(x : in_t) : unit = {} + }. + + module type FinRO_Distinguisher(G : RO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.sample} + } + + lemma RO_FinRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ MainD(D, RO).distinguish ~ MainD(D, FinRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_RO_FinRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ) &m (x : d_in_t) (p : + d_out_t -> bool), + Pr[MainD(D, RO).distinguish(x) @ &m : p res] = Pr[MainD(D, FinRO).distinguish(x) @ &m : p res]. + + theory MUniFinFun. + op mfun ['u] (d : in_t -> 'u distr) (f : in_t -> 'u) : real = + (big predT (fun (x : in_t) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma mfunE ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mfun d f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + lemma isdistr_dfun ['u]: forall (d : in_t -> 'u distr), isdistr (mfun d). + + op dfun ['u] (d : in_t -> 'u distr) : (in_t -> 'u) distr = mk (mfun d). + + lemma dfun1E ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mu1 (dfun d) f = (big predT (fun (x : in_t) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun1E_djoin ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mu1 (dfun d) f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + op tofun ['u] (us : 'u list) (x : in_t) : 'u = nth witness us (index x FinFrom.enum). + + op offun ['u] (f : in_t -> 'u) : 'u list = map f FinFrom.enum. + + lemma offunK ['u]: cancel offun tofun. + + lemma tofunK ['u]: forall (us : 'u list), size us = FinFrom.card => offun (tofun us) = us. + + lemma dfun_dmap ['u]: forall (d : in_t -> 'u distr), dfun d = dmap (djoinmap d FinFrom.enum) tofun. + + lemma dfun_supp ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), f \in dfun d <=> forall (x : in_t), f x \in d x. + + lemma dfun_fu ['u]: forall (d : in_t -> 'u distr), (forall (x : in_t), is_full (d x)) => is_full (dfun d). + + lemma dfun_uni ['u]: + forall (d : in_t -> 'u distr), (forall (x : in_t), is_uniform (d x)) => is_uniform (dfun d). + + lemma dfun_funi ['u]: + forall (d : in_t -> 'u distr), + (forall (x : in_t), is_uniform (d x)) => (forall (x : in_t), is_full (d x)) => is_funiform (dfun d). + + lemma dfunE_djoin ['u]: + forall (du : in_t -> 'u distr) (E : (in_t -> 'u) -> bool), + mu (dfun du) E = mu (djoinmap du FinFrom.enum) (E \o tofun). + + lemma dfunE ['u]: + forall (du : in_t -> 'u distr) (p : in_t -> 'u -> bool), + mu (dfun du) (fun (f : in_t -> 'u) => forall (x : in_t), p x (f x)) = + (big predT (fun (x : in_t) => mu (du x) (p x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma weight_dfun ['u]: + forall (df : in_t -> 'u distr), + weight (dfun df) = (big predT (fun (x : in_t) => weight (df x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun_ll ['u]: + forall (d : in_t -> 'u distr), (forall (x : in_t), is_lossless (d x)) => is_lossless (dfun d). + + lemma dlet_dfun ['u, 'v]: + forall (d1 : in_t -> 'u distr) (d2 : in_t -> 'u -> 'v distr), + dlet (dfun d1) (fun (f1 : in_t -> 'u) => dfun (fun (x : in_t) => d2 x (f1 x))) = + dfun (fun (x : in_t) => dlet (d1 x) (fun (x1 : 'u) => d2 x x1)). + + lemma dfun_unit ['u]: forall (f : in_t -> 'u), dfun (fun (x : in_t) => dunit (f x)) = dunit f. + + lemma dmap_dfun ['u, 'v]: + forall (d : in_t -> 'u distr) (F : in_t -> 'u -> 'v), + dmap (dfun d) (fun (f : in_t -> 'u) (x : in_t) => F x (f x)) = + dfun (fun (x : in_t) => dmap (d x) (fun (fx : 'u) => F x fx)). + + lemma dfun1E_fix1 ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t) (f : in_t -> 'u), + mu1 (dfun d) f = mu1 (d x0) (f x0) * mu1 (dfun d.[x0 <- dunit (f x0)]) f. + + lemma dlet_dfun_fupdate_ll ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t) (v : 'u), + dlet (dfun d.[x0 <- dunit v]) (fun (f : in_t -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + dfun d. + + lemma dfunE_dlet_fix1 ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t), dfun d = dlet (d x0) (fun (v : 'u) => dfun d.[x0 <- dunit v]). + + lemma dlet_dfun_update ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t), + dlet (dfun d) (fun (f : in_t -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + (weight (d x0) \cdot dfun d). + + lemma dfun_projE ['u]: + forall (df : in_t -> 'u distr) (x : in_t), + dmap (dfun df) (fun (f : in_t -> 'u) => f x) = (weight (dfun df) / weight (df x) \cdot df x). + + lemma eq_from_dfun_proj_eq ['u]: + forall (df1 df2 : in_t -> 'u distr), + (forall (x : in_t), is_lossless (df1 x)) => + (forall (x : in_t), is_lossless (df2 x)) => + (forall (x : in_t), + dmap (dfun df1) (fun (f : in_t -> 'u) => f x) = dmap (dfun df2) (fun (f : in_t -> 'u) => f x)) => + df1 == df2. + + lemma dfun_prod1E ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr) (f : + in_t -> 'u) (g : in_t -> 'v), + mu1 (dfun (fun (x : in_t) => df x `*` dg x)) (fun (x : in_t) => (f x, g x)) = + mu1 (dfun df) f * mu1 (dfun dg) g. + + lemma dprod_funE ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr), + dfun df `*` dfun dg = + dmap (dfun (fun (x : in_t) => df x `*` dg x)) + (fun (fg : in_t -> 'u * 'v) => ((fun (p : 'u * 'v) => p.`1) \o fg, (fun (p : 'u * 'v) => p.`2) \o fg)). + + lemma dfun_prodE ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr), + dfun (fun (x : in_t) => df x `*` dg x) = + dmap (dfun df `*` dfun dg) (fun (fg : (in_t -> 'u) * (in_t -> 'v)) (x : in_t) => (fg.`1 x, fg.`2 x)). + + lemma dfun_condE ['u]: + forall (X : in_t -> bool) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dmap (dfun dt `*` dfun df) + (fun (tf : (in_t -> 'u) * (in_t -> 'u)) (x : in_t) => if X x then tf.`1 x else tf.`2 x) = + dfun (fun (x : in_t) => if X x then dt x else df x). + + lemma dlet_dfun_cond ['u]: + forall (dX : in_t -> bool distr) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dX x)) => + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dlet (dfun dX) (fun (X : in_t -> bool) => dfun (fun (x : in_t) => if X x then dt x else df x)) = + dfun (fun (x : in_t) => dlet (dX x) (fun (b : bool) => if b then dt x else df x)). + + lemma dfun_dcondE ['u]: + forall (dX : in_t -> bool distr) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dX x)) => + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : in_t -> bool) => + dmap (dfun dt `*` dfun df) + (fun (tf : (in_t -> 'u) * (in_t -> 'u)) (x : in_t) => if X x then tf.`1 x else tf.`2 x)) = + dfun (fun (x : in_t) => (mu1 (dX x) true \cdot dt x) + (mu1 (dX x) false \cdot df x)). + end MUniFinFun. + + module FunRO = { + var f : in_t -> out_t + + proc init() : unit = { + FunRO.f <$ (dfun dout)%MUniFinFun; + } + + proc get(x : in_t) : out_t = { + return FunRO.f x; + } + + proc set(x : in_t, y : out_t) : unit = { + FunRO.f <- fun (z : in_t) => if z = x then y else FunRO.f z; + } + + proc sample(x : in_t) : unit = {} + + proc rem(x : in_t) : unit = {} + }. + + lemma FinRO_FunRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ), + equiv[ MainD(D, FinRO).distinguish ~ MainD(D, FunRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_FinRO_FunRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ) &m (x : d_in_t) + (p : d_out_t -> bool), + Pr[MainD(D, FinRO).distinguish(x) @ &m : p res] = Pr[MainD(D, FunRO).distinguish(x) @ &m : p res]. + end FinEager. + end RO_SMP. + + module type SMP_RO = { + proc get(x : RO_SMP.in_t) : RO_SMP.out_t {} + } + + module type Sampler(O : SMP_RO) = { + proc sampleA(sd : W8.t Array32.t) : polymat {O.get} + + proc sampleAT(sd : W8.t Array32.t) : polymat {O.get} + } + + module type PSampler = { + proc sampleA(sd : W8.t Array32.t) : polymat {} + + proc sampleAT(sd : W8.t Array32.t) : polymat {} + } + + module type SAdv_T(O : SMP_RO) = { + proc interact(sd : W8.t Array32.t, t : polyvec) : unit {O.get} + + proc guess(uv : polyvec * poly) : bool {O.get} + } + + module MLWE_SMP(Adv : SAdv_T, S : Sampler, O : RO_SMP.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var _A : polymat; + var u0 : polyvec; + var u1 : polyvec; + var t : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + O.init(); + sd <$ srand; + t <$ duni; + Adv(O).interact(sd, t); + if (tr) + _A <@ S(O).sampleAT(sd); + else + _A <@ S(O).sampleA(sd); + s <$ dshort; + e <$ dshort; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ Adv(O).guess(if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + end MLWE_SMP. + + theory SMP_vs_ROM. + theory MLWE_SMP. + theory RO_SMP. + type in_t = W8.t Array32.t. + + type out_t = polymat. + + op dout (_ : W8.t Array32.t) : polymat distr = duni_matrix. + + type d_in_t = bool. + + type d_out_t = bool. + + module type RO = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + } + + module type RO_Distinguisher(G : RO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.rem, G.sample} + } + + module MainD(D : RO_Distinguisher, RO0 : RO) = { + proc distinguish(x : d_in_t) : d_out_t = { + var r : d_out_t; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + module type ROmap = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + + proc restrK() : (in_t, out_t) SmtMap.fmap {} + } + + module type FRO = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + + proc queried(x : in_t, f : PROM.flag) : bool {} + + proc allKnown() : (in_t, out_t) SmtMap.fmap {} + } + + module type FRO_Distinguisher(G : FRO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.rem, G.sample, G.queried, G.allKnown} + } + + abstract theory MkRO. + module RO = { + var m : (in_t, out_t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : in_t, y : out_t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : in_t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + RO.get(x); + } + + proc restrK() : (in_t, out_t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : in_t) : out_t = RO.get + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc rem(x : in_t) : unit = RO.rem + + proc sample(x : in_t) : unit = {} + }. + end MkRO. + + module RO = { + var m : (in_t, out_t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : in_t, y : out_t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : in_t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + RO.get(x); + } + + proc restrK() : (in_t, out_t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : in_t) : out_t = RO.get + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc rem(x : in_t) : unit = RO.rem + + proc sample(x : in_t) : unit = {} + }. + + module FRO = { + var m : (in_t, out_t * PROM.flag) SmtMap.fmap + + proc init() : unit = { + FRO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \in FRO.m)%SmtMap) + r <- (oget (FRO.m.[x])%SmtMap).`1; + FRO.m.[x] <- (r, PROM.Known); + + return r; + } + + proc set(x : in_t, y : out_t) : unit = { + FRO.m.[x] <- (y, PROM.Known); + } + + proc rem(x : in_t) : unit = { + FRO.m <- (rem FRO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + if ((x \notin FRO.m)%SmtMap) + FRO.m.[x] <- (c, PROM.Unknown); + } + + proc queried(x : in_t, f : PROM.flag) : bool = { + return (x \in FRO.m)%SmtMap /\ ((FRO.m.[x])%SmtMap \is f)%PROM; + } + + proc allKnown() : (in_t, out_t) SmtMap.fmap = { + return (restr PROM.Known FRO.m)%SmtMap; + } + }. + + lemma RO_FRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_get: + equiv[ RO.get ~ FRO.get : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> ={res} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_sample: + equiv[ RO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(RO).distinguish ~ D(FRO).distinguish : + ={arg, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_init_ll: islossless RO.init. + + lemma FRO_init_ll: islossless FRO.init. + + lemma FRO_in_dom_ll: islossless FRO.queried. + + lemma FRO_restrK_ll: islossless FRO.allKnown. + + lemma RO_set_ll: islossless RO.set. + + lemma FRO_set_ll: islossless FRO.set. + + lemma RO_get_ll: (forall (x : in_t), is_lossless (dout x)) => islossless RO.get. + + lemma FRO_get_ll: (forall (x : in_t), is_lossless (dout x)) => islossless FRO.get. + + lemma RO_sample_ll: (forall (x : in_t), is_lossless (dout x)) => islossless RO.sample. + + lemma FRO_sample_ll: (forall (x : in_t), is_lossless (dout x)) => islossless FRO.sample. + + module type RM_Distinguisher(G : ROmap) = { + proc distinguish(_ : d_in_t) : d_out_t {G.get, G.sample, G.restrK} + } + + module MainRM(D : RM_Distinguisher, RO0 : ROmap) = { + proc distinguish(x : d_in_t) : d_out_t = { + var r : d_out_t; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + lemma fcoll_bound ['rT]: + forall (f : out_t -> 'rT) (Pc : real), + 0%r <= Pc => + (forall (x1 x2 : in_t) (y : 'rT), y \in dmap (dout x1) f => mu1 (dmap (dout x2) f) y <= Pc) => + forall (q : int), + 0 <= q => + forall (D(G : ROmap) <: RM_Distinguisher{+all mem, -RO} ) &m (z : d_in_t), + Pr[MainRM(D, RO).distinguish(z) @ &m : (fcoll f RO.m)%SmtMap /\ (fsize RO.m)%SmtMap <= q] <= + (q * (q - 1))%r / 2%r * Pc. + + theory FullEager. + abstract theory EagerCore. + axiom dout_ll: forall (x : in_t), is_lossless (dout x). + + module type Orcl = { + proc f(x : in_t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : in_t list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : in_t list, l2 : in_t list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : in_t, l : in_t list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : in_t, t2 : in_t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : in_t, t2 : in_t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : in_t list) (i : in_t) (s2 : in_t list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : in_t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : in_t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : in_t, y : out_t) : unit = FRO.set + + proc rem(x : in_t) : unit = FRO.rem + + proc sample(x : in_t) : unit = FRO.sample + + proc queried(x : in_t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (in_t, out_t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : in_t) (mx1 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : in_t) (mx1 mx2 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : in_t) (r1 : out_t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + end FullEager. + + abstract theory FinEager. + abstract theory EagerCore. + axiom dout_ll: forall (x : in_t), is_lossless (dout x). + + module type Orcl = { + proc f(x : in_t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : in_t list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : in_t list, l2 : in_t list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : in_t, l : in_t list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : in_t, t2 : in_t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : in_t, t2 : in_t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : in_t list) (i : in_t) (s2 : in_t list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : in_t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : in_t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : in_t, y : out_t) : unit = FRO.set + + proc rem(x : in_t) : unit = FRO.rem + + proc sample(x : in_t) : unit = FRO.sample + + proc queried(x : in_t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (in_t, out_t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : in_t) (mx1 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : in_t) (mx1 mx2 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : in_t) (r1 : out_t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + theory FinFrom. + type t = in_t. + + op enum : t list. + + op card : int = size enum. + + axiom enum_spec: forall (x : t), count (pred1 x) enum = 1. + + lemma enumP: forall (x : t), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : t list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinFrom. + + module FinRO = { + proc rem(x : in_t) : unit = RO.rem + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc init() : unit = { + var l : FinFrom.t list; + + l <- FinFrom.enum; + RO.init(); + while (l <> []){ + RO.sample(head witness l); + l <- behead l; + } + } + + proc get(x : in_t) : out_t = { + return oget (RO.m.[x])%SmtMap; + } + + proc sample(x : in_t) : unit = {} + }. + + module type FinRO_Distinguisher(G : RO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.sample} + } + + lemma RO_FinRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ MainD(D, RO).distinguish ~ MainD(D, FinRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_RO_FinRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ) &m (x : d_in_t) (p : + d_out_t -> bool), + Pr[MainD(D, RO).distinguish(x) @ &m : p res] = Pr[MainD(D, FinRO).distinguish(x) @ &m : p res]. + + theory MUniFinFun. + op mfun ['u] (d : in_t -> 'u distr) (f : in_t -> 'u) : real = + (big predT (fun (x : in_t) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma mfunE ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mfun d f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + lemma isdistr_dfun ['u]: forall (d : in_t -> 'u distr), isdistr (mfun d). + + op dfun ['u] (d : in_t -> 'u distr) : (in_t -> 'u) distr = mk (mfun d). + + lemma dfun1E ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mu1 (dfun d) f = (big predT (fun (x : in_t) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun1E_djoin ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mu1 (dfun d) f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + op tofun ['u] (us : 'u list) (x : in_t) : 'u = nth witness us (index x FinFrom.enum). + + op offun ['u] (f : in_t -> 'u) : 'u list = map f FinFrom.enum. + + lemma offunK ['u]: cancel offun tofun. + + lemma tofunK ['u]: forall (us : 'u list), size us = FinFrom.card => offun (tofun us) = us. + + lemma dfun_dmap ['u]: forall (d : in_t -> 'u distr), dfun d = dmap (djoinmap d FinFrom.enum) tofun. + + lemma dfun_supp ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), f \in dfun d <=> forall (x : in_t), f x \in d x. + + lemma dfun_fu ['u]: + forall (d : in_t -> 'u distr), (forall (x : in_t), is_full (d x)) => is_full (dfun d). + + lemma dfun_uni ['u]: + forall (d : in_t -> 'u distr), (forall (x : in_t), is_uniform (d x)) => is_uniform (dfun d). + + lemma dfun_funi ['u]: + forall (d : in_t -> 'u distr), + (forall (x : in_t), is_uniform (d x)) => (forall (x : in_t), is_full (d x)) => is_funiform (dfun d). + + lemma dfunE_djoin ['u]: + forall (du : in_t -> 'u distr) (E : (in_t -> 'u) -> bool), + mu (dfun du) E = mu (djoinmap du FinFrom.enum) (E \o tofun). + + lemma dfunE ['u]: + forall (du : in_t -> 'u distr) (p : in_t -> 'u -> bool), + mu (dfun du) (fun (f : in_t -> 'u) => forall (x : in_t), p x (f x)) = + (big predT (fun (x : in_t) => mu (du x) (p x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma weight_dfun ['u]: + forall (df : in_t -> 'u distr), + weight (dfun df) = (big predT (fun (x : in_t) => weight (df x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun_ll ['u]: + forall (d : in_t -> 'u distr), (forall (x : in_t), is_lossless (d x)) => is_lossless (dfun d). + + lemma dlet_dfun ['u, 'v]: + forall (d1 : in_t -> 'u distr) (d2 : in_t -> 'u -> 'v distr), + dlet (dfun d1) (fun (f1 : in_t -> 'u) => dfun (fun (x : in_t) => d2 x (f1 x))) = + dfun (fun (x : in_t) => dlet (d1 x) (fun (x1 : 'u) => d2 x x1)). + + lemma dfun_unit ['u]: forall (f : in_t -> 'u), dfun (fun (x : in_t) => dunit (f x)) = dunit f. + + lemma dmap_dfun ['u, 'v]: + forall (d : in_t -> 'u distr) (F : in_t -> 'u -> 'v), + dmap (dfun d) (fun (f : in_t -> 'u) (x : in_t) => F x (f x)) = + dfun (fun (x : in_t) => dmap (d x) (fun (fx : 'u) => F x fx)). + + lemma dfun1E_fix1 ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t) (f : in_t -> 'u), + mu1 (dfun d) f = mu1 (d x0) (f x0) * mu1 (dfun d.[x0 <- dunit (f x0)]) f. + + lemma dlet_dfun_fupdate_ll ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t) (v : 'u), + dlet (dfun d.[x0 <- dunit v]) (fun (f : in_t -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + dfun d. + + lemma dfunE_dlet_fix1 ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t), + dfun d = dlet (d x0) (fun (v : 'u) => dfun d.[x0 <- dunit v]). + + lemma dlet_dfun_update ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t), + dlet (dfun d) (fun (f : in_t -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + (weight (d x0) \cdot dfun d). + + lemma dfun_projE ['u]: + forall (df : in_t -> 'u distr) (x : in_t), + dmap (dfun df) (fun (f : in_t -> 'u) => f x) = (weight (dfun df) / weight (df x) \cdot df x). + + lemma eq_from_dfun_proj_eq ['u]: + forall (df1 df2 : in_t -> 'u distr), + (forall (x : in_t), is_lossless (df1 x)) => + (forall (x : in_t), is_lossless (df2 x)) => + (forall (x : in_t), + dmap (dfun df1) (fun (f : in_t -> 'u) => f x) = dmap (dfun df2) (fun (f : in_t -> 'u) => f x)) => + df1 == df2. + + lemma dfun_prod1E ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr) (f : + in_t -> 'u) (g : in_t -> 'v), + mu1 (dfun (fun (x : in_t) => df x `*` dg x)) (fun (x : in_t) => (f x, g x)) = + mu1 (dfun df) f * mu1 (dfun dg) g. + + lemma dprod_funE ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr), + dfun df `*` dfun dg = + dmap (dfun (fun (x : in_t) => df x `*` dg x)) + (fun (fg : in_t -> 'u * 'v) => + ((fun (p : 'u * 'v) => p.`1) \o fg, (fun (p : 'u * 'v) => p.`2) \o fg)). + + lemma dfun_prodE ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr), + dfun (fun (x : in_t) => df x `*` dg x) = + dmap (dfun df `*` dfun dg) (fun (fg : (in_t -> 'u) * (in_t -> 'v)) (x : in_t) => (fg.`1 x, fg.`2 x)). + + lemma dfun_condE ['u]: + forall (X : in_t -> bool) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dmap (dfun dt `*` dfun df) + (fun (tf : (in_t -> 'u) * (in_t -> 'u)) (x : in_t) => if X x then tf.`1 x else tf.`2 x) = + dfun (fun (x : in_t) => if X x then dt x else df x). + + lemma dlet_dfun_cond ['u]: + forall (dX : in_t -> bool distr) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dX x)) => + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dlet (dfun dX) (fun (X : in_t -> bool) => dfun (fun (x : in_t) => if X x then dt x else df x)) = + dfun (fun (x : in_t) => dlet (dX x) (fun (b : bool) => if b then dt x else df x)). + + lemma dfun_dcondE ['u]: + forall (dX : in_t -> bool distr) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dX x)) => + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : in_t -> bool) => + dmap (dfun dt `*` dfun df) + (fun (tf : (in_t -> 'u) * (in_t -> 'u)) (x : in_t) => if X x then tf.`1 x else tf.`2 x)) = + dfun (fun (x : in_t) => (mu1 (dX x) true \cdot dt x) + (mu1 (dX x) false \cdot df x)). + end MUniFinFun. + + module FunRO = { + var f : in_t -> out_t + + proc init() : unit = { + FunRO.f <$ (dfun dout)%MUniFinFun; + } + + proc get(x : in_t) : out_t = { + return FunRO.f x; + } + + proc set(x : in_t, y : out_t) : unit = { + FunRO.f <- fun (z : in_t) => if z = x then y else FunRO.f z; + } + + proc sample(x : in_t) : unit = {} + + proc rem(x : in_t) : unit = {} + }. + + lemma FinRO_FunRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ), + equiv[ MainD(D, FinRO).distinguish ~ MainD(D, FunRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_FinRO_FunRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ) &m (x : d_in_t) + (p : d_out_t -> bool), + Pr[MainD(D, FinRO).distinguish(x) @ &m : p res] = Pr[MainD(D, FunRO).distinguish(x) @ &m : p res]. + end FinEager. + end RO_SMP. + + module type SMP_RO = { + proc get(x : RO_SMP.in_t) : RO_SMP.out_t {} + } + + module type Sampler(O : SMP_RO) = { + proc sampleA(sd : W8.t Array32.t) : polymat {O.get} + + proc sampleAT(sd : W8.t Array32.t) : polymat {O.get} + } + + module type PSampler = { + proc sampleA(sd : W8.t Array32.t) : polymat {} + + proc sampleAT(sd : W8.t Array32.t) : polymat {} + } + + module type SAdv_T(O : SMP_RO) = { + proc interact(sd : W8.t Array32.t, t : polyvec) : unit {O.get} + + proc guess(uv : polyvec * poly) : bool {O.get} + } + + module MLWE_SMP(Adv : SAdv_T, S : Sampler, O : RO_SMP.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var _A : polymat; + var u0 : polyvec; + var u1 : polyvec; + var t : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + O.init(); + sd <$ srand; + t <$ duni; + Adv(O).interact(sd, t); + if (tr) + _A <@ S(O).sampleAT(sd); + else + _A <@ S(O).sampleA(sd); + s <$ dshort; + e <$ dshort; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ Adv(O).guess(if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + end MLWE_SMP. + + module S(H : MLWE_SMP.SMP_RO) = { + proc sampleA(sd : W8.t Array32.t) : polymat = { + var _A : MLWE_SMP.RO_SMP.out_t; + + _A <@ H.get(sd); + + return _A; + } + + proc sampleAT(sd : W8.t Array32.t) : polymat = { + var _A : MLWE_SMP.RO_SMP.out_t; + + _A <@ H.get(sd); + + return (trmx _A)%Matrix_.Matrix; + } + }. + + module BS(Adv : MLWE_SMP.SAdv_T, S0 : MLWE_SMP.Sampler, O : MLWE_SMP.SMP_RO) = { + proc guess(sd : W8.t Array32.t, t : polyvec, uv : polyvec * poly) : bool = { + var b : bool; + + Adv(O).interact(sd, t); + b <@ Adv(O).guess(uv); + + return b; + } + }. + + module MLWE_SMPs(Adv : MLWE_SMP.SAdv_T, S0 : MLWE_SMP.Sampler, O : MLWE_ROM.RO_H.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var _A : polymat; + var u0 : polyvec; + var u1 : polyvec; + var t : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + O.init(); + sd <$ srand; + t <$ duni; + O.sample(sd); + Adv(O).interact(sd, t); + if (tr) + _A <@ S0(O).sampleAT(sd); + else + _A <@ S0(O).sampleA(sd); + s <$ dshort; + e <$ dshort; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ Adv(O).guess(if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + module MLWE_ROs(Adv : MLWE_ROM.ROAdv_T, O : MLWE_ROM.RO_H.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var _A : MLWE_ROM.RO_H.out_t; + var u0 : polyvec; + var u1 : polyvec; + var t : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + O.init(); + sd <$ srand; + s <$ dshort; + e <$ dshort; + O.sample(sd); + _A <@ O.get(sd); + _A <- if tr then (trmx _A)%Matrix_.Matrix else _A; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + t <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ Adv(O).guess(sd, t, if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + module DLeftAux(A : MLWE_SMP.SAdv_T, O : MLWE_ROM.RO_H.RO) = { + proc run(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var t : polyvec; + var _A : MLWE_ROM.RO_H.out_t; + var s : polyvec; + var e : polyvec; + var u0 : polyvec; + var u1 : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + sd <$ srand; + s <$ dshort; + e <$ dshort; + O.sample(sd); + _A <@ O.get(sd); + _A <- if tr then (trmx _A)%Matrix_.Matrix else _A; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + t <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ BS(A, S, O).guess(sd, t, if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + module DLeft(A : MLWE_SMP.SAdv_T, O : MLWE_ROM.RO_H.RO) = { + proc distinguish(b : bool) : bool = { + var b' : bool; + + b' <@ DLeftAux(A, O).run(false, b); + + return b'; + } + }. + + module DLeftT(A : MLWE_SMP.SAdv_T, O : MLWE_ROM.RO_H.RO) = { + proc distinguish(b : bool) : bool = { + var b' : bool; + + b' <@ DLeftAux(A, O).run(true, b); + + return b'; + } + }. + + module DRightAux(A : MLWE_SMP.SAdv_T, O : MLWE_ROM.RO_H.RO) = { + proc run(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var t : polyvec; + var _A : polymat; + var s : polyvec; + var e : polyvec; + var u0 : polyvec; + var u1 : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + sd <$ srand; + t <$ duni; + O.sample(sd); + A(O).interact(sd, t); + if (tr) + _A <@ S(O).sampleAT(sd); + else + _A <@ S(O).sampleA(sd); + s <$ dshort; + e <$ dshort; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ A(O).guess(if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + module DRight(A : MLWE_SMP.SAdv_T, O : MLWE_ROM.RO_H.RO) = { + proc distinguish(b : bool) : bool = { + var b' : bool; + + b' <@ DRightAux(A, O).run(false, b); + + return b'; + } + }. + + module DRightT(A : MLWE_SMP.SAdv_T, O : MLWE_ROM.RO_H.RO) = { + proc distinguish(b : bool) : bool = { + var b' : bool; + + b' <@ DRightAux(A, O).run(true, b); + + return b'; + } + }. + + lemma MLWE_SMP_equiv_lel: + forall (tr b : bool) &m + (A(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.SAdv_T{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS} + ), + Pr[MLWE_ROs(BS(A, S), MLWE_ROM.RO_H.LRO).main(tr, b) @ &m : res] = + Pr[MLWE_ROs(BS(A, S), MLWE_ROM.RO_H.RO).main(tr, b) @ &m : res]. + + lemma MLWE_SMP_equiv_ler: + forall (tr b : bool) &m + (A(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.SAdv_T{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS} + ), + Pr[MLWE_SMPs(A, S, MLWE_ROM.RO_H.LRO).main(tr, b) @ &m : res] = + Pr[MLWE_SMPs(A, S, MLWE_ROM.RO_H.RO).main(tr, b) @ &m : res]. + + lemma MLWE_SMP_equiv: + forall (b : bool) &m + (A(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.SAdv_T{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS} + ), + Pr[MLWE(MLWE_ROM.MLWE_vs_MLWE_ROM.B(BS(A, S), MLWE_ROM.RO_H.LRO)).main(b) @ &m : res] = + Pr[MLWE_SMP.MLWE_SMP(A, S, MLWE_ROM.RO_H.LRO).main(false, b) @ &m : res]. + + lemma MLWE_SMP_equiv_t: + forall (b : bool) &m + (A(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.SAdv_T{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS} + ), + Pr[MLWE(MLWE_ROM.MLWE_vs_MLWE_ROM.Bt(BS(A, S), MLWE_ROM.RO_H.LRO)).main(b) @ &m : res] = + Pr[MLWE_SMP.MLWE_SMP(A, S, MLWE_ROM.RO_H.LRO).main(true, b) @ &m : res]. + end SMP_vs_ROM. + + theory SMP_vs_ROM_IND. + module type Simulator_t(O : MLWE_ROM.Ideal_RO) = { + proc init() : unit {} + + proc get(x : MLWE_SMP.RO_SMP.in_t) : MLWE_SMP.RO_SMP.out_t {O.get} + } + + module type Distinguisher_t(S : MLWE_SMP.PSampler, H : MLWE_SMP.SMP_RO) = { + proc distinguish(tr : bool, b : bool, sd : W8.t Array32.t) : bool {H.get, S.sampleA, S.sampleAT} + } + + module WIndfReal(D0 : Distinguisher_t, S : MLWE_SMP.Sampler, O : MLWE_SMP.RO_SMP.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var b' : bool; + + O.init(); + sd <$ srand; + b' <@ D0(S(O), O).distinguish(tr, b, sd); + + return b'; + } + }. + + module WIndfIdeal(D0 : Distinguisher_t, Sim : Simulator_t, O : MLWE_ROM.RO_H.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var b' : bool; + + O.init(); + Sim(O).init(); + sd <$ srand; + b' <@ D0(SMP_vs_ROM.S(O), Sim(O)).distinguish(tr, b, sd); + + return b'; + } + }. + + module BS(Adv : MLWE_SMP.SAdv_T, Sim : Simulator_t, H : MLWE_ROM.Ideal_RO) = { + proc guess(sd : W8.t Array32.t, t : polyvec, uv : polyvec * poly) : bool = { + var b : bool; + + Sim(H).init(); + Adv(Sim(H)).interact(sd, t); + b <@ Adv(Sim(H)).guess(uv); + + return b; + } + }. + + module D(A : MLWE_SMP.SAdv_T, S : MLWE_SMP.PSampler, H : MLWE_SMP.SMP_RO) = { + proc distinguish(tr : bool, b : bool, sd : W8.t Array32.t) : bool = { + var _A : polymat; + var t : polyvec; + var s : polyvec; + var e : polyvec; + var u0 : polyvec; + var u1 : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + t <$ duni; + A(H).interact(sd, t); + if (tr) + _A <@ S.sampleAT(sd); + else + _A <@ S.sampleA(sd); + s <$ dshort; + e <$ dshort; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ A(H).guess(if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + module DLeftAux(A : MLWE_SMP.SAdv_T, Sim : Simulator_t, O : MLWE_ROM.RO_H.RO) = { + proc run(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var t : polyvec; + var _A : MLWE_ROM.RO_H.out_t; + var s : polyvec; + var e : polyvec; + var u0 : polyvec; + var u1 : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + sd <$ srand; + s <$ dshort; + e <$ dshort; + O.sample(sd); + _A <@ O.get(sd); + _A <- if tr then (trmx _A)%Matrix_.Matrix else _A; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + t <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ BS(A, Sim, O).guess(sd, t, if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + module DLeft(A : MLWE_SMP.SAdv_T, Sim : Simulator_t, O : MLWE_ROM.RO_H.RO) = { + proc distinguish(b : bool) : bool = { + var b' : bool; + + b' <@ DLeftAux(A, Sim, O).run(false, b); + + return b'; + } + }. + + module DLeftT(A : MLWE_SMP.SAdv_T, Sim : Simulator_t, O : MLWE_ROM.RO_H.RO) = { + proc distinguish(b : bool) : bool = { + var b' : bool; + + b' <@ DLeftAux(A, Sim, O).run(true, b); + + return b'; + } + }. + + module DRightAux(A : MLWE_SMP.SAdv_T, Sim : Simulator_t, O : MLWE_ROM.RO_H.RO) = { + proc run(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var t : polyvec; + var _A : MLWE_ROM.RO_H.out_t; + var s : polyvec; + var e : polyvec; + var u0 : polyvec; + var u1 : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + sd <$ srand; + t <$ duni; + O.sample(sd); + Sim(O).init(); + A(Sim(O)).interact(sd, t); + if (tr) { + _A <@ O.get(sd); + _A <- (trmx _A)%Matrix_.Matrix; + } + else + _A <@ O.get(sd); + s <$ dshort; + e <$ dshort; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ A(Sim(O)).guess(if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + module DRight(A : MLWE_SMP.SAdv_T, Sim : Simulator_t, O : MLWE_ROM.RO_H.RO) = { + proc distinguish(b : bool) : bool = { + var b' : bool; + + b' <@ DRightAux(A, Sim, O).run(false, b); + + return b'; + } + }. + + module DRightT(A : MLWE_SMP.SAdv_T, Sim : Simulator_t, O : MLWE_ROM.RO_H.RO) = { + proc distinguish(b : bool) : bool = { + var b' : bool; + + b' <@ DRightAux(A, Sim, O).run(true, b); + + return b'; + } + }. + + module MLWE_ROs(Adv : MLWE_ROM.ROAdv_T, O : MLWE_ROM.RO_H.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var _A : MLWE_ROM.RO_H.out_t; + var u0 : polyvec; + var u1 : polyvec; + var t : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + O.init(); + sd <$ srand; + s <$ dshort; + e <$ dshort; + O.sample(sd); + _A <@ O.get(sd); + _A <- if tr then (trmx _A)%Matrix_.Matrix else _A; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + t <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ Adv(O).guess(sd, t, if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + module WIndfIdeals(D0 : Distinguisher_t, Sim : Simulator_t, O : MLWE_ROM.RO_H.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var b' : bool; + + O.init(); + Sim(O).init(); + sd <$ srand; + O.sample(sd); + b' <@ D0(SMP_vs_ROM.S(O), Sim(O)).distinguish(tr, b, sd); + + return b'; + } + }. + + lemma MLWE_SMP_equiv_lel: + forall (tr b : bool) &m + (A(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.SAdv_T{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS} + ) + (Sim(O : MLWE_ROM.Ideal_RO) <: + Simulator_t{+all mem, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS, -A} + ), + Pr[MLWE_ROs(BS(A, Sim), MLWE_ROM.RO_H.LRO).main(tr, b) @ &m : res] = + Pr[MLWE_ROs(BS(A, Sim), MLWE_ROM.RO_H.RO).main(tr, b) @ &m : res]. + + lemma MLWE_SMP_equiv_ler: + forall (tr b : bool) &m + (A(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.SAdv_T{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS, -D} + ) + (Sim(O : MLWE_ROM.Ideal_RO) <: + Simulator_t{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS, -D, -A} + ), + Pr[WIndfIdeals(D(A), Sim, MLWE_ROM.RO_H.LRO).main(tr, b) @ &m : res] = + Pr[WIndfIdeals(D(A), Sim, MLWE_ROM.RO_H.RO).main(tr, b) @ &m : res]. + + lemma MLWE_SMP_equiv: + forall (_b : bool) &m + (S(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.Sampler{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_SMP.RO_SMP.LRO, -D} + ) + (A(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.SAdv_T{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_SMP.RO_SMP.LRO, -D, -S} + ) + (Sim(O : MLWE_ROM.Ideal_RO) <: + Simulator_t{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS, -D, -A} + ), + Pr[MLWE_SMP.MLWE_SMP(A, S, MLWE_SMP.RO_SMP.LRO).main(false, _b) @ &m : res] - + Pr[MLWE(MLWE_ROM.MLWE_vs_MLWE_ROM.B(BS(A, Sim), MLWE_ROM.RO_H.LRO)).main(_b) @ &m : res] = + Pr[WIndfReal(D(A), S, MLWE_SMP.RO_SMP.LRO).main(false, _b) @ &m : res] - + Pr[WIndfIdeal(D(A), Sim, MLWE_ROM.RO_H.LRO).main(false, _b) @ &m : res]. + + lemma MLWE_SMP_equiv_t: + forall (_b : bool) &m + (S(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.Sampler{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_SMP.RO_SMP.LRO, -D} + ) + (A(O : MLWE_SMP.SMP_RO) <: + MLWE_SMP.SAdv_T{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_SMP.RO_SMP.LRO, -D, -S} + ) + (Sim(O : MLWE_ROM.Ideal_RO) <: + Simulator_t{+all mem, -MLWE_ROM.RO_H.RO, -MLWE_ROM.RO_H.LRO, -MLWE_ROM.RO_H.FRO, -MLWE_ROM.MLWE_vs_MLWE_ROM.B, -MLWE_ROM.MLWE_vs_MLWE_ROM.Bt, -BS, -D, -A} + ), + Pr[MLWE_SMP.MLWE_SMP(A, S, MLWE_SMP.RO_SMP.LRO).main(true, _b) @ &m : res] - + Pr[MLWE(MLWE_ROM.MLWE_vs_MLWE_ROM.Bt(BS(A, Sim), MLWE_ROM.RO_H.LRO)).main(_b) @ &m : res] = + Pr[WIndfReal(D(A), S, MLWE_SMP.RO_SMP.LRO).main(true, _b) @ &m : res] - + Pr[WIndfIdeal(D(A), Sim, MLWE_ROM.RO_H.LRO).main(true, _b) @ &m : res]. + end SMP_vs_ROM_IND. + end MLWE_. + + type raw_ciphertext = polyvec * poly. + + type raw_pkey = W8.t Array32.t * polyvec. + + type raw_skey = polyvec. + + lemma pk_encodeK: cancel pk_encode pk_decode. + + lemma sk_encodeK: cancel sk_encode sk_decode. + + lemma drand_ll: is_lossless srand. + + hint solve 0 lossless : drand_ll. + + hint solve 0 random : drand_ll. + + lemma drand_uni: is_uniform srand. + + hint solve 0 random : drand_uni. + + lemma drand_fu: is_full srand. + + hint solve 0 random : drand_fu. + + op prg_kg_ideal : (W8.t Array32.t * polyvec * polyvec) distr = + dlet srand + (fun (sd : W8.t Array32.t) => + dlet MLWE_.dshort (fun (s : polyvec) => dmap MLWE_.dshort (fun (e : polyvec) => (sd, s, e)))). + + op prg_enc_ideal : (polyvec * polyvec * poly) distr = + dlet MLWE_.dshort + (fun (r : polyvec) => dlet MLWE_.dshort (fun (e1 : polyvec) => dmap dshort_R (fun (e2 : poly) => (r, e1, e2)))). + + op kg (r : W8.t Array32.t) : publickey * W8.t Array1152.t = + let (sd, s, e) = prg_kg_inner r in + let t = ((H sd *^ s)%MLWE_.Matrix_.Matrix + e)%Vector in (pk_encode (sd, t), sk_encode s). + + op enc (rr : W8.t Array32.t) (pk : publickey) (m : plaintext) : W8.t Array960.t * W8.t Array128.t = + let (sd, t) = pk_decode pk in + let (r, e1, e2) = prg_enc_inner rr in + let u = ((trmx (H sd) *^ r)%MLWE_.Matrix_.Matrix + e1)%Vector in + let v = ((t `<*>` r) &+ e2 &+ m_encode m)%MLWE_ in c_encode (u, v). + + op dec (sk : W8.t Array1152.t) (c : W8.t Array960.t * W8.t Array128.t) : plaintext option = + let (u, v) = c_decode c in Some (m_decode (v &- (sk_decode sk `<*>` u))%MLWE_). + + theory FO_MLKEM. + theory UU. + theory TT. + lemma perm_eq_set ['a, 'b, 'c]: + forall (m : ('a, 'b) SmtMap.fmap) (l : 'a list) (x : 'a) (y : 'b), + perm_eq l ((elems ((fdom m))%SmtMap))%FSet => + ! (x \in l) => perm_eq (l ++ [x]) ((elems ((fdom (m.[x <- y])%SmtMap))%SmtMap))%FSet. + + lemma card_set ['a, 'b]: + forall (m : ('a, 'b) SmtMap.fmap) (x : 'a) (y : 'b) (n : int), + (card ((fdom m))%SmtMap)%FSet = n => (card ((fdom (m.[x <- y])%SmtMap))%SmtMap)%FSet <= n + 1. + + lemma card_set_bnd ['a, 'b]: + forall (m : ('a, 'b) SmtMap.fmap) (x : 'a) (y : 'b), + (card ((fdom (m.[x <- y])%SmtMap))%SmtMap)%FSet <= (card ((fdom m))%SmtMap)%FSet + 1. + + op find ['a, 'b] (f : 'a -> 'b -> bool) (m : ('a, 'b) SmtMap.fmap) : ('a * 'b) option = + let bindings = map (fun (a : 'a) => (a, oget (m.[a])%SmtMap)) ((elems ((fdom m))%SmtMap))%FSet in + let n = find (fun (p : 'a * 'b) => f p.`1 p.`2) bindings in + if n < size bindings then Some (nth witness bindings n) else None. + + lemma find_map ['a, 'b]: + forall (l : 'a list) (f : 'a -> 'b) (P : 'b -> bool), find P (map f l) = find (fun (x : 'a) => P (f x)) l. + + lemma findP_Some ['a, 'b]: + forall (f : 'a -> 'b -> bool) (m : ('a, 'b) SmtMap.fmap) (a : 'a) (b : 'b), + find f m = Some (a, b) => (m.[a])%SmtMap = Some b /\ f a b. + + lemma findP_None ['a, 'b]: + forall (f : 'a -> 'b -> bool) (m : ('a, 'b) SmtMap.fmap), + find f m = None => forall (a : 'a) (b : 'b), (m.[a])%SmtMap = Some b => ! f a b. + + lemma dplaintext_ll: is_lossless srand. + + hint solve 0 lossless : dplaintext_ll. + + hint solve 0 random : dplaintext_ll. + + lemma dplaintext_uni: is_uniform srand. + + hint solve 0 random : dplaintext_uni. + + lemma dplaintext_fu: is_full srand. + + hint solve 0 random : dplaintext_fu. + + theory FinT. + op enum : plaintext list. + + op card : int = size enum. + + axiom enum_spec: forall (x : plaintext), count (pred1 x) enum = 1. + + lemma enumP: forall (x : plaintext), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : plaintext list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinT. + + lemma randd_ll: is_lossless srand. + + hint solve 0 lossless : randd_ll. + + hint solve 0 random : randd_ll. + + theory PKE. + module type Scheme = { + proc kg() : publickey * W8.t Array1152.t {} + + proc enc(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t {} + + proc dec(sk : W8.t Array1152.t, c : W8.t Array960.t * W8.t Array128.t) : plaintext option {} + } + + module type CORR_ADV = { + proc find(pk : publickey, sk : W8.t Array1152.t) : plaintext {} + } + + module Correctness_Adv(S : Scheme, A : CORR_ADV) = { + proc main() : bool = { + var pk : publickey; + var sk : W8.t Array1152.t; + var c : W8.t Array960.t * W8.t Array128.t; + var m : plaintext; + var m' : plaintext option; + + (pk, sk) <@ S.kg(); + m <@ A.find(pk, sk); + c <@ S.enc(pk, m); + m' <@ S.dec(sk, c); + + return m' <> Some m; + } + }. + + module type Adversary = { + proc choose(pk : publickey) : plaintext * plaintext {} + + proc guess(c : W8.t Array960.t * W8.t Array128.t) : bool {} + } + + module CPA(S : Scheme, A : Adversary) = { + proc main() : bool = { + var pk : publickey; + var sk : W8.t Array1152.t; + var m0 : plaintext; + var m1 : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var b : bool; + var b' : bool; + + (pk, sk) <@ S.kg(); + (m0, m1) <@ A.choose(pk); + b <$ {0,1}; + c <@ S.enc(pk, if b then m1 else m0); + b' <@ A.guess(c); + + return b' = b; + } + }. + + module CPA_L(S : Scheme, A : Adversary) = { + proc main() : bool = { + var pk : publickey; + var sk : W8.t Array1152.t; + var m0 : plaintext; + var m1 : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var b' : bool; + + (pk, sk) <@ S.kg(); + (m0, m1) <@ A.choose(pk); + c <@ S.enc(pk, m0); + b' <@ A.guess(c); + + return b'; + } + }. + + module CPA_R(S : Scheme, A : Adversary) = { + proc main() : bool = { + var pk : publickey; + var sk : W8.t Array1152.t; + var m0 : plaintext; + var m1 : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var b' : bool; + + (pk, sk) <@ S.kg(); + (m0, m1) <@ A.choose(pk); + c <@ S.enc(pk, m1); + b' <@ A.guess(c); + + return b'; + } + }. + + theory LorR. + module type A = { + proc main(x : unit) : bool {} + } + + module RandomLR(L : A, R : A) = { + proc main(x : unit) : bool = { + var b : bool; + var r : bool; + + b <$ {0,1}; + if (b) + r <@ L.main(x); + else + r <@ R.main(x); + + return b = r; + } + }. + + lemma pr_AdvLR_AdvRndLR: + forall (L R <: A) &m (x' : unit), + Pr[R.main(x') @ &m : true] = 1%r => + `|Pr[L.main(x') @ &m : res] - Pr[R.main(x') @ &m : res]| = + 2%r * `|Pr[RandomLR(L, R).main(x') @ &m : res] - 1%r / 2%r|. + end LorR. + + lemma pr_CPA_LR: + forall (S <: Scheme) (A <: Adversary{+all mem, -S} ) &m, + islossless S.kg => + islossless S.enc => + islossless A.choose => + islossless A.guess => + `|Pr[CPA_L(S, A).main() @ &m : res] - Pr[CPA_R(S, A).main() @ &m : res]| = + 2%r * `|Pr[CPA(S, A).main() @ &m : res] - 1%r / 2%r|. + + module type OW_CPA_ADV = { + proc find(pk : publickey, c : W8.t Array960.t * W8.t Array128.t) : plaintext option {} + } + + theory MFinT. + op card : int = size FinT.enum. + + lemma enum_spec: forall (x : plaintext), count (pred1 x) FinT.enum = 1. + + lemma enumP: forall (x : plaintext), x \in FinT.enum. + + lemma enum_uniq: uniq FinT.enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : plaintext list), uniq xs => count (mem xs) FinT.enum = size xs. + + lemma is_finite_for: (is_finite_for predT FinT.enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq FinT.enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end MFinT. + + lemma dplaintext_ll: is_lossless srand. + + hint solve 0 lossless : dplaintext_ll. + + hint solve 0 random : dplaintext_ll. + + lemma dplaintext_uni: is_uniform srand. + + hint solve 0 random : dplaintext_uni. + + lemma dplaintext_fu: is_full srand. + + hint solve 0 random : dplaintext_fu. + + op eps_msg : real = 1%r / MFinT.card%r. + + lemma eps_msgE: forall (x : plaintext), mu1 srand x = eps_msg. + + module OW_CPA(S : Scheme, A : OW_CPA_ADV) = { + var pk : publickey + + var sk : W8.t Array1152.t + + var m : plaintext + + var cc : W8.t Array960.t * W8.t Array128.t + + var m' : plaintext option + + proc main_perfect() : bool = { + (OW_CPA.pk, OW_CPA.sk) <@ S.kg(); + OW_CPA.m <$ srand; + OW_CPA.cc <@ S.enc(OW_CPA.pk, OW_CPA.m); + OW_CPA.m' <@ A.find(OW_CPA.pk, OW_CPA.cc); + + return OW_CPA.m' = Some OW_CPA.m; + } + + module O = { + proc pco(sk : W8.t Array1152.t, m : plaintext, c : W8.t Array960.t * W8.t Array128.t) : bool = { + var m'' : plaintext option; + + m'' <@ S.dec(sk, c); + + return m'' = Some m; + } + } + + proc main() : bool = { + var b : bool; + + (OW_CPA.pk, OW_CPA.sk) <@ S.kg(); + OW_CPA.m <$ srand; + OW_CPA.cc <@ S.enc(OW_CPA.pk, OW_CPA.m); + OW_CPA.m' <@ A.find(OW_CPA.pk, OW_CPA.cc); + b <@ OW_CPA(S, A).O.pco(OW_CPA.sk, oget OW_CPA.m', OW_CPA.cc); + + return if OW_CPA.m' = None then false else b; + } + }. + + module BOWp(S : Scheme, A : OW_CPA_ADV) = { + var m'' : plaintext option + + proc find(pk : publickey, sk : W8.t Array1152.t) : plaintext = { + OW_CPA.m <$ srand; + + return OW_CPA.m; + } + + proc main() : bool = { + var pk : publickey; + var sk : W8.t Array1152.t; + + (pk, sk) <@ S.kg(); + BOWp(S, A).find(pk, sk); + OW_CPA.cc <@ S.enc(pk, OW_CPA.m); + OW_CPA.m' <@ A.find(pk, OW_CPA.cc); + BOWp.m'' <@ S.dec(sk, OW_CPA.cc); + + return BOWp.m'' <> Some OW_CPA.m; + } + }. + + lemma ow_perfect: + forall (S <: Scheme{+all mem, -OW_CPA, -BOWp} ) (A <: OW_CPA_ADV{+all mem, -OW_CPA, -BOWp, -S} ) &m, + islossless A.find => + islossless S.enc => + islossless S.dec => + `|Pr[OW_CPA(S, A).main() @ &m : res] - Pr[OW_CPA(S, A).main_perfect() @ &m : res]| <= + Pr[Correctness_Adv(S, BOWp(S, A)).main() @ &m : res]. + + module type OWL_CPA_ADV = { + proc find(pk : publickey, c : W8.t Array960.t * W8.t Array128.t) : plaintext list {} + } + + module OWL_CPA(S : Scheme, A : OWL_CPA_ADV) = { + var pk : publickey + + var sk : W8.t Array1152.t + + var m : plaintext + + var cc : W8.t Array960.t * W8.t Array128.t + + var l : plaintext list + + proc main() : bool = { + (OWL_CPA.pk, OWL_CPA.sk) <@ S.kg(); + OWL_CPA.m <$ srand; + OWL_CPA.cc <@ S.enc(OWL_CPA.pk, OWL_CPA.m); + OWL_CPA.l <@ A.find(OWL_CPA.pk, OWL_CPA.cc); + + return OWL_CPA.m \in OWL_CPA.l; + } + }. + + theory OWvsIND. + module Bowl(A : OWL_CPA_ADV) = { + var m0 : plaintext + + var m1 : plaintext + + var pk : publickey + + var l : plaintext list + + proc choose(_pk : publickey) : plaintext * plaintext = { + Bowl.pk <- _pk; + Bowl.m0 <$ srand; + Bowl.m1 <$ srand; + + return (Bowl.m0, Bowl.m1); + } + + proc guess(c : W8.t Array960.t * W8.t Array128.t) : bool = { + var b : bool; + + b <$ {0,1}; + Bowl.l <@ A.find(Bowl.pk, c); + + return + if (Bowl.m0 \in Bowl.l) = (Bowl.m1 \in Bowl.l) then b else if Bowl.m0 \in Bowl.l then false else true; + } + }. + + lemma boundl: + forall (l : plaintext list) (MAX : int), + 0 <= MAX => mu srand (fun (x : plaintext) => size l <= MAX /\ (x \in l)) <= MAX%r * eps_msg. + + pred bad (gB : plaintext list * plaintext * plaintext * publickey) = (gB.`2 \in gB.`1) = (gB.`3 \in gB.`1). + + lemma ow_ind_l: + forall (S <: Scheme{+all mem, -BOWp, -OWL_CPA, -Bowl} ) + (A <: OWL_CPA_ADV{+all mem, -BOWp, -OWL_CPA, -Bowl, -S} ) &m (MAX : int), + 0 <= MAX => + islossless S.kg => + islossless S.enc => + islossless S.dec => + islossless A.find => + hoare[ A.find : true ==> size res <= MAX] => + Pr[OWL_CPA(S, A).main() @ &m : OWL_CPA.m \in OWL_CPA.l] <= + 2%r * (MAX%r * eps_msg + `|Pr[CPA(S, Bowl(A)).main() @ &m : res] - 1%r / 2%r|). + + module BL(A : OW_CPA_ADV) = { + proc find(pk : publickey, c : W8.t Array960.t * W8.t Array128.t) : plaintext list = { + var m' : plaintext option; + + m' <@ A.find(pk, c); + + return if m' = None then [] else [oget m']; + } + }. + + lemma ow_ind: + forall (S <: Scheme{+all mem, -OW_CPA, -BOWp, -OWL_CPA, -Bowl} ) + (A <: OW_CPA_ADV{+all mem, -OW_CPA, -BOWp, -OWL_CPA, -Bowl, -S} ) &m, + islossless S.kg => + islossless S.enc => + islossless S.dec => + islossless A.find => + Pr[OW_CPA(S, A).main() @ &m : res] <= + 2%r * (eps_msg + `|Pr[CPA(S, Bowl(BL(A))).main() @ &m : res] - 1%r / 2%r|) + + Pr[Correctness_Adv(S, BOWp(S, A)).main() @ &m : res]. + end OWvsIND. + end PKE. + + op kg : (publickey * W8.t Array1152.t) distr = dmap srand MLWEPKEHash.kg. + + lemma kg_ll: is_lossless UU.TT.kg. + + hint solve 0 lossless : kg_ll. + + hint solve 0 random : kg_ll. + + module BasePKE = { + proc kg() : publickey * W8.t Array1152.t = { + var kpair : publickey * W8.t Array1152.t; + + kpair <$ UU.TT.kg; + + return kpair; + } + + proc enc(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = { + var r : W8.t Array32.t; + var c : W8.t Array960.t * W8.t Array128.t; + + r <$ srand; + c <- enc r pk m; + + return c; + } + + proc dec(sk : W8.t Array1152.t, c : W8.t Array960.t * W8.t Array128.t) : plaintext option = { + return dec sk c; + } + }. + + theory PKEROM. + type skey = publickey * W8.t Array1152.t. + + theory RO. + module type RO = { + proc init() : unit {} + + proc get(x : plaintext) : W8.t Array32.t {} + + proc set(x : plaintext, y : W8.t Array32.t) : unit {} + + proc rem(x : plaintext) : unit {} + + proc sample(x : plaintext) : unit {} + } + + module type RO_Distinguisher(G : RO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.rem, G.sample} + } + + module MainD(D : RO_Distinguisher, RO0 : RO) = { + proc distinguish(x : unit) : bool = { + var r : bool; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + module type ROmap = { + proc init() : unit {} + + proc get(x : plaintext) : W8.t Array32.t {} + + proc set(x : plaintext, y : W8.t Array32.t) : unit {} + + proc rem(x : plaintext) : unit {} + + proc sample(x : plaintext) : unit {} + + proc restrK() : (plaintext, W8.t Array32.t) SmtMap.fmap {} + } + + module type FRO = { + proc init() : unit {} + + proc get(x : plaintext) : W8.t Array32.t {} + + proc set(x : plaintext, y : W8.t Array32.t) : unit {} + + proc rem(x : plaintext) : unit {} + + proc sample(x : plaintext) : unit {} + + proc queried(x : plaintext, f : PROM.flag) : bool {} + + proc allKnown() : (plaintext, W8.t Array32.t) SmtMap.fmap {} + } + + module type FRO_Distinguisher(G : FRO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.rem, G.sample, G.queried, G.allKnown} + } + + abstract theory MkRO. + module RO = { + var m : (plaintext, W8.t Array32.t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : plaintext) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : plaintext) : unit = { + RO.get(x); + } + + proc restrK() : (plaintext, W8.t Array32.t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : plaintext) : W8.t Array32.t = RO.get + + proc set(x : plaintext, y : W8.t Array32.t) : unit = RO.set + + proc rem(x : plaintext) : unit = RO.rem + + proc sample(x : plaintext) : unit = {} + }. + end MkRO. + + module RO = { + var m : (plaintext, W8.t Array32.t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : plaintext) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : plaintext) : unit = { + RO.get(x); + } + + proc restrK() : (plaintext, W8.t Array32.t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : plaintext) : W8.t Array32.t = RO.get + + proc set(x : plaintext, y : W8.t Array32.t) : unit = RO.set + + proc rem(x : plaintext) : unit = RO.rem + + proc sample(x : plaintext) : unit = {} + }. + + module FRO = { + var m : (plaintext, W8.t Array32.t * PROM.flag) SmtMap.fmap + + proc init() : unit = { + FRO.m <- SmtMap.empty; + } + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \in FRO.m)%SmtMap) + r <- (oget (FRO.m.[x])%SmtMap).`1; + FRO.m.[x] <- (r, PROM.Known); + + return r; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = { + FRO.m.[x] <- (y, PROM.Known); + } + + proc rem(x : plaintext) : unit = { + FRO.m <- (rem FRO.m x)%SmtMap; + } + + proc sample(x : plaintext) : unit = { + var c : W8.t Array32.t; + + c <$ srand; + if ((x \notin FRO.m)%SmtMap) + FRO.m.[x] <- (c, PROM.Unknown); + } + + proc queried(x : plaintext, f : PROM.flag) : bool = { + return (x \in FRO.m)%SmtMap /\ ((FRO.m.[x])%SmtMap \is f)%PROM; + } + + proc allKnown() : (plaintext, W8.t Array32.t) SmtMap.fmap = { + return (restr PROM.Known FRO.m)%SmtMap; + } + }. + + lemma RO_FRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_get: + equiv[ RO.get ~ FRO.get : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> ={res} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_sample: + equiv[ RO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(RO).distinguish ~ D(FRO).distinguish : + ={arg, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_init_ll: islossless RO.init. + + lemma FRO_init_ll: islossless FRO.init. + + lemma FRO_in_dom_ll: islossless FRO.queried. + + lemma FRO_restrK_ll: islossless FRO.allKnown. + + lemma RO_set_ll: islossless RO.set. + + lemma FRO_set_ll: islossless FRO.set. + + lemma RO_get_ll: (forall (_ : plaintext), is_lossless srand) => islossless RO.get. + + lemma FRO_get_ll: (forall (_ : plaintext), is_lossless srand) => islossless FRO.get. + + lemma RO_sample_ll: (forall (_ : plaintext), is_lossless srand) => islossless RO.sample. + + lemma FRO_sample_ll: (forall (_ : plaintext), is_lossless srand) => islossless FRO.sample. + + module type RM_Distinguisher(G : ROmap) = { + proc distinguish(_ : unit) : bool {G.get, G.sample, G.restrK} + } + + module MainRM(D : RM_Distinguisher, RO0 : ROmap) = { + proc distinguish(x : unit) : bool = { + var r : bool; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + lemma fcoll_bound ['rT]: + forall (f : W8.t Array32.t -> 'rT) (Pc : real), + 0%r <= Pc => + (forall (_ _ : plaintext) (y : 'rT), y \in dmap srand f => mu1 (dmap srand f) y <= Pc) => + forall (q : int), + 0 <= q => + forall (D(G : ROmap) <: RM_Distinguisher{+all mem, -RO} ) &m (z : unit), + Pr[MainRM(D, RO).distinguish(z) @ &m : (fcoll f RO.m)%SmtMap /\ (fsize RO.m)%SmtMap <= q] <= + (q * (q - 1))%r / 2%r * Pc. + + theory FullEager. + abstract theory EagerCore. + axiom dout_ll: forall (_ : plaintext), is_lossless srand. + + module type Orcl = { + proc f(x : plaintext) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : plaintext list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : plaintext list, l2 : plaintext list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : plaintext, l : plaintext list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : plaintext list) (i : plaintext) (s2 : plaintext list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : + ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : plaintext), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : plaintext), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = FRO.set + + proc rem(x : plaintext) : unit = FRO.rem + + proc sample(x : plaintext) : unit = FRO.sample + + proc queried(x : plaintext, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (plaintext, W8.t Array32.t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : plaintext) : unit = { + var c : W8.t Array32.t; + + c <$ srand; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: + eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : plaintext) (mx1 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : plaintext) (mx1 mx2 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : plaintext) (r1 : W8.t Array32.t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + end FullEager. + + abstract theory FinEager. + abstract theory EagerCore. + axiom dout_ll: forall (_ : plaintext), is_lossless srand. + + module type Orcl = { + proc f(x : plaintext) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : plaintext list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : plaintext list, l2 : plaintext list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : plaintext, l : plaintext list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : plaintext list) (i : plaintext) (s2 : plaintext list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : + ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : plaintext), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : plaintext), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = FRO.set + + proc rem(x : plaintext) : unit = FRO.rem + + proc sample(x : plaintext) : unit = FRO.sample + + proc queried(x : plaintext, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (plaintext, W8.t Array32.t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : plaintext) : unit = { + var c : W8.t Array32.t; + + c <$ srand; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: + eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : plaintext) (mx1 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : plaintext) (mx1 mx2 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : plaintext) (r1 : W8.t Array32.t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + theory FinFrom. + type t = plaintext. + + op enum : t list. + + op card : int = size enum. + + axiom enum_spec: forall (x : t), count (pred1 x) enum = 1. + + lemma enumP: forall (x : t), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : t list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinFrom. + + module FinRO = { + proc rem(x : plaintext) : unit = RO.rem + + proc set(x : plaintext, y : W8.t Array32.t) : unit = RO.set + + proc init() : unit = { + var l : FinFrom.t list; + + l <- FinFrom.enum; + RO.init(); + while (l <> []){ + RO.sample(head witness l); + l <- behead l; + } + } + + proc get(x : plaintext) : W8.t Array32.t = { + return oget (RO.m.[x])%SmtMap; + } + + proc sample(x : plaintext) : unit = {} + }. + + module type FinRO_Distinguisher(G : RO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.sample} + } + + lemma RO_FinRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ MainD(D, RO).distinguish ~ MainD(D, FinRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_RO_FinRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ) &m (x : unit) (p : + bool -> bool), + Pr[MainD(D, RO).distinguish(x) @ &m : p res] = Pr[MainD(D, FinRO).distinguish(x) @ &m : p res]. + + theory MUniFinFun. + op mfun ['u] (d : plaintext -> 'u distr) (f : plaintext -> 'u) : real = + (big predT (fun (x : plaintext) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma mfunE ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mfun d f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + lemma isdistr_dfun ['u]: forall (d : plaintext -> 'u distr), isdistr (mfun d). + + op dfun ['u] (d : plaintext -> 'u distr) : (plaintext -> 'u) distr = mk (mfun d). + + lemma dfun1E ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mu1 (dfun d) f = + (big predT (fun (x : plaintext) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun1E_djoin ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mu1 (dfun d) f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + op tofun ['u] (us : 'u list) (x : plaintext) : 'u = nth witness us (index x FinFrom.enum). + + op offun ['u] (f : plaintext -> 'u) : 'u list = map f FinFrom.enum. + + lemma offunK ['u]: cancel offun tofun. + + lemma tofunK ['u]: forall (us : 'u list), size us = FinFrom.card => offun (tofun us) = us. + + lemma dfun_dmap ['u]: + forall (d : plaintext -> 'u distr), dfun d = dmap (djoinmap d FinFrom.enum) tofun. + + lemma dfun_supp ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + f \in dfun d <=> forall (x : plaintext), f x \in d x. + + lemma dfun_fu ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_full (d x)) => is_full (dfun d). + + lemma dfun_uni ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_uniform (d x)) => is_uniform (dfun d). + + lemma dfun_funi ['u]: + forall (d : plaintext -> 'u distr), + (forall (x : plaintext), is_uniform (d x)) => + (forall (x : plaintext), is_full (d x)) => is_funiform (dfun d). + + lemma dfunE_djoin ['u]: + forall (du : plaintext -> 'u distr) (E : (plaintext -> 'u) -> bool), + mu (dfun du) E = mu (djoinmap du FinFrom.enum) (E \o tofun). + + lemma dfunE ['u]: + forall (du : plaintext -> 'u distr) (p : plaintext -> 'u -> bool), + mu (dfun du) (fun (f : plaintext -> 'u) => forall (x : plaintext), p x (f x)) = + (big predT (fun (x : plaintext) => mu (du x) (p x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma weight_dfun ['u]: + forall (df : plaintext -> 'u distr), + weight (dfun df) = + (big predT (fun (x : plaintext) => weight (df x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun_ll ['u]: + forall (d : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (d x)) => is_lossless (dfun d). + + lemma dlet_dfun ['u, 'v]: + forall (d1 : plaintext -> 'u distr) (d2 : plaintext -> 'u -> 'v distr), + dlet (dfun d1) (fun (f1 : plaintext -> 'u) => dfun (fun (x : plaintext) => d2 x (f1 x))) = + dfun (fun (x : plaintext) => dlet (d1 x) (fun (x1 : 'u) => d2 x x1)). + + lemma dfun_unit ['u]: + forall (f : plaintext -> 'u), dfun (fun (x : plaintext) => dunit (f x)) = dunit f. + + lemma dmap_dfun ['u, 'v]: + forall (d : plaintext -> 'u distr) (F : plaintext -> 'u -> 'v), + dmap (dfun d) (fun (f : plaintext -> 'u) (x : plaintext) => F x (f x)) = + dfun (fun (x : plaintext) => dmap (d x) (fun (fx : 'u) => F x fx)). + + lemma dfun1E_fix1 ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext) (f : + plaintext -> 'u), mu1 (dfun d) f = mu1 (d x0) (f x0) * mu1 (dfun d.[x0 <- dunit (f x0)]) f. + + lemma dlet_dfun_fupdate_ll ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext) (v : 'u), + dlet (dfun d.[x0 <- dunit v]) + (fun (f : plaintext -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + dfun d. + + lemma dfunE_dlet_fix1 ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext), + dfun d = dlet (d x0) (fun (v : 'u) => dfun d.[x0 <- dunit v]). + + lemma dlet_dfun_update ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext), + dlet (dfun d) (fun (f : plaintext -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + (weight (d x0) \cdot dfun d). + + lemma dfun_projE ['u]: + forall (df : plaintext -> 'u distr) (x : plaintext), + dmap (dfun df) (fun (f : plaintext -> 'u) => f x) = (weight (dfun df) / weight (df x) \cdot df x). + + lemma eq_from_dfun_proj_eq ['u]: + forall (df1 df2 : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (df1 x)) => + (forall (x : plaintext), is_lossless (df2 x)) => + (forall (x : plaintext), + dmap (dfun df1) (fun (f : plaintext -> 'u) => f x) = + dmap (dfun df2) (fun (f : plaintext -> 'u) => f x)) => + df1 == df2. + + lemma dfun_prod1E ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr) (f : + plaintext -> 'u) (g : plaintext -> 'v), + mu1 (dfun (fun (x : plaintext) => df x `*` dg x)) (fun (x : plaintext) => (f x, g x)) = + mu1 (dfun df) f * mu1 (dfun dg) g. + + lemma dprod_funE ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr), + dfun df `*` dfun dg = + dmap (dfun (fun (x : plaintext) => df x `*` dg x)) + (fun (fg : plaintext -> 'u * 'v) => + ((fun (p : 'u * 'v) => p.`1) \o fg, (fun (p : 'u * 'v) => p.`2) \o fg)). + + lemma dfun_prodE ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr), + dfun (fun (x : plaintext) => df x `*` dg x) = + dmap (dfun df `*` dfun dg) + (fun (fg : (plaintext -> 'u) * (plaintext -> 'v)) (x : plaintext) => (fg.`1 x, fg.`2 x)). + + lemma dfun_condE ['u]: + forall (X : plaintext -> bool) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext -> 'u) * (plaintext -> 'u)) (x : plaintext) => + if X x then tf.`1 x else tf.`2 x) = + dfun (fun (x : plaintext) => if X x then dt x else df x). + + lemma dlet_dfun_cond ['u]: + forall (dX : plaintext -> bool distr) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dX x)) => + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : plaintext -> bool) => dfun (fun (x : plaintext) => if X x then dt x else df x)) = + dfun (fun (x : plaintext) => dlet (dX x) (fun (b : bool) => if b then dt x else df x)). + + lemma dfun_dcondE ['u]: + forall (dX : plaintext -> bool distr) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dX x)) => + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : plaintext -> bool) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext -> 'u) * (plaintext -> 'u)) (x : plaintext) => + if X x then tf.`1 x else tf.`2 x)) = + dfun (fun (x : plaintext) => (mu1 (dX x) true \cdot dt x) + (mu1 (dX x) false \cdot df x)). + end MUniFinFun. + + module FunRO = { + var f : plaintext -> W8.t Array32.t + + proc init() : unit = { + FunRO.f <$ (dfun (fun (_ : plaintext) => srand))%MUniFinFun; + } + + proc get(x : plaintext) : W8.t Array32.t = { + return FunRO.f x; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = { + FunRO.f <- fun (z : plaintext) => if z = x then y else FunRO.f z; + } + + proc sample(x : plaintext) : unit = {} + + proc rem(x : plaintext) : unit = {} + }. + + lemma FinRO_FunRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ), + equiv[ MainD(D, FinRO).distinguish ~ MainD(D, FunRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_FinRO_FunRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ) &m (x : unit) + (p : bool -> bool), + Pr[MainD(D, FinRO).distinguish(x) @ &m : p res] = Pr[MainD(D, FunRO).distinguish(x) @ &m : p res]. + end FinEager. + end RO. + + module type Oracle = { + proc init() : unit {} + + proc get(x : plaintext) : W8.t Array32.t {} + } + + module type POracle = { + proc get(x : plaintext) : W8.t Array32.t {} + } + + module type Scheme(H : POracle) = { + proc kg() : publickey * skey {H.get} + + proc enc(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t {H.get} + + proc dec(sk : skey, c : W8.t Array960.t * W8.t Array128.t) : plaintext option {H.get} + } + + module type Adversary(H : POracle) = { + proc choose(pk : publickey) : plaintext * plaintext {H.get} + + proc guess(c : W8.t Array960.t * W8.t Array128.t) : bool {H.get} + } + + module CPA(H : Oracle, S : Scheme, A : Adversary) = { + module A = A(H) + + proc main() : bool = { + var pk : publickey; + var sk : skey; + var m0 : plaintext; + var m1 : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var b : bool; + var b' : bool; + + H.init(); + (pk, sk) <@ S(H).kg(); + (m0, m1) <@ CPA(H, S, A).A.choose(pk); + b <$ {0,1}; + c <@ S(H).enc(pk, if b then m1 else m0); + b' <@ CPA(H, S, A).A.guess(c); + + return b' = b; + } + }. + + module CPA_L(H : Oracle, S : Scheme, A : Adversary) = { + module A = A(H) + + proc main() : bool = { + var pk : publickey; + var sk : skey; + var m0 : plaintext; + var m1 : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var b' : bool; + + H.init(); + (pk, sk) <@ S(H).kg(); + (m0, m1) <@ CPA_L(H, S, A).A.choose(pk); + c <@ S(H).enc(pk, m0); + b' <@ CPA_L(H, S, A).A.guess(c); + + return b'; + } + }. + + module CPA_R(H : Oracle, S : Scheme, A : Adversary) = { + module A = A(H) + + proc main() : bool = { + var pk : publickey; + var sk : skey; + var m0 : plaintext; + var m1 : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var b' : bool; + + H.init(); + (pk, sk) <@ S(H).kg(); + (m0, m1) <@ CPA_R(H, S, A).A.choose(pk); + c <@ S(H).enc(pk, m1); + b' <@ CPA_R(H, S, A).A.guess(c); + + return b'; + } + }. + + theory LorR. + module type A = { + proc main(x : unit) : bool {} + } + + module RandomLR(L : A, R : A) = { + proc main(x : unit) : bool = { + var b : bool; + var r : bool; + + b <$ {0,1}; + if (b) + r <@ L.main(x); + else + r <@ R.main(x); + + return b = r; + } + }. + + lemma pr_AdvLR_AdvRndLR: + forall (L R <: A) &m (x' : unit), + Pr[R.main(x') @ &m : true] = 1%r => + `|Pr[L.main(x') @ &m : res] - Pr[R.main(x') @ &m : res]| = + 2%r * `|Pr[RandomLR(L, R).main(x') @ &m : res] - 1%r / 2%r|. + end LorR. + + lemma pr_CPA_LR: + forall (S(H : POracle) <: Scheme) (H <: Oracle{+all mem, -S} ) + (A(H0 : POracle) <: Adversary{+all mem, -S, -H} ) &m, + islossless S(H).kg => + islossless S(H).enc => + islossless A(H).choose => + islossless A(H).guess => + islossless H.init => + `|Pr[CPA_L(H, S, A).main() @ &m : res] - Pr[CPA_R(H, S, A).main() @ &m : res]| = + 2%r * `|Pr[CPA(H, S, A).main() @ &m : res] - 1%r / 2%r|. + + module type CCA_ORC = { + proc dec(c : W8.t Array960.t * W8.t Array128.t) : plaintext option {} + } + + module type CCA_ADV(H : POracle, O : CCA_ORC) = { + proc choose(pk : publickey) : plaintext * plaintext {O.dec, H.get} + + proc guess(c : W8.t Array960.t * W8.t Array128.t) : bool {O.dec} + } + + module CCA(H : Oracle, S : Scheme, A : CCA_ADV) = { + var cstar : (W8.t Array960.t * W8.t Array128.t) option + + var sk : skey + + module O = { + proc dec(c : W8.t Array960.t * W8.t Array128.t) : plaintext option = { + var m : plaintext option; + + m <- None; + if (Some c <> CCA.cstar) + m <@ S(H).dec(CCA.sk, c); + + return m; + } + } + + module A = A(H, CCA(H, S, A).O) + + proc main() : bool = { + var pk : publickey; + var m0 : plaintext; + var m1 : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var b : bool; + var b' : bool; + + H.init(); + CCA.cstar <- None; + (pk, CCA.sk) <@ S(H).kg(); + (m0, m1) <@ CCA(H, S, A).A.choose(pk); + b <$ {0,1}; + c <@ S(H).enc(pk, if b then m1 else m0); + CCA.cstar <- Some c; + b' <@ CCA(H, S, A).A.guess(c); + + return b' = b; + } + }. + + module type CORR_ADV(H : POracle) = { + proc find(pk : publickey, sk : skey) : plaintext {H.get} + } + + module Correctness_Adv(H : Oracle, S : Scheme, A : CORR_ADV) = { + module A = A(H) + + proc main() : bool = { + var pk : publickey; + var sk : skey; + var c : W8.t Array960.t * W8.t Array128.t; + var m : plaintext; + var m' : plaintext option; + + H.init(); + (pk, sk) <@ S(H).kg(); + m <@ Correctness_Adv(H, S, A).A.find(pk, sk); + c <@ S(H).enc(pk, m); + m' <@ S(H).dec(sk, c); + + return m' <> Some m; + } + }. + + module type VA_ORC = { + proc cvo(c : W8.t Array960.t * W8.t Array128.t) : bool {} + + proc pco(m : plaintext, c : W8.t Array960.t * W8.t Array128.t) : bool {} + } + + module type PCVA_ADV(H : POracle, O : VA_ORC) = { + proc find(pk : publickey, c : W8.t Array960.t * W8.t Array128.t) : plaintext option {O.cvo, O.pco, H.get} + } + + hint solve 0 lossless : . + + hint solve 0 random : . + + module OW_PCVA(H : Oracle, S : Scheme, A : PCVA_ADV) = { + var sk : skey + + var cc : W8.t Array960.t * W8.t Array128.t + + module O = { + proc cvo(c : W8.t Array960.t * W8.t Array128.t) : bool = { + var m : plaintext option; + + m <- None; + if (c <> OW_PCVA.cc) + m <@ S(H).dec(OW_PCVA.sk, c); + + return m <> None; + } + + proc pco(m : plaintext, c : W8.t Array960.t * W8.t Array128.t) : bool = { + var m' : plaintext option; + + m' <@ S(H).dec(OW_PCVA.sk, c); + + return m' = Some m; + } + } + + module A = A(H, OW_PCVA(H, S, A).O) + + proc main() : bool = { + var pk : publickey; + var m : plaintext; + var m' : plaintext option; + var b : bool; + + H.init(); + (pk, OW_PCVA.sk) <@ S(H).kg(); + m <$ srand; + OW_PCVA.cc <@ S(H).enc(pk, m); + m' <@ OW_PCVA(H, S, A).A.find(pk, OW_PCVA.cc); + b <@ OW_PCVA(H, S, A).O.pco(oget m', OW_PCVA.cc); + + return if m' = None then false else b; + } + }. + end PKEROM. + + op qH : int. + + axiom ge0_qH: 0 <= qH. + + op qV : int. + + axiom ge0_qV: 0 <= qV. + + op qP : int. + + axiom ge0_qP: 0 <= qP. + + op qHC : int. + + axiom ge0_qHC: 0 <= qHC. + + module TT(H0 : PKEROM.POracle) = { + proc kg() : publickey * PKEROM.skey = { + var kpair : publickey * W8.t Array1152.t; + + kpair <$ UU.TT.kg; + + return (kpair.`1, kpair); + } + + proc enc(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = { + var r : W8.t Array32.t; + var c : W8.t Array960.t * W8.t Array128.t; + + r <@ H0.get(m); + c <- enc r pk m; + + return c; + } + + proc dec(sk : PKEROM.skey, c : W8.t Array960.t * W8.t Array128.t) : plaintext option = { + var m' : plaintext option; + var r : W8.t Array32.t; + var c' : W8.t Array960.t * W8.t Array128.t; + var rv : plaintext option; + + rv <- None; + m' <- dec sk.`2 c; + if (m' <> None) { + r <@ H0.get(oget m'); + c' <- enc r sk.`1 (oget m'); + rv <- if c = c' then m' else None; + } + + return rv; + } + }. + + module CO1(O : PKEROM.RO.RO) = { + var pk : publickey + + var sk : W8.t Array1152.t + + var counter : int + + var i : int + + var queried : plaintext list + + var bad : bool + + proc get(x : plaintext) : W8.t Array32.t = { + var y : W8.t Array32.t; + + y <- witness; + if (! (x \in CO1.queried)) { + if (size CO1.queried = CO1.i) { + O.sample(x); + CO1.bad <- true; + } + else + y <@ O.get(x); + CO1.queried <- CO1.queried ++ [x]; + } + else + if (find (pred1 x) CO1.queried <> CO1.i) + y <@ O.get(x); + CO1.counter <- CO1.counter + 1; + + return y; + } + }. + + module Correctness_Adv1(O : PKEROM.RO.RO, A : PKEROM.CORR_ADV) = { + module A = A(CO1(O)) + + proc main'(pk : publickey, sk : W8.t Array1152.t, i : int) : plaintext = { + var m : plaintext; + + CO1.i <- i; + O.init(); + CO1.counter <- 0; + CO1.pk <- pk; + CO1.sk <- sk; + CO1.queried <- []; + CO1.bad <- false; + m <@ Correctness_Adv1(O, A).A.find(CO1.pk, (CO1.pk, CO1.sk)); + + return m; + } + + proc main() : unit = { + var m : plaintext; + + (CO1.pk, CO1.sk) <@ BasePKE.kg(); + m <@ Correctness_Adv1(O, A).main'(CO1.pk, CO1.sk, -1); + CO1(O).get(m); + } + }. + + module B(A : PKEROM.CORR_ADV, O : PKEROM.RO.RO) = { + proc find(pk : publickey, sk : W8.t Array1152.t) : plaintext = { + var m : plaintext; + + CO1.i <$ [0..qHC]; + m <@ Correctness_Adv1(O, A).main'(pk, sk, CO1.i); + CO1(O).get(m); + + return + if 0 <= CO1.i && CO1.i < size CO1.queried then nth witness CO1.queried CO1.i + else head witness (filter (fun (x : plaintext) => ! (x \in CO1.queried)) FinT.enum); + } + + proc main() : bool = { + var m : plaintext; + var r : W8.t Array32.t; + var c : W8.t Array960.t * W8.t Array128.t; + var m' : plaintext option; + + (CO1.pk, CO1.sk) <@ BasePKE.kg(); + m <@ B(A, O).find(CO1.pk, CO1.sk); + r <@ O.get(m); + c <- enc r CO1.pk m; + m' <@ BasePKE.dec(CO1.sk, c); + + return m' <> Some m; + } + }. + + module DC0(O : PKEROM.RO.RO) = { + proc distinguish() : bool = PKE.Correctness_Adv(BasePKE, B(A/206960, O)).main + }. + + module DC1(O : PKEROM.RO.RO) = { + proc distinguish() : bool = B(A/206960, O).main + }. + + lemma CO1_lossless: islossless CO1(PKEROM.RO.RO).get. + + lemma corr_pnp: + forall (A(H0 : PKEROM.POracle) <: + PKEROM.CORR_ADV{+all mem, -PKEROM.RO.RO, -PKEROM.RO.FRO, -Correctness_Adv1, -B} ) &m, + qHC < FinT.card - 1 => + (forall (RO0 <: PKEROM.RO.RO{+all mem, -CO1, -A} ), + hoare[ Correctness_Adv1(RO0, A).A.find : CO1.counter = 0 ==> CO1.counter <= qHC]) => + (forall (H0 <: PKEROM.POracle{+all mem, -A} ), islossless H0.get => islossless A(H0).find) => + Pr[Correctness_Adv1(PKEROM.RO.RO, A).main() @ &m : + has (fun (m : plaintext) => Some m <> dec CO1.sk (enc (oget (PKEROM.RO.RO.m.[m])%SmtMap) CO1.pk m)) + CO1.queried] <= + (qHC + 1)%r * Pr[PKE.Correctness_Adv(BasePKE, B(A, PKEROM.RO.RO)).main() @ &m : res]. + + lemma correctness: + forall (A(H0 : PKEROM.POracle) <: + PKEROM.CORR_ADV{+all mem, -PKEROM.RO.RO, -PKEROM.RO.FRO, -Correctness_Adv1, -B} ) &m, + qHC < FinT.card - 1 => + (forall (RO0 <: PKEROM.RO.RO{+all mem, -CO1, -A} ), + hoare[ Correctness_Adv1(RO0, A).A.find : CO1.counter = 0 ==> CO1.counter <= qHC]) => + (forall (H0 <: PKEROM.POracle{+all mem, -A} ), islossless H0.get => islossless A(H0).find) => + Pr[PKEROM.Correctness_Adv(PKEROM.RO.RO, TT, A).main() @ &m : res] <= + (qHC + 1)%r * Pr[PKE.Correctness_Adv(BasePKE, B(A, PKEROM.RO.RO)).main() @ &m : res]. + + module CountO(O : PKEROM.VA_ORC) = { + var c_cvo : int + + var c_pco : int + + var c_h : int + + proc init() : unit = { + CountO.c_h <- 0; + CountO.c_cvo <- 0; + CountO.c_pco <- 0; + } + + proc cvo(c : W8.t Array960.t * W8.t Array128.t) : bool = { + var r : bool; + + r <@ O.cvo(c); + CountO.c_cvo <- CountO.c_cvo + 1; + + return r; + } + + proc pco(m : plaintext, c : W8.t Array960.t * W8.t Array128.t) : bool = { + var r : bool; + + r <@ O.pco(m, c); + CountO.c_pco <- CountO.c_pco + 1; + + return r; + } + }. + + module CountH(H0 : PKEROM.POracle) = { + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <@ H0.get(x); + CountO.c_h <- CountO.c_h + 1; + + return r; + } + }. + + module Gm = { + var m : plaintext + + var r : W8.t Array32.t + + var log : (plaintext, W8.t Array32.t) SmtMap.fmap + + var bad_corr : plaintext option + }. + + module O_AdvOW = { + var pk : publickey + + proc pco(m : plaintext, c : W8.t Array960.t * W8.t Array128.t) : bool = { + var r : W8.t Array32.t; + var c' : W8.t Array960.t * W8.t Array128.t; + + r <@ PKEROM.RO.RO.get(m); + c' <- enc r O_AdvOW.pk m; + + return c = c'; + } + + proc cvo(c : W8.t Array960.t * W8.t Array128.t) : bool = { + var rv : bool; + + rv <- false; + if (c <> PKEROM.OW_PCVA.cc) + rv <- find (fun (m : plaintext) (r : W8.t Array32.t) => c = enc r O_AdvOW.pk m) PKEROM.RO.RO.m <> None; + + return rv; + } + }. + + module AdvOW(A : PKEROM.PCVA_ADV) = { + module A = A(CountH(PKEROM.RO.RO), CountO(O_AdvOW)) + + proc find(pk0 : publickey, c : W8.t Array960.t * W8.t Array128.t) : plaintext option = { + var m' : plaintext option; + + O_AdvOW.pk <- pk0; + PKEROM.RO.RO.init(); + PKEROM.OW_PCVA.cc <- c; + CountO(O_AdvOW).init(); + m' <@ AdvOW(A).A.find(O_AdvOW.pk, PKEROM.OW_PCVA.cc); + + return m'; + } + }. + + module AdvOW_query(A : PKEROM.PCVA_ADV) = { + module A = A(CountH(PKEROM.RO.RO), CountO(O_AdvOW)) + + proc main(pk0 : publickey, c : W8.t Array960.t * W8.t Array128.t) : unit = { + var m' : plaintext option; + + O_AdvOW.pk <- pk0; + PKEROM.RO.RO.init(); + PKEROM.OW_PCVA.cc <- c; + CountO(O_AdvOW).init(); + m' <@ AdvOW_query(A).A.find(O_AdvOW.pk, PKEROM.OW_PCVA.cc); + } + + proc find(pk0 : publickey, c : W8.t Array960.t * W8.t Array128.t) : plaintext option = { + var i : int; + + AdvOW_query(A).main(pk0, c); + i <$ [0..qH + qP - 1]; + + return Some (nth witness ((elems ((fdom PKEROM.RO.RO.m))%SmtMap))%FSet i); + } + }. + + module AdvOWL_query(A : PKEROM.PCVA_ADV) = { + proc find(pk0 : publickey, c : W8.t Array960.t * W8.t Array128.t) : plaintext list = { + AdvOW_query(A).find(pk0, c); + + return (elems ((fdom PKEROM.RO.RO.m))%SmtMap)%FSet; + } + }. + + module type PCOT = { + proc pco(m : plaintext, c : W8.t Array960.t * W8.t Array128.t) : bool {} + } + + module type GT(PCO : PCOT, RO0 : PKEROM.RO.RO) = { + proc distinguish() : bool {RO0.init, RO0.get, RO0.set, RO0.rem, RO0.sample, PCO.pco} + } + + module H(RO0 : PKEROM.POracle) = { + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + if ((x \notin Gm.log)%SmtMap) { + r <@ RO0.get(x); + Gm.log.[x] <- r; + } + + return oget (Gm.log.[x])%SmtMap; + } + }. + + op incl ['a, 'b] (m1 m2 : ('a, 'b) SmtMap.fmap) : bool = + forall (x : 'a), (x \in m1)%SmtMap => (m1.[x])%SmtMap = (m2.[x])%SmtMap. + + pred gamma_spread_ok (gamma_spread : real) = + forall (pk : publickey) (sk : W8.t Array1152.t) (m : plaintext) (c : W8.t Array960.t * W8.t Array128.t), + (pk, sk) \in UU.TT.kg => mu srand (fun (r : W8.t Array32.t) => enc r pk m = c) <= gamma_spread. + + module G2_O(RO0 : PKEROM.POracle) = { + proc pco(m : plaintext, c : W8.t Array960.t * W8.t Array128.t) : bool = { + var m' : plaintext option; + var r : W8.t Array32.t; + var c' : W8.t Array960.t * W8.t Array128.t; + + m' <- dec PKEROM.OW_PCVA.sk.`2 c; + r <@ H(RO0).get(m); + c' <- enc r PKEROM.OW_PCVA.sk.`1 m; + Gm.bad_corr <- if c = c' /\ m' <> Some m then Some m else Gm.bad_corr; + + return c = c'; + } + + proc cvo(c : W8.t Array960.t * W8.t Array128.t) : bool = { + var rv : bool; + var m' : plaintext option; + + rv <- false; + if (c <> PKEROM.OW_PCVA.cc) { + m' <- dec PKEROM.OW_PCVA.sk.`2 c; + rv <- find (fun (m : plaintext) (r : W8.t Array32.t) => c = enc r PKEROM.OW_PCVA.sk.`1 m) Gm.log <> None; + if (m' <> None) + if ((oget m' \in Gm.log)%SmtMap) + Gm.bad_corr <- + let f = find (fun (m : plaintext) (r : W8.t Array32.t) => c = enc r PKEROM.OW_PCVA.sk.`1 m) Gm.log + in if f <> None /\ (oget f).`1 <> oget m' then Some (oget f).`1 else Gm.bad_corr; + else + if (oget m' = Gm.m) + Gm.bad_corr <- + let f = + find (fun (m : plaintext) (r : W8.t Array32.t) => c = enc r PKEROM.OW_PCVA.sk.`1 m) Gm.log in + if f <> None /\ (oget f).`1 <> oget m' then Some (oget f).`1 else Gm.bad_corr; + else + Gm.bad_corr <- + let f = + find (fun (m : plaintext) (r : W8.t Array32.t) => c = enc r PKEROM.OW_PCVA.sk.`1 m) Gm.log in + if f <> None then Some (oget f).`1 else Gm.bad_corr; + else + Gm.bad_corr <- + let f = find (fun (m : plaintext) (r : W8.t Array32.t) => c = enc r PKEROM.OW_PCVA.sk.`1 m) Gm.log in + if f <> None then Some (oget f).`1 else Gm.bad_corr; + } + + return rv; + } + }. + + module AdvCorr(A : PKEROM.PCVA_ADV, RO0 : PKEROM.POracle) = { + module H = H(RO0) + + module O = G2_O(RO0) + + module A = A(CountH(AdvCorr(A, RO0).H), CountO(AdvCorr(A, RO0).O)) + + proc find(pk : publickey, sk : PKEROM.skey) : plaintext = { + var m' : plaintext option; + + PKEROM.OW_PCVA.sk <- sk; + Gm.log <- SmtMap.empty; + Gm.bad_corr <- None; + Gm.m <$ srand; + Gm.r <@ RO0.get(Gm.m); + PKEROM.OW_PCVA.cc <- enc Gm.r pk Gm.m; + CountO(AdvCorr(A, RO0).O).init(); + m' <@ AdvCorr(A, RO0).A.find(pk, PKEROM.OW_PCVA.cc); + Gm.bad_corr <- if dec sk.`2 PKEROM.OW_PCVA.cc = m' /\ Some Gm.m <> m' then Some Gm.m else Gm.bad_corr; + + return oget Gm.bad_corr; + } + }. + + op inv_G2_corr (log ro : (plaintext, W8.t Array32.t) SmtMap.fmap) (sk : PKEROM.skey) + (bad_corr : plaintext option) (gm : plaintext) (gr : W8.t Array32.t) : bool = + incl log ro /\ + (gm \in ro)%SmtMap /\ + gr = oget (ro.[gm])%SmtMap /\ + (bad_corr <> None => + let m = oget bad_corr in + let r = oget (ro.[m])%SmtMap in + let c = enc r sk.`1 m in let m' = dec sk.`2 c in (m \in ro)%SmtMap /\ m' <> Some m). + + lemma corr_red_count: + forall (A(H0 : PKEROM.POracle, O : PKEROM.VA_ORC) <: + PKEROM.PCVA_ADV{+all mem, -PKE.OW_CPA, -PKE.BOWp, -PKE.OWL_CPA, -PKE.OWvsIND.Bowl, -PKEROM.RO.RO, -PKEROM.RO.FRO, -PKEROM.OW_PCVA, -Correctness_Adv1, -B, -CountO, -Gm, -O_AdvOW} + ) (RO0 <: PKEROM.RO.RO{+all mem, -PKEROM.OW_PCVA, -CO1, -CountO, -Gm, -A} ), + (forall (RO1 <: PKEROM.POracle{+all mem, -CountO, -A} ) (O <: PKEROM.VA_ORC{+all mem, -CountO, -A} ), + hoare[ A(CountH(RO1), CountO(O)).find : + CountO.c_h = 0 /\ CountO.c_cvo = 0 /\ CountO.c_pco = 0 ==> + CountO.c_h <= qH /\ CountO.c_cvo <= qV /\ CountO.c_pco <= qP]) => + hoare[ AdvCorr(A, CO1(RO0)).A.find : + CO1.counter = 1 /\ CountO.c_h = 0 /\ CountO.c_cvo = 0 /\ CountO.c_pco = 0 ==> + CO1.counter <= 1 + qH + qP]. + + lemma pre_conclusion: + forall (A(H0 : PKEROM.POracle, O : PKEROM.VA_ORC) <: + PKEROM.PCVA_ADV{+all mem, -PKE.OW_CPA, -PKE.BOWp, -PKE.OWL_CPA, -PKE.OWvsIND.Bowl, -PKEROM.RO.RO, -PKEROM.RO.FRO, -PKEROM.OW_PCVA, -Correctness_Adv1, -B, -CountO, -Gm, -O_AdvOW} + ) &m (gamma_spread : real), + gamma_spread_ok gamma_spread => + (forall (RO0 <: PKEROM.POracle{+all mem, -CountO, -A} ) (O <: PKEROM.VA_ORC{+all mem, -CountO, -A} ), + hoare[ A(CountH(RO0), CountO(O)).find : + CountO.c_h = 0 /\ CountO.c_cvo = 0 /\ CountO.c_pco = 0 ==> + CountO.c_h <= qH /\ CountO.c_cvo <= qV /\ CountO.c_pco <= qP]) => + (forall (H0 <: PKEROM.POracle{+all mem, -A} ) (O <: PKEROM.VA_ORC{+all mem, -A} ), + islossless O.cvo => islossless O.pco => islossless H0.get => islossless A(H0, O).find) => + Pr[PKEROM.OW_PCVA(PKEROM.RO.LRO, TT, A).main() @ &m : res] <= + Pr[PKE.OW_CPA(BasePKE, AdvOW(A)).main() @ &m : res] + + (qH + qP)%r * Pr[PKE.OW_CPA(BasePKE, AdvOW_query(A)).main() @ &m : res] + + Pr[PKEROM.Correctness_Adv(PKEROM.RO.RO, TT, AdvCorr(A)).main() @ &m : res] + + qV%r * gamma_spread. + + lemma conclusion: + forall (A(H0 : PKEROM.POracle, O : PKEROM.VA_ORC) <: + PKEROM.PCVA_ADV{+all mem, -PKE.OW_CPA, -PKE.BOWp, -PKE.OWL_CPA, -PKE.OWvsIND.Bowl, -PKEROM.RO.RO, -PKEROM.RO.FRO, -PKEROM.OW_PCVA, -Correctness_Adv1, -B, -CountO, -Gm, -O_AdvOW} + ) &m (gamma_spread : real), + qH + qP + 1 = qHC => + qHC < FinT.card - 1 => + gamma_spread_ok gamma_spread => + (forall (RO0 <: PKEROM.POracle{+all mem, -CountO, -A} ) (O <: PKEROM.VA_ORC{+all mem, -CountO, -A} ), + hoare[ A(CountH(RO0), CountO(O)).find : + CountO.c_h = 0 /\ CountO.c_cvo = 0 /\ CountO.c_pco = 0 ==> + CountO.c_h <= qH /\ CountO.c_cvo <= qV /\ CountO.c_pco <= qP]) => + (forall (H0 <: PKEROM.POracle{+all mem, -A} ) (O <: PKEROM.VA_ORC{+all mem, -A} ), + islossless O.cvo => islossless O.pco => islossless H0.get => islossless A(H0, O).find) => + Pr[PKEROM.OW_PCVA(PKEROM.RO.LRO, TT, A).main() @ &m : res] <= + Pr[PKE.OW_CPA(BasePKE, AdvOW(A)).main() @ &m : res] + + (qH + qP)%r * Pr[PKE.OW_CPA(BasePKE, AdvOW_query(A)).main() @ &m : res] + + (qH + qP + 2)%r * Pr[PKE.Correctness_Adv(BasePKE, B(AdvCorr(A), PKEROM.RO.RO)).main() @ &m : res] + + qV%r * gamma_spread. + + lemma pre_conclusion_cpa: + forall (A(H0 : PKEROM.POracle, O : PKEROM.VA_ORC) <: + PKEROM.PCVA_ADV{+all mem, -PKE.OW_CPA, -PKE.BOWp, -PKE.OWL_CPA, -PKE.OWvsIND.Bowl, -PKEROM.RO.RO, -PKEROM.RO.FRO, -PKEROM.OW_PCVA, -Correctness_Adv1, -B, -CountO, -Gm, -O_AdvOW} + ) &m (gamma_spread : real), + gamma_spread_ok gamma_spread => + (forall (RO0 <: PKEROM.POracle{+all mem, -CountO, -A} ) (O <: PKEROM.VA_ORC{+all mem, -CountO, -A} ), + hoare[ A(CountH(RO0), CountO(O)).find : + CountO.c_h = 0 /\ CountO.c_cvo = 0 /\ CountO.c_pco = 0 ==> + CountO.c_h <= qH /\ CountO.c_cvo <= qV /\ CountO.c_pco <= qP]) => + (forall (H0 <: PKEROM.POracle{+all mem, -A} ) (O <: PKEROM.VA_ORC{+all mem, -A} ), + islossless O.cvo => islossless O.pco => islossless H0.get => islossless A(H0, O).find) => + Pr[PKEROM.OW_PCVA(PKEROM.RO.LRO, TT, A).main() @ &m : res] <= + 2%r * `|Pr[PKE.CPA(BasePKE, PKE.OWvsIND.Bowl(PKE.OWvsIND.BL(AdvOW(A)))).main() @ &m : res] - 1%r / 2%r| + + 2%r * `|Pr[PKE.CPA(BasePKE, PKE.OWvsIND.Bowl(AdvOWL_query(A))).main() @ &m : res] - 1%r / 2%r| + + Pr[PKE.Correctness_Adv(BasePKE, PKE.BOWp(BasePKE, AdvOW(A))).main() @ &m : res] + + Pr[PKEROM.Correctness_Adv(PKEROM.RO.RO, TT, AdvCorr(A)).main() @ &m : res] + + qV%r * gamma_spread + 2%r * (qH + qP + 1)%r * PKE.eps_msg. + + lemma conclusion_cpa: + forall (A(H0 : PKEROM.POracle, O : PKEROM.VA_ORC) <: + PKEROM.PCVA_ADV{+all mem, -PKE.OW_CPA, -PKE.BOWp, -PKE.OWL_CPA, -PKE.OWvsIND.Bowl, -PKEROM.RO.RO, -PKEROM.RO.FRO, -PKEROM.OW_PCVA, -Correctness_Adv1, -B, -CountO, -Gm, -O_AdvOW} + ) &m (gamma_spread : real), + qH + qP + 1 = qHC => + qHC < FinT.card - 1 => + gamma_spread_ok gamma_spread => + (forall (RO0 <: PKEROM.POracle{+all mem, -CountO, -A} ) (O <: PKEROM.VA_ORC{+all mem, -CountO, -A} ), + hoare[ A(CountH(RO0), CountO(O)).find : + CountO.c_h = 0 /\ CountO.c_cvo = 0 /\ CountO.c_pco = 0 ==> + CountO.c_h <= qH /\ CountO.c_cvo <= qV /\ CountO.c_pco <= qP]) => + (forall (H0 <: PKEROM.POracle{+all mem, -A} ) (O <: PKEROM.VA_ORC{+all mem, -A} ), + islossless O.cvo => islossless O.pco => islossless H0.get => islossless A(H0, O).find) => + Pr[PKEROM.OW_PCVA(PKEROM.RO.LRO, TT, A).main() @ &m : res] <= + 2%r * `|Pr[PKE.CPA(BasePKE, PKE.OWvsIND.Bowl(PKE.OWvsIND.BL(AdvOW(A)))).main() @ &m : res] - 1%r / 2%r| + + 2%r * `|Pr[PKE.CPA(BasePKE, PKE.OWvsIND.Bowl(AdvOWL_query(A))).main() @ &m : res] - 1%r / 2%r| + + Pr[PKE.Correctness_Adv(BasePKE, PKE.BOWp(BasePKE, AdvOW(A))).main() @ &m : res] + + (qH + qP + 2)%r * Pr[PKE.Correctness_Adv(BasePKE, B(AdvCorr(A), PKEROM.RO.RO)).main() @ &m : res] + + qV%r * gamma_spread + 2%r * (qH + qP + 1)%r * PKE.eps_msg. + end TT. + + lemma dkey_ll: is_lossless srand. + + hint solve 0 lossless : dkey_ll. + + hint solve 0 random : dkey_ll. + + lemma dkey_uni: is_uniform srand. + + hint solve 0 random : dkey_uni. + + lemma dkey_fu: is_full srand. + + hint solve 0 random : dkey_fu. + + theory J. + module type PRF = { + proc init() : unit {} + + proc f(_ : W8.t Array960.t * W8.t Array128.t) : sharedsecret {} + } + + module type PRF_Oracles = { + proc f(_ : W8.t Array960.t * W8.t Array128.t) : sharedsecret {} + } + + module type Distinguisher(F : PRF_Oracles) = { + proc distinguish() : bool {F.f} + } + + module IND(F : PRF, D : Distinguisher) = { + proc main() : bool = { + var b : bool; + + F.init(); + b <@ D(F).distinguish(); + + return b; + } + }. + + abstract theory RF. + op dR : W8.t Array960.t * W8.t Array128.t -> sharedsecret distr. + + axiom dR_ll: forall (x : W8.t Array960.t * W8.t Array128.t), is_lossless (dR x). + + module RF = { + var m : (W8.t Array960.t * W8.t Array128.t, sharedsecret) SmtMap.fmap + + proc init() : unit = { + RF.m <- SmtMap.empty; + } + + proc f(x : W8.t Array960.t * W8.t Array128.t) : sharedsecret = { + var r : sharedsecret; + + if ((x \notin RF.m)%SmtMap) { + r <$ dR x; + RF.m.[x] <- r; + } + + return oget (RF.m.[x])%SmtMap; + } + }. + end RF. + + abstract theory PseudoRF. + type K. + + op dK : K distr. + + axiom dK_ll: is_lossless dK. + + op F : K -> W8.t Array960.t * W8.t Array128.t -> sharedsecret. + + module type PseudoRF = { + proc keygen() : K {} + + proc f(_ : K * (W8.t Array960.t * W8.t Array128.t)) : sharedsecret {} + } + + module PseudoRF = { + proc keygen() : K = { + var k : K; + + k <$ dK; + + return k; + } + + proc f(k : K, x : W8.t Array960.t * W8.t Array128.t) : sharedsecret = { + return F k x; + } + }. + + module PRF = { + var k : K + + proc init() : unit = { + PRF.k <$ dK; + } + + proc f(x : W8.t Array960.t * W8.t Array128.t) : sharedsecret = { + return F PRF.k x; + } + }. + end PseudoRF. + end J. + + theory RF. + lemma dR_ll: forall (_ : W8.t Array960.t * W8.t Array128.t), is_lossless srand. + + module RF = { + var m : (W8.t Array960.t * W8.t Array128.t, sharedsecret) SmtMap.fmap + + proc init() : unit = { + RF.m <- SmtMap.empty; + } + + proc f(x : W8.t Array960.t * W8.t Array128.t) : sharedsecret = { + var r : sharedsecret; + + if ((x \notin RF.m)%SmtMap) { + r <$ srand; + RF.m.[x] <- r; + } + + return oget (RF.m.[x])%SmtMap; + } + }. + end RF. + + theory PseudoRF. + lemma dK_ll: is_lossless srand. + + module type PseudoRF = { + proc keygen() : sharedsecret {} + + proc f(_ : sharedsecret * (W8.t Array960.t * W8.t Array128.t)) : sharedsecret {} + } + + module PseudoRF = { + proc keygen() : sharedsecret = { + var k : sharedsecret; + + k <$ srand; + + return k; + } + + proc f(k : sharedsecret, x : W8.t Array960.t * W8.t Array128.t) : sharedsecret = { + return J k x; + } + }. + + module PRF = { + var k : sharedsecret + + proc init() : unit = { + PRF.k <$ srand; + } + + proc f(x : W8.t Array960.t * W8.t Array128.t) : sharedsecret = { + return J PRF.k x; + } + }. + end PseudoRF. + + theory KEMROMx2. + type skey = (publickey * W8.t Array1152.t) * sharedsecret. + + lemma dkey_ll: is_lossless srand. + + hint solve 0 lossless : dkey_ll. + + hint solve 0 random : dkey_ll. + + lemma dkey_uni: is_uniform srand. + + hint solve 0 random : dkey_uni. + + lemma dkey_fu: is_full srand. + + hint solve 0 random : dkey_fu. + + theory RO1. + module type RO = { + proc init() : unit {} + + proc get(x : plaintext) : W8.t Array32.t {} + + proc set(x : plaintext, y : W8.t Array32.t) : unit {} + + proc rem(x : plaintext) : unit {} + + proc sample(x : plaintext) : unit {} + } + + module type RO_Distinguisher(G : RO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.rem, G.sample} + } + + module MainD(D : RO_Distinguisher, RO0 : RO) = { + proc distinguish(x : unit) : bool = { + var r : bool; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + module type ROmap = { + proc init() : unit {} + + proc get(x : plaintext) : W8.t Array32.t {} + + proc set(x : plaintext, y : W8.t Array32.t) : unit {} + + proc rem(x : plaintext) : unit {} + + proc sample(x : plaintext) : unit {} + + proc restrK() : (plaintext, W8.t Array32.t) SmtMap.fmap {} + } + + module type FRO = { + proc init() : unit {} + + proc get(x : plaintext) : W8.t Array32.t {} + + proc set(x : plaintext, y : W8.t Array32.t) : unit {} + + proc rem(x : plaintext) : unit {} + + proc sample(x : plaintext) : unit {} + + proc queried(x : plaintext, f : PROM.flag) : bool {} + + proc allKnown() : (plaintext, W8.t Array32.t) SmtMap.fmap {} + } + + module type FRO_Distinguisher(G : FRO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.rem, G.sample, G.queried, G.allKnown} + } + + abstract theory MkRO. + module RO = { + var m : (plaintext, W8.t Array32.t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : plaintext) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : plaintext) : unit = { + RO.get(x); + } + + proc restrK() : (plaintext, W8.t Array32.t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : plaintext) : W8.t Array32.t = RO.get + + proc set(x : plaintext, y : W8.t Array32.t) : unit = RO.set + + proc rem(x : plaintext) : unit = RO.rem + + proc sample(x : plaintext) : unit = {} + }. + end MkRO. + + module RO = { + var m : (plaintext, W8.t Array32.t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : plaintext) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : plaintext) : unit = { + RO.get(x); + } + + proc restrK() : (plaintext, W8.t Array32.t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : plaintext) : W8.t Array32.t = RO.get + + proc set(x : plaintext, y : W8.t Array32.t) : unit = RO.set + + proc rem(x : plaintext) : unit = RO.rem + + proc sample(x : plaintext) : unit = {} + }. + + module FRO = { + var m : (plaintext, W8.t Array32.t * PROM.flag) SmtMap.fmap + + proc init() : unit = { + FRO.m <- SmtMap.empty; + } + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \in FRO.m)%SmtMap) + r <- (oget (FRO.m.[x])%SmtMap).`1; + FRO.m.[x] <- (r, PROM.Known); + + return r; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = { + FRO.m.[x] <- (y, PROM.Known); + } + + proc rem(x : plaintext) : unit = { + FRO.m <- (rem FRO.m x)%SmtMap; + } + + proc sample(x : plaintext) : unit = { + var c : W8.t Array32.t; + + c <$ srand; + if ((x \notin FRO.m)%SmtMap) + FRO.m.[x] <- (c, PROM.Unknown); + } + + proc queried(x : plaintext, f : PROM.flag) : bool = { + return (x \in FRO.m)%SmtMap /\ ((FRO.m.[x])%SmtMap \is f)%PROM; + } + + proc allKnown() : (plaintext, W8.t Array32.t) SmtMap.fmap = { + return (restr PROM.Known FRO.m)%SmtMap; + } + }. + + lemma RO_FRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_get: + equiv[ RO.get ~ FRO.get : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> ={res} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_sample: + equiv[ RO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(RO).distinguish ~ D(FRO).distinguish : + ={arg, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_init_ll: islossless RO.init. + + lemma FRO_init_ll: islossless FRO.init. + + lemma FRO_in_dom_ll: islossless FRO.queried. + + lemma FRO_restrK_ll: islossless FRO.allKnown. + + lemma RO_set_ll: islossless RO.set. + + lemma FRO_set_ll: islossless FRO.set. + + lemma RO_get_ll: (forall (_ : plaintext), is_lossless srand) => islossless RO.get. + + lemma FRO_get_ll: (forall (_ : plaintext), is_lossless srand) => islossless FRO.get. + + lemma RO_sample_ll: (forall (_ : plaintext), is_lossless srand) => islossless RO.sample. + + lemma FRO_sample_ll: (forall (_ : plaintext), is_lossless srand) => islossless FRO.sample. + + module type RM_Distinguisher(G : ROmap) = { + proc distinguish(_ : unit) : bool {G.get, G.sample, G.restrK} + } + + module MainRM(D : RM_Distinguisher, RO0 : ROmap) = { + proc distinguish(x : unit) : bool = { + var r : bool; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + lemma fcoll_bound ['rT]: + forall (f : W8.t Array32.t -> 'rT) (Pc : real), + 0%r <= Pc => + (forall (_ _ : plaintext) (y : 'rT), y \in dmap srand f => mu1 (dmap srand f) y <= Pc) => + forall (q : int), + 0 <= q => + forall (D(G : ROmap) <: RM_Distinguisher{+all mem, -RO} ) &m (z : unit), + Pr[MainRM(D, RO).distinguish(z) @ &m : (fcoll f RO.m)%SmtMap /\ (fsize RO.m)%SmtMap <= q] <= + (q * (q - 1))%r / 2%r * Pc. + + theory FullEager. + abstract theory EagerCore. + axiom dout_ll: forall (_ : plaintext), is_lossless srand. + + module type Orcl = { + proc f(x : plaintext) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : plaintext list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : plaintext list, l2 : plaintext list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : plaintext, l : plaintext list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : plaintext list) (i : plaintext) (s2 : plaintext list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : plaintext), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : plaintext), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = FRO.set + + proc rem(x : plaintext) : unit = FRO.rem + + proc sample(x : plaintext) : unit = FRO.sample + + proc queried(x : plaintext, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (plaintext, W8.t Array32.t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : plaintext) : unit = { + var c : W8.t Array32.t; + + c <$ srand; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : plaintext) (mx1 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : plaintext) (mx1 mx2 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : plaintext) (r1 : W8.t Array32.t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + end FullEager. + + abstract theory FinEager. + abstract theory EagerCore. + axiom dout_ll: forall (_ : plaintext), is_lossless srand. + + module type Orcl = { + proc f(x : plaintext) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : plaintext list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : plaintext list, l2 : plaintext list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : plaintext, l : plaintext list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : plaintext list) (i : plaintext) (s2 : plaintext list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : plaintext), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : plaintext), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = FRO.set + + proc rem(x : plaintext) : unit = FRO.rem + + proc sample(x : plaintext) : unit = FRO.sample + + proc queried(x : plaintext, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (plaintext, W8.t Array32.t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : plaintext) : unit = { + var c : W8.t Array32.t; + + c <$ srand; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : plaintext) (mx1 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : plaintext) (mx1 mx2 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : plaintext) (r1 : W8.t Array32.t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + theory FinFrom. + type t = plaintext. + + op enum : t list. + + op card : int = size enum. + + axiom enum_spec: forall (x : t), count (pred1 x) enum = 1. + + lemma enumP: forall (x : t), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : t list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinFrom. + + module FinRO = { + proc rem(x : plaintext) : unit = RO.rem + + proc set(x : plaintext, y : W8.t Array32.t) : unit = RO.set + + proc init() : unit = { + var l : FinFrom.t list; + + l <- FinFrom.enum; + RO.init(); + while (l <> []){ + RO.sample(head witness l); + l <- behead l; + } + } + + proc get(x : plaintext) : W8.t Array32.t = { + return oget (RO.m.[x])%SmtMap; + } + + proc sample(x : plaintext) : unit = {} + }. + + module type FinRO_Distinguisher(G : RO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.sample} + } + + lemma RO_FinRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ MainD(D, RO).distinguish ~ MainD(D, FinRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_RO_FinRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ) &m (x : unit) (p : + bool -> bool), + Pr[MainD(D, RO).distinguish(x) @ &m : p res] = Pr[MainD(D, FinRO).distinguish(x) @ &m : p res]. + + theory MUniFinFun. + op mfun ['u] (d : plaintext -> 'u distr) (f : plaintext -> 'u) : real = + (big predT (fun (x : plaintext) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma mfunE ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mfun d f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + lemma isdistr_dfun ['u]: forall (d : plaintext -> 'u distr), isdistr (mfun d). + + op dfun ['u] (d : plaintext -> 'u distr) : (plaintext -> 'u) distr = mk (mfun d). + + lemma dfun1E ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mu1 (dfun d) f = + (big predT (fun (x : plaintext) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun1E_djoin ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mu1 (dfun d) f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + op tofun ['u] (us : 'u list) (x : plaintext) : 'u = nth witness us (index x FinFrom.enum). + + op offun ['u] (f : plaintext -> 'u) : 'u list = map f FinFrom.enum. + + lemma offunK ['u]: cancel offun tofun. + + lemma tofunK ['u]: forall (us : 'u list), size us = FinFrom.card => offun (tofun us) = us. + + lemma dfun_dmap ['u]: forall (d : plaintext -> 'u distr), dfun d = dmap (djoinmap d FinFrom.enum) tofun. + + lemma dfun_supp ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + f \in dfun d <=> forall (x : plaintext), f x \in d x. + + lemma dfun_fu ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_full (d x)) => is_full (dfun d). + + lemma dfun_uni ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_uniform (d x)) => is_uniform (dfun d). + + lemma dfun_funi ['u]: + forall (d : plaintext -> 'u distr), + (forall (x : plaintext), is_uniform (d x)) => + (forall (x : plaintext), is_full (d x)) => is_funiform (dfun d). + + lemma dfunE_djoin ['u]: + forall (du : plaintext -> 'u distr) (E : (plaintext -> 'u) -> bool), + mu (dfun du) E = mu (djoinmap du FinFrom.enum) (E \o tofun). + + lemma dfunE ['u]: + forall (du : plaintext -> 'u distr) (p : plaintext -> 'u -> bool), + mu (dfun du) (fun (f : plaintext -> 'u) => forall (x : plaintext), p x (f x)) = + (big predT (fun (x : plaintext) => mu (du x) (p x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma weight_dfun ['u]: + forall (df : plaintext -> 'u distr), + weight (dfun df) = + (big predT (fun (x : plaintext) => weight (df x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun_ll ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_lossless (d x)) => is_lossless (dfun d). + + lemma dlet_dfun ['u, 'v]: + forall (d1 : plaintext -> 'u distr) (d2 : plaintext -> 'u -> 'v distr), + dlet (dfun d1) (fun (f1 : plaintext -> 'u) => dfun (fun (x : plaintext) => d2 x (f1 x))) = + dfun (fun (x : plaintext) => dlet (d1 x) (fun (x1 : 'u) => d2 x x1)). + + lemma dfun_unit ['u]: forall (f : plaintext -> 'u), dfun (fun (x : plaintext) => dunit (f x)) = dunit f. + + lemma dmap_dfun ['u, 'v]: + forall (d : plaintext -> 'u distr) (F : plaintext -> 'u -> 'v), + dmap (dfun d) (fun (f : plaintext -> 'u) (x : plaintext) => F x (f x)) = + dfun (fun (x : plaintext) => dmap (d x) (fun (fx : 'u) => F x fx)). + + lemma dfun1E_fix1 ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext) (f : + plaintext -> 'u), mu1 (dfun d) f = mu1 (d x0) (f x0) * mu1 (dfun d.[x0 <- dunit (f x0)]) f. + + lemma dlet_dfun_fupdate_ll ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext) (v : 'u), + dlet (dfun d.[x0 <- dunit v]) + (fun (f : plaintext -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + dfun d. + + lemma dfunE_dlet_fix1 ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext), + dfun d = dlet (d x0) (fun (v : 'u) => dfun d.[x0 <- dunit v]). + + lemma dlet_dfun_update ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext), + dlet (dfun d) (fun (f : plaintext -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + (weight (d x0) \cdot dfun d). + + lemma dfun_projE ['u]: + forall (df : plaintext -> 'u distr) (x : plaintext), + dmap (dfun df) (fun (f : plaintext -> 'u) => f x) = (weight (dfun df) / weight (df x) \cdot df x). + + lemma eq_from_dfun_proj_eq ['u]: + forall (df1 df2 : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (df1 x)) => + (forall (x : plaintext), is_lossless (df2 x)) => + (forall (x : plaintext), + dmap (dfun df1) (fun (f : plaintext -> 'u) => f x) = + dmap (dfun df2) (fun (f : plaintext -> 'u) => f x)) => + df1 == df2. + + lemma dfun_prod1E ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr) (f : + plaintext -> 'u) (g : plaintext -> 'v), + mu1 (dfun (fun (x : plaintext) => df x `*` dg x)) (fun (x : plaintext) => (f x, g x)) = + mu1 (dfun df) f * mu1 (dfun dg) g. + + lemma dprod_funE ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr), + dfun df `*` dfun dg = + dmap (dfun (fun (x : plaintext) => df x `*` dg x)) + (fun (fg : plaintext -> 'u * 'v) => + ((fun (p : 'u * 'v) => p.`1) \o fg, (fun (p : 'u * 'v) => p.`2) \o fg)). + + lemma dfun_prodE ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr), + dfun (fun (x : plaintext) => df x `*` dg x) = + dmap (dfun df `*` dfun dg) + (fun (fg : (plaintext -> 'u) * (plaintext -> 'v)) (x : plaintext) => (fg.`1 x, fg.`2 x)). + + lemma dfun_condE ['u]: + forall (X : plaintext -> bool) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext -> 'u) * (plaintext -> 'u)) (x : plaintext) => + if X x then tf.`1 x else tf.`2 x) = + dfun (fun (x : plaintext) => if X x then dt x else df x). + + lemma dlet_dfun_cond ['u]: + forall (dX : plaintext -> bool distr) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dX x)) => + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : plaintext -> bool) => dfun (fun (x : plaintext) => if X x then dt x else df x)) = + dfun (fun (x : plaintext) => dlet (dX x) (fun (b : bool) => if b then dt x else df x)). + + lemma dfun_dcondE ['u]: + forall (dX : plaintext -> bool distr) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dX x)) => + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : plaintext -> bool) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext -> 'u) * (plaintext -> 'u)) (x : plaintext) => + if X x then tf.`1 x else tf.`2 x)) = + dfun (fun (x : plaintext) => (mu1 (dX x) true \cdot dt x) + (mu1 (dX x) false \cdot df x)). + end MUniFinFun. + + module FunRO = { + var f : plaintext -> W8.t Array32.t + + proc init() : unit = { + FunRO.f <$ (dfun (fun (_ : plaintext) => srand))%MUniFinFun; + } + + proc get(x : plaintext) : W8.t Array32.t = { + return FunRO.f x; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = { + FunRO.f <- fun (z : plaintext) => if z = x then y else FunRO.f z; + } + + proc sample(x : plaintext) : unit = {} + + proc rem(x : plaintext) : unit = {} + }. + + lemma FinRO_FunRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ), + equiv[ MainD(D, FinRO).distinguish ~ MainD(D, FunRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_FinRO_FunRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ) &m (x : unit) (p : + bool -> bool), + Pr[MainD(D, FinRO).distinguish(x) @ &m : p res] = Pr[MainD(D, FunRO).distinguish(x) @ &m : p res]. + end FinEager. + end RO1. + + theory RO2. + module type RO = { + proc init() : unit {} + + proc get(x : plaintext) : sharedsecret {} + + proc set(x : plaintext, y : sharedsecret) : unit {} + + proc rem(x : plaintext) : unit {} + + proc sample(x : plaintext) : unit {} + } + + module type RO_Distinguisher(G : RO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.rem, G.sample} + } + + module MainD(D : RO_Distinguisher, RO0 : RO) = { + proc distinguish(x : unit) : bool = { + var r : bool; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + module type ROmap = { + proc init() : unit {} + + proc get(x : plaintext) : sharedsecret {} + + proc set(x : plaintext, y : sharedsecret) : unit {} + + proc rem(x : plaintext) : unit {} + + proc sample(x : plaintext) : unit {} + + proc restrK() : (plaintext, sharedsecret) SmtMap.fmap {} + } + + module type FRO = { + proc init() : unit {} + + proc get(x : plaintext) : sharedsecret {} + + proc set(x : plaintext, y : sharedsecret) : unit {} + + proc rem(x : plaintext) : unit {} + + proc sample(x : plaintext) : unit {} + + proc queried(x : plaintext, f : PROM.flag) : bool {} + + proc allKnown() : (plaintext, sharedsecret) SmtMap.fmap {} + } + + module type FRO_Distinguisher(G : FRO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.rem, G.sample, G.queried, G.allKnown} + } + + abstract theory MkRO. + module RO = { + var m : (plaintext, sharedsecret) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : plaintext) : sharedsecret = { + var r : sharedsecret; + + r <$ srand; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : plaintext, y : sharedsecret) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : plaintext) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : plaintext) : unit = { + RO.get(x); + } + + proc restrK() : (plaintext, sharedsecret) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : plaintext) : sharedsecret = RO.get + + proc set(x : plaintext, y : sharedsecret) : unit = RO.set + + proc rem(x : plaintext) : unit = RO.rem + + proc sample(x : plaintext) : unit = {} + }. + end MkRO. + + module RO = { + var m : (plaintext, sharedsecret) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : plaintext) : sharedsecret = { + var r : sharedsecret; + + r <$ srand; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : plaintext, y : sharedsecret) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : plaintext) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : plaintext) : unit = { + RO.get(x); + } + + proc restrK() : (plaintext, sharedsecret) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : plaintext) : sharedsecret = RO.get + + proc set(x : plaintext, y : sharedsecret) : unit = RO.set + + proc rem(x : plaintext) : unit = RO.rem + + proc sample(x : plaintext) : unit = {} + }. + + module FRO = { + var m : (plaintext, sharedsecret * PROM.flag) SmtMap.fmap + + proc init() : unit = { + FRO.m <- SmtMap.empty; + } + + proc get(x : plaintext) : sharedsecret = { + var r : sharedsecret; + + r <$ srand; + if ((x \in FRO.m)%SmtMap) + r <- (oget (FRO.m.[x])%SmtMap).`1; + FRO.m.[x] <- (r, PROM.Known); + + return r; + } + + proc set(x : plaintext, y : sharedsecret) : unit = { + FRO.m.[x] <- (y, PROM.Known); + } + + proc rem(x : plaintext) : unit = { + FRO.m <- (rem FRO.m x)%SmtMap; + } + + proc sample(x : plaintext) : unit = { + var c : sharedsecret; + + c <$ srand; + if ((x \notin FRO.m)%SmtMap) + FRO.m.[x] <- (c, PROM.Unknown); + } + + proc queried(x : plaintext, f : PROM.flag) : bool = { + return (x \in FRO.m)%SmtMap /\ ((FRO.m.[x])%SmtMap \is f)%PROM; + } + + proc allKnown() : (plaintext, sharedsecret) SmtMap.fmap = { + return (restr PROM.Known FRO.m)%SmtMap; + } + }. + + lemma RO_FRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_get: + equiv[ RO.get ~ FRO.get : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> ={res} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_sample: + equiv[ RO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(RO).distinguish ~ D(FRO).distinguish : + ={arg, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_init_ll: islossless RO.init. + + lemma FRO_init_ll: islossless FRO.init. + + lemma FRO_in_dom_ll: islossless FRO.queried. + + lemma FRO_restrK_ll: islossless FRO.allKnown. + + lemma RO_set_ll: islossless RO.set. + + lemma FRO_set_ll: islossless FRO.set. + + lemma RO_get_ll: (forall (_ : plaintext), is_lossless srand) => islossless RO.get. + + lemma FRO_get_ll: (forall (_ : plaintext), is_lossless srand) => islossless FRO.get. + + lemma RO_sample_ll: (forall (_ : plaintext), is_lossless srand) => islossless RO.sample. + + lemma FRO_sample_ll: (forall (_ : plaintext), is_lossless srand) => islossless FRO.sample. + + module type RM_Distinguisher(G : ROmap) = { + proc distinguish(_ : unit) : bool {G.get, G.sample, G.restrK} + } + + module MainRM(D : RM_Distinguisher, RO0 : ROmap) = { + proc distinguish(x : unit) : bool = { + var r : bool; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + lemma fcoll_bound ['rT]: + forall (f : sharedsecret -> 'rT) (Pc : real), + 0%r <= Pc => + (forall (_ _ : plaintext) (y : 'rT), y \in dmap srand f => mu1 (dmap srand f) y <= Pc) => + forall (q : int), + 0 <= q => + forall (D(G : ROmap) <: RM_Distinguisher{+all mem, -RO} ) &m (z : unit), + Pr[MainRM(D, RO).distinguish(z) @ &m : (fcoll f RO.m)%SmtMap /\ (fsize RO.m)%SmtMap <= q] <= + (q * (q - 1))%r / 2%r * Pc. + + theory FullEager. + abstract theory EagerCore. + axiom dout_ll: forall (_ : plaintext), is_lossless srand. + + module type Orcl = { + proc f(x : plaintext) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : plaintext list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : plaintext list, l2 : plaintext list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : plaintext, l : plaintext list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : plaintext list) (i : plaintext) (s2 : plaintext list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : plaintext), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : plaintext), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : plaintext) : sharedsecret = { + var r : sharedsecret; + + r <$ srand; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : plaintext, y : sharedsecret) : unit = FRO.set + + proc rem(x : plaintext) : unit = FRO.rem + + proc sample(x : plaintext) : unit = FRO.sample + + proc queried(x : plaintext, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (plaintext, sharedsecret) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : plaintext) : unit = { + var c : sharedsecret; + + c <$ srand; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : plaintext) (mx1 : (sharedsecret * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : plaintext) (mx1 mx2 : (sharedsecret * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : plaintext) (r1 : sharedsecret), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + end FullEager. + + abstract theory FinEager. + abstract theory EagerCore. + axiom dout_ll: forall (_ : plaintext), is_lossless srand. + + module type Orcl = { + proc f(x : plaintext) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : plaintext list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : plaintext list, l2 : plaintext list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : plaintext, l : plaintext list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : plaintext list) (i : plaintext) (s2 : plaintext list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : plaintext), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : plaintext), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : plaintext) : sharedsecret = { + var r : sharedsecret; + + r <$ srand; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : plaintext, y : sharedsecret) : unit = FRO.set + + proc rem(x : plaintext) : unit = FRO.rem + + proc sample(x : plaintext) : unit = FRO.sample + + proc queried(x : plaintext, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (plaintext, sharedsecret) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : plaintext) : unit = { + var c : sharedsecret; + + c <$ srand; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : plaintext) (mx1 : (sharedsecret * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : plaintext) (mx1 mx2 : (sharedsecret * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : plaintext) (r1 : sharedsecret), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + theory FinFrom. + type t = plaintext. + + op enum : t list. + + op card : int = size enum. + + axiom enum_spec: forall (x : t), count (pred1 x) enum = 1. + + lemma enumP: forall (x : t), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : t list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinFrom. + + module FinRO = { + proc rem(x : plaintext) : unit = RO.rem + + proc set(x : plaintext, y : sharedsecret) : unit = RO.set + + proc init() : unit = { + var l : FinFrom.t list; + + l <- FinFrom.enum; + RO.init(); + while (l <> []){ + RO.sample(head witness l); + l <- behead l; + } + } + + proc get(x : plaintext) : sharedsecret = { + return oget (RO.m.[x])%SmtMap; + } + + proc sample(x : plaintext) : unit = {} + }. + + module type FinRO_Distinguisher(G : RO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.sample} + } + + lemma RO_FinRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ MainD(D, RO).distinguish ~ MainD(D, FinRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_RO_FinRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ) &m (x : unit) (p : + bool -> bool), + Pr[MainD(D, RO).distinguish(x) @ &m : p res] = Pr[MainD(D, FinRO).distinguish(x) @ &m : p res]. + + theory MUniFinFun. + op mfun ['u] (d : plaintext -> 'u distr) (f : plaintext -> 'u) : real = + (big predT (fun (x : plaintext) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma mfunE ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mfun d f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + lemma isdistr_dfun ['u]: forall (d : plaintext -> 'u distr), isdistr (mfun d). + + op dfun ['u] (d : plaintext -> 'u distr) : (plaintext -> 'u) distr = mk (mfun d). + + lemma dfun1E ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mu1 (dfun d) f = + (big predT (fun (x : plaintext) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun1E_djoin ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mu1 (dfun d) f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + op tofun ['u] (us : 'u list) (x : plaintext) : 'u = nth witness us (index x FinFrom.enum). + + op offun ['u] (f : plaintext -> 'u) : 'u list = map f FinFrom.enum. + + lemma offunK ['u]: cancel offun tofun. + + lemma tofunK ['u]: forall (us : 'u list), size us = FinFrom.card => offun (tofun us) = us. + + lemma dfun_dmap ['u]: forall (d : plaintext -> 'u distr), dfun d = dmap (djoinmap d FinFrom.enum) tofun. + + lemma dfun_supp ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + f \in dfun d <=> forall (x : plaintext), f x \in d x. + + lemma dfun_fu ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_full (d x)) => is_full (dfun d). + + lemma dfun_uni ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_uniform (d x)) => is_uniform (dfun d). + + lemma dfun_funi ['u]: + forall (d : plaintext -> 'u distr), + (forall (x : plaintext), is_uniform (d x)) => + (forall (x : plaintext), is_full (d x)) => is_funiform (dfun d). + + lemma dfunE_djoin ['u]: + forall (du : plaintext -> 'u distr) (E : (plaintext -> 'u) -> bool), + mu (dfun du) E = mu (djoinmap du FinFrom.enum) (E \o tofun). + + lemma dfunE ['u]: + forall (du : plaintext -> 'u distr) (p : plaintext -> 'u -> bool), + mu (dfun du) (fun (f : plaintext -> 'u) => forall (x : plaintext), p x (f x)) = + (big predT (fun (x : plaintext) => mu (du x) (p x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma weight_dfun ['u]: + forall (df : plaintext -> 'u distr), + weight (dfun df) = + (big predT (fun (x : plaintext) => weight (df x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun_ll ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_lossless (d x)) => is_lossless (dfun d). + + lemma dlet_dfun ['u, 'v]: + forall (d1 : plaintext -> 'u distr) (d2 : plaintext -> 'u -> 'v distr), + dlet (dfun d1) (fun (f1 : plaintext -> 'u) => dfun (fun (x : plaintext) => d2 x (f1 x))) = + dfun (fun (x : plaintext) => dlet (d1 x) (fun (x1 : 'u) => d2 x x1)). + + lemma dfun_unit ['u]: forall (f : plaintext -> 'u), dfun (fun (x : plaintext) => dunit (f x)) = dunit f. + + lemma dmap_dfun ['u, 'v]: + forall (d : plaintext -> 'u distr) (F : plaintext -> 'u -> 'v), + dmap (dfun d) (fun (f : plaintext -> 'u) (x : plaintext) => F x (f x)) = + dfun (fun (x : plaintext) => dmap (d x) (fun (fx : 'u) => F x fx)). + + lemma dfun1E_fix1 ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext) (f : + plaintext -> 'u), mu1 (dfun d) f = mu1 (d x0) (f x0) * mu1 (dfun d.[x0 <- dunit (f x0)]) f. + + lemma dlet_dfun_fupdate_ll ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext) (v : 'u), + dlet (dfun d.[x0 <- dunit v]) + (fun (f : plaintext -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + dfun d. + + lemma dfunE_dlet_fix1 ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext), + dfun d = dlet (d x0) (fun (v : 'u) => dfun d.[x0 <- dunit v]). + + lemma dlet_dfun_update ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext), + dlet (dfun d) (fun (f : plaintext -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + (weight (d x0) \cdot dfun d). + + lemma dfun_projE ['u]: + forall (df : plaintext -> 'u distr) (x : plaintext), + dmap (dfun df) (fun (f : plaintext -> 'u) => f x) = (weight (dfun df) / weight (df x) \cdot df x). + + lemma eq_from_dfun_proj_eq ['u]: + forall (df1 df2 : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (df1 x)) => + (forall (x : plaintext), is_lossless (df2 x)) => + (forall (x : plaintext), + dmap (dfun df1) (fun (f : plaintext -> 'u) => f x) = + dmap (dfun df2) (fun (f : plaintext -> 'u) => f x)) => + df1 == df2. + + lemma dfun_prod1E ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr) (f : + plaintext -> 'u) (g : plaintext -> 'v), + mu1 (dfun (fun (x : plaintext) => df x `*` dg x)) (fun (x : plaintext) => (f x, g x)) = + mu1 (dfun df) f * mu1 (dfun dg) g. + + lemma dprod_funE ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr), + dfun df `*` dfun dg = + dmap (dfun (fun (x : plaintext) => df x `*` dg x)) + (fun (fg : plaintext -> 'u * 'v) => + ((fun (p : 'u * 'v) => p.`1) \o fg, (fun (p : 'u * 'v) => p.`2) \o fg)). + + lemma dfun_prodE ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr), + dfun (fun (x : plaintext) => df x `*` dg x) = + dmap (dfun df `*` dfun dg) + (fun (fg : (plaintext -> 'u) * (plaintext -> 'v)) (x : plaintext) => (fg.`1 x, fg.`2 x)). + + lemma dfun_condE ['u]: + forall (X : plaintext -> bool) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext -> 'u) * (plaintext -> 'u)) (x : plaintext) => + if X x then tf.`1 x else tf.`2 x) = + dfun (fun (x : plaintext) => if X x then dt x else df x). + + lemma dlet_dfun_cond ['u]: + forall (dX : plaintext -> bool distr) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dX x)) => + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : plaintext -> bool) => dfun (fun (x : plaintext) => if X x then dt x else df x)) = + dfun (fun (x : plaintext) => dlet (dX x) (fun (b : bool) => if b then dt x else df x)). + + lemma dfun_dcondE ['u]: + forall (dX : plaintext -> bool distr) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dX x)) => + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : plaintext -> bool) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext -> 'u) * (plaintext -> 'u)) (x : plaintext) => + if X x then tf.`1 x else tf.`2 x)) = + dfun (fun (x : plaintext) => (mu1 (dX x) true \cdot dt x) + (mu1 (dX x) false \cdot df x)). + end MUniFinFun. + + module FunRO = { + var f : plaintext -> sharedsecret + + proc init() : unit = { + FunRO.f <$ (dfun (fun (_ : plaintext) => srand))%MUniFinFun; + } + + proc get(x : plaintext) : sharedsecret = { + return FunRO.f x; + } + + proc set(x : plaintext, y : sharedsecret) : unit = { + FunRO.f <- fun (z : plaintext) => if z = x then y else FunRO.f z; + } + + proc sample(x : plaintext) : unit = {} + + proc rem(x : plaintext) : unit = {} + }. + + lemma FinRO_FunRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ), + equiv[ MainD(D, FinRO).distinguish ~ MainD(D, FunRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_FinRO_FunRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ) &m (x : unit) (p : + bool -> bool), + Pr[MainD(D, FinRO).distinguish(x) @ &m : p res] = Pr[MainD(D, FunRO).distinguish(x) @ &m : p res]. + end FinEager. + end RO2. + + module type Oracle_x2 = { + proc init() : unit {} + + proc get1(_ : plaintext) : W8.t Array32.t {} + + proc get2(_ : plaintext) : sharedsecret {} + } + + module type POracle_x2 = { + proc get1(_ : plaintext) : W8.t Array32.t {} + + proc get2(_ : plaintext) : sharedsecret {} + } + + module RO_x2(H1 : RO1.RO, H2 : RO2.RO) = { + proc init() : unit = { + H1.init(); + H2.init(); + } + + proc get1(x : plaintext) : W8.t Array32.t = H1.get + + proc get2(x : plaintext) : sharedsecret = H2.get + }. + + module type Scheme(O : POracle_x2) = { + proc kg() : publickey * skey {O.get1, O.get2} + + proc enc(pk : publickey) : (W8.t Array960.t * W8.t Array128.t) * sharedsecret {O.get1, O.get2} + + proc dec(sk : skey, c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option {O.get1, O.get2} + } + + module Correctness(H : Oracle_x2, S : Scheme) = { + proc main() : bool = { + var pk : publickey; + var sk : skey; + var c : W8.t Array960.t * W8.t Array128.t; + var k : sharedsecret; + var k' : sharedsecret option; + + H.init(); + (pk, sk) <@ S(H).kg(); + (c, k) <@ S(H).enc(pk); + k' <@ S(H).dec(sk, c); + + return k' <> Some k; + } + }. + + module type CCA_ORC = { + proc dec(c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option {} + } + + module type CCA_ADV(H : POracle_x2, O : CCA_ORC) = { + proc guess(pk : publickey, c : W8.t Array960.t * W8.t Array128.t, k : sharedsecret) : bool + {O.dec, H.get1, H.get2} + } + + module CCA(H : Oracle_x2, S : Scheme, A : CCA_ADV) = { + var cstar : (W8.t Array960.t * W8.t Array128.t) option + + var sk : skey + + module O = { + proc dec(c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var k : sharedsecret option; + + k <- None; + if (Some c <> CCA.cstar) + k <@ S(H).dec(CCA.sk, c); + + return k; + } + } + + module A = A(H, CCA(H, S, A).O) + + proc main() : bool = { + var pk : publickey; + var k1 : sharedsecret; + var ck0 : (W8.t Array960.t * W8.t Array128.t) * sharedsecret; + var b : bool; + var b' : bool; + + H.init(); + CCA.cstar <- None; + (pk, CCA.sk) <@ S(H).kg(); + k1 <$ srand; + b <$ {0,1}; + ck0 <@ S(H).enc(pk); + CCA.cstar <- Some ck0.`1; + b' <@ CCA(H, S, A).A.guess(pk, ck0.`1, if b then k1 else ck0.`2); + + return b' = b; + } + }. + end KEMROMx2. + + op qHT : int. + + axiom ge0_qHT: 0 <= qHT. + + op qHU : int. + + axiom ge0_qHU: 0 <= qHU. + + op qD : int. + + axiom ge0_qD: 0 <= qD. + + module UU(H : KEMROMx2.POracle_x2) = { + module HT = { + proc get(_ : plaintext) : W8.t Array32.t = H.get1 + } + + module HU = { + proc get(_ : plaintext) : sharedsecret = H.get2 + } + + proc kg() : publickey * KEMROMx2.skey = { + var pk : publickey; + var sk : W8.t Array1152.t; + var k : sharedsecret; + + (pk, sk) <$ TT.kg; + k <$ srand; + + return (pk, ((pk, sk), k)); + } + + proc enc(pk : publickey) : (W8.t Array960.t * W8.t Array128.t) * sharedsecret = { + var m : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var k : sharedsecret; + + m <$ srand; + c <@ TT.TT(UU(H).HT).enc(pk, m); + k <@ UU(H).HU.get(m); + + return (c, k); + } + + proc dec(sk : KEMROMx2.skey, c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var m' : plaintext option; + var k : sharedsecret; + + k <- witness; + m' <@ TT.TT(UU(H).HT).dec(sk.`1, c); + if (m' = None) + k <- J sk.`2 c; + else + k <@ UU(H).HU.get(oget m'); + + return Some k; + } + }. + + module B_UC(HT : TT.PKEROM.POracle) = { + proc find(pk : publickey, sk : TT.PKEROM.skey) : plaintext = { + var m : plaintext; + + m <$ srand; + + return m; + } + }. + + lemma correctness_rel: + forall &m, + Pr[KEMROMx2.Correctness(KEMROMx2.RO_x2(KEMROMx2.RO1.RO, KEMROMx2.RO2.RO), UU).main() @ &m : res] <= + Pr[TT.PKEROM.Correctness_Adv(TT.PKEROM.RO.RO, TT.TT, B_UC).main() @ &m : res]. + + lemma correctness: + forall &m, + TT.qHC = 0 => + 1 < TT.FinT.card => + Pr[KEMROMx2.Correctness(KEMROMx2.RO_x2(KEMROMx2.RO1.RO, KEMROMx2.RO2.RO), UU).main() @ &m : res] <= + Pr[TT.PKE.Correctness_Adv(TT.BasePKE, TT.B(B_UC, TT.PKEROM.RO.RO)).main() @ &m : res]. + + module CountHx2(H : KEMROMx2.POracle_x2) = { + var c_hu : int + + var c_ht : int + + proc init() : unit = { + CountHx2.c_ht <- 0; + CountHx2.c_hu <- 0; + } + + proc get1(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <@ H.get1(x); + CountHx2.c_ht <- CountHx2.c_ht + 1; + + return r; + } + + proc get2(x : plaintext) : sharedsecret = { + var r : sharedsecret; + + r <@ H.get2(x); + CountHx2.c_hu <- CountHx2.c_hu + 1; + + return r; + } + }. + + module UU1(PRFO : J.PRF_Oracles, H : KEMROMx2.POracle_x2) = { + proc enc(pk : publickey) : (W8.t Array960.t * W8.t Array128.t) * sharedsecret = UU(H).enc + + proc kg() : publickey * KEMROMx2.skey = { + var pk : publickey; + var sk : W8.t Array1152.t; + + (pk, sk) <$ TT.kg; + + return (pk, ((pk, sk), witness)); + } + + proc dec(sk : KEMROMx2.skey, c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var m' : plaintext option; + var k : sharedsecret; + + k <- witness; + m' <@ TT.TT(UU(H).HT).dec(sk.`1, c); + if (m' = None) + k <@ PRFO.f(c); + else + k <@ UU(H).HU.get(oget m'); + + return Some k; + } + }. + + module Gm1P(H : KEMROMx2.Oracle_x2, A : KEMROMx2.CCA_ADV, PRFO : J.PRF_Oracles) = { + proc main'() : bool = { + var pk : publickey; + var k1 : sharedsecret; + var ck0 : (W8.t Array960.t * W8.t Array128.t) * sharedsecret; + var b : bool; + var b' : bool; + + H.init(); + KEMROMx2.CCA.cstar <- None; + (pk, KEMROMx2.CCA.sk) <@ UU1(PRFO, H).kg(); + k1 <$ srand; + b <$ {0,1}; + ck0 <@ UU1(PRFO, H).enc(pk); + KEMROMx2.CCA.cstar <- Some ck0.`1; + b' <@ KEMROMx2.CCA(H, UU1(PRFO), A).A.guess(pk, ck0.`1, if b then k1 else ck0.`2); + + return b' = b; + } + }. + + module Gm1(H : KEMROMx2.Oracle_x2, A : KEMROMx2.CCA_ADV) = { + proc main() : bool = { + var b : bool; + + RF.RF.init(); + b <@ Gm1P(H, A, RF.RF).main'(); + + return b; + } + }. + + module D(A : KEMROMx2.CCA_ADV, PRFO : J.PRF_Oracles) = { + proc distinguish() : bool = Gm1P(KEMROMx2.RO_x2(KEMROMx2.RO1.RO, KEMROMx2.RO2.RO), A, PRFO).main' + }. + + theory RO1E. + abstract theory EagerCore. + axiom dout_ll: forall (_ : plaintext), is_lossless srand. + + module type Orcl = { + proc f(x : plaintext) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : plaintext list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : plaintext list, l2 : plaintext list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : plaintext, l : plaintext list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : plaintext, t2 : plaintext) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : plaintext list) (i : plaintext) (s2 : plaintext list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : plaintext), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : plaintext -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : plaintext), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = KEMROMx2.RO1.FRO.init + + proc get(x : plaintext) : W8.t Array32.t = { + var r : W8.t Array32.t; + + r <$ srand; + if ((x \notin KEMROMx2.RO1.FRO.m)%SmtMap \/ ((KEMROMx2.RO1.FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + KEMROMx2.RO1.FRO.m.[x] <- (r, PROM.Known); + + return (oget (KEMROMx2.RO1.FRO.m.[x])%SmtMap).`1; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = KEMROMx2.RO1.FRO.set + + proc rem(x : plaintext) : unit = KEMROMx2.RO1.FRO.rem + + proc sample(x : plaintext) : unit = KEMROMx2.RO1.FRO.sample + + proc queried(x : plaintext, f : PROM.flag) : bool = KEMROMx2.RO1.FRO.queried + + proc allKnown() : (plaintext, W8.t Array32.t) SmtMap.fmap = KEMROMx2.RO1.FRO.allKnown + + module I = { + proc f(x : plaintext) : unit = { + var c : W8.t Array32.t; + + c <$ srand; + KEMROMx2.RO1.FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown KEMROMx2.RO1.FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: + eager[ RRO.resample();, KEMROMx2.RO1.FRO.init ~ RRO.init, RRO.resample( + ); : ={KEMROMx2.RO1.FRO.m} ==> ={KEMROMx2.RO1.FRO.m}]. + + lemma iter_perm2: + equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={KEMROMx2.RO1.FRO.m, t1, t2} ==> ={KEMROMx2.RO1.FRO.m}]. + + lemma I_f_neq: + forall (x1 : plaintext) (mx1 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, KEMROMx2.RO1.FRO.m} /\ x1 <> arg{1} /\ (KEMROMx2.RO1.FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={KEMROMx2.RO1.FRO.m} /\ (KEMROMx2.RO1.FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : plaintext) (mx1 mx2 : (W8.t Array32.t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) KEMROMx2.RO1.FRO.m{1} KEMROMx2.RO1.FRO.m{2})%SmtMap /\ + (KEMROMx2.RO1.FRO.m{1}.[x1])%SmtMap = mx1 /\ (KEMROMx2.RO1.FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) KEMROMx2.RO1.FRO.m{1} KEMROMx2.RO1.FRO.m{2})%SmtMap /\ + (KEMROMx2.RO1.FRO.m{1}.[x1])%SmtMap = mx1 /\ (KEMROMx2.RO1.FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : plaintext) (r1 : W8.t Array32.t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (KEMROMx2.RO1.FRO.m{1}.[x1])%SmtMap = None /\ + KEMROMx2.RO1.FRO.m{2} = (KEMROMx2.RO1.FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (KEMROMx2.RO1.FRO.m{1}.[x1])%SmtMap = None /\ + KEMROMx2.RO1.FRO.m{2} = (KEMROMx2.RO1.FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, KEMROMx2.RO1.FRO.get ~ RRO.get, RRO.resample( + ); : ={arg, KEMROMx2.RO1.FRO.m} ==> ={res, KEMROMx2.RO1.FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, KEMROMx2.RO1.FRO.set ~ RRO.set, RRO.resample( + ); : ={x, y} /\ ={KEMROMx2.RO1.FRO.m} ==> ={res, KEMROMx2.RO1.FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, KEMROMx2.RO1.FRO.rem ~ RRO.rem, RRO.resample( + ); : ={arg, KEMROMx2.RO1.FRO.m} ==> ={res, KEMROMx2.RO1.FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, KEMROMx2.RO1.FRO.sample ~ RRO.sample, RRO.resample( + ); : ={arg, KEMROMx2.RO1.FRO.m} ==> ={res, KEMROMx2.RO1.FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, KEMROMx2.RO1.FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={KEMROMx2.RO1.FRO.m} ==> ={res, KEMROMx2.RO1.FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, KEMROMx2.RO1.FRO.allKnown ~ RRO.allKnown, RRO.resample( + ); : ={KEMROMx2.RO1.FRO.m} ==> ={res, KEMROMx2.RO1.FRO.m}]. + + lemma eager_D: + forall (D0(G : KEMROMx2.RO1.FRO) <: KEMROMx2.RO1.FRO_Distinguisher{+all mem, -KEMROMx2.RO1.FRO} ), + eager[ RRO.resample();, D0(KEMROMx2.RO1.FRO).distinguish ~ D0(RRO).distinguish, RRO.resample( + ); : ={glob D0, KEMROMx2.RO1.FRO.m, arg} ==> ={KEMROMx2.RO1.FRO.m, glob D0} /\ ={res}]. + + module Eager(D0 : KEMROMx2.RO1.FRO_Distinguisher) = { + proc main1(x : unit) : bool = { + var b : bool; + + KEMROMx2.RO1.FRO.init(); + b <@ D0(KEMROMx2.RO1.FRO).distinguish(x); + + return b; + } + + proc main2(x : unit) : bool = { + var b : bool; + + KEMROMx2.RO1.FRO.init(); + b <@ D0(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D0(G : KEMROMx2.RO1.FRO) <: KEMROMx2.RO1.FRO_Distinguisher{+all mem, -KEMROMx2.RO1.FRO} ), + equiv[ Eager(D0).main1 ~ Eager(D0).main2 : ={glob D0, arg} ==> ={res, KEMROMx2.RO1.FRO.m, glob D0}]. + + lemma LRO_RRO_init: + equiv[ KEMROMx2.RO1.RO.init ~ KEMROMx2.RO1.FRO.init : + true ==> KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ KEMROMx2.RO1.RO.get ~ RRO.get : + ={arg} /\ KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap ==> + ={res} /\ KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ KEMROMx2.RO1.RO.set ~ KEMROMx2.RO1.FRO.set : + ={x, y} /\ KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap ==> + KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ KEMROMx2.RO1.RO.rem ~ KEMROMx2.RO1.FRO.rem : + ={arg} /\ KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap ==> + KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ KEMROMx2.RO1.LRO.sample ~ KEMROMx2.RO1.FRO.sample : + ={arg} /\ KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap ==> + KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D0(G : KEMROMx2.RO1.RO) <: + KEMROMx2.RO1.RO_Distinguisher{+all mem, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO} ), + equiv[ D0(KEMROMx2.RO1.LRO).distinguish ~ D0(RRO).distinguish : + ={glob D0, arg} /\ KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap ==> + ={res, glob D0} /\ KEMROMx2.RO1.RO.m{1} = (restr PROM.Known KEMROMx2.RO1.FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D0(G : KEMROMx2.RO1.RO) <: + KEMROMx2.RO1.RO_Distinguisher{+all mem, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ D0(KEMROMx2.RO1.RO).distinguish ~ D0(KEMROMx2.RO1.LRO).distinguish : + ={glob D0, KEMROMx2.RO1.RO.m, arg} ==> ={res, glob D0}]. + + lemma RO_LRO: + forall (D0(G : KEMROMx2.RO1.RO) <: + KEMROMx2.RO1.RO_Distinguisher{+all mem, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO} ), + (forall (_ : plaintext), is_lossless srand) => + equiv[ KEMROMx2.RO1.MainD(D0, KEMROMx2.RO1.RO).distinguish ~ + KEMROMx2.RO1.MainD(D0, KEMROMx2.RO1.LRO).distinguish : + ={glob D0, arg} ==> ={res, glob D0}]. + + theory FinFrom. + type t = plaintext. + + op enum : plaintext list = TT.FinT.enum. + + op card : int = size enum. + + lemma enum_spec: forall (x : t), count (pred1 x) enum = 1. + + lemma enumP: forall (x : t), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : t list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinFrom. + + module FinRO = { + proc rem(x : plaintext) : unit = KEMROMx2.RO1.RO.rem + + proc set(x : plaintext, y : W8.t Array32.t) : unit = KEMROMx2.RO1.RO.set + + proc init() : unit = { + var l : FinFrom.t list; + + l <- FinFrom.enum; + KEMROMx2.RO1.RO.init(); + while (l <> []){ + KEMROMx2.RO1.RO.sample(head witness l); + l <- behead l; + } + } + + proc get(x : plaintext) : W8.t Array32.t = { + return oget (KEMROMx2.RO1.RO.m.[x])%SmtMap; + } + + proc sample(x : plaintext) : unit = {} + }. + + module type FinRO_Distinguisher(G : KEMROMx2.RO1.RO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.sample} + } + + lemma RO_FinRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D0(G : KEMROMx2.RO1.RO) <: FinRO_Distinguisher{+all mem, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO} ), + equiv[ KEMROMx2.RO1.MainD(D0, KEMROMx2.RO1.RO).distinguish ~ KEMROMx2.RO1.MainD(D0, FinRO).distinguish : + ={glob D0, arg} ==> ={res, glob D0}]. + + lemma pr_RO_FinRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D0(G : KEMROMx2.RO1.RO) <: FinRO_Distinguisher{+all mem, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO} ) &m + (x : unit) (p : bool -> bool), + Pr[KEMROMx2.RO1.MainD(D0, KEMROMx2.RO1.RO).distinguish(x) @ &m : p res] = + Pr[KEMROMx2.RO1.MainD(D0, FinRO).distinguish(x) @ &m : p res]. + + theory MUniFinFun. + op mfun ['u] (d : plaintext -> 'u distr) (f : plaintext -> 'u) : real = + (big predT (fun (x : plaintext) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma mfunE ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mfun d f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + lemma isdistr_dfun ['u]: forall (d : plaintext -> 'u distr), isdistr (mfun d). + + op dfun ['u] (d : plaintext -> 'u distr) : (plaintext -> 'u) distr = mk (mfun d). + + lemma dfun1E ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mu1 (dfun d) f = (big predT (fun (x : plaintext) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun1E_djoin ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + mu1 (dfun d) f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + op tofun ['u] (us : 'u list) (x : plaintext) : 'u = nth witness us (index x FinFrom.enum). + + op offun ['u] (f : plaintext -> 'u) : 'u list = map f FinFrom.enum. + + lemma offunK ['u]: cancel offun tofun. + + lemma tofunK ['u]: forall (us : 'u list), size us = FinFrom.card => offun (tofun us) = us. + + lemma dfun_dmap ['u]: forall (d : plaintext -> 'u distr), dfun d = dmap (djoinmap d FinFrom.enum) tofun. + + lemma dfun_supp ['u]: + forall (d : plaintext -> 'u distr) (f : plaintext -> 'u), + f \in dfun d <=> forall (x : plaintext), f x \in d x. + + lemma dfun_fu ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_full (d x)) => is_full (dfun d). + + lemma dfun_uni ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_uniform (d x)) => is_uniform (dfun d). + + lemma dfun_funi ['u]: + forall (d : plaintext -> 'u distr), + (forall (x : plaintext), is_uniform (d x)) => + (forall (x : plaintext), is_full (d x)) => is_funiform (dfun d). + + lemma dfunE_djoin ['u]: + forall (du : plaintext -> 'u distr) (E : (plaintext -> 'u) -> bool), + mu (dfun du) E = mu (djoinmap du FinFrom.enum) (E \o tofun). + + lemma dfunE ['u]: + forall (du : plaintext -> 'u distr) (p : plaintext -> 'u -> bool), + mu (dfun du) (fun (f : plaintext -> 'u) => forall (x : plaintext), p x (f x)) = + (big predT (fun (x : plaintext) => mu (du x) (p x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma weight_dfun ['u]: + forall (df : plaintext -> 'u distr), + weight (dfun df) = (big predT (fun (x : plaintext) => weight (df x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun_ll ['u]: + forall (d : plaintext -> 'u distr), (forall (x : plaintext), is_lossless (d x)) => is_lossless (dfun d). + + lemma dlet_dfun ['u, 'v]: + forall (d1 : plaintext -> 'u distr) (d2 : plaintext -> 'u -> 'v distr), + dlet (dfun d1) (fun (f1 : plaintext -> 'u) => dfun (fun (x : plaintext) => d2 x (f1 x))) = + dfun (fun (x : plaintext) => dlet (d1 x) (fun (x1 : 'u) => d2 x x1)). + + lemma dfun_unit ['u]: forall (f : plaintext -> 'u), dfun (fun (x : plaintext) => dunit (f x)) = dunit f. + + lemma dmap_dfun ['u, 'v]: + forall (d : plaintext -> 'u distr) (F : plaintext -> 'u -> 'v), + dmap (dfun d) (fun (f : plaintext -> 'u) (x : plaintext) => F x (f x)) = + dfun (fun (x : plaintext) => dmap (d x) (fun (fx : 'u) => F x fx)). + + lemma dfun1E_fix1 ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext) (f : + plaintext -> 'u), mu1 (dfun d) f = mu1 (d x0) (f x0) * mu1 (dfun d.[x0 <- dunit (f x0)]) f. + + lemma dlet_dfun_fupdate_ll ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext) (v : 'u), + dlet (dfun d.[x0 <- dunit v]) (fun (f : plaintext -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + dfun d. + + lemma dfunE_dlet_fix1 ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext), + dfun d = dlet (d x0) (fun (v : 'u) => dfun d.[x0 <- dunit v]). + + lemma dlet_dfun_update ['u]: + forall (d : plaintext -> 'u distr) (x0 : plaintext), + dlet (dfun d) (fun (f : plaintext -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + (weight (d x0) \cdot dfun d). + + lemma dfun_projE ['u]: + forall (df : plaintext -> 'u distr) (x : plaintext), + dmap (dfun df) (fun (f : plaintext -> 'u) => f x) = (weight (dfun df) / weight (df x) \cdot df x). + + lemma eq_from_dfun_proj_eq ['u]: + forall (df1 df2 : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (df1 x)) => + (forall (x : plaintext), is_lossless (df2 x)) => + (forall (x : plaintext), + dmap (dfun df1) (fun (f : plaintext -> 'u) => f x) = + dmap (dfun df2) (fun (f : plaintext -> 'u) => f x)) => + df1 == df2. + + lemma dfun_prod1E ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr) (f : + plaintext -> 'u) (g : plaintext -> 'v), + mu1 (dfun (fun (x : plaintext) => df x `*` dg x)) (fun (x : plaintext) => (f x, g x)) = + mu1 (dfun df) f * mu1 (dfun dg) g. + + lemma dprod_funE ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr), + dfun df `*` dfun dg = + dmap (dfun (fun (x : plaintext) => df x `*` dg x)) + (fun (fg : plaintext -> 'u * 'v) => + ((fun (p : 'u * 'v) => p.`1) \o fg, (fun (p : 'u * 'v) => p.`2) \o fg)). + + lemma dfun_prodE ['u, 'v]: + forall (df : plaintext -> 'u distr) (dg : plaintext -> 'v distr), + dfun (fun (x : plaintext) => df x `*` dg x) = + dmap (dfun df `*` dfun dg) + (fun (fg : (plaintext -> 'u) * (plaintext -> 'v)) (x : plaintext) => (fg.`1 x, fg.`2 x)). + + lemma dfun_condE ['u]: + forall (X : plaintext -> bool) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext -> 'u) * (plaintext -> 'u)) (x : plaintext) => if X x then tf.`1 x else tf.`2 x) = + dfun (fun (x : plaintext) => if X x then dt x else df x). + + lemma dlet_dfun_cond ['u]: + forall (dX : plaintext -> bool distr) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dX x)) => + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dlet (dfun dX) (fun (X : plaintext -> bool) => dfun (fun (x : plaintext) => if X x then dt x else df x)) = + dfun (fun (x : plaintext) => dlet (dX x) (fun (b : bool) => if b then dt x else df x)). + + lemma dfun_dcondE ['u]: + forall (dX : plaintext -> bool distr) (dt df : plaintext -> 'u distr), + (forall (x : plaintext), is_lossless (dX x)) => + (forall (x : plaintext), is_lossless (dt x)) => + (forall (x : plaintext), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : plaintext -> bool) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext -> 'u) * (plaintext -> 'u)) (x : plaintext) => + if X x then tf.`1 x else tf.`2 x)) = + dfun (fun (x : plaintext) => (mu1 (dX x) true \cdot dt x) + (mu1 (dX x) false \cdot df x)). + end MUniFinFun. + + module FunRO = { + var f : plaintext -> W8.t Array32.t + + proc init() : unit = { + FunRO.f <$ (dfun (fun (_ : plaintext) => srand))%MUniFinFun; + } + + proc get(x : plaintext) : W8.t Array32.t = { + return FunRO.f x; + } + + proc set(x : plaintext, y : W8.t Array32.t) : unit = { + FunRO.f <- fun (z : plaintext) => if z = x then y else FunRO.f z; + } + + proc sample(x : plaintext) : unit = {} + + proc rem(x : plaintext) : unit = {} + }. + + lemma FinRO_FunRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D0(G : KEMROMx2.RO1.RO) <: + FinRO_Distinguisher{+all mem, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -FunRO} ), + equiv[ KEMROMx2.RO1.MainD(D0, FinRO).distinguish ~ KEMROMx2.RO1.MainD(D0, FunRO).distinguish : + ={glob D0, arg} ==> ={res, glob D0}]. + + lemma pr_FinRO_FunRO_D: + (forall (_ : plaintext), is_lossless srand) => + forall (D0(G : KEMROMx2.RO1.RO) <: + FinRO_Distinguisher{+all mem, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -FunRO} ) &m (x : unit) + (p : bool -> bool), + Pr[KEMROMx2.RO1.MainD(D0, FinRO).distinguish(x) @ &m : p res] = + Pr[KEMROMx2.RO1.MainD(D0, FunRO).distinguish(x) @ &m : p res]. + end RO1E. + + module RO_x2E = KEMROMx2.RO_x2(RO1E.FunRO, KEMROMx2.RO2.RO). + + module UU2(H : KEMROMx2.POracle_x2) = { + proc kg() : publickey * KEMROMx2.skey = UU1(RF.RF, H).kg + + proc enc(pk : publickey) : (W8.t Array960.t * W8.t Array128.t) * sharedsecret = UU1(RF.RF, H).enc + + var lD : ((W8.t Array960.t * W8.t Array128.t) * sharedsecret) list + + proc dec(sk : KEMROMx2.skey, c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var k : sharedsecret; + var ko : sharedsecret option; + + ko <- None; + if (assoc UU2.lD c <> None) + ko <- assoc UU2.lD c; + else { + k <$ srand; + ko <- Some k; + UU2.lD <- (c, k) :: UU2.lD; + } + + return ko; + } + }. + + module H1 = { + var bad : bool + + proc init() : unit = {} + + proc get1(x : plaintext) : W8.t Array32.t = RO_x2E.get1 + + proc get2(m : plaintext) : sharedsecret = { + var k : sharedsecret; + var cm : W8.t Array960.t * W8.t Array128.t; + + cm <- enc (RO1E.FunRO.f m) KEMROMx2.CCA.sk.`1.`1 m; + H1.bad <- if dec KEMROMx2.CCA.sk.`1.`2 cm <> Some m then true else H1.bad; + k <$ srand; + if ((m \notin KEMROMx2.RO2.RO.m)%SmtMap) + KEMROMx2.RO2.RO.m.[m] <- k; + + return oget (KEMROMx2.RO2.RO.m.[m])%SmtMap; + } + }. + + module H2(O1 : TT.PKEROM.POracle) = { + var merr : plaintext option + + var invert : bool + + var mtgt : plaintext + + proc init() : unit = {} + + proc get1(x : plaintext) : W8.t Array32.t = RO_x2E.get1 + + proc get2(m : plaintext) : sharedsecret = { + var k : sharedsecret; + var cm : W8.t Array960.t * W8.t Array128.t; + var r : W8.t Array32.t; + + H2.mtgt <- if KEMROMx2.CCA.cstar = None then m else H2.mtgt; + r <@ O1.get(m); + cm <- enc r KEMROMx2.CCA.sk.`1.`1 m; + H1.bad <- if dec KEMROMx2.CCA.sk.`1.`2 cm <> Some m then true else H1.bad; + H2.merr <- if H2.merr = None && H1.bad then Some m else H2.merr; + H2.invert <- + if KEMROMx2.CCA.cstar <> None && + m = H2.mtgt && dec KEMROMx2.CCA.sk.`1.`2 (oget KEMROMx2.CCA.cstar) = Some H2.mtgt then true + else H2.invert; + k <$ srand; + if ((m \notin KEMROMx2.RO2.RO.m)%SmtMap) { + if (assoc UU2.lD cm <> None) + k <- oget (assoc UU2.lD cm); + else + UU2.lD <- if H1.bad then UU2.lD else (cm, k) :: UU2.lD; + KEMROMx2.RO2.RO.m <- if H1.bad then KEMROMx2.RO2.RO.m else (KEMROMx2.RO2.RO.m.[m <- k])%SmtMap; + } + + return if H1.bad then witness else oget (KEMROMx2.RO2.RO.m.[m])%SmtMap; + } + }. + + module Gm2(H : KEMROMx2.Oracle_x2, S : KEMROMx2.Scheme, A : KEMROMx2.CCA_ADV) = { + module O = { + proc dec(c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var k : sharedsecret option; + + k <- None; + if (Some c <> KEMROMx2.CCA.cstar) + k <@ S(H).dec(KEMROMx2.CCA.sk, c); + + return k; + } + } + + proc main2() : bool = { + var pk : publickey; + var k1 : sharedsecret; + var ck0 : (W8.t Array960.t * W8.t Array128.t) * sharedsecret; + var cstar : (W8.t Array960.t * W8.t Array128.t) option; + var b : bool; + var b' : bool; + + H1.bad <- false; + H2.merr <- None; + H2.invert <- false; + RF.RF.init(); + RO_x2E.init(); + UU2.lD <- []; + KEMROMx2.CCA.cstar <- None; + (pk, KEMROMx2.CCA.sk) <@ S(H).kg(); + k1 <$ srand; + b <$ {0,1}; + ck0 <@ UU2(H).enc(pk); + KEMROMx2.CCA.cstar <- Some ck0.`1; + b' <@ KEMROMx2.CCA(H, S, A).A.guess(pk, ck0.`1, if b then k1 else ck0.`2); + + return b' = b; + } + + proc main() : bool = { + var win : bool; + var nobias : bool; + + win <@ Gm2(H, S, A).main2(); + nobias <$ {0,1}; + + return if H1.bad then nobias else win; + } + }. + + module BUUC(A : KEMROMx2.CCA_ADV, H : TT.PKEROM.POracle) = { + module H2B = { + proc get2(m : plaintext) : sharedsecret = H2(H).get2 + + proc init() : unit = H2(H).init + + proc get1(x : plaintext) : W8.t Array32.t = H.get + } + + proc find(pk : publickey, sk : TT.PKEROM.skey) : plaintext = { + var k1 : sharedsecret; + var ck0 : (W8.t Array960.t * W8.t Array128.t) * sharedsecret; + var cstar : (W8.t Array960.t * W8.t Array128.t) option; + var b : bool; + var b' : bool; + var z : sharedsecret; + + H1.bad <- false; + H2.merr <- None; + H2.invert <- false; + RF.RF.init(); + KEMROMx2.RO2.RO.init(); + UU2.lD <- []; + KEMROMx2.CCA.cstar <- None; + KEMROMx2.CCA.sk <- (sk, witness); + k1 <$ srand; + b <$ {0,1}; + ck0 <@ UU2(BUUC(A, H).H2B).enc(pk); + KEMROMx2.CCA.cstar <- Some ck0.`1; + CountHx2(BUUC(A, H).H2B).init(); + b' <@ KEMROMx2.CCA(CountHx2(BUUC(A, H).H2B), UU2, A).A.guess(pk, ck0.`1, if b then k1 else ck0.`2); + + return oget H2.merr; + } + }. + + module Gm3(H : KEMROMx2.Oracle_x2, S : KEMROMx2.Scheme, A : KEMROMx2.CCA_ADV) = { + module O = { + proc dec(c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var k : sharedsecret option; + + k <- None; + if (Some c <> KEMROMx2.CCA.cstar) + k <@ S(H).dec(KEMROMx2.CCA.sk, c); + + return k; + } + } + + proc main() : bool = { + var pk : publickey; + var k1 : sharedsecret; + var k2 : sharedsecret; + var b : bool; + var b' : bool; + var r : W8.t Array32.t; + var cm : W8.t Array960.t * W8.t Array128.t; + var nobias : bool; + + H1.bad <- false; + H2.merr <- None; + H2.invert <- false; + RF.RF.init(); + RO_x2E.init(); + UU2.lD <- []; + KEMROMx2.CCA.cstar <- None; + (pk, KEMROMx2.CCA.sk) <@ S(H).kg(); + k1 <$ srand; + k2 <$ srand; + b <$ {0,1}; + H2.mtgt <$ srand; + r <@ H.get1(H2.mtgt); + cm <- enc r pk H2.mtgt; + H1.bad <- if dec KEMROMx2.CCA.sk.`1.`2 cm <> Some H2.mtgt then true else H1.bad; + H2.merr <- if H2.merr = None && H1.bad then Some H2.mtgt else H2.merr; + UU2.lD <- (cm, witness) :: UU2.lD; + KEMROMx2.CCA.cstar <- Some cm; + b' <@ KEMROMx2.CCA(H, S, A).A.guess(pk, cm, if b then k1 else k2); + nobias <$ {0,1}; + + return if H1.bad then nobias else b' = b; + } + + proc main_0adv() : bool = { + var pk : publickey; + var k : sharedsecret; + var b : bool; + var b' : bool; + var r : W8.t Array32.t; + var cm : W8.t Array960.t * W8.t Array128.t; + var nobias : bool; + + H1.bad <- false; + H2.merr <- None; + H2.invert <- false; + RF.RF.init(); + RO_x2E.init(); + UU2.lD <- []; + KEMROMx2.CCA.cstar <- None; + (pk, KEMROMx2.CCA.sk) <@ S(H).kg(); + k <$ srand; + H2.mtgt <$ srand; + r <@ H.get1(H2.mtgt); + cm <- enc r pk H2.mtgt; + H1.bad <- if dec KEMROMx2.CCA.sk.`1.`2 cm <> Some H2.mtgt then true else H1.bad; + H2.merr <- if H2.merr = None && H1.bad then Some H2.mtgt else H2.merr; + UU2.lD <- (cm, witness) :: UU2.lD; + KEMROMx2.CCA.cstar <- Some cm; + b' <@ KEMROMx2.CCA(H, S, A).A.guess(pk, cm, k); + nobias <$ {0,1}; + b <$ {0,1}; + + return if H1.bad then nobias else b' = b; + } + }. + + module H2BOW(OO1 : TT.PKEROM.POracle) = { + proc init() : unit = {} + + proc get1(x : plaintext) : W8.t Array32.t = RO_x2E.get1 + + proc get2(m : plaintext) : sharedsecret = { + var k : sharedsecret; + var cm : W8.t Array960.t * W8.t Array128.t; + var r : W8.t Array32.t; + + r <@ OO1.get(m); + cm <- enc r KEMROMx2.CCA.sk.`1.`1 m; + H1.bad <- if dec KEMROMx2.CCA.sk.`1.`2 cm <> Some m then true else H1.bad; + H2.merr <- if H2.merr = None && H1.bad then Some m else H2.merr; + H2.invert <- + if KEMROMx2.CCA.cstar <> None && + m = H2.mtgt && dec KEMROMx2.CCA.sk.`1.`2 (oget KEMROMx2.CCA.cstar) = Some H2.mtgt then true + else H2.invert; + k <$ srand; + if ((m \notin KEMROMx2.RO2.RO.m)%SmtMap) { + if (assoc UU2.lD cm <> None) + k <- oget (assoc UU2.lD cm); + else + UU2.lD <- (cm, k) :: UU2.lD; + KEMROMx2.RO2.RO.m.[m] <- k; + } + + return oget (KEMROMx2.RO2.RO.m.[m])%SmtMap; + } + }. + + module BUUOW(A : KEMROMx2.CCA_ADV, H : TT.PKEROM.POracle, O : TT.PKEROM.VA_ORC) = { + module H2B = { + proc get2(m : plaintext) : sharedsecret = H2BOW(H).get2 + + proc init() : unit = H2BOW(H).init + + proc get1(x : plaintext) : W8.t Array32.t = H.get + } + + proc find(pk : publickey, cm : W8.t Array960.t * W8.t Array128.t) : plaintext option = { + var k1 : sharedsecret; + var k2 : sharedsecret; + var b : bool; + var b' : bool; + var r : W8.t Array32.t; + + H1.bad <- false; + H2.merr <- None; + H2.invert <- false; + RF.RF.init(); + KEMROMx2.RO2.RO.init(); + UU2.lD <- []; + KEMROMx2.CCA.cstar <- None; + KEMROMx2.CCA.sk <- ((pk, witness), witness); + k1 <$ srand; + k2 <$ srand; + b <$ {0,1}; + UU2.lD <- (cm, witness) :: UU2.lD; + KEMROMx2.CCA.cstar <- Some cm; + b' <@ KEMROMx2.CCA(BUUOW(A, H, O).H2B, UU2, A).A.guess(pk, cm, if b then k1 else k2); + + return + if (card + ((filter (fun (m0 : plaintext) => enc (RO1E.FunRO.f m0) pk m0 = oget KEMROMx2.CCA.cstar) + ((fdom KEMROMx2.RO2.RO.m))%SmtMap))%FSet)%FSet = + 1 then + Some + (head witness + ((elems + ((filter (fun (m0 : plaintext) => enc (RO1E.FunRO.f m0) pk m0 = oget KEMROMx2.CCA.cstar) + ((fdom KEMROMx2.RO2.RO.m))%SmtMap))%FSet))%FSet) + else None; + } + }. + + module H2BOWMod(OO1 : TT.PKEROM.POracle) = { + var crd : int + + var mf : plaintext option + + proc init() : unit = {} + + proc get1(x : plaintext) : W8.t Array32.t = RO_x2E.get1 + + proc get2(m : plaintext) : sharedsecret = { + var k : sharedsecret; + var cm : W8.t Array960.t * W8.t Array128.t; + var r : W8.t Array32.t; + + r <@ OO1.get(m); + cm <- enc r KEMROMx2.CCA.sk.`1.`1 m; + H1.bad <- if dec KEMROMx2.CCA.sk.`1.`2 cm <> Some m then true else H1.bad; + H2.merr <- if H2.merr = None && H1.bad then Some m else H2.merr; + H2.invert <- + if KEMROMx2.CCA.cstar <> None && + m = H2.mtgt && dec KEMROMx2.CCA.sk.`1.`2 (oget KEMROMx2.CCA.cstar) = Some H2.mtgt then true + else H2.invert; + k <$ srand; + if ((m \notin KEMROMx2.RO2.RO.m)%SmtMap) { + H2BOWMod.crd <- H2BOWMod.crd + b2i (Some cm = KEMROMx2.CCA.cstar); + H2BOWMod.mf <- if Some cm = KEMROMx2.CCA.cstar then Some m else H2BOWMod.mf; + if (assoc UU2.lD cm <> None) + k <- oget (assoc UU2.lD cm); + else + UU2.lD <- (cm, k) :: UU2.lD; + KEMROMx2.RO2.RO.m.[m] <- k; + } + + return oget (KEMROMx2.RO2.RO.m.[m])%SmtMap; + } + }. + + module BUUOWMod(A : KEMROMx2.CCA_ADV, H : TT.PKEROM.POracle, O : TT.PKEROM.VA_ORC) = { + module H2B = { + proc get2(m : plaintext) : sharedsecret = H2BOWMod(H).get2 + + proc init() : unit = H2BOWMod(H).init + + proc get1(x : plaintext) : W8.t Array32.t = H.get + } + + proc find(pk : publickey, cm : W8.t Array960.t * W8.t Array128.t) : plaintext option = { + var k1 : sharedsecret; + var k2 : sharedsecret; + var b : bool; + var b' : bool; + + H1.bad <- false; + H2.merr <- None; + H2.invert <- false; + RF.RF.init(); + KEMROMx2.RO2.RO.init(); + UU2.lD <- []; + KEMROMx2.CCA.cstar <- None; + KEMROMx2.CCA.sk <- ((pk, witness), witness); + k1 <$ srand; + k2 <$ srand; + b <$ {0,1}; + UU2.lD <- (cm, witness) :: UU2.lD; + KEMROMx2.CCA.cstar <- Some cm; + H2BOWMod.crd <- 0; + H2BOWMod.mf <- None; + CountHx2(BUUOWMod(A, H, O).H2B).init(); + b' <@ KEMROMx2.CCA(CountHx2(BUUOWMod(A, H, O).H2B), UU2, A).A.guess(pk, cm, if b then k1 else k2); + + return if H2BOWMod.crd = 1 then H2BOWMod.mf else None; + } + }. + + module BUUCI(A : KEMROMx2.CCA_ADV, H : TT.PKEROM.POracle) = { + module H2B = { + proc get2(m : plaintext) : sharedsecret = H2(H).get2 + + proc init() : unit = H2(H).init + + proc get1(x : plaintext) : W8.t Array32.t = H.get + } + + proc find(pk : publickey, sk : TT.PKEROM.skey) : plaintext = { + var k1 : sharedsecret; + var k2 : sharedsecret; + var b : bool; + var b' : bool; + var r : W8.t Array32.t; + var cm : W8.t Array960.t * W8.t Array128.t; + var nobias : bool; + + H1.bad <- false; + H2.merr <- None; + H2.invert <- false; + RF.RF.init(); + KEMROMx2.RO2.RO.init(); + UU2.lD <- []; + KEMROMx2.CCA.cstar <- None; + KEMROMx2.CCA.sk <- (sk, witness); + k1 <$ srand; + k2 <$ srand; + b <$ {0,1}; + H2.mtgt <$ srand; + r <@ BUUCI(A, H).H2B.get1(H2.mtgt); + cm <- enc r pk H2.mtgt; + H1.bad <- if dec KEMROMx2.CCA.sk.`1.`2 cm <> Some H2.mtgt then true else H1.bad; + H2.merr <- if H2.merr = None && H1.bad then Some H2.mtgt else H2.merr; + UU2.lD <- (cm, witness) :: UU2.lD; + KEMROMx2.CCA.cstar <- Some cm; + CountHx2(BUUCI(A, H).H2B).init(); + b' <@ KEMROMx2.CCA(CountHx2(BUUCI(A, H).H2B), UU2, A).A.guess(pk, cm, if b then k1 else k2); + + return oget H2.merr; + } + }. + + lemma Gm0_Gm1: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + Pr[KEMROMx2.CCA(KEMROMx2.RO_x2(KEMROMx2.RO1.RO, KEMROMx2.RO2.RO), UU, A).main() @ &m : res] - + Pr[Gm1(KEMROMx2.RO_x2(KEMROMx2.RO1.RO, KEMROMx2.RO2.RO), A).main() @ &m : res] = + Pr[J.IND(PseudoRF.PRF, D(A)).main() @ &m : res] - Pr[J.IND(RF.RF, D(A)).main() @ &m : res]. + + lemma uu_goal_eager: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + Pr[Gm1(KEMROMx2.RO_x2(KEMROMx2.RO1.RO, KEMROMx2.RO2.RO), A).main() @ &m : res] = + Pr[Gm1(RO_x2E, A).main() @ &m : res]. + + op c2m (c : W8.t Array960.t * W8.t Array128.t) (sk : TT.PKEROM.skey) : plaintext option = dec sk.`2 c. + + op oc2m (c : W8.t Array960.t * W8.t Array128.t) (sk : TT.PKEROM.skey) : plaintext = oget (dec sk.`2 c). + + op m2c (m : plaintext) (sk : TT.PKEROM.skey) (f : plaintext -> W8.t Array32.t) : W8.t Array960.t * + W8.t Array128.t = enc (f m) sk.`1 m. + + op goodc (c : W8.t Array960.t * W8.t Array128.t) (sk : TT.PKEROM.skey) (f : + plaintext -> W8.t Array32.t) : bool = c2m c sk <> None /\ m2c (oc2m c sk) sk f = c. + + lemma bound_bad: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + Pr[Gm2(H2(RO1E.FunRO), UU2, A).main() @ &m : H1.bad] <= + Pr[TT.PKEROM.Correctness_Adv(RO1E.FunRO, TT.TT, BUUC(A)).main() @ &m : res]. + + lemma bound_invert: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + (forall (H0 <: KEMROMx2.POracle_x2{+all mem, -A} ) (O <: KEMROMx2.CCA_ORC{+all mem, -A} ), + islossless O.dec => islossless H0.get1 => islossless H0.get2 => islossless A(H0, O).guess) => + `|Pr[TT.PKEROM.OW_PCVA(RO1E.FunRO, TT.TT, BUUOW(A)).main() @ &m : res] - + Pr[Gm3(H2(RO1E.FunRO), UU2, A).main() @ &m : H2.invert]| <= + Pr[Gm3(H2(RO1E.FunRO), UU2, A).main() @ &m : H1.bad]. + + lemma bound_bad2: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + Pr[Gm3(H2(RO1E.FunRO), UU2, A).main() @ &m : H1.bad] <= + Pr[TT.PKEROM.Correctness_Adv(RO1E.FunRO, TT.TT, BUUCI(A)).main() @ &m : res]. + + lemma G3adv: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + (forall (H0 <: KEMROMx2.POracle_x2{+all mem, -A} ) (O <: KEMROMx2.CCA_ORC{+all mem, -A} ), + islossless O.dec => islossless H0.get1 => islossless H0.get2 => islossless A(H0, O).guess) => + Pr[Gm3(H2(RO1E.FunRO), UU2, A).main() @ &m : res] = 1%r / 2%r. + + lemma corr_goal_eagerC: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + Pr[TT.PKEROM.Correctness_Adv(RO1E.FunRO, TT.TT, BUUC(A)).main() @ &m : res] = + Pr[TT.PKEROM.Correctness_Adv(KEMROMx2.RO1.RO, TT.TT, BUUC(A)).main() @ &m : res]. + + lemma corr_goal_eagerCI: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + Pr[TT.PKEROM.Correctness_Adv(RO1E.FunRO, TT.TT, BUUCI(A)).main() @ &m : res] = + Pr[TT.PKEROM.Correctness_Adv(KEMROMx2.RO1.RO, TT.TT, BUUCI(A)).main() @ &m : res]. + + lemma owmod: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + Pr[TT.PKEROM.OW_PCVA(RO1E.FunRO, TT.TT, BUUOW(A)).main() @ &m : res] = + Pr[TT.PKEROM.OW_PCVA(RO1E.FunRO, TT.TT, BUUOWMod(A)).main() @ &m : res]. + + lemma corr_goal_eagerOW: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + Pr[TT.PKEROM.OW_PCVA(KEMROMx2.RO1.RO, TT.TT, BUUOWMod(A)).main() @ &m : res] = + Pr[TT.PKEROM.OW_PCVA(RO1E.FunRO, TT.TT, BUUOW(A)).main() @ &m : res]. + + lemma count_buuc: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) (H1_0 <: TT.PKEROM.RO.RO{+all mem, -TT.CO1, -BUUC(A)} ), + qHT + qHU + 2 <= TT.qHC => + (forall (RO0 <: KEMROMx2.POracle_x2{+all mem, -CountHx2, -A} ) + (O <: KEMROMx2.CCA_ORC{+all mem, -CountHx2, -A} ), + hoare[ A(CountHx2(RO0), O).guess : + CountHx2.c_ht = 0 /\ CountHx2.c_hu = 0 ==> CountHx2.c_ht <= qHT /\ CountHx2.c_hu <= qHU]) => + hoare[ TT.Correctness_Adv1(H1_0, BUUC(A)).A.find : TT.CO1.counter = 0 ==> TT.CO1.counter <= TT.qHC]. + + lemma count_buuci: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) (H1_0 <: TT.PKEROM.RO.RO{+all mem, -TT.CO1, -BUUCI(A)} ), + qHT + qHU + 1 <= TT.qHC => + (forall (RO0 <: KEMROMx2.POracle_x2{+all mem, -CountHx2, -A} ) + (O <: KEMROMx2.CCA_ORC{+all mem, -CountHx2, -A} ), + hoare[ A(CountHx2(RO0), O).guess : + CountHx2.c_ht = 0 /\ CountHx2.c_hu = 0 ==> CountHx2.c_ht <= qHT /\ CountHx2.c_hu <= qHU]) => + hoare[ TT.Correctness_Adv1(H1_0, BUUCI(A)).A.find : TT.CO1.counter = 0 ==> TT.CO1.counter <= TT.qHC]. + + lemma count_buuowmod: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) (RO0 <: TT.PKEROM.POracle{+all mem, -TT.CountO, -BUUOWMod(A)} ) + (O <: TT.PKEROM.VA_ORC{+all mem, -TT.CountO, -BUUOWMod(A)} ), + TT.qP = 0 => + TT.qV = 0 => + qHT + qHU + 1 <= TT.qH => + (forall (RO1 <: KEMROMx2.POracle_x2{+all mem, -CountHx2, -A} ) + (O0 <: KEMROMx2.CCA_ORC{+all mem, -CountHx2, -A} ), + hoare[ A(CountHx2(RO1), O0).guess : + CountHx2.c_ht = 0 /\ CountHx2.c_hu = 0 ==> CountHx2.c_ht <= qHT /\ CountHx2.c_hu <= qHU]) => + hoare[ BUUOWMod(A, TT.CountH(RO0), TT.CountO(O)).find : + TT.CountO.c_h = 0 /\ TT.CountO.c_cvo = 0 /\ TT.CountO.c_pco = 0 ==> + TT.CountO.c_h <= TT.qH /\ TT.CountO.c_cvo <= TT.qV /\ TT.CountO.c_pco <= TT.qP]. + + lemma conclusion_cca_pre: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKEROM.OW_PCVA, -TT.CO1, -TT.CountO, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + (forall (H0 <: KEMROMx2.POracle_x2{+all mem, -A} ) (O <: KEMROMx2.CCA_ORC{+all mem, -A} ), + islossless O.dec => islossless H0.get1 => islossless H0.get2 => islossless A(H0, O).guess) => + `|Pr[KEMROMx2.CCA(KEMROMx2.RO_x2(KEMROMx2.RO1.RO, KEMROMx2.RO2.RO), UU, A).main() @ &m : res] - 1%r / 2%r| <= + `|Pr[J.IND(PseudoRF.PRF, D(A)).main() @ &m : res] - Pr[J.IND(RF.RF, D(A)).main() @ &m : res]| + + Pr[TT.PKEROM.Correctness_Adv(KEMROMx2.RO1.RO, TT.TT, BUUC(A)).main() @ &m : res] + + Pr[TT.PKEROM.Correctness_Adv(KEMROMx2.RO1.RO, TT.TT, BUUCI(A)).main() @ &m : res] + + Pr[TT.PKEROM.OW_PCVA(KEMROMx2.RO1.RO, TT.TT, BUUOWMod(A)).main() @ &m : res]. + + lemma conclusion: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKE.OW_CPA, -TT.PKE.BOWp, -TT.PKE.OWL_CPA, -TT.PKE.OWvsIND.Bowl, -TT.BasePKE, -TT.PKEROM.RO.RO, -TT.PKEROM.RO.FRO, -TT.PKEROM.OW_PCVA, -TT.Correctness_Adv1, -TT.B, -TT.CountO, -TT.Gm, -TT.O_AdvOW, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + TT.qH = qHT + qHU + 1 => + TT.qV = 0 => + TT.qP = 0 => + TT.qH + 1 = TT.qHC => + TT.qHC < TT.FinT.card - 1 => + (forall (RO0 <: KEMROMx2.POracle_x2{+all mem, -CountHx2, -A} ) + (O <: KEMROMx2.CCA_ORC{+all mem, -CountHx2, -A} ), + hoare[ A(CountHx2(RO0), O).guess : + CountHx2.c_ht = 0 /\ CountHx2.c_hu = 0 ==> CountHx2.c_ht <= qHT /\ CountHx2.c_hu <= qHU]) => + (forall (H0 <: KEMROMx2.POracle_x2{+all mem, -A} ) (O <: KEMROMx2.CCA_ORC{+all mem, -A} ), + islossless O.dec => islossless H0.get1 => islossless H0.get2 => islossless A(H0, O).guess) => + `|Pr[KEMROMx2.CCA(KEMROMx2.RO_x2(KEMROMx2.RO1.RO, KEMROMx2.RO2.RO), UU, A).main() @ &m : res] - 1%r / 2%r| <= + `|Pr[J.IND(PseudoRF.PRF, D(A)).main() @ &m : res] - Pr[J.IND(RF.RF, D(A)).main() @ &m : res]| + + (qHT + qHU + 3)%r * Pr[TT.PKE.Correctness_Adv(TT.BasePKE, TT.B(BUUC(A), TT.PKEROM.RO.RO)).main() @ &m : res] + + (qHT + qHU + 3)%r * Pr[TT.PKE.Correctness_Adv(TT.BasePKE, TT.B(BUUCI(A), TT.PKEROM.RO.RO)).main() @ &m : res] + + (qHT + qHU + 3)%r * + Pr[TT.PKE.Correctness_Adv(TT.BasePKE, TT.B(TT.AdvCorr(BUUOWMod(A)), TT.PKEROM.RO.RO)).main() @ &m : res] + + Pr[TT.PKE.Correctness_Adv(TT.BasePKE, TT.PKE.BOWp(TT.BasePKE, TT.AdvOW(BUUOWMod(A)))).main() @ &m : res] + + 2%r * + `|Pr[TT.PKE.CPA(TT.BasePKE, TT.PKE.OWvsIND.Bowl(TT.PKE.OWvsIND.BL(TT.AdvOW(BUUOWMod(A))))).main() @ &m : res] - + 1%r / 2%r| + + (qHT + qHU + 1)%r * Pr[TT.PKE.OW_CPA(TT.BasePKE, TT.AdvOW_query(BUUOWMod(A))).main() @ &m : res] + + 2%r * TT.PKE.eps_msg. + + module X(O : TT.PKEROM.POracle) = BUUC(A/218906, O).H2B. + + module XI(O : TT.PKEROM.POracle) = BUUCI(A/218906, O).H2B. + + lemma conclusion_cpa: + forall (A(H : KEMROMx2.POracle_x2, O : KEMROMx2.CCA_ORC) <: + KEMROMx2.CCA_ADV{+all mem, -TT.PKE.OW_CPA, -TT.PKE.BOWp, -TT.PKE.OWL_CPA, -TT.PKE.OWvsIND.Bowl, -TT.BasePKE, -TT.PKEROM.RO.RO, -TT.PKEROM.RO.FRO, -TT.PKEROM.OW_PCVA, -TT.Correctness_Adv1, -TT.B, -TT.CountO, -TT.Gm, -TT.O_AdvOW, -RF.RF, -PseudoRF.PRF, -KEMROMx2.RO1.RO, -KEMROMx2.RO1.FRO, -KEMROMx2.RO2.RO, -KEMROMx2.CCA, -CountHx2, -RO1E.FunRO, -UU2, -H2, -Gm2, -Gm3, -H2BOWMod} + ) &m, + TT.qH = qHT + qHU + 1 => + TT.qV = 0 => + TT.qP = 0 => + TT.qH + 1 = TT.qHC => + TT.qHC < TT.FinT.card - 1 => + (forall (RO0 <: KEMROMx2.POracle_x2{+all mem, -CountHx2, -A} ) + (O <: KEMROMx2.CCA_ORC{+all mem, -CountHx2, -A} ), + hoare[ A(CountHx2(RO0), O).guess : + CountHx2.c_ht = 0 /\ CountHx2.c_hu = 0 ==> CountHx2.c_ht <= qHT /\ CountHx2.c_hu <= qHU]) => + (forall (H0 <: KEMROMx2.POracle_x2{+all mem, -A} ) (O <: KEMROMx2.CCA_ORC{+all mem, -A} ), + islossless O.dec => islossless H0.get1 => islossless H0.get2 => islossless A(H0, O).guess) => + `|Pr[KEMROMx2.CCA(KEMROMx2.RO_x2(KEMROMx2.RO1.RO, KEMROMx2.RO2.RO), UU, A).main() @ &m : res] - 1%r / 2%r| <= + 2%r * + `|Pr[TT.PKE.CPA(TT.BasePKE, TT.PKE.OWvsIND.Bowl(TT.PKE.OWvsIND.BL(TT.AdvOW(BUUOWMod(A))))).main() @ &m : res] - + 1%r / 2%r| + + 2%r * + `|Pr[TT.PKE.CPA(TT.BasePKE, TT.PKE.OWvsIND.Bowl(TT.AdvOWL_query(BUUOWMod(A)))).main() @ &m : res] - 1%r / 2%r| + + (qHT + qHU + 3)%r * Pr[TT.PKE.Correctness_Adv(TT.BasePKE, TT.B(BUUC(A), TT.PKEROM.RO.RO)).main() @ &m : res] + + (qHT + qHU + 3)%r * Pr[TT.PKE.Correctness_Adv(TT.BasePKE, TT.B(BUUCI(A), TT.PKEROM.RO.RO)).main() @ &m : res] + + (qHT + qHU + 3)%r * + Pr[TT.PKE.Correctness_Adv(TT.BasePKE, TT.B(TT.AdvCorr(BUUOWMod(A)), TT.PKEROM.RO.RO)).main() @ &m : res] + + Pr[TT.PKE.Correctness_Adv(TT.BasePKE, TT.PKE.BOWp(TT.BasePKE, TT.AdvOW(BUUOWMod(A)))).main() @ &m : res] + + `|Pr[J.IND(PseudoRF.PRF, D(A)).main() @ &m : res] - Pr[J.IND(RF.RF, D(A)).main() @ &m : res]| + + 2%r * (qHT + qHU + 2)%r * TT.PKE.eps_msg. + end UU. + + theory KEMROM. + type skey = (publickey * W8.t Array1152.t) * sharedsecret. + + lemma dkey_ll: is_lossless srand. + + hint solve 0 lossless : dkey_ll. + + hint solve 0 random : dkey_ll. + + lemma dkey_uni: is_uniform srand. + + hint solve 0 random : dkey_uni. + + lemma dkey_fu: is_full srand. + + hint solve 0 random : dkey_fu. + + theory RO. + module type RO = { + proc init() : unit {} + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t {} + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit {} + + proc rem(x : plaintext * W8.t Array32.t) : unit {} + + proc sample(x : plaintext * W8.t Array32.t) : unit {} + } + + module type RO_Distinguisher(G : RO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.rem, G.sample} + } + + module MainD(D : RO_Distinguisher, RO0 : RO) = { + proc distinguish(x : unit) : bool = { + var r : bool; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + module type ROmap = { + proc init() : unit {} + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t {} + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit {} + + proc rem(x : plaintext * W8.t Array32.t) : unit {} + + proc sample(x : plaintext * W8.t Array32.t) : unit {} + + proc restrK() : (plaintext * W8.t Array32.t, sharedsecret * W8.t Array32.t) SmtMap.fmap {} + } + + module type FRO = { + proc init() : unit {} + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t {} + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit {} + + proc rem(x : plaintext * W8.t Array32.t) : unit {} + + proc sample(x : plaintext * W8.t Array32.t) : unit {} + + proc queried(x : plaintext * W8.t Array32.t, f : PROM.flag) : bool {} + + proc allKnown() : (plaintext * W8.t Array32.t, sharedsecret * W8.t Array32.t) SmtMap.fmap {} + } + + module type FRO_Distinguisher(G : FRO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.rem, G.sample, G.queried, G.allKnown} + } + + abstract theory MkRO. + module RO = { + var m : (plaintext * W8.t Array32.t, sharedsecret * W8.t Array32.t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = { + var r : sharedsecret * W8.t Array32.t; + + r <$ dRO; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : plaintext * W8.t Array32.t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : plaintext * W8.t Array32.t) : unit = { + RO.get(x); + } + + proc restrK() : (plaintext * W8.t Array32.t, sharedsecret * W8.t Array32.t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = RO.get + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit = RO.set + + proc rem(x : plaintext * W8.t Array32.t) : unit = RO.rem + + proc sample(x : plaintext * W8.t Array32.t) : unit = {} + }. + end MkRO. + + module RO = { + var m : (plaintext * W8.t Array32.t, sharedsecret * W8.t Array32.t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = { + var r : sharedsecret * W8.t Array32.t; + + r <$ dRO; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : plaintext * W8.t Array32.t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : plaintext * W8.t Array32.t) : unit = { + RO.get(x); + } + + proc restrK() : (plaintext * W8.t Array32.t, sharedsecret * W8.t Array32.t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = RO.get + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit = RO.set + + proc rem(x : plaintext * W8.t Array32.t) : unit = RO.rem + + proc sample(x : plaintext * W8.t Array32.t) : unit = {} + }. + + module FRO = { + var m : (plaintext * W8.t Array32.t, (sharedsecret * W8.t Array32.t) * PROM.flag) SmtMap.fmap + + proc init() : unit = { + FRO.m <- SmtMap.empty; + } + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = { + var r : sharedsecret * W8.t Array32.t; + + r <$ dRO; + if ((x \in FRO.m)%SmtMap) + r <- (oget (FRO.m.[x])%SmtMap).`1; + FRO.m.[x] <- (r, PROM.Known); + + return r; + } + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit = { + FRO.m.[x] <- (y, PROM.Known); + } + + proc rem(x : plaintext * W8.t Array32.t) : unit = { + FRO.m <- (rem FRO.m x)%SmtMap; + } + + proc sample(x : plaintext * W8.t Array32.t) : unit = { + var c : sharedsecret * W8.t Array32.t; + + c <$ dRO; + if ((x \notin FRO.m)%SmtMap) + FRO.m.[x] <- (c, PROM.Unknown); + } + + proc queried(x : plaintext * W8.t Array32.t, f : PROM.flag) : bool = { + return (x \in FRO.m)%SmtMap /\ ((FRO.m.[x])%SmtMap \is f)%PROM; + } + + proc allKnown() : (plaintext * W8.t Array32.t, sharedsecret * W8.t Array32.t) SmtMap.fmap = { + return (restr PROM.Known FRO.m)%SmtMap; + } + }. + + lemma RO_FRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_get: + equiv[ RO.get ~ FRO.get : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> ={res} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_sample: + equiv[ RO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(RO).distinguish ~ D(FRO).distinguish : + ={arg, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_init_ll: islossless RO.init. + + lemma FRO_init_ll: islossless FRO.init. + + lemma FRO_in_dom_ll: islossless FRO.queried. + + lemma FRO_restrK_ll: islossless FRO.allKnown. + + lemma RO_set_ll: islossless RO.set. + + lemma FRO_set_ll: islossless FRO.set. + + lemma RO_get_ll: (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => islossless RO.get. + + lemma FRO_get_ll: (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => islossless FRO.get. + + lemma RO_sample_ll: (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => islossless RO.sample. + + lemma FRO_sample_ll: (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => islossless FRO.sample. + + module type RM_Distinguisher(G : ROmap) = { + proc distinguish(_ : unit) : bool {G.get, G.sample, G.restrK} + } + + module MainRM(D : RM_Distinguisher, RO0 : ROmap) = { + proc distinguish(x : unit) : bool = { + var r : bool; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + lemma fcoll_bound ['rT]: + forall (f : sharedsecret * W8.t Array32.t -> 'rT) (Pc : real), + 0%r <= Pc => + (forall (_ _ : plaintext * W8.t Array32.t) (y : 'rT), y \in dmap dRO f => mu1 (dmap dRO f) y <= Pc) => + forall (q : int), + 0 <= q => + forall (D(G : ROmap) <: RM_Distinguisher{+all mem, -RO} ) &m (z : unit), + Pr[MainRM(D, RO).distinguish(z) @ &m : (fcoll f RO.m)%SmtMap /\ (fsize RO.m)%SmtMap <= q] <= + (q * (q - 1))%r / 2%r * Pc. + + theory FullEager. + abstract theory EagerCore. + axiom dout_ll: forall (_ : plaintext * W8.t Array32.t), is_lossless dRO. + + module type Orcl = { + proc f(x : plaintext * W8.t Array32.t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : (plaintext * W8.t Array32.t) list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : (plaintext * W8.t Array32.t) list, l2 : (plaintext * W8.t Array32.t) list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : plaintext * W8.t Array32.t, l : (plaintext * W8.t Array32.t) list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : plaintext * W8.t Array32.t, t2 : plaintext * W8.t Array32.t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : plaintext * W8.t Array32.t, t2 : plaintext * W8.t Array32.t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : (plaintext * W8.t Array32.t) list) (i : plaintext * W8.t Array32.t) (s2 : (plaintext * + W8.t Array32.t) list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : plaintext * W8.t Array32.t -> bool) (Inv : + (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ + (forall (x : plaintext * W8.t Array32.t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : plaintext * W8.t Array32.t -> bool) (Inv : + (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : + (forall (x : plaintext * W8.t Array32.t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = { + var r : sharedsecret * W8.t Array32.t; + + r <$ dRO; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit = FRO.set + + proc rem(x : plaintext * W8.t Array32.t) : unit = FRO.rem + + proc sample(x : plaintext * W8.t Array32.t) : unit = FRO.sample + + proc queried(x : plaintext * W8.t Array32.t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (plaintext * W8.t Array32.t, sharedsecret * W8.t Array32.t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : plaintext * W8.t Array32.t) : unit = { + var c : sharedsecret * W8.t Array32.t; + + c <$ dRO; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : plaintext * W8.t Array32.t) (mx1 : ((sharedsecret * W8.t Array32.t) * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : plaintext * W8.t Array32.t) (mx1 mx2 : ((sharedsecret * W8.t Array32.t) * + PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : plaintext * W8.t Array32.t) (r1 : sharedsecret * W8.t Array32.t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + end FullEager. + + abstract theory FinEager. + abstract theory EagerCore. + axiom dout_ll: forall (_ : plaintext * W8.t Array32.t), is_lossless dRO. + + module type Orcl = { + proc f(x : plaintext * W8.t Array32.t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : (plaintext * W8.t Array32.t) list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : (plaintext * W8.t Array32.t) list, l2 : (plaintext * W8.t Array32.t) list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : plaintext * W8.t Array32.t, l : (plaintext * W8.t Array32.t) list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : plaintext * W8.t Array32.t, t2 : plaintext * W8.t Array32.t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : plaintext * W8.t Array32.t, t2 : plaintext * W8.t Array32.t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : (plaintext * W8.t Array32.t) list) (i : plaintext * W8.t Array32.t) (s2 : (plaintext * + W8.t Array32.t) list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : plaintext * W8.t Array32.t -> bool) (Inv : + (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ + (forall (x : plaintext * W8.t Array32.t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : plaintext * W8.t Array32.t -> bool) (Inv : + (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : + (forall (x : plaintext * W8.t Array32.t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = { + var r : sharedsecret * W8.t Array32.t; + + r <$ dRO; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit = FRO.set + + proc rem(x : plaintext * W8.t Array32.t) : unit = FRO.rem + + proc sample(x : plaintext * W8.t Array32.t) : unit = FRO.sample + + proc queried(x : plaintext * W8.t Array32.t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (plaintext * W8.t Array32.t, sharedsecret * W8.t Array32.t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : plaintext * W8.t Array32.t) : unit = { + var c : sharedsecret * W8.t Array32.t; + + c <$ dRO; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : plaintext * W8.t Array32.t) (mx1 : ((sharedsecret * W8.t Array32.t) * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : plaintext * W8.t Array32.t) (mx1 mx2 : ((sharedsecret * W8.t Array32.t) * + PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : plaintext * W8.t Array32.t) (r1 : sharedsecret * W8.t Array32.t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : unit) : bool = { + var b : bool; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + theory FinFrom. + type t = plaintext * W8.t Array32.t. + + op enum : t list. + + op card : int = size enum. + + axiom enum_spec: forall (x : t), count (pred1 x) enum = 1. + + lemma enumP: forall (x : t), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : t list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinFrom. + + module FinRO = { + proc rem(x : plaintext * W8.t Array32.t) : unit = RO.rem + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit = RO.set + + proc init() : unit = { + var l : FinFrom.t list; + + l <- FinFrom.enum; + RO.init(); + while (l <> []){ + RO.sample(head witness l); + l <- behead l; + } + } + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = { + return oget (RO.m.[x])%SmtMap; + } + + proc sample(x : plaintext * W8.t Array32.t) : unit = {} + }. + + module type FinRO_Distinguisher(G : RO) = { + proc distinguish(_ : unit) : bool {G.init, G.get, G.set, G.sample} + } + + lemma RO_FinRO_D: + (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ MainD(D, RO).distinguish ~ MainD(D, FinRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_RO_FinRO_D: + (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ) &m (x : unit) (p : + bool -> bool), + Pr[MainD(D, RO).distinguish(x) @ &m : p res] = Pr[MainD(D, FinRO).distinguish(x) @ &m : p res]. + + theory MUniFinFun. + op mfun ['u] (d : plaintext * W8.t Array32.t -> 'u distr) (f : + plaintext * W8.t Array32.t -> 'u) : real = + (big predT (fun (x : plaintext * W8.t Array32.t) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma mfunE ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr) (f : + plaintext * W8.t Array32.t -> 'u), mfun d f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + lemma isdistr_dfun ['u]: forall (d : plaintext * W8.t Array32.t -> 'u distr), isdistr (mfun d). + + op dfun ['u] (d : plaintext * W8.t Array32.t -> 'u distr) : ( + plaintext * W8.t Array32.t -> 'u) distr = mk (mfun d). + + lemma dfun1E ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr) (f : + plaintext * W8.t Array32.t -> 'u), + mu1 (dfun d) f = + (big predT (fun (x : plaintext * W8.t Array32.t) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun1E_djoin ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr) (f : + plaintext * W8.t Array32.t -> 'u), mu1 (dfun d) f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + op tofun ['u] (us : 'u list) (x : plaintext * W8.t Array32.t) : 'u = nth witness us (index x FinFrom.enum). + + op offun ['u] (f : plaintext * W8.t Array32.t -> 'u) : 'u list = map f FinFrom.enum. + + lemma offunK ['u]: cancel offun tofun. + + lemma tofunK ['u]: forall (us : 'u list), size us = FinFrom.card => offun (tofun us) = us. + + lemma dfun_dmap ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr), dfun d = dmap (djoinmap d FinFrom.enum) tofun. + + lemma dfun_supp ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr) (f : + plaintext * W8.t Array32.t -> 'u), + f \in dfun d <=> forall (x : plaintext * W8.t Array32.t), f x \in d x. + + lemma dfun_fu ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr), + (forall (x : plaintext * W8.t Array32.t), is_full (d x)) => is_full (dfun d). + + lemma dfun_uni ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr), + (forall (x : plaintext * W8.t Array32.t), is_uniform (d x)) => is_uniform (dfun d). + + lemma dfun_funi ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr), + (forall (x : plaintext * W8.t Array32.t), is_uniform (d x)) => + (forall (x : plaintext * W8.t Array32.t), is_full (d x)) => is_funiform (dfun d). + + lemma dfunE_djoin ['u]: + forall (du : plaintext * W8.t Array32.t -> 'u distr) (E : + (plaintext * W8.t Array32.t -> 'u) -> bool), + mu (dfun du) E = mu (djoinmap du FinFrom.enum) (E \o tofun). + + lemma dfunE ['u]: + forall (du : plaintext * W8.t Array32.t -> 'u distr) (p : + plaintext * W8.t Array32.t -> 'u -> bool), + mu (dfun du) + (fun (f : plaintext * W8.t Array32.t -> 'u) => forall (x : plaintext * W8.t Array32.t), p x (f x)) = + (big predT (fun (x : plaintext * W8.t Array32.t) => mu (du x) (p x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma weight_dfun ['u]: + forall (df : plaintext * W8.t Array32.t -> 'u distr), + weight (dfun df) = + (big predT (fun (x : plaintext * W8.t Array32.t) => weight (df x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun_ll ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr), + (forall (x : plaintext * W8.t Array32.t), is_lossless (d x)) => is_lossless (dfun d). + + lemma dlet_dfun ['u, 'v]: + forall (d1 : plaintext * W8.t Array32.t -> 'u distr) (d2 : + plaintext * W8.t Array32.t -> 'u -> 'v distr), + dlet (dfun d1) + (fun (f1 : plaintext * W8.t Array32.t -> 'u) => + dfun (fun (x : plaintext * W8.t Array32.t) => d2 x (f1 x))) = + dfun (fun (x : plaintext * W8.t Array32.t) => dlet (d1 x) (fun (x1 : 'u) => d2 x x1)). + + lemma dfun_unit ['u]: + forall (f : plaintext * W8.t Array32.t -> 'u), + dfun (fun (x : plaintext * W8.t Array32.t) => dunit (f x)) = dunit f. + + lemma dmap_dfun ['u, 'v]: + forall (d : plaintext * W8.t Array32.t -> 'u distr) (F : + plaintext * W8.t Array32.t -> 'u -> 'v), + dmap (dfun d) + (fun (f : plaintext * W8.t Array32.t -> 'u) (x : plaintext * W8.t Array32.t) => F x (f x)) = + dfun (fun (x : plaintext * W8.t Array32.t) => dmap (d x) (fun (fx : 'u) => F x fx)). + + lemma dfun1E_fix1 ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr) (x0 : plaintext * W8.t Array32.t) + (f : plaintext * W8.t Array32.t -> 'u), + mu1 (dfun d) f = mu1 (d x0) (f x0) * mu1 (dfun d.[x0 <- dunit (f x0)]) f. + + lemma dlet_dfun_fupdate_ll ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr) (x0 : plaintext * W8.t Array32.t) (v : 'u), + dlet (dfun d.[x0 <- dunit v]) + (fun (f : plaintext * W8.t Array32.t -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + dfun d. + + lemma dfunE_dlet_fix1 ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr) (x0 : plaintext * W8.t Array32.t), + dfun d = dlet (d x0) (fun (v : 'u) => dfun d.[x0 <- dunit v]). + + lemma dlet_dfun_update ['u]: + forall (d : plaintext * W8.t Array32.t -> 'u distr) (x0 : plaintext * W8.t Array32.t), + dlet (dfun d) (fun (f : plaintext * W8.t Array32.t -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + (weight (d x0) \cdot dfun d). + + lemma dfun_projE ['u]: + forall (df : plaintext * W8.t Array32.t -> 'u distr) (x : plaintext * W8.t Array32.t), + dmap (dfun df) (fun (f : plaintext * W8.t Array32.t -> 'u) => f x) = + (weight (dfun df) / weight (df x) \cdot df x). + + lemma eq_from_dfun_proj_eq ['u]: + forall (df1 df2 : plaintext * W8.t Array32.t -> 'u distr), + (forall (x : plaintext * W8.t Array32.t), is_lossless (df1 x)) => + (forall (x : plaintext * W8.t Array32.t), is_lossless (df2 x)) => + (forall (x : plaintext * W8.t Array32.t), + dmap (dfun df1) (fun (f : plaintext * W8.t Array32.t -> 'u) => f x) = + dmap (dfun df2) (fun (f : plaintext * W8.t Array32.t -> 'u) => f x)) => + df1 == df2. + + lemma dfun_prod1E ['u, 'v]: + forall (df : plaintext * W8.t Array32.t -> 'u distr) (dg : + plaintext * W8.t Array32.t -> 'v distr) (f : plaintext * W8.t Array32.t -> 'u) + (g : plaintext * W8.t Array32.t -> 'v), + mu1 (dfun (fun (x : plaintext * W8.t Array32.t) => df x `*` dg x)) + (fun (x : plaintext * W8.t Array32.t) => (f x, g x)) = + mu1 (dfun df) f * mu1 (dfun dg) g. + + lemma dprod_funE ['u, 'v]: + forall (df : plaintext * W8.t Array32.t -> 'u distr) (dg : + plaintext * W8.t Array32.t -> 'v distr), + dfun df `*` dfun dg = + dmap (dfun (fun (x : plaintext * W8.t Array32.t) => df x `*` dg x)) + (fun (fg : plaintext * W8.t Array32.t -> 'u * 'v) => + ((fun (p : 'u * 'v) => p.`1) \o fg, (fun (p : 'u * 'v) => p.`2) \o fg)). + + lemma dfun_prodE ['u, 'v]: + forall (df : plaintext * W8.t Array32.t -> 'u distr) (dg : + plaintext * W8.t Array32.t -> 'v distr), + dfun (fun (x : plaintext * W8.t Array32.t) => df x `*` dg x) = + dmap (dfun df `*` dfun dg) + (fun (fg : (plaintext * W8.t Array32.t -> 'u) * (plaintext * W8.t Array32.t -> 'v)) (x : plaintext * + W8.t Array32.t) => (fg.`1 x, fg.`2 x)). + + lemma dfun_condE ['u]: + forall (X : plaintext * W8.t Array32.t -> bool) (dt df : + plaintext * W8.t Array32.t -> 'u distr), + (forall (x : plaintext * W8.t Array32.t), is_lossless (dt x)) => + (forall (x : plaintext * W8.t Array32.t), is_lossless (df x)) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext * W8.t Array32.t -> 'u) * (plaintext * W8.t Array32.t -> 'u)) (x : plaintext * + W8.t Array32.t) => if X x then tf.`1 x else tf.`2 x) = + dfun (fun (x : plaintext * W8.t Array32.t) => if X x then dt x else df x). + + lemma dlet_dfun_cond ['u]: + forall (dX : plaintext * W8.t Array32.t -> bool distr) (dt df : + plaintext * W8.t Array32.t -> 'u distr), + (forall (x : plaintext * W8.t Array32.t), is_lossless (dX x)) => + (forall (x : plaintext * W8.t Array32.t), is_lossless (dt x)) => + (forall (x : plaintext * W8.t Array32.t), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : plaintext * W8.t Array32.t -> bool) => + dfun (fun (x : plaintext * W8.t Array32.t) => if X x then dt x else df x)) = + dfun (fun (x : plaintext * W8.t Array32.t) => dlet (dX x) (fun (b : bool) => if b then dt x else df x)). + + lemma dfun_dcondE ['u]: + forall (dX : plaintext * W8.t Array32.t -> bool distr) (dt df : + plaintext * W8.t Array32.t -> 'u distr), + (forall (x : plaintext * W8.t Array32.t), is_lossless (dX x)) => + (forall (x : plaintext * W8.t Array32.t), is_lossless (dt x)) => + (forall (x : plaintext * W8.t Array32.t), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : plaintext * W8.t Array32.t -> bool) => + dmap (dfun dt `*` dfun df) + (fun (tf : (plaintext * W8.t Array32.t -> 'u) * ( + plaintext * W8.t Array32.t -> 'u)) (x : plaintext * W8.t Array32.t) => + if X x then tf.`1 x else tf.`2 x)) = + dfun + (fun (x : plaintext * W8.t Array32.t) => (mu1 (dX x) true \cdot dt x) + (mu1 (dX x) false \cdot df x)). + end MUniFinFun. + + module FunRO = { + var f : plaintext * W8.t Array32.t -> sharedsecret * W8.t Array32.t + + proc init() : unit = { + FunRO.f <$ (dfun (fun (_ : plaintext * W8.t Array32.t) => dRO))%MUniFinFun; + } + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = { + return FunRO.f x; + } + + proc set(x : plaintext * W8.t Array32.t, y : sharedsecret * W8.t Array32.t) : unit = { + FunRO.f <- fun (z : plaintext * W8.t Array32.t) => if z = x then y else FunRO.f z; + } + + proc sample(x : plaintext * W8.t Array32.t) : unit = {} + + proc rem(x : plaintext * W8.t Array32.t) : unit = {} + }. + + lemma FinRO_FunRO_D: + (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ), + equiv[ MainD(D, FinRO).distinguish ~ MainD(D, FunRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_FinRO_FunRO_D: + (forall (_ : plaintext * W8.t Array32.t), is_lossless dRO) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ) &m (x : unit) (p : + bool -> bool), + Pr[MainD(D, FinRO).distinguish(x) @ &m : p res] = Pr[MainD(D, FunRO).distinguish(x) @ &m : p res]. + end FinEager. + end RO. + + module type Oracle = { + proc init() : unit {} + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t {} + } + + module type POracle = { + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t {} + } + + module type Scheme(O : POracle) = { + proc kg() : publickey * skey {O.get} + + proc enc(pk : publickey) : (W8.t Array960.t * W8.t Array128.t) * sharedsecret {O.get} + + proc dec(sk : skey, c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option {O.get} + } + + module Correctness(H : Oracle, S : Scheme) = { + proc main() : bool = { + var pk : publickey; + var sk : skey; + var c : W8.t Array960.t * W8.t Array128.t; + var k : sharedsecret; + var k' : sharedsecret option; + + H.init(); + (pk, sk) <@ S(H).kg(); + (c, k) <@ S(H).enc(pk); + k' <@ S(H).dec(sk, c); + + return k' <> Some k; + } + }. + + module type CCA_ORC = { + proc dec(c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option {} + } + + module type CCA_ADV(H : POracle, O : CCA_ORC) = { + proc guess(pk : publickey, c : W8.t Array960.t * W8.t Array128.t, k : sharedsecret) : bool {O.dec, H.get} + } + + module CCA(H : Oracle, S : Scheme, A : CCA_ADV) = { + var cstar : (W8.t Array960.t * W8.t Array128.t) option + + var sk : skey + + module O = { + proc dec(c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var k : sharedsecret option; + + k <- None; + if (Some c <> CCA.cstar) + k <@ S(H).dec(CCA.sk, c); + + return k; + } + } + + module A = A(H, CCA(H, S, A).O) + + proc main() : bool = { + var pk : publickey; + var k1 : sharedsecret; + var ck0 : (W8.t Array960.t * W8.t Array128.t) * sharedsecret; + var b : bool; + var b' : bool; + + H.init(); + CCA.cstar <- None; + (pk, CCA.sk) <@ S(H).kg(); + k1 <$ srand; + b <$ {0,1}; + ck0 <@ S(H).enc(pk); + CCA.cstar <- Some ck0.`1; + b' <@ CCA(H, S, A).A.guess(pk, ck0.`1, if b then k1 else ck0.`2); + + return b' = b; + } + }. + end KEMROM. + + op qHK : int. + + axiom ge0_qHK: 0 <= qHK. + + module CountH(H : KEMROM.POracle) = { + var c_h : int + + proc init() : unit = { + CountH.c_h <- 0; + } + + proc get(x : plaintext * W8.t Array32.t) : sharedsecret * W8.t Array32.t = { + var r : sharedsecret * W8.t Array32.t; + + r <@ H.get(x); + CountH.c_h <- CountH.c_h + 1; + + return r; + } + }. + + module FO_K(H : KEMROM.POracle) = { + proc kg() : publickey * KEMROM.skey = { + var pk : publickey; + var sk : W8.t Array1152.t; + var k : sharedsecret; + + (pk, sk) <$ UU.TT.kg; + k <$ srand; + + return (pk, ((pk, sk), k)); + } + + proc enc(pk : publickey) : (W8.t Array960.t * W8.t Array128.t) * sharedsecret = { + var m : plaintext; + var r : W8.t Array32.t; + var c : W8.t Array960.t * W8.t Array128.t; + var k : sharedsecret; + + m <$ srand; + (k, r) <@ H.get(m, H_pk pk); + c <- enc r pk m; + + return (c, k); + } + + proc dec(sk : KEMROM.skey, c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var m' : plaintext option; + var c' : W8.t Array960.t * W8.t Array128.t; + var ks : sharedsecret; + var r : W8.t Array32.t; + var kn : sharedsecret; + + m' <- dec sk.`1.`2 c; + (ks, r) <@ H.get(oget m', H_pk sk.`1.`1); + kn <- J sk.`2 c; + c' <- enc r sk.`1.`1 (oget m'); + + return if m' <> None /\ c' = c then Some ks else Some kn; + } + }. + + module UU_L(H1 : UU.KEMROMx2.RO1.RO, H2 : UU.KEMROMx2.RO2.RO) = { + proc enc(pk : publickey) : (W8.t Array960.t * W8.t Array128.t) * sharedsecret = UU.UU(UU.KEMROMx2.RO_x2(H1, H2)).enc + + proc kg() : publickey * UU.KEMROMx2.skey = UU.UU(UU.KEMROMx2.RO_x2(H1, H2)).kg + + proc dec(sk : KEMROM.skey, c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var m' : plaintext option; + var r : W8.t Array32.t; + var c' : W8.t Array960.t * W8.t Array128.t; + var rv : plaintext option; + var k : sharedsecret; + + rv <- None; + m' <- dec sk.`1.`2 c; + H1.sample(oget m'); + H2.sample(oget m'); + if (m' <> None) { + r <@ H1.get(oget m'); + c' <- enc r sk.`1.`1 (oget m'); + rv <- if c = c' then m' else None; + } + if (rv = None) + k <- J sk.`2 c; + else + k <@ H2.get(oget m'); + + return Some k; + } + }. + + module Correctness_L(H1 : UU.KEMROMx2.RO1.RO, H2 : UU.KEMROMx2.RO2.RO) = { + proc main() : bool = { + var pk : publickey; + var sk : UU.KEMROMx2.skey; + var c : W8.t Array960.t * W8.t Array128.t; + var k : sharedsecret; + var k' : sharedsecret option; + + H1.init(); + H2.init(); + (pk, sk) <@ UU_L(H1, H2).kg(); + (c, k) <@ UU_L(H1, H2).enc(pk); + k' <@ UU_L(H1, H2).dec(sk, c); + + return k' <> Some k; + } + }. + + lemma go_parametric_corr_lro: + forall &m, + Pr[UU.KEMROMx2.Correctness(UU.KEMROMx2.RO_x2(UU.KEMROMx2.RO1.RO, UU.KEMROMx2.RO2.RO), UU.UU).main() @ &m : res] = + Pr[Correctness_L(UU.KEMROMx2.RO1.LRO, UU.KEMROMx2.RO2.LRO).main() @ &m : res]. + + module DC1(G1 : UU.KEMROMx2.RO1.RO) = { + proc distinguish() : bool = Correctness_L(G1, UU.KEMROMx2.RO2.LRO).main + }. + + module DC2(G2 : UU.KEMROMx2.RO2.RO) = { + proc distinguish() : bool = Correctness_L(UU.KEMROMx2.RO1.RO, G2).main + }. + + lemma go_parametric_corr: + forall &m, + Pr[UU.KEMROMx2.Correctness(UU.KEMROMx2.RO_x2(UU.KEMROMx2.RO1.RO, UU.KEMROMx2.RO2.RO), UU.UU).main() @ &m : res] = + Pr[Correctness_L(UU.KEMROMx2.RO1.RO, UU.KEMROMx2.RO2.RO).main() @ &m : res]. + + lemma correctness_fo_k: + forall &m, + UU.TT.qHC = 0 => + 1 < UU.TT.FinT.card => + Pr[KEMROM.Correctness(KEMROM.RO.RO, FO_K).main() @ &m : res] <= + Pr[UU.TT.PKE.Correctness_Adv(UU.TT.BasePKE, UU.TT.B(UU.B_UC, UU.TT.PKEROM.RO.RO)).main() @ &m : res]. + + module CCAL(H1 : UU.KEMROMx2.RO1.RO, H2 : UU.KEMROMx2.RO2.RO, A : UU.KEMROMx2.CCA_ADV) = { + module O = { + proc dec(c : W8.t Array960.t * W8.t Array128.t) : sharedsecret option = { + var k : sharedsecret option; + + k <- None; + if (Some c <> KEMROM.CCA.cstar) + k <@ UU_L(H1, H2).dec(KEMROM.CCA.sk, c); + + return k; + } + } + + module A = A(UU.KEMROMx2.RO_x2(H1, H2), CCAL(H1, H2, A).O) + + proc main() : bool = { + var pk : publickey; + var k1 : sharedsecret; + var ck0 : (W8.t Array960.t * W8.t Array128.t) * sharedsecret; + var b : bool; + var b' : bool; + + H1.init(); + H2.init(); + KEMROM.CCA.cstar <- None; + (pk, KEMROM.CCA.sk) <@ UU_L(H1, H2).kg(); + k1 <$ srand; + b <$ {0,1}; + ck0 <@ UU_L(H1, H2).enc(pk); + KEMROM.CCA.cstar <- Some ck0.`1; + b' <@ CCAL(H1, H2, A).A.guess(pk, ck0.`1, if b then k1 else ck0.`2); + + return b' = b; + } + }. + + module B1x2(A : KEMROM.CCA_ADV, H2x : UU.KEMROMx2.POracle_x2, DO : KEMROM.CCA_ORC) = { + var _pk : publickey + + module BH = { + proc get(m : plaintext, hpk : W8.t Array32.t) : sharedsecret * W8.t Array32.t = { + var r : W8.t Array32.t; + var k : sharedsecret; + + (k, r) <@ KEMROM.RO.RO.get(m, hpk); + if (hpk = H_pk B1x2._pk) { + r <@ H2x.get1(m); + k <@ H2x.get2(m); + } + + return (k, r); + } + } + + proc guess(pk : publickey, c : W8.t Array960.t * W8.t Array128.t, k : sharedsecret) : bool = { + var b : bool; + + B1x2._pk <- pk; + CountH(KEMROM.RO.RO).init(); + KEMROM.RO.RO.init(); + b <@ A(CountH(B1x2(A, H2x, DO).BH), DO).guess(pk, c, k); + + return b; + } + }. + + module DKK1(A : UU.KEMROMx2.CCA_ADV, G1 : UU.KEMROMx2.RO1.RO) = { + proc distinguish() : bool = CCAL(G1, UU.KEMROMx2.RO2.LRO, A).main + }. + + module DKK2(A : UU.KEMROMx2.CCA_ADV, G2 : UU.KEMROMx2.RO2.RO) = { + proc distinguish() : bool = CCAL(UU.KEMROMx2.RO1.RO, G2, A).main + }. + + lemma go_parametric: + forall (A(H : UU.KEMROMx2.POracle_x2, O : UU.KEMROMx2.CCA_ORC) <: + UU.KEMROMx2.CCA_ADV{+all mem, -UU.KEMROMx2.RO1.RO, -UU.KEMROMx2.RO1.FRO, -UU.KEMROMx2.RO2.RO, -UU.KEMROMx2.RO2.FRO, -UU.KEMROMx2.CCA, -KEMROM.CCA} + ) &m, + Pr[UU.KEMROMx2.CCA(UU.KEMROMx2.RO_x2(UU.KEMROMx2.RO1.RO, UU.KEMROMx2.RO2.RO), UU.UU, A).main() @ &m : res] = + Pr[CCAL(UU.KEMROMx2.RO1.RO, UU.KEMROMx2.RO2.RO, A).main() @ &m : res]. + + lemma same_scheme: + forall (A(H : KEMROM.POracle, O : KEMROM.CCA_ORC) <: + KEMROM.CCA_ADV{+all mem, -KEMROM.RO.RO.m, -UU.TT.PKE.OW_CPA, -UU.TT.PKE.BOWp, -UU.TT.PKE.OWL_CPA, -UU.TT.PKE.OWvsIND.Bowl, -UU.TT.BasePKE, -UU.TT.PKEROM.RO.RO, -UU.TT.PKEROM.RO.FRO, -UU.TT.PKEROM.OW_PCVA, -UU.TT.Correctness_Adv1, -UU.TT.B, -UU.TT.CountO, -UU.TT.Gm, -UU.TT.O_AdvOW, -UU.RF.RF, -UU.PseudoRF.PRF, -UU.KEMROMx2.RO1.RO, -UU.KEMROMx2.RO1.FRO, -UU.KEMROMx2.RO2.RO, -UU.KEMROMx2.RO2.FRO, -UU.KEMROMx2.CCA, -UU.CountHx2, -UU.RO1E.FunRO, -UU.UU2, -UU.H2, -UU.Gm2, -UU.Gm3, -UU.H2BOWMod, -KEMROM.CCA, -B1x2} + ) &m, + Pr[UU.KEMROMx2.CCA(UU.KEMROMx2.RO_x2(UU.KEMROMx2.RO1.RO, UU.KEMROMx2.RO2.RO), UU.UU, B1x2(A)).main() @ &m : res] = + Pr[KEMROM.CCA(KEMROM.RO.RO, FO_K, A).main() @ &m : res]. + + lemma countB1x2: + forall (A(H : KEMROM.POracle, O : KEMROM.CCA_ORC) <: + KEMROM.CCA_ADV{+all mem, -KEMROM.RO.RO.m, -UU.TT.PKE.OW_CPA, -UU.TT.PKE.BOWp, -UU.TT.PKE.OWL_CPA, -UU.TT.PKE.OWvsIND.Bowl, -UU.TT.BasePKE, -UU.TT.PKEROM.RO.RO, -UU.TT.PKEROM.RO.FRO, -UU.TT.PKEROM.OW_PCVA, -UU.TT.Correctness_Adv1, -UU.TT.B, -UU.TT.CountO, -UU.TT.Gm, -UU.TT.O_AdvOW, -UU.RF.RF, -UU.PseudoRF.PRF, -UU.KEMROMx2.RO1.RO, -UU.KEMROMx2.RO1.FRO, -UU.KEMROMx2.RO2.RO, -UU.KEMROMx2.RO2.FRO, -UU.KEMROMx2.CCA, -UU.CountHx2, -UU.RO1E.FunRO, -UU.UU2, -UU.H2, -UU.Gm2, -UU.Gm3, -UU.H2BOWMod, -KEMROM.CCA, -B1x2} + ) (RO0 <: UU.KEMROMx2.POracle_x2{+all mem, -UU.CountHx2, -B1x2(A)} ) + (O <: UU.KEMROMx2.CCA_ORC{+all mem, -UU.CountHx2, -B1x2(A)} ), + UU.qHT = qHK => + UU.qHU = qHK => + (forall (RO1 <: KEMROM.POracle{+all mem, -CountH, -A} ) (O0 <: KEMROM.CCA_ORC{+all mem, -CountH, -A} ), + hoare[ A(CountH(RO1), O0).guess : CountH.c_h = 0 ==> CountH.c_h <= qHK]) => + hoare[ B1x2(A, UU.CountHx2(RO0), O).guess : + UU.CountHx2.c_ht = 0 /\ UU.CountHx2.c_hu = 0 ==> + UU.CountHx2.c_ht <= UU.qHT /\ UU.CountHx2.c_hu <= UU.qHU]. + + lemma conclusion_fo_mlkem: + forall (A(H : KEMROM.POracle, O : KEMROM.CCA_ORC) <: + KEMROM.CCA_ADV{+all mem, -KEMROM.RO.RO.m, -UU.TT.PKE.OW_CPA, -UU.TT.PKE.BOWp, -UU.TT.PKE.OWL_CPA, -UU.TT.PKE.OWvsIND.Bowl, -UU.TT.BasePKE, -UU.TT.PKEROM.RO.RO, -UU.TT.PKEROM.RO.FRO, -UU.TT.PKEROM.OW_PCVA, -UU.TT.Correctness_Adv1, -UU.TT.B, -UU.TT.CountO, -UU.TT.Gm, -UU.TT.O_AdvOW, -UU.RF.RF, -UU.PseudoRF.PRF, -UU.KEMROMx2.RO1.RO, -UU.KEMROMx2.RO1.FRO, -UU.KEMROMx2.RO2.RO, -UU.KEMROMx2.RO2.FRO, -UU.KEMROMx2.CCA, -UU.CountHx2, -UU.RO1E.FunRO, -UU.UU2, -UU.H2, -UU.Gm2, -UU.Gm3, -UU.H2BOWMod, -KEMROM.CCA, -B1x2} + ) &m, + UU.qHT = qHK => + UU.qHU = qHK => + UU.TT.qH = UU.qHT + UU.qHU + 1 => + UU.TT.qV = 0 => + UU.TT.qP = 0 => + UU.TT.qH + 1 = UU.TT.qHC => + UU.TT.qHC < UU.TT.FinT.card - 1 => + (forall (RO0 <: KEMROM.POracle{+all mem, -CountH, -A} ) (O0 <: KEMROM.CCA_ORC{+all mem, -CountH, -A} ), + hoare[ A(CountH(RO0), O0).guess : CountH.c_h = 0 ==> CountH.c_h <= qHK]) => + (forall (H0 <: KEMROM.POracle{+all mem, -A} ) (O <: UU.KEMROMx2.CCA_ORC{+all mem, -A} ), + islossless O.dec => islossless H0.get => islossless A(H0, O).guess) => + `|Pr[KEMROM.CCA(KEMROM.RO.RO, FO_K, A).main() @ &m : res] - 1%r / 2%r| <= + 2%r * + `|Pr[UU.TT.PKE.CPA(UU.TT.BasePKE, UU.TT.PKE.OWvsIND.Bowl(UU.TT.PKE.OWvsIND.BL(UU.TT.AdvOW(UU.BUUOWMod(B1x2(A)))))).main + () @ &m : res] - + 1%r / 2%r| + + 2%r * + `|Pr[UU.TT.PKE.CPA(UU.TT.BasePKE, UU.TT.PKE.OWvsIND.Bowl(UU.TT.AdvOWL_query(UU.BUUOWMod(B1x2(A))))).main + () @ &m : res] - + 1%r / 2%r| + + (2 * qHK + 3)%r * + Pr[UU.TT.PKE.Correctness_Adv(UU.TT.BasePKE, UU.TT.B(UU.BUUC(B1x2(A)), UU.TT.PKEROM.RO.RO)).main() @ &m : res] + + (2 * qHK + 3)%r * + Pr[UU.TT.PKE.Correctness_Adv(UU.TT.BasePKE, UU.TT.B(UU.BUUCI(B1x2(A)), UU.TT.PKEROM.RO.RO)).main() @ &m : res] + + (2 * qHK + 3)%r * + Pr[UU.TT.PKE.Correctness_Adv(UU.TT.BasePKE, UU.TT.B(UU.TT.AdvCorr(UU.BUUOWMod(B1x2(A))), UU.TT.PKEROM.RO.RO)).main + () @ &m : res] + + Pr[UU.TT.PKE.Correctness_Adv(UU.TT.BasePKE, UU.TT.PKE.BOWp(UU.TT.BasePKE, UU.TT.AdvOW(UU.BUUOWMod(B1x2(A))))).main + () @ &m : res] + + `|Pr[UU.J.IND(UU.PseudoRF.PRF, UU.D(B1x2(A))).main() @ &m : res] - + Pr[UU.J.IND(UU.RF.RF, UU.D(B1x2(A))).main() @ &m : res]| + + 2%r * (2 * qHK + 2)%r * UU.TT.PKE.eps_msg. + end FO_MLKEM. + + module MLWE_PKE_HASH = { + proc kg() : publickey * W8.t Array1152.t = { + var r : W8.t Array32.t; + var pk : publickey; + var sk : W8.t Array1152.t; + + r <$ srand; + (pk, sk) <- kg r; + + return (pk, sk); + } + + proc enc(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = { + var rr : W8.t Array32.t; + var c : W8.t Array960.t * W8.t Array128.t; + + rr <$ srand; + c <- enc rr pk m; + + return c; + } + + proc dec(sk : W8.t Array1152.t, c : W8.t Array960.t * W8.t Array128.t) : plaintext option = { + var mo : plaintext option; + + mo <- dec sk c; + + return mo; + } + }. + + module MLWE_PKE_HASH_PROC = { + proc kg_bridge() : publickey * W8.t Array1152.t = { + var r : W8.t Array32.t; + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var t : polyvec; + + r <$ srand; + (sd, s, e) <- prg_kg_inner r; + t <- ((H sd *^ s)%MLWE_.Matrix_.Matrix + e)%Vector; + + return (pk_encode (sd, t), sk_encode s); + } + + proc kg() : publickey * W8.t Array1152.t = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var t : polyvec; + + sd <$ srand; + s <$ MLWE_.dshort; + e <$ MLWE_.dshort; + t <- ((H sd *^ s)%MLWE_.Matrix_.Matrix + e)%Vector; + + return (pk_encode (sd, t), sk_encode s); + } + + proc enc_bridge(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = { + var sd : W8.t Array32.t; + var t : polyvec; + var rr : W8.t Array32.t; + var r : polyvec; + var e1 : polyvec; + var e2 : poly; + var u : polyvec; + var v : poly; + + (sd, t) <- pk_decode pk; + rr <$ srand; + (r, e1, e2) <- prg_enc_inner rr; + u <- ((trmx (H sd) *^ r)%MLWE_.Matrix_.Matrix + e1)%Vector; + v <- ((t `<*>` r) &+ e2 &+ m_encode m)%MLWE_; + + return c_encode (u, v); + } + + proc enc(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = { + var sd : W8.t Array32.t; + var t : polyvec; + var r : polyvec; + var e1 : polyvec; + var e2 : poly; + var u : polyvec; + var v : poly; + + (sd, t) <- pk_decode pk; + r <$ MLWE_.dshort; + e1 <$ MLWE_.dshort; + e2 <$ dshort_R; + u <- ((trmx (H sd) *^ r)%MLWE_.Matrix_.Matrix + e1)%Vector; + v <- ((t `<*>` r) &+ e2 &+ m_encode m)%MLWE_; + + return c_encode (u, v); + } + + proc dec(sk : W8.t Array1152.t, c : W8.t Array960.t * W8.t Array128.t) : plaintext option = { + var u : polyvec; + var v : poly; + + (u, v) <- c_decode c; + + return Some (m_decode (v &- (sk_decode sk `<*>` u))%MLWE_); + } + }. + + theory PRG_KG. + module type RG = { + proc get(sd : W8.t Array32.t) : W8.t Array32.t * polyvec * polyvec {} + } + + module type Distinguisher = { + proc distinguish(x : W8.t Array32.t * polyvec * polyvec) : bool {} + } + + module IND(PRG : RG, D : Distinguisher) = { + proc main() : bool = { + var b : bool; + var sd : W8.t Array32.t; + var x : W8.t Array32.t * polyvec * polyvec; + + sd <$ srand; + x <@ PRG.get(sd); + b <@ D.distinguish(x); + + return b; + } + }. + + module PRGr = { + proc get(sd : W8.t Array32.t) : W8.t Array32.t * polyvec * polyvec = { + var r : W8.t Array32.t * polyvec * polyvec; + + r <- prg_kg_inner sd; + + return r; + } + }. + + module PRGi = { + proc get(sd : W8.t Array32.t) : W8.t Array32.t * polyvec * polyvec = { + var r : W8.t Array32.t * polyvec * polyvec; + + r <$ prg_kg_ideal; + + return r; + } + }. + end PRG_KG. + + theory PRG_ENC. + module type RG = { + proc get(sd : W8.t Array32.t) : polyvec * polyvec * poly {} + } + + module type Distinguisher = { + proc distinguish(x : polyvec * polyvec * poly) : bool {} + } + + module IND(PRG : RG, D : Distinguisher) = { + proc main() : bool = { + var b : bool; + var sd : W8.t Array32.t; + var x : polyvec * polyvec * poly; + + sd <$ srand; + x <@ PRG.get(sd); + b <@ D.distinguish(x); + + return b; + } + }. + + module PRGr = { + proc get(sd : W8.t Array32.t) : polyvec * polyvec * poly = { + var r : polyvec * polyvec * poly; + + r <- prg_enc_inner sd; + + return r; + } + }. + + module PRGi = { + proc get(sd : W8.t Array32.t) : polyvec * polyvec * poly = { + var r : polyvec * polyvec * poly; + + r <$ prg_enc_ideal; + + return r; + } + }. + end PRG_ENC. + + module MLWE_PKE_HASH_PRG = { + var sd : W8.t Array32.t + + var s : polyvec + + var e : polyvec + + var r : polyvec + + var e1 : polyvec + + var e2 : poly + + proc kg() : publickey * W8.t Array1152.t = { + var t : polyvec; + + t <- ((H MLWE_PKE_HASH_PRG.sd *^ MLWE_PKE_HASH_PRG.s)%MLWE_.Matrix_.Matrix + MLWE_PKE_HASH_PRG.e)%Vector; + + return (pk_encode (MLWE_PKE_HASH_PRG.sd, t), sk_encode MLWE_PKE_HASH_PRG.s); + } + + proc enc(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = { + var sd : W8.t Array32.t; + var t : polyvec; + var u : polyvec; + var v : poly; + + (sd, t) <- pk_decode pk; + u <- ((trmx (H sd) *^ MLWE_PKE_HASH_PRG.r)%MLWE_.Matrix_.Matrix + MLWE_PKE_HASH_PRG.e1)%Vector; + v <- ((t `<*>` MLWE_PKE_HASH_PRG.r) &+ MLWE_PKE_HASH_PRG.e2 &+ m_encode m)%MLWE_; + + return c_encode (u, v); + } + + proc dec(sk : W8.t Array1152.t, c : W8.t Array960.t * W8.t Array128.t) : plaintext option = MLWE_PKE_HASH.dec + }. + + module D_KG(A : FO_MLKEM.UU.TT.PKE.Adversary) = { + proc distinguish(sd : W8.t Array32.t, s : polyvec, e : polyvec) : bool = { + var coins : W8.t Array32.t; + var b : bool; + + MLWE_PKE_HASH_PRG.sd <- sd; + MLWE_PKE_HASH_PRG.s <- s; + MLWE_PKE_HASH_PRG.e <- e; + coins <$ srand; + (MLWE_PKE_HASH_PRG.r, MLWE_PKE_HASH_PRG.e1, MLWE_PKE_HASH_PRG.e2) <- prg_enc_inner coins; + b <@ FO_MLKEM.UU.TT.PKE.CPA(MLWE_PKE_HASH_PRG, A).main(); + + return b; + } + }. + + module D_ENC(A : FO_MLKEM.UU.TT.PKE.Adversary) = { + proc distinguish(r : polyvec, e1 : polyvec, e2 : poly) : bool = { + var b : bool; + + (MLWE_PKE_HASH_PRG.sd, MLWE_PKE_HASH_PRG.s, MLWE_PKE_HASH_PRG.e) <$ prg_kg_ideal; + MLWE_PKE_HASH_PRG.r <- r; + MLWE_PKE_HASH_PRG.e1 <- e1; + MLWE_PKE_HASH_PRG.e2 <- e2; + b <@ FO_MLKEM.UU.TT.PKE.CPA(MLWE_PKE_HASH_PRG, A).main(); + + return b; + } + }. + + lemma cpa_proc: + forall (A <: FO_MLKEM.UU.TT.PKE.Adversary{+all mem, -MLWE_PKE_HASH_PRG} ) &m, + Pr[FO_MLKEM.UU.TT.PKE.CPA(MLWE_PKE_HASH, A).main() @ &m : res] - + Pr[FO_MLKEM.UU.TT.PKE.CPA(MLWE_PKE_HASH_PROC, A).main() @ &m : res] = + Pr[PRG_KG.IND(PRG_KG.PRGr, D_KG(A)).main() @ &m : res] - Pr[PRG_KG.IND(PRG_KG.PRGi, D_KG(A)).main() @ &m : res] + + Pr[PRG_ENC.IND(PRG_ENC.PRGr, D_ENC(A)).main() @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, D_ENC(A)).main() @ &m : res]. + + module DC_KG(A : FO_MLKEM.UU.TT.PKE.CORR_ADV) = { + proc distinguish(sd : W8.t Array32.t, s : polyvec, e : polyvec) : bool = { + var coins : W8.t Array32.t; + var b : bool; + + MLWE_PKE_HASH_PRG.sd <- sd; + MLWE_PKE_HASH_PRG.s <- s; + MLWE_PKE_HASH_PRG.e <- e; + coins <$ srand; + (MLWE_PKE_HASH_PRG.r, MLWE_PKE_HASH_PRG.e1, MLWE_PKE_HASH_PRG.e2) <- prg_enc_inner coins; + b <@ FO_MLKEM.UU.TT.PKE.Correctness_Adv(MLWE_PKE_HASH_PRG, A).main(); + + return b; + } + }. + + module DC_ENC(A : FO_MLKEM.UU.TT.PKE.CORR_ADV) = { + proc distinguish(r : polyvec, e1 : polyvec, e2 : poly) : bool = { + var b : bool; + + (MLWE_PKE_HASH_PRG.sd, MLWE_PKE_HASH_PRG.s, MLWE_PKE_HASH_PRG.e) <$ prg_kg_ideal; + MLWE_PKE_HASH_PRG.r <- r; + MLWE_PKE_HASH_PRG.e1 <- e1; + MLWE_PKE_HASH_PRG.e2 <- e2; + b <@ FO_MLKEM.UU.TT.PKE.Correctness_Adv(MLWE_PKE_HASH_PRG, A).main(); + + return b; + } + }. + + lemma corr_proc: + forall (A <: FO_MLKEM.UU.TT.PKE.CORR_ADV{+all mem, -MLWE_PKE_HASH_PRG} ) &m, + Pr[FO_MLKEM.UU.TT.PKE.Correctness_Adv(MLWE_PKE_HASH, A).main() @ &m : res] - + Pr[FO_MLKEM.UU.TT.PKE.Correctness_Adv(MLWE_PKE_HASH_PROC, A).main() @ &m : res] = + Pr[PRG_KG.IND(PRG_KG.PRGr, DC_KG(A)).main() @ &m : res] - Pr[PRG_KG.IND(PRG_KG.PRGi, DC_KG(A)).main() @ &m : res] + + Pr[PRG_ENC.IND(PRG_ENC.PRGr, DC_ENC(A)).main() @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, DC_ENC(A)).main() @ &m : res]. + + module MLWE_PKE_HASH1 = { + proc kg() : publickey * W8.t Array1152.t = { + var sd : W8.t Array32.t; + var s : polyvec; + var t : polyvec; + + sd <$ srand; + s <$ MLWE_.dshort; + t <$ MLWE_.duni; + + return (pk_encode (sd, t), sk_encode s); + } + + proc dec(sk : W8.t Array1152.t, c : W8.t Array960.t * W8.t Array128.t) : plaintext option = MLWE_PKE_HASH_PROC.dec + + proc enc(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = MLWE_PKE_HASH_PROC.enc + + proc enc_bridge(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = MLWE_PKE_HASH_PROC.enc_bridge + + proc kg_bridge() : publickey * W8.t Array1152.t = MLWE_PKE_HASH_PROC.kg_bridge + }. + + module B1(A : FO_MLKEM.UU.TT.PKE.Adversary) = { + proc kg(sd : W8.t Array32.t, t : polyvec) : publickey * W8.t Array1152.t = { + return (pk_encode (sd, t), witness); + } + + proc guess(sd : W8.t Array32.t, t : polyvec, uv : polyvec * poly) : bool = { + var pk : publickey; + var sk : W8.t Array1152.t; + var m0 : plaintext; + var m1 : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var b : bool; + var b' : bool; + + (pk, sk) <@ B1(A).kg(sd, uv.`1); + (m0, m1) <@ A.choose(pk); + b <$ {0,1}; + c <@ MLWE_PKE_HASH1.enc(pk, if b then m1 else m0); + b' <@ A.guess(c); + + return b' = b; + } + }. + + lemma hop1_left: + forall (A <: FO_MLKEM.UU.TT.PKE.Adversary) &m, + Pr[FO_MLKEM.UU.TT.PKE.CPA(MLWE_PKE_HASH_PROC, A).main() @ &m : res] = + Pr[MLWE_.MLWE_H(B1(A)).main(false, false) @ &m : res]. + + lemma hop1_right: + forall (A <: FO_MLKEM.UU.TT.PKE.Adversary) &m, + Pr[MLWE_.MLWE_H(B1(A)).main(false, true) @ &m : res] = + Pr[FO_MLKEM.UU.TT.PKE.CPA(MLWE_PKE_HASH1, A).main() @ &m : res]. + + module MLWE_PKE_HASH2 = { + proc enc(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = { + var _A : polymat; + var u : polyvec; + var v : poly; + + _A <- (trmx (H (pk_decode pk).`1))%MLWE_.Matrix_.Matrix; + u <$ MLWE_.duni; + v <$ duni_R; + + return c_encode (u, (v &+ m_encode m)%MLWE_); + } + + proc kg_bridge() : publickey * W8.t Array1152.t = MLWE_PKE_HASH1.kg_bridge + + proc enc_bridge(pk : publickey, m : plaintext) : W8.t Array960.t * W8.t Array128.t = MLWE_PKE_HASH1.enc_bridge + + proc dec(sk : W8.t Array1152.t, c : W8.t Array960.t * W8.t Array128.t) : plaintext option = MLWE_PKE_HASH1.dec + + proc kg() : publickey * W8.t Array1152.t = MLWE_PKE_HASH1.kg + }. + + module B2(A : FO_MLKEM.UU.TT.PKE.Adversary) = { + proc kg(sd : W8.t Array32.t, t : polyvec) : publickey * W8.t Array1152.t = { + return (pk_encode (sd, t), witness); + } + + proc enc(pk : publickey, m : plaintext, uv : polyvec * poly) : W8.t Array960.t * W8.t Array128.t = { + return c_encode (uv.`1, (uv.`2 &+ m_encode m)%MLWE_); + } + + proc guess(sd : W8.t Array32.t, t : polyvec, uv : polyvec * poly) : bool = { + var pk : publickey; + var sk : W8.t Array1152.t; + var m0 : plaintext; + var m1 : plaintext; + var c : W8.t Array960.t * W8.t Array128.t; + var b : bool; + var b' : bool; + + (pk, sk) <@ B2(A).kg(sd, t); + (m0, m1) <@ A.choose(pk); + b <$ {0,1}; + c <@ B2(A).enc(pk, if b then m1 else m0, uv); + b' <@ A.guess(c); + + return b' = b; + } + }. + + lemma hop2_left: + forall (A <: FO_MLKEM.UU.TT.PKE.Adversary) &m, + Pr[FO_MLKEM.UU.TT.PKE.CPA(MLWE_PKE_HASH1, A).main() @ &m : res] = + Pr[MLWE_.MLWE_H(B2(A)).main(true, false) @ &m : res]. + + lemma hop2_right: + forall (A <: FO_MLKEM.UU.TT.PKE.Adversary) &m, + Pr[MLWE_.MLWE_H(B2(A)).main(true, true) @ &m : res] = + Pr[FO_MLKEM.UU.TT.PKE.CPA(MLWE_PKE_HASH2, A).main() @ &m : res]. + + lemma main_theorem: + forall (A <: FO_MLKEM.UU.TT.PKE.Adversary{+all mem, -MLWE_PKE_HASH_PRG} ) &m, + islossless A.guess => + islossless A.choose => + `|Pr[FO_MLKEM.UU.TT.PKE.CPA(MLWE_PKE_HASH, A).main() @ &m : res] - 1%r / 2%r| <= + `|Pr[MLWE_.MLWE_H(B1(A)).main(false, false) @ &m : res] - Pr[MLWE_.MLWE_H(B1(A)).main(false, true) @ &m : res]| + + `|Pr[MLWE_.MLWE_H(B2(A)).main(true, false) @ &m : res] - Pr[MLWE_.MLWE_H(B2(A)).main(true, true) @ &m : res]| + + `|Pr[PRG_KG.IND(PRG_KG.PRGr, D_KG(A)).main() @ &m : res] - Pr[PRG_KG.IND(PRG_KG.PRGi, D_KG(A)).main() @ &m : res]| + + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, D_ENC(A)).main() @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, D_ENC(A)).main() @ &m : res]|. + + op noise_exp (_A : polymat) (s e r e1 : polyvec) (e2 : poly) (m : plaintext) : poly = + let t = ((_A *^ s)%MLWE_.Matrix_.Matrix + e)%Vector in + let u = ((trmx _A *^ r)%MLWE_.Matrix_.Matrix + e1)%Vector in + let v = ((t `<*>` r) &+ e2 &+ m_encode m)%MLWE_ in + let (u', v') = c_decode (c_encode (u, v)) in (v' &- (s `<*>` u') &- m_encode m)%MLWE_. + + lemma encode_noise: + forall (u : polyvec) (v : poly), c_decode (c_encode (u, v)) = ((u + rnd_err_u u)%Vector, (v &+ rnd_err_v v)%MLWE_). + + lemma matrix_props1: + forall (_A : polymat) (s e r : polyvec), + (((_A *^ s)%MLWE_.Matrix_.Matrix + e)%Vector `<*>` r)%MLWE_ = + (((s ^* trmx _A)%MLWE_.Matrix_.Matrix `<*>` r) &+ (e `<*>` r))%MLWE_. + + lemma matrix_props2: + forall (s : polyvec) (_A : polymat) (r e1 cu : polyvec), + (s `<*>` ((trmx _A *^ r)%MLWE_.Matrix_.Matrix + e1 + cu)%Vector)%MLWE_ = + (((s ^* trmx _A)%MLWE_.Matrix_.Matrix `<*>` r) &+ (s `<*>` e1) &+ (s `<*>` cu))%MLWE_. + + lemma noise_exp_val: + forall (_A : polymat) (s e r e1 : polyvec) (e2 : poly) (m : plaintext), + noise_exp _A s e r e1 e2 m = + let t = ((_A *^ s)%MLWE_.Matrix_.Matrix + e)%Vector in + let u = ((trmx _A *^ r)%MLWE_.Matrix_.Matrix + e1)%Vector in + let v = ((t `<*>` r) &+ e2 &+ m_encode m)%MLWE_ in + let cu = rnd_err_u u in let cv = rnd_err_v v in ((e `<*>` r) &- (s `<*>` e1) &- (s `<*>` cu) &+ e2 &+ cv)%MLWE_. + + lemma good_decode: + forall (m : plaintext) (n : poly), under_noise_bound n max_noise => m_decode (m_encode m &+ n)%MLWE_ = m. + + module CorrectnessAdvNoise(A : FO_MLKEM.UU.TT.PKE.CORR_ADV) = { + proc main() : bool = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var _A : polymat; + var r : polyvec; + var e1 : polyvec; + var e2 : poly; + var m : plaintext; + var n : poly; + + sd <$ srand; + _A <- H sd; + r <$ MLWE_.dshort; + s <$ MLWE_.dshort; + e <$ MLWE_.dshort; + e1 <$ MLWE_.dshort; + e2 <$ dshort_R; + m <@ A.find(pk_encode (sd, ((_A *^ s)%MLWE_.Matrix_.Matrix + e)%Vector), sk_encode s); + n <- noise_exp _A s e r e1 e2 m; + + return ! under_noise_bound n max_noise; + } + }. + + lemma correctness_noise: + forall (A <: FO_MLKEM.UU.TT.PKE.CORR_ADV{+all mem, -MLWE_PKE_HASH_PRG} ) &m, + Pr[FO_MLKEM.UU.TT.PKE.Correctness_Adv(MLWE_PKE_HASH, A).main() @ &m : res] <= + Pr[CorrectnessAdvNoise(A).main() @ &m : res] + Pr[PRG_KG.IND(PRG_KG.PRGr, DC_KG(A)).main() @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, DC_KG(A)).main() @ &m : res] + + Pr[PRG_ENC.IND(PRG_ENC.PRGr, DC_ENC(A)).main() @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, DC_ENC(A)).main() @ &m : res]. + + lemma noise_commutes: + forall (n n' : poly) (maxn b : int), + under_noise_bound n' b => under_noise_bound n (maxn - b) => under_noise_bound (n &+ n')%MLWE_ maxn. + + lemma noise_preserved: forall (n : poly) (maxn : int), under_noise_bound n maxn = under_noise_bound ((&-) n) maxn. + + op noise_exp_part1 (_A : polymat) (s e r e1 : polyvec) (e2 : poly) : poly = + let u = ((trmx _A *^ r)%MLWE_.Matrix_.Matrix + e1)%Vector in + let cu = rnd_err_u u in ((e `<*>` r) &- (s `<*>` e1) &+ e2 &- (s `<*>` cu))%MLWE_. + + op noise_exp_part2 (_A : polymat) (s e r : polyvec) (e2 : poly) (m : plaintext) : poly = + let t = ((_A *^ s)%MLWE_.Matrix_.Matrix + e)%Vector in + let v = ((t `<*>` r) &+ e2 &+ m_encode m)%MLWE_ in let cv = rnd_err_v v in cv. + + lemma parts_work: + forall (_A : polymat) (s e r e1 : polyvec) (e2 : poly) (m : plaintext), + noise_exp _A s e r e1 e2 m = (noise_exp_part1 _A s e r e1 e2 &+ noise_exp_part2 _A s e r e2 m)%MLWE_. + + module CB(A : FO_MLKEM.UU.TT.PKE.CORR_ADV) = { + var s : polyvec + + var e : polyvec + + var _A : polymat + + var r : polyvec + + var e1 : polyvec + + var e2 : poly + + var n1 : poly + + var n2 : poly + + var u : polyvec + + var cu : polyvec + + var m : plaintext + + proc main() : unit = { + var sd : W8.t Array32.t; + + sd <$ srand; + CB._A <- H sd; + CB.r <$ MLWE_.dshort; + CB.s <$ MLWE_.dshort; + CB.e <$ MLWE_.dshort; + CB.e1 <$ MLWE_.dshort; + CB.e2 <$ dshort_R; + CB.m <@ A.find(pk_encode (sd, ((CB._A *^ CB.s)%MLWE_.Matrix_.Matrix + CB.e)%Vector), sk_encode CB.s); + CB.n1 <- noise_exp_part1 CB._A CB.s CB.e CB.r CB.e1 CB.e2; + CB.n2 <- noise_exp_part2 CB._A CB.s CB.e CB.r CB.e2 CB.m; + } + }. + + lemma cv_bound_valid: + forall (_A : polymat) (s e r : polyvec) (e2 : poly) (m : plaintext), + s \in MLWE_.dshort => + e \in MLWE_.dshort => + r \in MLWE_.dshort => + e2 \in dshort_R => + let t = ((_A *^ s)%MLWE_.Matrix_.Matrix + e)%Vector in + let v = ((t `<*>` r) &+ e2 &+ m_encode m)%MLWE_ in under_noise_bound (rnd_err_v v) cv_bound_max. + + module CorrectnessBound = { + proc main() : bool = { + var sd : W8.t Array32.t; + var _A : polymat; + var r : polyvec; + var s : polyvec; + var e : polyvec; + var e1 : polyvec; + var e2 : poly; + var n : poly; + + sd <$ srand; + _A <- H sd; + r <$ MLWE_.dshort; + s <$ MLWE_.dshort; + e <$ MLWE_.dshort; + e1 <$ MLWE_.dshort; + e2 <$ dshort_R; + n <- noise_exp_part1 _A s e r e1 e2; + + return ! under_noise_bound n (max_noise - cv_bound_max); + } + }. + + lemma correctness_split: + forall (A <: FO_MLKEM.UU.TT.PKE.CORR_ADV{+all mem, -MLWE_PKE_HASH_PRG, -CB} ) &m (cv_bound : int) (failprob1 + failprob2 : real), + Pr[CB(A).main() @ &m : ! under_noise_bound CB.n1 (max_noise - cv_bound)] <= failprob1 => + Pr[CB(A).main() @ &m : ! under_noise_bound CB.n2 cv_bound] <= failprob2 => + Pr[CorrectnessAdvNoise(A).main() @ &m : res] <= failprob1 + failprob2. + + lemma correctness_bound_aux: + forall (A <: FO_MLKEM.UU.TT.PKE.CORR_ADV{+all mem, -MLWE_PKE_HASH_PRG, -CB} ) &m, + islossless A.find => + Pr[CB(A).main() @ &m : ! under_noise_bound CB.n1 (max_noise - cv_bound_max)] = + Pr[CorrectnessBound.main() @ &m : res]. + + lemma cv_max: + forall (A <: FO_MLKEM.UU.TT.PKE.CORR_ADV{+all mem, -MLWE_PKE_HASH_PRG, -CB} ) &m, + Pr[CB(A).main() @ &m : ! under_noise_bound CB.n2 cv_bound_max] = 0%r. + + lemma correctness_theorem: + forall (A <: FO_MLKEM.UU.TT.PKE.CORR_ADV{+all mem, -MLWE_PKE_HASH_PRG, -CB} ) &m, + islossless A.find => + Pr[FO_MLKEM.UU.TT.PKE.Correctness_Adv(MLWE_PKE_HASH, A).main() @ &m : res] <= + Pr[CorrectnessBound.main() @ &m : res] + + `|Pr[PRG_KG.IND(PRG_KG.PRGr, DC_KG(A)).main() @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, DC_KG(A)).main() @ &m : res]| + + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, DC_ENC(A)).main() @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, DC_ENC(A)).main() @ &m : res]|. + + lemma kg_same: equiv[ FO_MLKEM.UU.TT.BasePKE.kg ~ MLWE_PKE_HASH.kg : true ==> ={res}]. + + lemma correctness: + forall &m (fail_prob : real), + Pr[CorrectnessBound.main() @ &m : res] <= fail_prob => + FO_MLKEM.UU.TT.qHC = 0 => + 1 < FO_MLKEM.UU.TT.FinT.card => + Pr[FO_MLKEM.KEMROM.Correctness(FO_MLKEM.KEMROM.RO.RO, FO_MLKEM.FO_K).main() @ &m : res] <= + fail_prob + + `|Pr[PRG_KG.IND(PRG_KG.PRGr, DC_KG(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.B_UC, FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, DC_KG(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.B_UC, FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res]| + + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, DC_ENC(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.B_UC, FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, DC_ENC(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.B_UC, FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res]|. + + module BUOOOWMod_Hx2 = FO_MLKEM.CountH(FO_MLKEM.B1x2(A/227860, FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CountH(FO_MLKEM.UU.TT.H(FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO))), FO_MLKEM.UU.TT.CountO(FO_MLKEM.UU.TT.G2_O(FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO)))).H2B), FO_MLKEM.UU.KEMROMx2.CCA(FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CountH(FO_MLKEM.UU.TT.H(FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO))), FO_MLKEM.UU.TT.CountO(FO_MLKEM.UU.TT.G2_O(FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO)))).H2B), FO_MLKEM.UU.UU2, FO_MLKEM.B1x2(A/227860)).O).BH). + + module BUOOOWMod_dec = FO_MLKEM.UU.KEMROMx2.CCA(FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CountH(FO_MLKEM.UU.TT.H(FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO))), FO_MLKEM.UU.TT.CountO(FO_MLKEM.UU.TT.G2_O(FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO)))).H2B), FO_MLKEM.UU.UU2, FO_MLKEM.B1x2(A/227860)).O. + + module BUOOOWModCPA_Hx2 = FO_MLKEM.CountH(FO_MLKEM.B1x2(A/227860, FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CountH(FO_MLKEM.UU.TT.PKEROM.RO.RO), FO_MLKEM.UU.TT.CountO(FO_MLKEM.UU.TT.O_AdvOW)).H2B), FO_MLKEM.UU.KEMROMx2.CCA(FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CountH(FO_MLKEM.UU.TT.PKEROM.RO.RO), FO_MLKEM.UU.TT.CountO(FO_MLKEM.UU.TT.O_AdvOW)).H2B), FO_MLKEM.UU.UU2, FO_MLKEM.B1x2(A/227860)).O).BH). + + module BUOOOWModCPA_dec = FO_MLKEM.UU.KEMROMx2.CCA(FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CountH(FO_MLKEM.UU.TT.PKEROM.RO.RO), FO_MLKEM.UU.TT.CountO(FO_MLKEM.UU.TT.O_AdvOW)).H2B), FO_MLKEM.UU.UU2, FO_MLKEM.B1x2(A/227860)).O. + + module BUUCI_Hx2 = FO_MLKEM.CountH(FO_MLKEM.B1x2(A/227860, FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUCI(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO)).H2B), FO_MLKEM.UU.KEMROMx2.CCA(FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUCI(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO)).H2B), FO_MLKEM.UU.UU2, FO_MLKEM.B1x2(A/227860)).O).BH). + + module BUUCI_dec = FO_MLKEM.UU.KEMROMx2.CCA(FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUCI(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO)).H2B), FO_MLKEM.UU.UU2, FO_MLKEM.B1x2(A/227860)).O. + + module BUUC_Hx2 = FO_MLKEM.CountH(FO_MLKEM.B1x2(A/227860, FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUC(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO)).H2B), FO_MLKEM.UU.KEMROMx2.CCA(FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUC(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO)).H2B), FO_MLKEM.UU.UU2, FO_MLKEM.B1x2(A/227860)).O).BH). + + module BUUC_dec = FO_MLKEM.UU.KEMROMx2.CCA(FO_MLKEM.UU.CountHx2(FO_MLKEM.UU.BUUC(FO_MLKEM.B1x2(A/227860), FO_MLKEM.UU.TT.CO1(FO_MLKEM.UU.TT.PKEROM.RO.RO)).H2B), FO_MLKEM.UU.UU2, FO_MLKEM.B1x2(A/227860)).O. + + lemma conclusion: + forall (A(H : FO_MLKEM.KEMROM.POracle, O : FO_MLKEM.KEMROM.CCA_ORC) <: + FO_MLKEM.KEMROM.CCA_ADV{+all mem, -FO_MLKEM.KEMROM.RO.RO.m, -FO_MLKEM.UU.TT.PKE.OW_CPA, -FO_MLKEM.UU.TT.PKE.BOWp, -FO_MLKEM.UU.TT.PKE.OWL_CPA, -FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl, -FO_MLKEM.UU.TT.BasePKE, -FO_MLKEM.UU.TT.PKEROM.RO.RO, -FO_MLKEM.UU.TT.PKEROM.RO.FRO, -FO_MLKEM.UU.TT.PKEROM.OW_PCVA, -FO_MLKEM.UU.TT.Correctness_Adv1, -FO_MLKEM.UU.TT.B, -FO_MLKEM.UU.TT.CountO, -FO_MLKEM.UU.TT.Gm, -FO_MLKEM.UU.TT.O_AdvOW, -FO_MLKEM.UU.RF.RF, -FO_MLKEM.UU.PseudoRF.PRF, -FO_MLKEM.UU.KEMROMx2.RO1.RO, -FO_MLKEM.UU.KEMROMx2.RO1.FRO, -FO_MLKEM.UU.KEMROMx2.RO2.RO, -FO_MLKEM.UU.KEMROMx2.RO2.FRO, -FO_MLKEM.UU.KEMROMx2.CCA, -FO_MLKEM.UU.CountHx2, -FO_MLKEM.UU.RO1E.FunRO, -FO_MLKEM.UU.UU2, -FO_MLKEM.UU.H2, -FO_MLKEM.UU.Gm2, -FO_MLKEM.UU.Gm3, -FO_MLKEM.UU.H2BOWMod, -FO_MLKEM.KEMROM.CCA, -FO_MLKEM.B1x2, -MLWE_PKE_HASH_PRG, -CB} + ) &m (fail_prob prg_kg_bound prg_enc_bound : real), + Pr[CorrectnessBound.main() @ &m : res] <= fail_prob => + `|Pr[PRG_KG.IND(PRG_KG.PRGr, DC_KG(FO_MLKEM.UU.TT.PKE.BOWp(FO_MLKEM.UU.TT.BasePKE, FO_MLKEM.UU.TT.AdvOW_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, DC_KG(FO_MLKEM.UU.TT.PKE.BOWp(FO_MLKEM.UU.TT.BasePKE, FO_MLKEM.UU.TT.AdvOW_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res]| <= + prg_kg_bound => + `|Pr[PRG_KG.IND(PRG_KG.PRGr, DC_KG(FO_MLKEM.UU.TT.PKE.BOWp(FO_MLKEM.UU.TT.BasePKE, FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, DC_KG(FO_MLKEM.UU.TT.PKE.BOWp(FO_MLKEM.UU.TT.BasePKE, FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res]| <= + prg_kg_bound => + `|Pr[PRG_KG.IND(PRG_KG.PRGr, DC_KG(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.TT.AdvCorr(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, DC_KG(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.TT.AdvCorr(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res]| <= + prg_kg_bound => + `|Pr[PRG_KG.IND(PRG_KG.PRGr, DC_KG(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.BUUCI(FO_MLKEM.B1x2(A)), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, DC_KG(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.BUUCI(FO_MLKEM.B1x2(A)), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res]| <= + prg_kg_bound => + `|Pr[PRG_KG.IND(PRG_KG.PRGr, DC_KG(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.BUUC(FO_MLKEM.B1x2(A)), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, DC_KG(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.BUUC(FO_MLKEM.B1x2(A)), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res]| <= + prg_kg_bound => + `|Pr[PRG_KG.IND(PRG_KG.PRGr, D_KG(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.PKE.OWvsIND.BL(FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))))))).main + () @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, D_KG(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.PKE.OWvsIND.BL(FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))))))).main + () @ &m : res]| <= + prg_kg_bound => + `|Pr[PRG_KG.IND(PRG_KG.PRGr, D_KG(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.AdvOWL_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res] - + Pr[PRG_KG.IND(PRG_KG.PRGi, D_KG(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.AdvOWL_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res]| <= + prg_kg_bound => + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, DC_ENC(FO_MLKEM.UU.TT.PKE.BOWp(FO_MLKEM.UU.TT.BasePKE, FO_MLKEM.UU.TT.AdvOW_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, DC_ENC(FO_MLKEM.UU.TT.PKE.BOWp(FO_MLKEM.UU.TT.BasePKE, FO_MLKEM.UU.TT.AdvOW_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res]| <= + prg_enc_bound => + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, DC_ENC(FO_MLKEM.UU.TT.PKE.BOWp(FO_MLKEM.UU.TT.BasePKE, FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, DC_ENC(FO_MLKEM.UU.TT.PKE.BOWp(FO_MLKEM.UU.TT.BasePKE, FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res]| <= + prg_enc_bound => + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, DC_ENC(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.TT.AdvCorr(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, DC_ENC(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.TT.AdvCorr(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res]| <= + prg_enc_bound => + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, DC_ENC(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.BUUCI(FO_MLKEM.B1x2(A)), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, DC_ENC(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.BUUCI(FO_MLKEM.B1x2(A)), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res]| <= + prg_enc_bound => + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, DC_ENC(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.BUUC(FO_MLKEM.B1x2(A)), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, DC_ENC(FO_MLKEM.UU.TT.B(FO_MLKEM.UU.BUUC(FO_MLKEM.B1x2(A)), FO_MLKEM.UU.TT.PKEROM.RO.RO))).main + () @ &m : res]| <= + prg_enc_bound => + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, D_ENC(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.PKE.OWvsIND.BL(FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))))))).main + () @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, D_ENC(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.PKE.OWvsIND.BL(FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))))))).main + () @ &m : res]| <= + prg_enc_bound => + `|Pr[PRG_ENC.IND(PRG_ENC.PRGr, D_ENC(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.AdvOWL_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res] - + Pr[PRG_ENC.IND(PRG_ENC.PRGi, D_ENC(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.AdvOWL_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + () @ &m : res]| <= + prg_enc_bound => + FO_MLKEM.UU.qHT = FO_MLKEM.qHK => + FO_MLKEM.UU.qHU = FO_MLKEM.qHK => + FO_MLKEM.UU.TT.qH = FO_MLKEM.UU.qHT + FO_MLKEM.UU.qHU + 1 => + FO_MLKEM.UU.TT.qV = 0 => + FO_MLKEM.UU.TT.qP = 0 => + FO_MLKEM.UU.TT.qH + 1 = FO_MLKEM.UU.TT.qHC => + FO_MLKEM.UU.TT.qHC < FO_MLKEM.UU.TT.FinT.card - 1 => + (forall (RO0 <: FO_MLKEM.KEMROM.POracle{+all mem, -FO_MLKEM.CountH, -A} ) + (O0 <: FO_MLKEM.KEMROM.CCA_ORC{+all mem, -FO_MLKEM.CountH, -A} ), + hoare[ A(FO_MLKEM.CountH(RO0), O0).guess : FO_MLKEM.CountH.c_h = 0 ==> FO_MLKEM.CountH.c_h <= FO_MLKEM.qHK]) => + (forall (H0 <: FO_MLKEM.KEMROM.POracle{+all mem, -A} ) (O <: FO_MLKEM.UU.KEMROMx2.CCA_ORC{+all mem, -A} ), + islossless O.dec => islossless H0.get => islossless A(H0, O).guess) => + `|Pr[FO_MLKEM.KEMROM.CCA(FO_MLKEM.KEMROM.RO.RO, FO_MLKEM.FO_K, A).main() @ &m : res] - 1%r / 2%r| <= + 2%r * + (`|Pr[MLWE_.MLWE_H(B1(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.AdvOWL_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + (false, false) @ &m : res] - + Pr[MLWE_.MLWE_H(B1(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.AdvOWL_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + (false, true) @ &m : res]| + + `|Pr[MLWE_.MLWE_H(B2(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.AdvOWL_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + (true, false) @ &m : res] - + Pr[MLWE_.MLWE_H(B2(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.AdvOWL_query(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A)))))).main + (true, true) @ &m : res]| + + prg_kg_bound + prg_enc_bound) + + 2%r * + (`|Pr[MLWE_.MLWE_H(B1(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.PKE.OWvsIND.BL(FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))))))).main + (false, false) @ &m : res] - + Pr[MLWE_.MLWE_H(B1(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.PKE.OWvsIND.BL(FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))))))).main + (false, true) @ &m : res]| + + `|Pr[MLWE_.MLWE_H(B2(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.PKE.OWvsIND.BL(FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))))))).main + (true, false) @ &m : res] - + Pr[MLWE_.MLWE_H(B2(FO_MLKEM.UU.TT.PKE.OWvsIND.Bowl(FO_MLKEM.UU.TT.PKE.OWvsIND.BL(FO_MLKEM.UU.TT.AdvOW(FO_MLKEM.UU.BUUOWMod(FO_MLKEM.B1x2(A))))))).main + (true, true) @ &m : res]| + + prg_kg_bound + prg_enc_bound) + + (3%r * (2 * FO_MLKEM.qHK + 3)%r + 1%r) * (fail_prob + prg_kg_bound + prg_enc_bound) + + `|Pr[FO_MLKEM.UU.J.IND(FO_MLKEM.UU.PseudoRF.PRF, FO_MLKEM.UU.D(FO_MLKEM.B1x2(A))).main() @ &m : res] - + Pr[FO_MLKEM.UU.J.IND(FO_MLKEM.UU.RF.RF, FO_MLKEM.UU.D(FO_MLKEM.B1x2(A))).main() @ &m : res]| + + 2%r * (2 * FO_MLKEM.qHK + 2)%r * FO_MLKEM.UU.TT.PKE.eps_msg. +end MLWEPKEHash. + +[|] [0921] 39.8% (-1.0B / [frag -1.0B]) * In [theories]: + +theory MLWEPKEHash. + theory MLWE_. + theory Matrix_. + theory ZR. + lemma nosmt addrA: associative (&+). + + lemma nosmt addrC: commutative (&+). + + lemma nosmt add0r: left_id Rq.zero (&+). + + lemma nosmt addNr: left_inverse Rq.zero (&-) (&+). + + theory AddMonoid. + lemma addmA: associative (&+). + + lemma addmC: commutative (&+). + + lemma add0m: left_id Rq.zero (&+). + + lemma addm0: right_id Rq.zero (&+). + + lemma addmCA: left_commutative (&+). + + lemma addmAC: right_commutative (&+). + + lemma addmACA: interchange (&+) (&+). + + lemma iteropE: forall (n : int) (x : poly), iterop n (&+) x Rq.zero = iter n ((&+) x) Rq.zero. + end AddMonoid. + + abbrev (-) (x y : poly) : poly = (x - y)%KMatrix.ZR. + + lemma nosmt addr0: right_id Rq.zero (&+). + + lemma nosmt addrN: right_inverse Rq.zero (&-) (&+). + + lemma nosmt addrCA: left_commutative (&+). + + lemma nosmt addrAC: right_commutative (&+). + + lemma nosmt addrACA: interchange (&+) (&+). + + lemma nosmt subrr: forall (x : poly), x - x = Rq.zero. + + lemma nosmt addKr: left_loop (&-) (&+). + + lemma nosmt addNKr: rev_left_loop (&-) (&+). + + lemma nosmt addrK: right_loop (&-) (&+). + + lemma nosmt addrNK: rev_right_loop (&-) (&+). + + lemma nosmt subrK: forall (x y : poly), (x - y) &+ y = x. + + lemma nosmt addrI: right_injective (&+). + + lemma nosmt addIr: left_injective (&+). + + lemma nosmt opprK: involutive (&-). + + lemma oppr_inj: injective (&-). + + lemma nosmt oppr0: (&-) Rq.zero = Rq.zero. + + lemma oppr_eq0: forall (x : poly), (&-) x = Rq.zero <=> x = Rq.zero. + + lemma nosmt subr0: forall (x : poly), x - Rq.zero = x. + + lemma nosmt sub0r: forall (x : poly), Rq.zero - x = (&-) x. + + lemma nosmt opprD: forall (x y : poly), (&-) (x &+ y) = (&-) x - y. + + lemma nosmt opprB: forall (x y : poly), (&-) (x - y) = y - x. + + lemma nosmt subrACA: interchange (fun (x y : poly) => x - y) (&+). + + lemma nosmt subr_eq: forall (x y z : poly), x - z = y <=> x = y &+ z. + + lemma nosmt subr_eq0: forall (x y : poly), x - y = Rq.zero <=> x = y. + + lemma nosmt addr_eq0: forall (x y : poly), x &+ y = Rq.zero <=> x = (&-) y. + + lemma nosmt eqr_opp: forall (x y : poly), (&-) x = (&-) y <=> x = y. + + lemma eqr_oppLR: forall (x y : poly), (&-) x = y <=> x = (&-) y. + + lemma nosmt eqr_sub: forall (x y z t : poly), x - y = z - t <=> x &+ t = z &+ y. + + lemma subr_add2r: forall (z x y : poly), x &+ z - y &+ z = x - y. + + op intmul (x : poly) (n : int) : poly = + if n < 0 then (&-) (iterop (-n) (&+) x Rq.zero) else iterop n (&+) x Rq.zero. + + lemma intmulpE: forall (z : poly) (c : int), 0 <= c => intmul z c = iterop c (&+) z Rq.zero. + + lemma mulr0z: forall (x : poly), intmul x 0 = Rq.zero. + + lemma mulr1z: forall (x : poly), intmul x 1 = x. + + lemma mulr2z: forall (x : poly), intmul x 2 = x &+ x. + + lemma mulrNz: forall (x : poly) (n : int), intmul x (-n) = (&-) (intmul x n). + + lemma mulrS: forall (x : poly) (n : int), 0 <= n => intmul x (n + 1) = x &+ intmul x n. + + lemma mulNrz: forall (x : poly) (n : int), intmul ((&-) x) n = (&-) (intmul x n). + + lemma mulNrNz: forall (x : poly) (n : int), intmul ((&-) x) (-n) = intmul x n. + + lemma mulrSz: forall (x : poly) (n : int), intmul x (n + 1) = x &+ intmul x n. + + lemma mulrDz: forall (x : poly) (n m : int), intmul x (n + m) = intmul x n &+ intmul x m. + + abbrev (/) (x y : poly) : poly = (x / y)%KMatrix.ZR. + + lemma nosmt oner_neq0: Rq.one <> Rq.zero. + + lemma nosmt mulrA: associative ( &* ). + + lemma nosmt mulrC: commutative ( &* ). + + lemma nosmt mul1r: left_id Rq.one ( &* ). + + lemma nosmt mulrDl: left_distributive ( &* ) (&+). + + lemma nosmt mulVr: left_inverse_in KMatrix.ZR.unit Rq.one Top.Correctness.invr ( &* ). + + lemma nosmt unitP: forall (x y : poly), y &* x = Rq.one => (unit x)%KMatrix.ZR. + + lemma nosmt unitout: forall (x : poly), ! (unit x)%KMatrix.ZR => invr x = x. + + theory MulMonoid. + lemma addmA: associative ( &* ). + + lemma addmC: commutative ( &* ). + + lemma add0m: left_id Rq.one ( &* ). + + lemma addm0: right_id Rq.one ( &* ). + + lemma addmCA: left_commutative ( &* ). + + lemma addmAC: right_commutative ( &* ). + + lemma addmACA: interchange ( &* ) ( &* ). + + lemma iteropE: forall (n : int) (x : poly), iterop n ( &* ) x Rq.one = iter n (( &* ) x) Rq.one. + end MulMonoid. + + lemma nosmt mulr1: right_id Rq.one ( &* ). + + lemma nosmt mulrCA: left_commutative ( &* ). + + lemma nosmt mulrAC: right_commutative ( &* ). + + lemma nosmt mulrACA: interchange ( &* ) ( &* ). + + lemma nosmt mulrSl: forall (x y : poly), x &+ Rq.one &* y = x &* y &+ y. + + lemma nosmt mulrDr: right_distributive ( &* ) (&+). + + lemma nosmt mul0r: left_zero Rq.zero ( &* ). + + lemma nosmt mulr0: right_zero Rq.zero ( &* ). + + lemma nosmt mulrN: forall (x y : poly), x &* (&-) y = (&-) (x &* y). + + lemma nosmt mulNr: forall (x y : poly), (&-) x &* y = (&-) (x &* y). + + lemma nosmt mulrNN: forall (x y : poly), (&-) x &* (&-) y = x &* y. + + lemma nosmt mulN1r: forall (x : poly), (&-) Rq.one &* x = (&-) x. + + lemma nosmt mulrN1: forall (x : poly), x &* (&-) Rq.one = (&-) x. + + lemma nosmt mulrBl: left_distributive ( &* ) (fun (x y : poly) => x - y). + + lemma nosmt mulrBr: right_distributive ( &* ) (fun (x y : poly) => x - y). + + lemma mulrnAl: forall (x y : poly) (n : int), 0 <= n => intmul x n &* y = intmul (x &* y) n. + + lemma mulrnAr: forall (x y : poly) (n : int), 0 <= n => x &* intmul y n = intmul (x &* y) n. + + lemma mulrzAl: forall (x y : poly) (z : int), intmul x z &* y = intmul (x &* y) z. + + lemma mulrzAr: forall (x y : poly) (z : int), x &* intmul y z = intmul (x &* y) z. + + lemma nosmt mulrV: right_inverse_in KMatrix.ZR.unit Rq.one Top.Correctness.invr ( &* ). + + lemma nosmt divrr: forall (x : poly), (unit x)%KMatrix.ZR => x / x = Rq.one. + + lemma nosmt invr_out: forall (x : poly), ! (unit x)%KMatrix.ZR => invr x = x. + + lemma nosmt unitrP: forall (x : poly), (unit x)%KMatrix.ZR <=> exists (y : poly), y &* x = Rq.one. + + lemma nosmt mulKr: left_loop_in KMatrix.ZR.unit Top.Correctness.invr ( &* ). + + lemma nosmt mulrK: right_loop_in KMatrix.ZR.unit Top.Correctness.invr ( &* ). + + lemma nosmt mulVKr: rev_left_loop_in KMatrix.ZR.unit Top.Correctness.invr ( &* ). + + lemma nosmt mulrVK: rev_right_loop_in KMatrix.ZR.unit Top.Correctness.invr ( &* ). + + lemma nosmt mulrI: right_injective_in KMatrix.ZR.unit ( &* ). + + lemma nosmt mulIr: left_injective_in KMatrix.ZR.unit ( &* ). + + lemma nosmt unitrE: forall (x : poly), (unit x)%KMatrix.ZR <=> x / x = Rq.one. + + lemma nosmt invrK: involutive Top.Correctness.invr. + + lemma nosmt invr_inj: injective Top.Correctness.invr. + + lemma nosmt unitrV: forall (x : poly), (unit (invr x))%KMatrix.ZR <=> (unit x)%KMatrix.ZR. + + lemma nosmt unitr1: (unit Rq.one)%KMatrix.ZR. + + lemma nosmt invr1: invr Rq.one = Rq.one. + + lemma nosmt div1r: forall (x : poly), Rq.one / x = invr x. + + lemma nosmt divr1: forall (x : poly), x / Rq.one = x. + + lemma nosmt unitr0: ! (unit Rq.zero)%KMatrix.ZR. + + lemma nosmt invr0: invr Rq.zero = Rq.zero. + + lemma nosmt unitrN1: (unit ((&-) Rq.one))%KMatrix.ZR. + + lemma nosmt invrN1: invr ((&-) Rq.one) = (&-) Rq.one. + + lemma nosmt unitrMl: + forall (x y : poly), (unit y)%KMatrix.ZR => (unit (x &* y))%KMatrix.ZR <=> (unit x)%KMatrix.ZR. + + lemma nosmt unitrMr: + forall (x y : poly), (unit x)%KMatrix.ZR => (unit (x &* y))%KMatrix.ZR <=> (unit y)%KMatrix.ZR. + + lemma nosmt unitrM: + forall (x y : poly), (unit (x &* y))%KMatrix.ZR <=> (unit x)%KMatrix.ZR /\ (unit y)%KMatrix.ZR. + + lemma nosmt unitrN: forall (x : poly), (unit ((&-) x))%KMatrix.ZR <=> (unit x)%KMatrix.ZR. + + lemma nosmt invrM: + forall (x y : poly), (unit x)%KMatrix.ZR => (unit y)%KMatrix.ZR => invr (x &* y) = invr y / x. + + lemma nosmt invrN: forall (x : poly), invr ((&-) x) = (&-) (invr x). + + lemma nosmt invr_neq0: forall (x : poly), x <> Rq.zero => invr x <> Rq.zero. + + lemma nosmt invr_eq0: forall (x : poly), invr x = Rq.zero <=> x = Rq.zero. + + lemma nosmt invr_eq1: forall (x : poly), invr x = Rq.one <=> x = Rq.one. + + op ofint (n : int) : poly = intmul Rq.one n. + + lemma ofint0: (ofint 0)%ZR = Rq.zero. + + lemma ofint1: (ofint 1)%ZR = Rq.one. + + lemma ofintS: forall (i : int), 0 <= i => (ofint (i + 1))%ZR = Rq.one &+ (ofint i)%ZR. + + lemma ofintN: forall (i : int), (ofint (-i))%ZR = (&-) ((ofint i))%ZR. + + lemma mul1r0z: forall (x : poly), x &* (ofint 0)%ZR = Rq.zero. + + lemma mul1r1z: forall (x : poly), x &* (ofint 1)%ZR = x. + + lemma mul1r2z: forall (x : poly), x &* (ofint 2)%ZR = x &+ x. + + lemma mulr_intl: forall (x : poly) (z : int), (ofint z)%ZR &* x = intmul x z. + + lemma mulr_intr: forall (x : poly) (z : int), x &* (ofint z)%ZR = intmul x z. + + lemma fracrDE: + forall (n1 n2 d1 d2 : poly), + (unit d1)%KMatrix.ZR => (unit d2)%KMatrix.ZR => n1 / d1 &+ (n2 / d2) = n1 &* d2 &+ (n2 &* d1) / (d1 &* d2). + + op exp (x : poly) (n : int) : poly = + if n < 0 then invr (iterop (-n) ( &* ) x Rq.one) else iterop n ( &* ) x Rq.one. + + lemma expr0: forall (x : poly), exp x 0 = Rq.one. + + lemma expr1: forall (x : poly), exp x 1 = x. + + lemma exprS: forall (x : poly) (i : int), 0 <= i => exp x (i + 1) = x &* exp x i. + + lemma expr_pred: forall (x : poly) (i : int), 0 < i => exp x i = x &* exp x (i - 1). + + lemma exprSr: forall (x : poly) (i : int), 0 <= i => exp x (i + 1) = exp x i &* x. + + lemma expr2: forall (x : poly), exp x 2 = x &* x. + + lemma exprN: forall (x : poly) (i : int), exp x (-i) = invr (exp x i). + + lemma exprN1: forall (x : poly), exp x (-1) = invr x. + + lemma unitrX: forall (x : poly) (m : int), (unit x)%KMatrix.ZR => (unit (exp x m))%KMatrix.ZR. + + lemma unitrX_neq0: forall (x : poly) (m : int), m <> 0 => (unit (exp x m))%KMatrix.ZR => (unit x)%KMatrix.ZR. + + lemma exprV: forall (x : poly) (i : int), exp (invr x) i = exp x (-i). + + lemma exprVn: forall (x : poly) (n : int), 0 <= n => exp (invr x) n = invr (exp x n). + + lemma exprMn: forall (x y : poly) (n : int), 0 <= n => exp (x &* y) n = exp x n &* exp y n. + + lemma exprD_nneg: forall (x : poly) (m n : int), 0 <= m => 0 <= n => exp x (m + n) = exp x m &* exp x n. + + lemma exprD: forall (x : poly) (m n : int), (unit x)%KMatrix.ZR => exp x (m + n) = exp x m &* exp x n. + + lemma exprM: forall (x : poly) (m n : int), exp x (m * n) = exp (exp x m) n. + + lemma expr0n: forall (n : int), 0 <= n => exp Rq.zero n = if n = 0 then Rq.one else Rq.zero. + + lemma expr0z: forall (z : int), exp Rq.zero z = if z = 0 then Rq.one else Rq.zero. + + lemma expr1z: forall (z : int), exp Rq.one z = Rq.one. + + lemma sqrrD: forall (x y : poly), exp (x &+ y) 2 = exp x 2 &+ intmul (x &* y) 2 &+ exp y 2. + + lemma sqrrN: forall (x : poly), exp ((&-) x) 2 = exp x 2. + + lemma sqrrB: forall (x y : poly), exp (x - y) 2 = (exp x 2 - intmul (x &* y) 2) &+ exp y 2. + + lemma signr_odd: forall (n : int), 0 <= n => exp ((&-) Rq.one) (b2i (odd n)) = exp ((&-) Rq.one) n. + + lemma subr_sqr_1: forall (x : poly), exp x 2 - Rq.one = (x - Rq.one) &* (x &+ Rq.one). + + op lreg (x : poly) : bool = injective (fun (y : poly) => x &* y). + + lemma mulrI_eq0: forall (x y : poly), lreg x => x &* y = Rq.zero <=> y = Rq.zero. + + lemma lreg_neq0: forall (x : poly), lreg x => x <> Rq.zero. + + lemma mulrI0_lreg: forall (x : poly), (forall (y : poly), x &* y = Rq.zero => y = Rq.zero) => lreg x. + + lemma lregN: forall (x : poly), lreg x => lreg ((&-) x). + + lemma lreg1: lreg Rq.one. + + lemma lregM: forall (x y : poly), lreg x => lreg y => lreg (x &* y). + + lemma lregXn: forall (x : poly) (n : int), 0 <= n => lreg x => lreg (exp x n). + + instance ring with [()] poly + op rzero = Top.Rq.zero + op rone = Top.Rq.one + op add = Top.Rq.&+ + op opp = Top.Rq.&- + op mul = Top.Rq.&* + op expr = Top.MLWEPKEHash.MLWE_.Matrix_.ZR.exp + op ofint = Top.MLWEPKEHash.MLWE_.Matrix_.ZR.ofint + + instance poly with Top.Ring.ZModule.zmodule. + + instance poly with Top.MLWEPKEHash.MLWE_.Matrix_.ZR.ring. + + instance poly with Top.Ring.IDomain.idomain. + end ZR. + + theory Big. + theory CR. + instance ring with [()] poly + op rzero = Top.Rq.zero + op rone = Top.Rq.one + op add = Top.Rq.&+ + op opp = Top.Rq.&- + op mul = Top.Rq.&* + op expr = Top.MLWEPKEHash.MLWE_.Matrix_.ZR.exp + op ofint = Top.MLWEPKEHash.MLWE_.Matrix_.ZR.ofint + + instance poly with Top.Ring.ZModule.zmodule. + + instance poly with Top.MLWEPKEHash.MLWE_.Matrix_.Big.CR.ring. + + instance poly with Top.Ring.IDomain.idomain. + end CR. + + theory BAdd. + op big ['a] (P : 'a -> bool) (F : 'a -> poly) (r : 'a list) : poly = foldr (&+) Rq.zero (map F (filter P r)). + + abbrev bigi (P : int -> bool) (F : int -> poly) (i j : int) : poly = big P F (range i j). + + lemma big_nil ['a]: forall (P : 'a -> bool) (F : 'a -> poly), big P F [] = Rq.zero. + + lemma big_cons ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (x : 'a) (s : 'a list), + big P F (x :: s) = if P x then F x &+ big P F s else big P F s. + + lemma big_consT ['a]: + forall (F : 'a -> poly) (x : 'a) (s : 'a list), big predT F (x :: s) = F x &+ big predT F s. + + lemma nosmt big_rec ['a]: + forall (K : poly -> bool) (r : 'a list) (P : 'a -> bool) (F : + 'a -> poly), K Rq.zero => (forall (i : 'a) (x : poly), P i => K x => K (F i &+ x)) => K (big P F r). + + lemma nosmt big_ind ['a]: + forall (K : poly -> bool) (r : 'a list) (P : 'a -> bool) (F : + 'a -> poly), + (forall (x y : poly), K x => K y => K (x &+ y)) => + K Rq.zero => (forall (i : 'a), P i => K (F i)) => K (big P F r). + + lemma nosmt big_rec2 ['a]: + forall (K : poly -> poly -> bool) (r : 'a list) (P : 'a -> bool) (F1 F2 : + 'a -> poly), + K Rq.zero Rq.zero => + (forall (i : 'a) (y1 y2 : poly), P i => K y1 y2 => K (F1 i &+ y1) (F2 i &+ y2)) => + K (big P F1 r) (big P F2 r). + + lemma nosmt big_ind2 ['a]: + forall (K : poly -> poly -> bool) (r : 'a list) (P : 'a -> bool) (F1 F2 : + 'a -> poly), + (forall (x1 x2 y1 y2 : poly), K x1 x2 => K y1 y2 => K (x1 &+ y1) (x2 &+ y2)) => + K Rq.zero Rq.zero => (forall (i : 'a), P i => K (F1 i) (F2 i)) => K (big P F1 r) (big P F2 r). + + lemma nosmt big_endo ['a]: + forall (f : poly -> poly), + f Rq.zero = Rq.zero => + (forall (x y : poly), f (x &+ y) = f x &+ f y) => + forall (r : 'a list) (P : 'a -> bool) (F : 'a -> poly), f (big P F r) = big P (f \o F) r. + + lemma nosmt big_map ['a, 'b]: + forall (h : 'b -> 'a) (P : 'a -> bool) (F : 'a -> poly) (s : 'b list), + big P F (map h s) = big (P \o h) (F \o h) s. + + lemma nosmt big_mapT ['a, 'b]: + forall (h : 'b -> 'a) (F : 'a -> poly) (s : 'b list), big predT F (map h s) = big predT (F \o h) s. + + lemma nosmt big_comp ['a]: + forall (h : poly -> poly) (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + h Rq.zero = Rq.zero => morphism_2 h (&+) (&+) => h (big P F s) = big P (h \o F) s. + + lemma nosmt big_nth ['a]: + forall (x0 : 'a) (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = bigi (P \o nth x0 s) (F \o nth x0 s) 0 (size s). + + lemma nosmt big_const ['a]: + forall (P : 'a -> bool) (x : poly) (s : 'a list), + big P (fun (_ : 'a) => x) s = iter (count P s) ((&+) x) Rq.zero. + + lemma nosmt big_seq1 ['a]: forall (F : 'a -> poly) (x : 'a), big predT F [x] = F x. + + lemma nosmt big_mkcond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = big predT (fun (i : 'a) => if P i then F i else Rq.zero) s. + + lemma nosmt big_filter ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), big predT F (filter P s) = big P F s. + + lemma big_filter_cond ['a]: + forall (P1 P2 : 'a -> bool) (F : 'a -> poly) (s : 'a list), big P2 F (filter P1 s) = big (predI P1 P2) F s. + + lemma nosmt eq_bigl ['a]: + forall (P1 P2 : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P1 i <=> P2 i) => big P1 F s = big P2 F s. + + lemma nosmt eq_bigr ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i => F1 i = F2 i) => big P F1 s = big P F2 s. + + lemma nosmt big_distrl ['a]: + forall (op_ : poly -> poly -> poly) (P : 'a -> bool) (F : + 'a -> poly) (s : 'a list) (t : poly), + left_zero Rq.zero op_ => + left_distributive op_ (&+) => op_ (big P F s) t = big P (fun (a : 'a) => op_ (F a) t) s. + + lemma nosmt big_distrr ['a]: + forall (op_ : poly -> poly -> poly) (P : 'a -> bool) (F : + 'a -> poly) (s : 'a list) (t : poly), + right_zero Rq.zero op_ => + right_distributive op_ (&+) => op_ t (big P F s) = big P (fun (a : 'a) => op_ t (F a)) s. + + lemma nosmt big_distr ['a, 'b]: + forall (op_ : poly -> poly -> poly) (P1 : 'a -> bool) (P2 : + 'b -> bool) (F1 : 'a -> poly) (s1 : 'a list) (F2 : 'b -> poly) (s2 : 'b list), + commutative op_ => + left_zero Rq.zero op_ => + left_distributive op_ (&+) => + op_ (big P1 F1 s1) (big P2 F2 s2) = + big P1 (fun (a1 : 'a) => big P2 (fun (a2 : 'b) => op_ (F1 a1) (F2 a2)) s2) s1. + + lemma big_andbC ['a]: + forall (P Q : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big (fun (x : 'a) => P x /\ Q x) F s = big (fun (x : 'a) => Q x /\ P x) F s. + + lemma nosmt eq_big ['a]: + forall (P1 P2 : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + (forall (i : 'a), P1 i <=> P2 i) => (forall (i : 'a), P1 i => F1 i = F2 i) => big P1 F1 s = big P2 F2 s. + + lemma nosmt congr_big ['a]: + forall (r1 r2 : 'a list) (P1 P2 : 'a -> bool) (F1 F2 : 'a -> poly), + r1 = r2 => + (forall (x : 'a), P1 x <=> P2 x) => (forall (i : 'a), P1 i => F1 i = F2 i) => big P1 F1 r1 = big P2 F2 r2. + + lemma big_hasC ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), ! has P s => big P F s = Rq.zero. + + lemma big_pred0_eq ['a]: forall (F : 'a -> poly) (s : 'a list), big pred0 F s = Rq.zero. + + lemma big_pred0 ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i <=> false) => big P F s = Rq.zero. + + lemma big_cat ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), big P F (s1 ++ s2) = big P F s1 &+ big P F s2. + + lemma nosmt big_catl ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), ! has P s2 => big P F (s1 ++ s2) = big P F s1. + + lemma nosmt big_catr ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), ! has P s1 => big P F (s1 ++ s2) = big P F s2. + + lemma nosmt big_rcons ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + big P F (rcons s x) = if P x then big P F s &+ F x else big P F s. + + lemma nosmt eq_big_perm ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), perm_eq s1 s2 => big P F s1 = big P F s2. + + lemma nosmt eq_big_perm_map ['a]: + forall (F : 'a -> poly) (s1 s2 : 'a list), perm_eq (map F s1) (map F s2) => big predT F s1 = big predT F s2. + + lemma nosmt big_seq_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = big (fun (i : 'a) => (i \in s) /\ P i) F s. + + lemma nosmt big_seq ['a]: + forall (F : 'a -> poly) (s : 'a list), big predT F s = big (fun (i : 'a) => i \in s) F s. + + lemma nosmt big_rem ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + x \in s => big P F s = (if P x then F x else Rq.zero) &+ big P F (rem x s). + + lemma nosmt bigD1 ['a]: + forall (F : 'a -> poly) (s : 'a list) (x : 'a), + x \in s => uniq s => big predT F s = F x &+ big (predC1 x) F s. + + lemma nosmt bigD1_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + P x => x \in s => uniq s => big P F s = F x &+ big (predI P (predC1 x)) F s. + + lemma nosmt bigD1_cond_if ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + uniq s => big P F s = (if (x \in s) /\ P x then F x else Rq.zero) &+ big (predI P (predC1 x)) F s. + + lemma big_split ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + big P (fun (i : 'a) => F1 i &+ F2 i) s = big P F1 s &+ big P F2 s. + + lemma nosmt bigID ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (a : 'a -> bool) (s : 'a list), + big P F s = big (predI P a) F s &+ big (predI P (predC a)) F s. + + lemma bigU ['a]: + forall (P Q : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (x : 'a), ! (P x /\ Q x)) => big (predU P Q) F s = big P F s &+ big Q F s. + + lemma bigEM ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), big predT F s = big P F s &+ big (predC P) F s. + + lemma nosmt big_reindex ['a, 'b]: + forall (P : 'a -> bool) (F : 'a -> poly) (f : 'b -> 'a) (f' : + 'a -> 'b) (s : 'a list), + (forall (x : 'a), x \in s => f (f' x) = x) => big P F s = big (P \o f) (F \o f) (map f' s). + + lemma nosmt big_pair_pswap ['a, 'b]: + forall (p : 'a * 'b -> bool) (f : 'a * 'b -> poly) (s : ('a * 'b) list), + big p f s = big (p \o pswap) (f \o pswap) (map pswap s). + + lemma nosmt eq_big_seq ['a]: + forall (F1 F2 : 'a -> poly) (s : 'a list), + (forall (x : 'a), x \in s => F1 x = F2 x) => big predT F1 s = big predT F2 s. + + lemma nosmt congr_big_seq ['a]: + forall (P1 P2 : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + (forall (x : 'a), x \in s => P1 x = P2 x) => + (forall (x : 'a), x \in s => P1 x => P2 x => F1 x = F2 x) => big P1 F1 s = big P2 F2 s. + + lemma big1_eq ['a]: forall (P : 'a -> bool) (s : 'a list), big P (fun (_ : 'a) => Rq.zero) s = Rq.zero. + + lemma big1 ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i => F i = Rq.zero) => big P F s = Rq.zero. + + lemma big1_seq ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i /\ (i \in s) => F i = Rq.zero) => big P F s = Rq.zero. + + lemma big_eq_idm_filter ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (x : 'a), ! P x => F x = Rq.zero) => big predT F s = big P F s. + + lemma big_flatten ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (rr : 'a list list), + big P F (flatten rr) = big predT (fun (s : 'a list) => big P F s) rr. + + lemma nosmt big_pair ['a, 'b]: + forall (F : 'a * 'b -> poly) (s : ('a * 'b) list), + uniq s => + big predT F s = + big predT (fun (a : 'a) => big predT F (filter (fun (xy : 'a * 'b) => xy.`1 = a) s)) (undup (unzip1 s)). + + lemma big_nseq_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (n : int) (x : 'a), + big P F (nseq n x) = if P x then iter n ((&+) (F x)) Rq.zero else Rq.zero. + + lemma big_nseq ['a]: + forall (F : 'a -> poly) (n : int) (x : 'a), big predT F (nseq n x) = iter n ((&+) (F x)) Rq.zero. + + lemma big_undup ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = big P (fun (a : 'a) => iter (count (pred1 a) s) ((&+) (F a)) Rq.zero) (undup s). + + lemma nosmt exchange_big ['a, 'b]: + forall (P1 : 'a -> bool) (P2 : 'b -> bool) (F : 'a -> 'b -> poly) (s1 : 'a list) (s2 : 'b list), + big P1 (fun (a : 'a) => big P2 (F a) s2) s1 = + big P2 (fun (b : 'b) => big P1 (fun (a : 'a) => F a b) s1) s2. + + lemma partition_big ['a, 'b]: + forall (px : 'a -> 'b) (P : 'a -> bool) (Q : 'b -> bool) (F : + 'a -> poly) (s : 'a list) (s' : 'b list), + uniq s' => + (forall (x : 'a), x \in s => P x => (px x \in s') /\ Q (px x)) => + big P F s = big Q (fun (x : 'b) => big (fun (y : 'a) => P y /\ px y = x) F s) s'. + + lemma nosmt big_allpairs ['a, 'b, 'c]: + forall (f : 'a -> 'b -> 'c) (F : 'c -> poly) (s : 'a list) (t : 'b list), + big predT F (allpairs f s t) = big predT (fun (x : 'a) => big predT (fun (y : 'b) => F (f x y)) t) s. + + lemma nosmt big_int_cond: + forall (m n : int) (P : int -> bool) (F : int -> poly), + bigi P F m n = bigi (fun (i : int) => (m <= i && i < n) /\ P i) F m n. + + lemma nosmt big_int: + forall (m n : int) (F : int -> poly), bigi predT F m n = bigi (fun (i : int) => m <= i && i < n) F m n. + + lemma nosmt congr_big_int: + forall (m1 n1 m2 n2 : int) (P1 P2 : int -> bool) (F1 F2 : + int -> poly), + m1 = m2 => + n1 = n2 => + (forall (i : int), m1 <= i && i < n2 => P1 i = P2 i) => + (forall (i : int), P1 i /\ m1 <= i && i < n2 => F1 i = F2 i) => bigi P1 F1 m1 n1 = bigi P2 F2 m2 n2. + + lemma nosmt eq_big_int: + forall (m n : int) (F1 F2 : int -> poly), + (forall (i : int), m <= i && i < n => F1 i = F2 i) => bigi predT F1 m n = bigi predT F2 m n. + + lemma nosmt big_ltn_cond: + forall (m n : int) (P : int -> bool) (F : int -> poly), + m < n => let x = bigi P F (m + 1) n in bigi P F m n = if P m then F m &+ x else x. + + lemma nosmt big_ltn: + forall (m n : int) (F : int -> poly), m < n => bigi predT F m n = F m &+ bigi predT F (m + 1) n. + + lemma nosmt big_geq: + forall (m n : int) (P : int -> bool) (F : int -> poly), n <= m => bigi P F m n = Rq.zero. + + lemma nosmt big_addn: + forall (m n a : int) (P : int -> bool) (F : int -> poly), + bigi P F (m + a) n = bigi (fun (i : int) => P (i + a)) (fun (i : int) => F (i + a)) m (n - a). + + lemma nosmt big_int1: forall (n : int) (F : int -> poly), bigi predT F n (n + 1) = F n. + + lemma nosmt big_cat_int: + forall (n m p : int) (P : int -> bool) (F : int -> poly), + m <= n => n <= p => bigi P F m p = bigi P F m n &+ bigi P F n p. + + lemma nosmt big_int_recl: + forall (n m : int) (F : int -> poly), + m <= n => bigi predT F m (n + 1) = F m &+ bigi predT (fun (i : int) => F (i + 1)) m n. + + lemma nosmt big_int_recr: + forall (n m : int) (F : int -> poly), m <= n => bigi predT F m (n + 1) = bigi predT F m n &+ F n. + + lemma nosmt big_int_recl_cond: + forall (n m : int) (P : int -> bool) (F : int -> poly), + m <= n => + bigi P F m (n + 1) = + (if P m then F m else Rq.zero) &+ bigi (fun (i : int) => P (i + 1)) (fun (i : int) => F (i + 1)) m n. + + lemma nosmt big_int_recr_cond: + forall (n m : int) (P : int -> bool) (F : int -> poly), + m <= n => bigi P F m (n + 1) = bigi P F m n &+ if P n then F n else Rq.zero. + + lemma nosmt bigi_split_odd_even: + forall (n : int) (F : int -> poly), + 0 <= n => bigi predT (fun (i : int) => F (2 * i) &+ F (2 * i + 1)) 0 n = bigi predT F 0 (2 * n). + + lemma sumrD ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> poly) (r : 'a list), + big P F1 r &+ big P F2 r = big P (fun (x : 'a) => F1 x &+ F2 x) r. + + lemma sumrN ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (r : 'a list), + (&-) (big P F r) = big P (fun (x : 'a) => (&-) (F x)) r. + + lemma sumrB ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> poly) (r : 'a list), + (big P F1 r - big P F2 r)%ZR = big P (fun (x : 'a) => (F1 x - F2 x)%ZR) r. + + lemma nosmt sumr_const ['a]: + forall (P : 'a -> bool) (x : poly) (s : 'a list), big P (fun (_ : 'a) => x) s = (intmul x (count P s))%ZR. + + lemma sumri_const: + forall (k : poly) (n m : int), n <= m => bigi predT (fun (_ : int) => k) n m = (intmul k (m - n))%ZR. + + lemma sumr_undup ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = big P (fun (a : 'a) => (intmul (F a) (count (pred1 a) s))%ZR) (undup s). + + lemma telescoping_sum: + forall (F : int -> poly) (m n : int), + m <= n => (F m - F n)%ZR = bigi predT (fun (i : int) => (F i - F (i + 1))%ZR) m n. + + lemma telescoping_sum_down: + forall (F : int -> poly) (m n : int), + m <= n => (F n - F m)%ZR = bigi predT (fun (i : int) => (F (i + 1) - F i)%ZR) m n. + + lemma nosmt sumr_1 ['a]: + forall (P : 'a -> bool) (s : 'a list), big P (fun (_ : 'a) => Rq.one) s = (ofint (count P s))%ZR. + + lemma mulr_suml ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : poly), + big P F s &* x = big P (fun (i : 'a) => F i &* x) s. + + lemma mulr_sumr ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : poly), + x &* big P F s = big P (fun (i : 'a) => x &* F i) s. + + lemma divr_suml ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : poly), + (big P F s / x)%ZR = big P (fun (i : 'a) => (F i / x)%ZR) s. + + lemma nosmt sum_pair_dep ['a, 'b]: + forall (u : 'a -> poly) (v : 'a -> 'b -> poly) (J : ('a * 'b) list), + uniq J => + big predT (fun (ij : 'a * 'b) => u ij.`1 &* v ij.`1 ij.`2) J = + big predT + (fun (i : 'a) => + u i &* big predT (fun (ij : 'a * 'b) => v ij.`1 ij.`2) (filter (fun (ij : 'a * 'b) => ij.`1 = i) J)) + (undup (unzip1 J)). + + lemma nosmt sum_pair ['a, 'b]: + forall (u : 'a -> poly) (v : 'b -> poly) (J : ('a * 'b) list), + uniq J => + big predT (fun (ij : 'a * 'b) => u ij.`1 &* v ij.`2) J = + big predT (fun (i : 'a) => u i &* big predT v (unzip2 (filter (fun (ij : 'a * 'b) => ij.`1 = i) J))) + (undup (unzip1 J)). + end BAdd. + + theory BMul. + op big ['a] (P : 'a -> bool) (F : 'a -> poly) (r : 'a list) : poly = + foldr ( &* ) Rq.one (map F (filter P r)). + + abbrev bigi (P : int -> bool) (F : int -> poly) (i j : int) : poly = big P F (range i j). + + lemma big_nil ['a]: forall (P : 'a -> bool) (F : 'a -> poly), big P F [] = Rq.one. + + lemma big_cons ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (x : 'a) (s : 'a list), + big P F (x :: s) = if P x then F x &* big P F s else big P F s. + + lemma big_consT ['a]: + forall (F : 'a -> poly) (x : 'a) (s : 'a list), big predT F (x :: s) = F x &* big predT F s. + + lemma nosmt big_rec ['a]: + forall (K : poly -> bool) (r : 'a list) (P : 'a -> bool) (F : + 'a -> poly), K Rq.one => (forall (i : 'a) (x : poly), P i => K x => K (F i &* x)) => K (big P F r). + + lemma nosmt big_ind ['a]: + forall (K : poly -> bool) (r : 'a list) (P : 'a -> bool) (F : + 'a -> poly), + (forall (x y : poly), K x => K y => K (x &* y)) => + K Rq.one => (forall (i : 'a), P i => K (F i)) => K (big P F r). + + lemma nosmt big_rec2 ['a]: + forall (K : poly -> poly -> bool) (r : 'a list) (P : 'a -> bool) (F1 F2 : + 'a -> poly), + K Rq.one Rq.one => + (forall (i : 'a) (y1 y2 : poly), P i => K y1 y2 => K (F1 i &* y1) (F2 i &* y2)) => + K (big P F1 r) (big P F2 r). + + lemma nosmt big_ind2 ['a]: + forall (K : poly -> poly -> bool) (r : 'a list) (P : 'a -> bool) (F1 F2 : + 'a -> poly), + (forall (x1 x2 y1 y2 : poly), K x1 x2 => K y1 y2 => K (x1 &* y1) (x2 &* y2)) => + K Rq.one Rq.one => (forall (i : 'a), P i => K (F1 i) (F2 i)) => K (big P F1 r) (big P F2 r). + + lemma nosmt big_endo ['a]: + forall (f : poly -> poly), + f Rq.one = Rq.one => + (forall (x y : poly), f (x &* y) = f x &* f y) => + forall (r : 'a list) (P : 'a -> bool) (F : 'a -> poly), f (big P F r) = big P (f \o F) r. + + lemma nosmt big_map ['a, 'b]: + forall (h : 'b -> 'a) (P : 'a -> bool) (F : 'a -> poly) (s : 'b list), + big P F (map h s) = big (P \o h) (F \o h) s. + + lemma nosmt big_mapT ['a, 'b]: + forall (h : 'b -> 'a) (F : 'a -> poly) (s : 'b list), big predT F (map h s) = big predT (F \o h) s. + + lemma nosmt big_comp ['a]: + forall (h : poly -> poly) (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + h Rq.one = Rq.one => morphism_2 h ( &* ) ( &* ) => h (big P F s) = big P (h \o F) s. + + lemma nosmt big_nth ['a]: + forall (x0 : 'a) (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = bigi (P \o nth x0 s) (F \o nth x0 s) 0 (size s). + + lemma nosmt big_const ['a]: + forall (P : 'a -> bool) (x : poly) (s : 'a list), + big P (fun (_ : 'a) => x) s = iter (count P s) (( &* ) x) Rq.one. + + lemma nosmt big_seq1 ['a]: forall (F : 'a -> poly) (x : 'a), big predT F [x] = F x. + + lemma nosmt big_mkcond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = big predT (fun (i : 'a) => if P i then F i else Rq.one) s. + + lemma nosmt big_filter ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), big predT F (filter P s) = big P F s. + + lemma big_filter_cond ['a]: + forall (P1 P2 : 'a -> bool) (F : 'a -> poly) (s : 'a list), big P2 F (filter P1 s) = big (predI P1 P2) F s. + + lemma nosmt eq_bigl ['a]: + forall (P1 P2 : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P1 i <=> P2 i) => big P1 F s = big P2 F s. + + lemma nosmt eq_bigr ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i => F1 i = F2 i) => big P F1 s = big P F2 s. + + lemma nosmt big_distrl ['a]: + forall (op_ : poly -> poly -> poly) (P : 'a -> bool) (F : + 'a -> poly) (s : 'a list) (t : poly), + left_zero Rq.one op_ => + left_distributive op_ ( &* ) => op_ (big P F s) t = big P (fun (a : 'a) => op_ (F a) t) s. + + lemma nosmt big_distrr ['a]: + forall (op_ : poly -> poly -> poly) (P : 'a -> bool) (F : + 'a -> poly) (s : 'a list) (t : poly), + right_zero Rq.one op_ => + right_distributive op_ ( &* ) => op_ t (big P F s) = big P (fun (a : 'a) => op_ t (F a)) s. + + lemma nosmt big_distr ['a, 'b]: + forall (op_ : poly -> poly -> poly) (P1 : 'a -> bool) (P2 : + 'b -> bool) (F1 : 'a -> poly) (s1 : 'a list) (F2 : 'b -> poly) (s2 : 'b list), + commutative op_ => + left_zero Rq.one op_ => + left_distributive op_ ( &* ) => + op_ (big P1 F1 s1) (big P2 F2 s2) = + big P1 (fun (a1 : 'a) => big P2 (fun (a2 : 'b) => op_ (F1 a1) (F2 a2)) s2) s1. + + lemma big_andbC ['a]: + forall (P Q : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big (fun (x : 'a) => P x /\ Q x) F s = big (fun (x : 'a) => Q x /\ P x) F s. + + lemma nosmt eq_big ['a]: + forall (P1 P2 : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + (forall (i : 'a), P1 i <=> P2 i) => (forall (i : 'a), P1 i => F1 i = F2 i) => big P1 F1 s = big P2 F2 s. + + lemma nosmt congr_big ['a]: + forall (r1 r2 : 'a list) (P1 P2 : 'a -> bool) (F1 F2 : 'a -> poly), + r1 = r2 => + (forall (x : 'a), P1 x <=> P2 x) => (forall (i : 'a), P1 i => F1 i = F2 i) => big P1 F1 r1 = big P2 F2 r2. + + lemma big_hasC ['a]: forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), ! has P s => big P F s = Rq.one. + + lemma big_pred0_eq ['a]: forall (F : 'a -> poly) (s : 'a list), big pred0 F s = Rq.one. + + lemma big_pred0 ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i <=> false) => big P F s = Rq.one. + + lemma big_cat ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), big P F (s1 ++ s2) = big P F s1 &* big P F s2. + + lemma nosmt big_catl ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), ! has P s2 => big P F (s1 ++ s2) = big P F s1. + + lemma nosmt big_catr ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), ! has P s1 => big P F (s1 ++ s2) = big P F s2. + + lemma nosmt big_rcons ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + big P F (rcons s x) = if P x then big P F s &* F x else big P F s. + + lemma nosmt eq_big_perm ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s1 s2 : 'a list), perm_eq s1 s2 => big P F s1 = big P F s2. + + lemma nosmt eq_big_perm_map ['a]: + forall (F : 'a -> poly) (s1 s2 : 'a list), perm_eq (map F s1) (map F s2) => big predT F s1 = big predT F s2. + + lemma nosmt big_seq_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = big (fun (i : 'a) => (i \in s) /\ P i) F s. + + lemma nosmt big_seq ['a]: + forall (F : 'a -> poly) (s : 'a list), big predT F s = big (fun (i : 'a) => i \in s) F s. + + lemma nosmt big_rem ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + x \in s => big P F s = (if P x then F x else Rq.one) &* big P F (rem x s). + + lemma nosmt bigD1 ['a]: + forall (F : 'a -> poly) (s : 'a list) (x : 'a), + x \in s => uniq s => big predT F s = F x &* big (predC1 x) F s. + + lemma nosmt bigD1_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + P x => x \in s => uniq s => big P F s = F x &* big (predI P (predC1 x)) F s. + + lemma nosmt bigD1_cond_if ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list) (x : 'a), + uniq s => big P F s = (if (x \in s) /\ P x then F x else Rq.one) &* big (predI P (predC1 x)) F s. + + lemma big_split ['a]: + forall (P : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + big P (fun (i : 'a) => F1 i &* F2 i) s = big P F1 s &* big P F2 s. + + lemma nosmt bigID ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (a : 'a -> bool) (s : 'a list), + big P F s = big (predI P a) F s &* big (predI P (predC a)) F s. + + lemma bigU ['a]: + forall (P Q : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (x : 'a), ! (P x /\ Q x)) => big (predU P Q) F s = big P F s &* big Q F s. + + lemma bigEM ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), big predT F s = big P F s &* big (predC P) F s. + + lemma nosmt big_reindex ['a, 'b]: + forall (P : 'a -> bool) (F : 'a -> poly) (f : 'b -> 'a) (f' : + 'a -> 'b) (s : 'a list), + (forall (x : 'a), x \in s => f (f' x) = x) => big P F s = big (P \o f) (F \o f) (map f' s). + + lemma nosmt big_pair_pswap ['a, 'b]: + forall (p : 'a * 'b -> bool) (f : 'a * 'b -> poly) (s : ('a * 'b) list), + big p f s = big (p \o pswap) (f \o pswap) (map pswap s). + + lemma nosmt eq_big_seq ['a]: + forall (F1 F2 : 'a -> poly) (s : 'a list), + (forall (x : 'a), x \in s => F1 x = F2 x) => big predT F1 s = big predT F2 s. + + lemma nosmt congr_big_seq ['a]: + forall (P1 P2 : 'a -> bool) (F1 F2 : 'a -> poly) (s : 'a list), + (forall (x : 'a), x \in s => P1 x = P2 x) => + (forall (x : 'a), x \in s => P1 x => P2 x => F1 x = F2 x) => big P1 F1 s = big P2 F2 s. + + lemma big1_eq ['a]: forall (P : 'a -> bool) (s : 'a list), big P (fun (_ : 'a) => Rq.one) s = Rq.one. + + lemma big1 ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i => F i = Rq.one) => big P F s = Rq.one. + + lemma big1_seq ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (i : 'a), P i /\ (i \in s) => F i = Rq.one) => big P F s = Rq.one. + + lemma big_eq_idm_filter ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + (forall (x : 'a), ! P x => F x = Rq.one) => big predT F s = big P F s. + + lemma big_flatten ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (rr : 'a list list), + big P F (flatten rr) = big predT (fun (s : 'a list) => big P F s) rr. + + lemma nosmt big_pair ['a, 'b]: + forall (F : 'a * 'b -> poly) (s : ('a * 'b) list), + uniq s => + big predT F s = + big predT (fun (a : 'a) => big predT F (filter (fun (xy : 'a * 'b) => xy.`1 = a) s)) (undup (unzip1 s)). + + lemma big_nseq_cond ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (n : int) (x : 'a), + big P F (nseq n x) = if P x then iter n (( &* ) (F x)) Rq.one else Rq.one. + + lemma big_nseq ['a]: + forall (F : 'a -> poly) (n : int) (x : 'a), big predT F (nseq n x) = iter n (( &* ) (F x)) Rq.one. + + lemma big_undup ['a]: + forall (P : 'a -> bool) (F : 'a -> poly) (s : 'a list), + big P F s = big P (fun (a : 'a) => iter (count (pred1 a) s) (( &* ) (F a)) Rq.one) (undup s). + + lemma nosmt exchange_big ['a, 'b]: + forall (P1 : 'a -> bool) (P2 : 'b -> bool) (F : 'a -> 'b -> poly) (s1 : 'a list) (s2 : 'b list), + big P1 (fun (a : 'a) => big P2 (F a) s2) s1 = + big P2 (fun (b : 'b) => big P1 (fun (a : 'a) => F a b) s1) s2. + + lemma partition_big ['a, 'b]: + forall (px : 'a -> 'b) (P : 'a -> bool) (Q : 'b -> bool) (F : + 'a -> poly) (s : 'a list) (s' : 'b list), + uniq s' => + (forall (x : 'a), x \in s => P x => (px x \in s') /\ Q (px x)) => + big P F s = big Q (fun (x : 'b) => big (fun (y : 'a) => P y /\ px y = x) F s) s'. + + lemma nosmt big_allpairs ['a, 'b, 'c]: + forall (f : 'a -> 'b -> 'c) (F : 'c -> poly) (s : 'a list) (t : 'b list), + big predT F (allpairs f s t) = big predT (fun (x : 'a) => big predT (fun (y : 'b) => F (f x y)) t) s. + + lemma nosmt big_int_cond: + forall (m n : int) (P : int -> bool) (F : int -> poly), + bigi P F m n = bigi (fun (i : int) => (m <= i && i < n) /\ P i) F m n. + + lemma nosmt big_int: + forall (m n : int) (F : int -> poly), bigi predT F m n = bigi (fun (i : int) => m <= i && i < n) F m n. + + lemma nosmt congr_big_int: + forall (m1 n1 m2 n2 : int) (P1 P2 : int -> bool) (F1 F2 : + int -> poly), + m1 = m2 => + n1 = n2 => + (forall (i : int), m1 <= i && i < n2 => P1 i = P2 i) => + (forall (i : int), P1 i /\ m1 <= i && i < n2 => F1 i = F2 i) => bigi P1 F1 m1 n1 = bigi P2 F2 m2 n2. + + lemma nosmt eq_big_int: + forall (m n : int) (F1 F2 : int -> poly), + (forall (i : int), m <= i && i < n => F1 i = F2 i) => bigi predT F1 m n = bigi predT F2 m n. + + lemma nosmt big_ltn_cond: + forall (m n : int) (P : int -> bool) (F : int -> poly), + m < n => let x = bigi P F (m + 1) n in bigi P F m n = if P m then F m &* x else x. + + lemma nosmt big_ltn: + forall (m n : int) (F : int -> poly), m < n => bigi predT F m n = F m &* bigi predT F (m + 1) n. + + lemma nosmt big_geq: forall (m n : int) (P : int -> bool) (F : int -> poly), n <= m => bigi P F m n = Rq.one. + + lemma nosmt big_addn: + forall (m n a : int) (P : int -> bool) (F : int -> poly), + bigi P F (m + a) n = bigi (fun (i : int) => P (i + a)) (fun (i : int) => F (i + a)) m (n - a). + + lemma nosmt big_int1: forall (n : int) (F : int -> poly), bigi predT F n (n + 1) = F n. + + lemma nosmt big_cat_int: + forall (n m p : int) (P : int -> bool) (F : int -> poly), + m <= n => n <= p => bigi P F m p = bigi P F m n &* bigi P F n p. + + lemma nosmt big_int_recl: + forall (n m : int) (F : int -> poly), + m <= n => bigi predT F m (n + 1) = F m &* bigi predT (fun (i : int) => F (i + 1)) m n. + + lemma nosmt big_int_recr: + forall (n m : int) (F : int -> poly), m <= n => bigi predT F m (n + 1) = bigi predT F m n &* F n. + + lemma nosmt big_int_recl_cond: + forall (n m : int) (P : int -> bool) (F : int -> poly), + m <= n => + bigi P F m (n + 1) = + (if P m then F m else Rq.one) &* bigi (fun (i : int) => P (i + 1)) (fun (i : int) => F (i + 1)) m n. + + lemma nosmt big_int_recr_cond: + forall (n m : int) (P : int -> bool) (F : int -> poly), + m <= n => bigi P F m (n + 1) = bigi P F m n &* if P n then F n else Rq.one. + + lemma nosmt bigi_split_odd_even: + forall (n : int) (F : int -> poly), + 0 <= n => bigi predT (fun (i : int) => F (2 * i) &* F (2 * i + 1)) 0 n = bigi predT F 0 (2 * n). + end BMul. + + lemma mulr_big ['a]: + forall (P Q : 'a -> bool) (f g : 'a -> poly) (r s : 'a list), + (big P f r)%BAdd &* (big Q g s)%BAdd = + (big P (fun (x : 'a) => (big Q (fun (y : 'a) => f x &* g y) s)%BAdd) r)%BAdd. + + lemma subrXX: + forall (x y : poly) (n : int), + 0 <= n => + (exp x n - exp y n)%ZR = + (x - y)%ZR &* (bigi predT (fun (i : int) => (exp x (n - 1 - i))%ZR &* (exp y i)%ZR) 0 n)%BAdd. + + lemma nosmt mulr_const_cond ['a]: + forall (p : 'a -> bool) (s : 'a list) (c : poly), (big p (fun (_ : 'a) => c) s)%BMul = (exp c (count p s))%ZR. + + lemma nosmt mulr_const ['a]: + forall (s : 'a list) (c : poly), (big predT (fun (_ : 'a) => c) s)%BMul = (exp c (size s))%ZR. + end Big. + + lemma ge0_size: 0 <= kvec. + + hint solve 0 : ge0_size. + + theory Vector. + lemma tofunv_prevector: forall (v : polyvec), prevector (tofunv v). + + lemma tofunvK: cancel tofunv offunv. + + lemma offunvK: forall (v : int -> poly), tofunv (offunv v) = vclamp v. + + op "_.[_]" (v : polyvec) (i : int) : poly = tofunv v i. + + lemma offunvE: forall (v : int -> poly) (i : int), 0 <= i && i < kvec => ((offunv v).[i])%Vector = v i. + + lemma getv_out: forall (v : polyvec) (i : int), ! (0 <= i && i < kvec) => (v.[i])%Vector = Rq.zero. + + lemma eq_vectorP: + forall (v1 v2 : polyvec), + v1 = v2 <=> forall (i : int), 0 <= i && i < kvec => (v1.[i])%Vector = (v2.[i])%Vector. + + lemma vectorW: + forall (P : polyvec -> bool), + (forall (f : int -> poly), prevector f => P (offunv f)) => forall (v : polyvec), P v. + + op vectc (c : poly) : polyvec = offunv (fun (_ : int) => c). + + abbrev zerov : polyvec = (vectc Rq.zero)%Vector. + + lemma offunCE: forall (c : poly) (i : int), 0 <= i && i < kvec => ((vectc c).[i])%Vector = c. + + lemma offun0E: forall (i : int), (Vector.zerov.[i])%Vector = Rq.zero. + + hint simplify. + + op [-] (v : polyvec) : polyvec = offunv (fun (i : int) => (&-) (v.[i])%Vector). + + lemma offunD: + forall (v1 v2 : polyvec) (i : int), + ((v1 + v2)%KMatrix.Vector.[i])%Vector = (v1.[i])%Vector &+ (v2.[i])%Vector. + + hint simplify. + + lemma offunN: forall (v : polyvec) (i : int), ((-v).[i])%Vector = (&-) (v.[i])%Vector. + + hint simplify. + + theory ZModule. + lemma nosmt addrA: associative KMatrix.Vector.(+). + + lemma nosmt addrC: commutative KMatrix.Vector.(+). + + lemma nosmt add0r: left_id Vector.zerov KMatrix.Vector.(+). + + lemma nosmt addNr: left_inverse Vector.zerov Vector.[-] KMatrix.Vector.(+). + + theory AddMonoid. + lemma addmA: associative KMatrix.Vector.(+). + + lemma addmC: commutative KMatrix.Vector.(+). + + lemma add0m: left_id Vector.zerov KMatrix.Vector.(+). + + lemma addm0: right_id Vector.zerov KMatrix.Vector.(+). + + lemma addmCA: left_commutative KMatrix.Vector.(+). + + lemma addmAC: right_commutative KMatrix.Vector.(+). + + lemma addmACA: interchange KMatrix.Vector.(+) KMatrix.Vector.(+). + + lemma iteropE: + forall (n : int) (x : polyvec), + iterop n KMatrix.Vector.(+) x Vector.zerov = iter n (((+) x))%KMatrix.Vector Vector.zerov. + end AddMonoid. + + abbrev (-) (x y : polyvec) : polyvec = (x + (-y)%Vector)%KMatrix.Vector. + + lemma nosmt addr0: right_id Vector.zerov KMatrix.Vector.(+). + + lemma nosmt addrN: right_inverse Vector.zerov Vector.[-] KMatrix.Vector.(+). + + lemma nosmt addrCA: left_commutative KMatrix.Vector.(+). + + lemma nosmt addrAC: right_commutative KMatrix.Vector.(+). + + lemma nosmt addrACA: interchange KMatrix.Vector.(+) KMatrix.Vector.(+). + + lemma nosmt subrr: forall (x : polyvec), x - x = Vector.zerov. + + lemma nosmt addKr: left_loop Vector.[-] KMatrix.Vector.(+). + + lemma nosmt addNKr: rev_left_loop Vector.[-] KMatrix.Vector.(+). + + lemma nosmt addrK: right_loop Vector.[-] KMatrix.Vector.(+). + + lemma nosmt addrNK: rev_right_loop Vector.[-] KMatrix.Vector.(+). + + lemma nosmt subrK: forall (x y : polyvec), (x - y + y)%KMatrix.Vector = x. + + lemma nosmt addrI: right_injective KMatrix.Vector.(+). + + lemma nosmt addIr: left_injective KMatrix.Vector.(+). + + lemma nosmt opprK: involutive Vector.[-]. + + lemma oppr_inj: injective Vector.[-]. + + lemma nosmt oppr0: (- Vector.zerov)%Vector = Vector.zerov. + + lemma oppr_eq0: forall (x : polyvec), (-x)%Vector = Vector.zerov <=> x = Vector.zerov. + + lemma nosmt subr0: forall (x : polyvec), x - Vector.zerov = x. + + lemma nosmt sub0r: forall (x : polyvec), Vector.zerov - x = (-x)%Vector. + + lemma nosmt opprD: forall (x y : polyvec), (- (x + y)%KMatrix.Vector)%Vector = (-x)%Vector - y. + + lemma nosmt opprB: forall (x y : polyvec), (- (x - y))%Vector = y - x. + + lemma nosmt subrACA: interchange (fun (x y : polyvec) => x - y) KMatrix.Vector.(+). + + lemma nosmt subr_eq: forall (x y z : polyvec), x - z = y <=> x = (y + z)%KMatrix.Vector. + + lemma nosmt subr_eq0: forall (x y : polyvec), x - y = Vector.zerov <=> x = y. + + lemma nosmt addr_eq0: forall (x y : polyvec), (x + y)%KMatrix.Vector = Vector.zerov <=> x = (-y)%Vector. + + lemma nosmt eqr_opp: forall (x y : polyvec), (-x)%Vector = (-y)%Vector <=> x = y. + + lemma eqr_oppLR: forall (x y : polyvec), (-x)%Vector = y <=> x = (-y)%Vector. + + lemma nosmt eqr_sub: + forall (x y z t : polyvec), x - y = z - t <=> (x + t)%KMatrix.Vector = (z + y)%KMatrix.Vector. + + lemma subr_add2r: forall (z x y : polyvec), (x + z)%KMatrix.Vector - (y + z)%KMatrix.Vector = x - y. + + op intmul (x : polyvec) (n : int) : polyvec = + if n < 0 then (- iterop (-n) KMatrix.Vector.(+) x Vector.zerov)%Vector + else iterop n KMatrix.Vector.(+) x Vector.zerov. + + lemma intmulpE: + forall (z : polyvec) (c : int), 0 <= c => intmul z c = iterop c KMatrix.Vector.(+) z Vector.zerov. + + lemma mulr0z: forall (x : polyvec), intmul x 0 = Vector.zerov. + + lemma mulr1z: forall (x : polyvec), intmul x 1 = x. + + lemma mulr2z: forall (x : polyvec), intmul x 2 = (x + x)%KMatrix.Vector. + + lemma mulrNz: forall (x : polyvec) (n : int), intmul x (-n) = (- intmul x n)%Vector. + + lemma mulrS: forall (x : polyvec) (n : int), 0 <= n => intmul x (n + 1) = (x + intmul x n)%KMatrix.Vector. + + lemma mulNrz: forall (x : polyvec) (n : int), intmul (-x)%Vector n = (- intmul x n)%Vector. + + lemma mulNrNz: forall (x : polyvec) (n : int), intmul (-x)%Vector (-n) = intmul x n. + + lemma mulrSz: forall (x : polyvec) (n : int), intmul x (n + 1) = (x + intmul x n)%KMatrix.Vector. + + lemma mulrDz: forall (x : polyvec) (n m : int), intmul x (n + m) = (intmul x n + intmul x m)%KMatrix.Vector. + end ZModule. + + lemma offunB: + forall (v1 v2 : polyvec) (i : int), ((v1 - v2)%ZModule.[i])%Vector = ((v1.[i])%Vector - (v2.[i])%Vector)%ZR. + + lemma dotpC: commutative dotp. + + lemma dotpDr: forall (v1 v2 v3 : polyvec), dotp v1 (v2 + v3)%KMatrix.Vector = dotp v1 v2 &+ dotp v1 v3. + + lemma dotpDl: forall (v1 v2 v3 : polyvec), dotp (v1 + v2)%KMatrix.Vector v3 = dotp v1 v3 &+ dotp v2 v3. + + op scalev (a : poly) (v : polyvec) : polyvec = offunv (fun (i : int) => a &* (v.[i])%Vector). + + abbrev ( ** ) : poly -> polyvec -> polyvec = Vector.scalev. + + lemma scalevE: forall (a : poly) (v : polyvec) (i : int), ((a ** v).[i])%Vector = a &* (v.[i])%Vector. + + lemma scalevDl: + forall (a b : poly) (v : polyvec), (a &+ b ** v)%Vector = ((a ** v)%Vector + (b ** v)%Vector)%KMatrix.Vector. + + lemma scalevDr: + forall (a : poly) (v w : polyvec), + (a ** (v + w)%KMatrix.Vector)%Vector = ((a ** v)%Vector + (a ** w)%Vector)%KMatrix.Vector. + + lemma scalevA: forall (a b : poly) (v : polyvec), (a &* b ** v)%Vector = (a ** (b ** v))%Vector. + + lemma scalevAC: forall (a b : poly) (v : polyvec), (a ** (b ** v))%Vector = (b ** (a ** v))%Vector. + end Vector. + + export Vector. + + theory Matrix. + abbrev mrange (i j : int) : bool = (mrange i j)%KMatrix.Matrix. + + lemma nosmt mrangeL: forall (i j : int), (mrange i j)%Matrix => 0 <= i && i < kvec. + + lemma nosmt mrangeR: forall (i j : int), (mrange i j)%Matrix => 0 <= j && j < kvec. + + lemma nosmt mrangeC: forall (i j : int), (mrange i j)%Matrix = (mrange j i)%Matrix. + + lemma tofunm_prematrix: forall (m : polymat), prematrix (tofunm m). + + lemma tofunmK: cancel tofunm offunm. + + lemma offunmK: forall (m : int -> int -> poly), tofunm (offunm m) = mclamp m. + + op "_.[_]" (m : polymat) (ij : int * int) : poly = tofunm m ij.`1 ij.`2. + + lemma offunmE: + forall (m : int -> int -> poly) (i j : int), (mrange i j)%Matrix => ((offunm m).[i, j])%Matrix = m i j. + + lemma getm_out: forall (m : polymat) (i j : int), ! (mrange i j)%Matrix => (m.[i, j])%Matrix = Rq.zero. + + lemma getm_outL: forall (m : polymat) (i j : int), ! (0 <= i && i < kvec) => (m.[i, j])%Matrix = Rq.zero. + + lemma getm_outR: forall (m : polymat) (i j : int), ! (0 <= j && j < kvec) => (m.[i, j])%Matrix = Rq.zero. + + lemma eq_matrixP: + forall (m1 m2 : polymat), + m1 = m2 <=> forall (i j : int), (mrange i j)%Matrix => (m1.[i, j])%Matrix = (m2.[i, j])%Matrix. + + lemma matrixW: + forall (P : polymat -> bool), + (forall (f : int -> int -> poly), prematrix f => P (offunm f)) => forall (v : polymat), P v. + + op matrixc (c : poly) : polymat = offunm (fun (_ _ : int) => c). + + op diagmx (v : polyvec) : polymat = offunm (fun (i j : int) => if i = j then (v.[i])%Vector else Rq.zero). + + abbrev diagc (c : poly) : polymat = (diagmx ((vectc c))%Vector)%Matrix. + + abbrev zerom : polymat = (matrixc Rq.zero)%Matrix. + + abbrev onem : polymat = (diagc Rq.one)%Matrix. + + lemma offunCE: forall (c : poly) (i j : int), (mrange i j)%Matrix => ((matrixc c).[i, j])%Matrix = c. + + lemma diagmxE: + forall (v : polyvec) (i j : int), ((diagmx v).[i, j])%Matrix = if i = j then (v.[i])%Vector else Rq.zero. + + lemma offun0E: forall (i j : int), (Matrix.zerom.[i, j])%Matrix = Rq.zero. + + lemma offun1E: + forall (i j : int), (mrange i j)%Matrix => (Matrix.onem.[i, j])%Matrix = if i = j then Rq.one else Rq.zero. + + lemma offun1_neqE: forall (i j : int), i <> j => (Matrix.onem.[i, j])%Matrix = Rq.zero. + + hint simplify. + + op (+) (m1 m2 : polymat) : polymat = offunm (fun (i j : int) => (m1.[i, j])%Matrix &+ (m2.[i, j])%Matrix). + + op [-] (m : polymat) : polymat = offunm (fun (i j : int) => (&-) (m.[i, j])%Matrix). + + lemma offunD: + forall (m1 m2 : polymat) (i j : int), ((m1 + m2).[i, j])%Matrix = (m1.[i, j])%Matrix &+ (m2.[i, j])%Matrix. + + hint simplify. + + lemma offunN: forall (m : polymat) (i j : int), ((-m).[i, j])%Matrix = (&-) (m.[i, j])%Matrix. + + hint simplify. + + theory ZModule. + lemma nosmt addrA: associative Matrix.(+). + + lemma nosmt addrC: commutative Matrix.(+). + + lemma nosmt add0r: left_id Matrix.zerom Matrix.(+). + + lemma nosmt addNr: left_inverse Matrix.zerom Matrix.[-] Matrix.(+). + + theory AddMonoid. + lemma addmA: associative Matrix.(+). + + lemma addmC: commutative Matrix.(+). + + lemma add0m: left_id Matrix.zerom Matrix.(+). + + lemma addm0: right_id Matrix.zerom Matrix.(+). + + lemma addmCA: left_commutative Matrix.(+). + + lemma addmAC: right_commutative Matrix.(+). + + lemma addmACA: interchange Matrix.(+) Matrix.(+). + + lemma iteropE: + forall (n : int) (x : polymat), iterop n Matrix.(+) x Matrix.zerom = iter n (((+) x))%Matrix Matrix.zerom. + end AddMonoid. + + abbrev (-) (x y : polymat) : polymat = (x + -y)%Matrix. + + lemma nosmt addr0: right_id Matrix.zerom Matrix.(+). + + lemma nosmt addrN: right_inverse Matrix.zerom Matrix.[-] Matrix.(+). + + lemma nosmt addrCA: left_commutative Matrix.(+). + + lemma nosmt addrAC: right_commutative Matrix.(+). + + lemma nosmt addrACA: interchange Matrix.(+) Matrix.(+). + + lemma nosmt subrr: forall (x : polymat), x - x = Matrix.zerom. + + lemma nosmt addKr: left_loop Matrix.[-] Matrix.(+). + + lemma nosmt addNKr: rev_left_loop Matrix.[-] Matrix.(+). + + lemma nosmt addrK: right_loop Matrix.[-] Matrix.(+). + + lemma nosmt addrNK: rev_right_loop Matrix.[-] Matrix.(+). + + lemma nosmt subrK: forall (x y : polymat), (x - y + y)%Matrix = x. + + lemma nosmt addrI: right_injective Matrix.(+). + + lemma nosmt addIr: left_injective Matrix.(+). + + lemma nosmt opprK: involutive Matrix.[-]. + + lemma oppr_inj: injective Matrix.[-]. + + lemma nosmt oppr0: (- Matrix.zerom)%Matrix = Matrix.zerom. + + lemma oppr_eq0: forall (x : polymat), (-x)%Matrix = Matrix.zerom <=> x = Matrix.zerom. + + lemma nosmt subr0: forall (x : polymat), x - Matrix.zerom = x. + + lemma nosmt sub0r: forall (x : polymat), Matrix.zerom - x = (-x)%Matrix. + + lemma nosmt opprD: forall (x y : polymat), (- (x + y))%Matrix = (-x)%Matrix - y. + + lemma nosmt opprB: forall (x y : polymat), (- (x - y))%Matrix = y - x. + + lemma nosmt subrACA: interchange (fun (x y : polymat) => x - y) Matrix.(+). + + lemma nosmt subr_eq: forall (x y z : polymat), x - z = y <=> x = (y + z)%Matrix. + + lemma nosmt subr_eq0: forall (x y : polymat), x - y = Matrix.zerom <=> x = y. + + lemma nosmt addr_eq0: forall (x y : polymat), (x + y)%Matrix = Matrix.zerom <=> x = (-y)%Matrix. + + lemma nosmt eqr_opp: forall (x y : polymat), (-x)%Matrix = (-y)%Matrix <=> x = y. + + lemma eqr_oppLR: forall (x y : polymat), (-x)%Matrix = y <=> x = (-y)%Matrix. + + lemma nosmt eqr_sub: forall (x y z t : polymat), x - y = z - t <=> (x + t)%Matrix = (z + y)%Matrix. + + lemma subr_add2r: forall (z x y : polymat), (x + z)%Matrix - (y + z)%Matrix = x - y. + + op intmul (x : polymat) (n : int) : polymat = + if n < 0 then (- iterop (-n) Matrix.(+) x Matrix.zerom)%Matrix else iterop n Matrix.(+) x Matrix.zerom. + + lemma intmulpE: forall (z : polymat) (c : int), 0 <= c => intmul z c = iterop c Matrix.(+) z Matrix.zerom. + + lemma mulr0z: forall (x : polymat), intmul x 0 = Matrix.zerom. + + lemma mulr1z: forall (x : polymat), intmul x 1 = x. + + lemma mulr2z: forall (x : polymat), intmul x 2 = (x + x)%Matrix. + + lemma mulrNz: forall (x : polymat) (n : int), intmul x (-n) = (- intmul x n)%Matrix. + + lemma mulrS: forall (x : polymat) (n : int), 0 <= n => intmul x (n + 1) = (x + intmul x n)%Matrix. + + lemma mulNrz: forall (x : polymat) (n : int), intmul (-x)%Matrix n = (- intmul x n)%Matrix. + + lemma mulNrNz: forall (x : polymat) (n : int), intmul (-x)%Matrix (-n) = intmul x n. + + lemma mulrSz: forall (x : polymat) (n : int), intmul x (n + 1) = (x + intmul x n)%Matrix. + + lemma mulrDz: forall (x : polymat) (n m : int), intmul x (n + m) = (intmul x n + intmul x m)%Matrix. + end ZModule. + + lemma offunB: + forall (m1 m2 : polymat) (i j : int), + ((m1 - m2)%ZModule.[i, j])%Matrix = ((m1.[i, j])%Matrix - (m2.[i, j])%Matrix)%ZR. + + op trace (m : polymat) : poly = (bigi predT (fun (i : int) => (m.[i, i])%Matrix) 0 kvec)%Big.BAdd. + + op trmx (m : polymat) : polymat = offunm (fun (i j : int) => (m.[j, i])%Matrix). + + lemma trmxE: forall (m : polymat) (i j : int), ((trmx m).[i, j])%Matrix = (m.[j, i])%Matrix. + + lemma trmxK: forall (m : polymat), (trmx ((trmx m))%Matrix)%Matrix = m. + + lemma trmx1: (trmx Matrix.onem)%Matrix = Matrix.onem. + + lemma trmxD: forall (m1 m2 : polymat), (trmx (m1 + m2)%Matrix)%Matrix = (trmx m1 + trmx m2)%Matrix. + + lemma trace_trmx: forall (m : polymat), (trace ((trmx m))%Matrix)%Matrix = (trace m)%Matrix. + + op ( * ) (m1 m2 : polymat) : polymat = + offunm + (fun (i j : int) => + (bigi predT (fun (k : int) => (m1.[i, k])%Matrix &* (m2.[k, j])%Matrix) 0 kvec)%Big.BAdd). + + lemma offunM: + forall (m1 m2 : polymat) (i j : int), + ((m1 * m2).[i, j])%Matrix = + (bigi predT (fun (k : int) => (m1.[i, k])%Matrix &* (m2.[k, j])%Matrix) 0 kvec)%Big.BAdd. + + hint simplify. + + lemma mulmx1: right_id Matrix.onem Matrix.( * ). + + lemma mul1mx: left_id Matrix.onem Matrix.( * ). + + lemma mulmxDl: forall (m1 m2 m : polymat), ((m1 + m2) * m)%Matrix = (m1 * m + m2 * m)%Matrix. + + lemma mulmxDr: forall (m1 m2 m : polymat), (m * (m1 + m2))%Matrix = (m * m1 + m * m2)%Matrix. + + lemma mulmxA: associative Matrix.( * ). + + lemma trmxM: forall (m1 m2 : polymat), (trmx (m1 * m2)%Matrix)%Matrix = (trmx m2 * trmx m1)%Matrix. + + op ( *^ ) (m : polymat) (v : polyvec) : polyvec = + offunv (fun (i : int) => (bigi predT (fun (j : int) => (m.[i, j])%Matrix &* (v.[j])%Vector) 0 kvec)%Big.BAdd). + + op ( ^* ) (v : polyvec) (m : polymat) : polyvec = + offunv (fun (j : int) => (bigi predT (fun (i : int) => (v.[i])%Vector &* (m.[i, j])%Matrix) 0 kvec)%Big.BAdd). + + lemma mulmxTv: forall (m : polymat) (v : polyvec), (trmx m *^ v)%Matrix = (v ^* m)%Matrix. + + lemma mulmxv0: forall (m : polymat), (m *^ Vector.zerov)%Matrix = Vector.zerov. + + lemma mulmx1v: forall (v : polyvec), (Matrix.onem *^ v)%Matrix = v. + + lemma mulmxvDl: + forall (m1 m2 : polymat) (v : polyvec), + ((m1 + m2) *^ v)%Matrix = ((m1 *^ v)%Matrix + (m2 *^ v)%Matrix)%KMatrix.Vector. + + lemma mulmxvDr: + forall (m : polymat) (v1 v2 : polyvec), + (m *^ (v1 + v2)%KMatrix.Vector)%Matrix = ((m *^ v1)%Matrix + (m *^ v2)%Matrix)%KMatrix.Vector. + + lemma mulmxvA: forall (m1 m2 : polymat) (v : polyvec), (m1 * m2 *^ v)%Matrix = (m1 *^ (m2 *^ v))%Matrix. + + lemma mulvmxT: forall (v : polyvec) (m : polymat), (v ^* trmx m)%Matrix = (m *^ v)%Matrix. + + lemma mulv0mx: forall (m : polymat), (Vector.zerov ^* m)%Matrix = Vector.zerov. + + lemma mulvmx1: forall (v : polyvec), (v ^* Matrix.onem)%Matrix = v. + + lemma mulvmxDr: + forall (v : polyvec) (m1 m2 : polymat), + (v ^* (m1 + m2))%Matrix = ((v ^* m1)%Matrix + (v ^* m2)%Matrix)%KMatrix.Vector. + + lemma mulvmxDl: + forall (v1 v2 : polyvec) (m : polymat), + ((v1 + v2)%KMatrix.Vector ^* m)%Matrix = ((v1 ^* m)%Matrix + (v2 ^* m)%Matrix)%KMatrix.Vector. + + lemma mulvmxA: forall (v : polyvec) (m1 m2 : polymat), (v ^* m1 ^* m2)%Matrix = (v ^* (m1 * m2))%Matrix. + + lemma mulmxvE: + forall (m : polymat) (v : polyvec) (i : int), + ((m *^ v)%Matrix.[i])%Vector = + (bigi predT (fun (j : int) => (m.[i, j])%Matrix &* (v.[j])%Vector) 0 kvec)%Big.BAdd. + + lemma mulvmxE: + forall (m : polymat) (v : polyvec) (j : int), + ((v ^* m)%Matrix.[j])%Vector = + (bigi predT (fun (i : int) => (v.[i])%Vector &* (m.[i, j])%Matrix) 0 kvec)%Big.BAdd. + + op colmx (v : polyvec) : polymat = offunm (fun (i _ : int) => (v.[i])%Vector). + + op rowmx (v : polyvec) : polymat = offunm (fun (_ j : int) => (v.[j])%Vector). + + lemma colmxT: forall (v : polyvec), (trmx ((colmx v))%Matrix)%Matrix = (rowmx v)%Matrix. + + lemma rowmxT: forall (v : polyvec), (trmx ((rowmx v))%Matrix)%Matrix = (colmx v)%Matrix. + + lemma colmxE: + forall (v : polyvec) (i j : int), 0 <= j && j < kvec => ((colmx v).[i, j])%Matrix = (v.[i])%Vector. + + lemma rowmxE: + forall (v : polyvec) (i j : int), 0 <= i && i < kvec => ((rowmx v).[i, j])%Matrix = (v.[j])%Vector. + + lemma colmx_mulmxv: forall (m : polymat) (v : polyvec), (colmx (m *^ v)%Matrix)%Matrix = (m * colmx v)%Matrix. + + lemma rowmx_mulvmx: forall (v : polyvec) (m : polymat), (rowmx (v ^* m)%Matrix)%Matrix = (rowmx v * m)%Matrix. + + lemma mulmx_diag: + forall (v1 v2 : polyvec), + (diagmx v1 * diagmx v2)%Matrix = + (diagmx (offunv (fun (i : int) => (v1.[i])%Vector &* (v2.[i])%Vector)))%Matrix. + + lemma dotp_tr: forall (v1 v2 : polyvec), dotp v1 v2 = (trace (diagmx v1 * diagmx v2)%Matrix)%Matrix. + + lemma dotp_mulmxv: forall (m : polymat) (v1 v2 : polyvec), dotp (m *^ v1)%Matrix v2 = dotp v1 (v2 ^* m)%Matrix. + + op dvector (d : poly distr) : polyvec distr = + dmap (djoin (nseq kvec d)) (fun (xs : poly list) => offunv (nth witness xs)). + + lemma dvector1E: + forall (d : poly distr) (v : polyvec), + mu1 ((dvector d))%Matrix v = + (bigi predT (fun (i : int) => mu1 d (v.[i])%Vector) 0 kvec)%StdBigop.Bigreal.BRM. + + lemma dvector_uni: forall (d : poly distr), is_uniform d => is_uniform ((dvector d))%Matrix. + + lemma dvector_ll: forall (d : poly distr), is_lossless d => is_lossless ((dvector d))%Matrix. + + lemma dvector_fu: forall (d : poly distr), is_full d => is_full ((dvector d))%Matrix. + + lemma dvector_funi: forall (d : poly distr), is_full d => is_uniform d => is_funiform ((dvector d))%Matrix. + + op dmatrix (d : poly distr) : polymat distr = + dmap (djoin (nseq kvec (djoin (nseq kvec d)))) + (fun (xs : poly list list) => offunm (fun (i j : int) => nth witness (nth witness xs j) i)). + + lemma dmatrix1E: + forall (d : poly distr) (m : polymat), + mu1 ((dmatrix d))%Matrix m = + (bigi predT + (fun (i : int) => (bigi predT (fun (j : int) => mu1 d (m.[i, j])%Matrix) 0 kvec)%StdBigop.Bigreal.BRM) 0 + kvec)%StdBigop.Bigreal.BRM. + + lemma dmatrix_dvector: + forall (d : poly distr), + (dmatrix d)%Matrix = + dmap (djoin (nseq kvec ((dvector d))%Matrix)) + (fun (vs : polyvec list) => offunm (fun (i j : int) => ((nth witness vs j).[i])%Vector)). + + lemma dmatrix_uni: forall (d : poly distr), is_uniform d => is_uniform ((dmatrix d))%Matrix. + + lemma dmatrix_ll: forall (d : poly distr), is_lossless d => is_lossless ((dmatrix d))%Matrix. + + lemma dmatrix_fu: forall (d : poly distr), is_full d => is_full ((dmatrix d))%Matrix. + + lemma dmatrix_funi: forall (d : poly distr), is_full d => is_uniform d => is_funiform ((dmatrix d))%Matrix. + end Matrix. + + export Matrix. + end Matrix_. + + instance ring with [()] poly + op rzero = Top.Rq.zero + op rone = Top.Rq.one + op add = Top.Rq.&+ + op opp = Top.Rq.&- + op mul = Top.Rq.&* + op expr = Top.MLWEPKEHash.MLWE_.Matrix_.ZR.exp + op ofint = Top.MLWEPKEHash.MLWE_.Matrix_.ZR.ofint + + instance poly with Top.Ring.ZModule.zmodule. + + instance poly with Top.Ring.ComRing.ring. + + instance poly with Top.Ring.IDomain.idomain. + + lemma duni_R_ll: is_lossless duni_R. + + hint solve 0 lossless : duni_R_ll. + + hint solve 0 random : duni_R_ll. + + lemma duni_R_uni: is_uniform duni_R. + + hint solve 0 random : duni_R_uni. + + lemma duni_R_fu: is_full duni_R. + + hint solve 0 random : duni_R_fu. + + lemma duni_R_funi: is_funiform duni_R. + + lemma dshort_R_ll: is_lossless dshort_R. + + hint solve 0 lossless : dshort_R_ll. + + hint solve 0 random : dshort_R_ll. + + op duni : polyvec distr = (dvector duni_R)%Matrix_.Matrix. + + op dshort : polyvec distr = (dvector dshort_R)%Matrix_.Matrix. + + lemma duni_ll: is_lossless duni. + + lemma duni_fu: is_full duni. + + lemma duni_uni: is_uniform duni. + + lemma duni_funi: is_funiform duni. + + lemma dshort_ll: is_lossless dshort. + + op duni_matrix : polymat distr = (dmatrix duni_R)%Matrix_.Matrix. + + lemma duni_matrix_ll: is_lossless duni_matrix. + + lemma duni_matrix_fu: is_full duni_matrix. + + lemma duni_matrix_uni: is_uniform duni_matrix. + + lemma duni_matrix_funi: is_funiform duni_matrix. + + module type Adv_T = { + proc guess(A : polymat, t : polyvec, uv : polyvec * poly) : bool {} + } + + abbrev m_transpose : polymat -> polymat = Matrix_.Matrix.trmx. + + abbrev (`<*>`) : polyvec -> polyvec -> poly = dotp. + + abbrev (&+) : poly -> poly -> poly = Rq.(&+). + + abbrev (&-) : poly -> poly -> poly = fun (x y : poly) => (x &+ ((&-) y)%Rq)%MLWE_. + + module MLWE(Adv : Adv_T) = { + proc main(b : bool) : bool = { + var s : polyvec; + var e : polyvec; + var _A : polymat; + var u0 : polyvec; + var u1 : polyvec; + var t : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + _A <$ duni_matrix; + s <$ dshort; + e <$ dshort; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + t <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ Adv.guess(_A, t, if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + lemma dseed_ll: is_lossless srand. + + hint solve 0 lossless : dseed_ll. + + hint solve 0 random : dseed_ll. + + module type HAdv_T = { + proc guess(sd : W8.t Array32.t, t : polyvec, uv : polyvec * poly) : bool {} + } + + module MLWE_H(Adv : HAdv_T) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var _A : polymat; + var u0 : polyvec; + var u1 : polyvec; + var t : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + sd <$ srand; + s <$ dshort; + e <$ dshort; + _A <- if tr then (trmx (H sd))%Matrix_.Matrix else H sd; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + t <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ Adv.guess(sd, t, if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + theory MLWE_ROM. + theory RO_H. + type in_t = W8.t Array32.t. + + type out_t = polymat. + + op dout (_ : W8.t Array32.t) : polymat distr = duni_matrix. + + type d_in_t = bool. + + type d_out_t = bool. + + module type RO = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + } + + module type RO_Distinguisher(G : RO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.rem, G.sample} + } + + module MainD(D : RO_Distinguisher, RO0 : RO) = { + proc distinguish(x : d_in_t) : d_out_t = { + var r : d_out_t; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + module type ROmap = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + + proc restrK() : (in_t, out_t) SmtMap.fmap {} + } + + module type FRO = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + + proc queried(x : in_t, f : PROM.flag) : bool {} + + proc allKnown() : (in_t, out_t) SmtMap.fmap {} + } + + module type FRO_Distinguisher(G : FRO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.rem, G.sample, G.queried, G.allKnown} + } + + abstract theory MkRO. + module RO = { + var m : (in_t, out_t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : in_t, y : out_t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : in_t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + RO.get(x); + } + + proc restrK() : (in_t, out_t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : in_t) : out_t = RO.get + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc rem(x : in_t) : unit = RO.rem + + proc sample(x : in_t) : unit = {} + }. + end MkRO. + + module RO = { + var m : (in_t, out_t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : in_t, y : out_t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : in_t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + RO.get(x); + } + + proc restrK() : (in_t, out_t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : in_t) : out_t = RO.get + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc rem(x : in_t) : unit = RO.rem + + proc sample(x : in_t) : unit = {} + }. + + module FRO = { + var m : (in_t, out_t * PROM.flag) SmtMap.fmap + + proc init() : unit = { + FRO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \in FRO.m)%SmtMap) + r <- (oget (FRO.m.[x])%SmtMap).`1; + FRO.m.[x] <- (r, PROM.Known); + + return r; + } + + proc set(x : in_t, y : out_t) : unit = { + FRO.m.[x] <- (y, PROM.Known); + } + + proc rem(x : in_t) : unit = { + FRO.m <- (rem FRO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + if ((x \notin FRO.m)%SmtMap) + FRO.m.[x] <- (c, PROM.Unknown); + } + + proc queried(x : in_t, f : PROM.flag) : bool = { + return (x \in FRO.m)%SmtMap /\ ((FRO.m.[x])%SmtMap \is f)%PROM; + } + + proc allKnown() : (in_t, out_t) SmtMap.fmap = { + return (restr PROM.Known FRO.m)%SmtMap; + } + }. + + lemma RO_FRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_get: + equiv[ RO.get ~ FRO.get : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> ={res} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_sample: + equiv[ RO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(RO).distinguish ~ D(FRO).distinguish : + ={arg, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_init_ll: islossless RO.init. + + lemma FRO_init_ll: islossless FRO.init. + + lemma FRO_in_dom_ll: islossless FRO.queried. + + lemma FRO_restrK_ll: islossless FRO.allKnown. + + lemma RO_set_ll: islossless RO.set. + + lemma FRO_set_ll: islossless FRO.set. + + lemma RO_get_ll: (forall (x : in_t), is_lossless (dout x)) => islossless RO.get. + + lemma FRO_get_ll: (forall (x : in_t), is_lossless (dout x)) => islossless FRO.get. + + lemma RO_sample_ll: (forall (x : in_t), is_lossless (dout x)) => islossless RO.sample. + + lemma FRO_sample_ll: (forall (x : in_t), is_lossless (dout x)) => islossless FRO.sample. + + module type RM_Distinguisher(G : ROmap) = { + proc distinguish(_ : d_in_t) : d_out_t {G.get, G.sample, G.restrK} + } + + module MainRM(D : RM_Distinguisher, RO0 : ROmap) = { + proc distinguish(x : d_in_t) : d_out_t = { + var r : d_out_t; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + lemma fcoll_bound ['rT]: + forall (f : out_t -> 'rT) (Pc : real), + 0%r <= Pc => + (forall (x1 x2 : in_t) (y : 'rT), y \in dmap (dout x1) f => mu1 (dmap (dout x2) f) y <= Pc) => + forall (q : int), + 0 <= q => + forall (D(G : ROmap) <: RM_Distinguisher{+all mem, -RO} ) &m (z : d_in_t), + Pr[MainRM(D, RO).distinguish(z) @ &m : (fcoll f RO.m)%SmtMap /\ (fsize RO.m)%SmtMap <= q] <= + (q * (q - 1))%r / 2%r * Pc. + + theory FullEager. + abstract theory EagerCore. + axiom dout_ll: forall (x : in_t), is_lossless (dout x). + + module type Orcl = { + proc f(x : in_t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : in_t list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : in_t list, l2 : in_t list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : in_t, l : in_t list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : in_t, t2 : in_t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : in_t, t2 : in_t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : in_t list) (i : in_t) (s2 : in_t list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : in_t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : in_t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : in_t, y : out_t) : unit = FRO.set + + proc rem(x : in_t) : unit = FRO.rem + + proc sample(x : in_t) : unit = FRO.sample + + proc queried(x : in_t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (in_t, out_t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : in_t) (mx1 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : in_t) (mx1 mx2 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : in_t) (r1 : out_t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + end FullEager. + + abstract theory FinEager. + abstract theory EagerCore. + axiom dout_ll: forall (x : in_t), is_lossless (dout x). + + module type Orcl = { + proc f(x : in_t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : in_t list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : in_t list, l2 : in_t list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : in_t, l : in_t list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : in_t, t2 : in_t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : in_t, t2 : in_t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : in_t list) (i : in_t) (s2 : in_t list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : in_t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : in_t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : in_t, y : out_t) : unit = FRO.set + + proc rem(x : in_t) : unit = FRO.rem + + proc sample(x : in_t) : unit = FRO.sample + + proc queried(x : in_t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (in_t, out_t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : in_t) (mx1 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : in_t) (mx1 mx2 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : in_t) (r1 : out_t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + theory FinFrom. + type t = in_t. + + op enum : t list. + + op card : int = size enum. + + axiom enum_spec: forall (x : t), count (pred1 x) enum = 1. + + lemma enumP: forall (x : t), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : t list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinFrom. + + module FinRO = { + proc rem(x : in_t) : unit = RO.rem + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc init() : unit = { + var l : FinFrom.t list; + + l <- FinFrom.enum; + RO.init(); + while (l <> []){ + RO.sample(head witness l); + l <- behead l; + } + } + + proc get(x : in_t) : out_t = { + return oget (RO.m.[x])%SmtMap; + } + + proc sample(x : in_t) : unit = {} + }. + + module type FinRO_Distinguisher(G : RO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.sample} + } + + lemma RO_FinRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ MainD(D, RO).distinguish ~ MainD(D, FinRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_RO_FinRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ) &m (x : d_in_t) (p : + d_out_t -> bool), + Pr[MainD(D, RO).distinguish(x) @ &m : p res] = Pr[MainD(D, FinRO).distinguish(x) @ &m : p res]. + + theory MUniFinFun. + op mfun ['u] (d : in_t -> 'u distr) (f : in_t -> 'u) : real = + (big predT (fun (x : in_t) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma mfunE ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mfun d f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + lemma isdistr_dfun ['u]: forall (d : in_t -> 'u distr), isdistr (mfun d). + + op dfun ['u] (d : in_t -> 'u distr) : (in_t -> 'u) distr = mk (mfun d). + + lemma dfun1E ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mu1 (dfun d) f = (big predT (fun (x : in_t) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun1E_djoin ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mu1 (dfun d) f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + op tofun ['u] (us : 'u list) (x : in_t) : 'u = nth witness us (index x FinFrom.enum). + + op offun ['u] (f : in_t -> 'u) : 'u list = map f FinFrom.enum. + + lemma offunK ['u]: cancel offun tofun. + + lemma tofunK ['u]: forall (us : 'u list), size us = FinFrom.card => offun (tofun us) = us. + + lemma dfun_dmap ['u]: forall (d : in_t -> 'u distr), dfun d = dmap (djoinmap d FinFrom.enum) tofun. + + lemma dfun_supp ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), f \in dfun d <=> forall (x : in_t), f x \in d x. + + lemma dfun_fu ['u]: forall (d : in_t -> 'u distr), (forall (x : in_t), is_full (d x)) => is_full (dfun d). + + lemma dfun_uni ['u]: + forall (d : in_t -> 'u distr), (forall (x : in_t), is_uniform (d x)) => is_uniform (dfun d). + + lemma dfun_funi ['u]: + forall (d : in_t -> 'u distr), + (forall (x : in_t), is_uniform (d x)) => (forall (x : in_t), is_full (d x)) => is_funiform (dfun d). + + lemma dfunE_djoin ['u]: + forall (du : in_t -> 'u distr) (E : (in_t -> 'u) -> bool), + mu (dfun du) E = mu (djoinmap du FinFrom.enum) (E \o tofun). + + lemma dfunE ['u]: + forall (du : in_t -> 'u distr) (p : in_t -> 'u -> bool), + mu (dfun du) (fun (f : in_t -> 'u) => forall (x : in_t), p x (f x)) = + (big predT (fun (x : in_t) => mu (du x) (p x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma weight_dfun ['u]: + forall (df : in_t -> 'u distr), + weight (dfun df) = (big predT (fun (x : in_t) => weight (df x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun_ll ['u]: + forall (d : in_t -> 'u distr), (forall (x : in_t), is_lossless (d x)) => is_lossless (dfun d). + + lemma dlet_dfun ['u, 'v]: + forall (d1 : in_t -> 'u distr) (d2 : in_t -> 'u -> 'v distr), + dlet (dfun d1) (fun (f1 : in_t -> 'u) => dfun (fun (x : in_t) => d2 x (f1 x))) = + dfun (fun (x : in_t) => dlet (d1 x) (fun (x1 : 'u) => d2 x x1)). + + lemma dfun_unit ['u]: forall (f : in_t -> 'u), dfun (fun (x : in_t) => dunit (f x)) = dunit f. + + lemma dmap_dfun ['u, 'v]: + forall (d : in_t -> 'u distr) (F : in_t -> 'u -> 'v), + dmap (dfun d) (fun (f : in_t -> 'u) (x : in_t) => F x (f x)) = + dfun (fun (x : in_t) => dmap (d x) (fun (fx : 'u) => F x fx)). + + lemma dfun1E_fix1 ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t) (f : in_t -> 'u), + mu1 (dfun d) f = mu1 (d x0) (f x0) * mu1 (dfun d.[x0 <- dunit (f x0)]) f. + + lemma dlet_dfun_fupdate_ll ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t) (v : 'u), + dlet (dfun d.[x0 <- dunit v]) (fun (f : in_t -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + dfun d. + + lemma dfunE_dlet_fix1 ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t), dfun d = dlet (d x0) (fun (v : 'u) => dfun d.[x0 <- dunit v]). + + lemma dlet_dfun_update ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t), + dlet (dfun d) (fun (f : in_t -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + (weight (d x0) \cdot dfun d). + + lemma dfun_projE ['u]: + forall (df : in_t -> 'u distr) (x : in_t), + dmap (dfun df) (fun (f : in_t -> 'u) => f x) = (weight (dfun df) / weight (df x) \cdot df x). + + lemma eq_from_dfun_proj_eq ['u]: + forall (df1 df2 : in_t -> 'u distr), + (forall (x : in_t), is_lossless (df1 x)) => + (forall (x : in_t), is_lossless (df2 x)) => + (forall (x : in_t), + dmap (dfun df1) (fun (f : in_t -> 'u) => f x) = dmap (dfun df2) (fun (f : in_t -> 'u) => f x)) => + df1 == df2. + + lemma dfun_prod1E ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr) (f : + in_t -> 'u) (g : in_t -> 'v), + mu1 (dfun (fun (x : in_t) => df x `*` dg x)) (fun (x : in_t) => (f x, g x)) = + mu1 (dfun df) f * mu1 (dfun dg) g. + + lemma dprod_funE ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr), + dfun df `*` dfun dg = + dmap (dfun (fun (x : in_t) => df x `*` dg x)) + (fun (fg : in_t -> 'u * 'v) => ((fun (p : 'u * 'v) => p.`1) \o fg, (fun (p : 'u * 'v) => p.`2) \o fg)). + + lemma dfun_prodE ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr), + dfun (fun (x : in_t) => df x `*` dg x) = + dmap (dfun df `*` dfun dg) (fun (fg : (in_t -> 'u) * (in_t -> 'v)) (x : in_t) => (fg.`1 x, fg.`2 x)). + + lemma dfun_condE ['u]: + forall (X : in_t -> bool) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dmap (dfun dt `*` dfun df) + (fun (tf : (in_t -> 'u) * (in_t -> 'u)) (x : in_t) => if X x then tf.`1 x else tf.`2 x) = + dfun (fun (x : in_t) => if X x then dt x else df x). + + lemma dlet_dfun_cond ['u]: + forall (dX : in_t -> bool distr) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dX x)) => + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dlet (dfun dX) (fun (X : in_t -> bool) => dfun (fun (x : in_t) => if X x then dt x else df x)) = + dfun (fun (x : in_t) => dlet (dX x) (fun (b : bool) => if b then dt x else df x)). + + lemma dfun_dcondE ['u]: + forall (dX : in_t -> bool distr) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dX x)) => + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : in_t -> bool) => + dmap (dfun dt `*` dfun df) + (fun (tf : (in_t -> 'u) * (in_t -> 'u)) (x : in_t) => if X x then tf.`1 x else tf.`2 x)) = + dfun (fun (x : in_t) => (mu1 (dX x) true \cdot dt x) + (mu1 (dX x) false \cdot df x)). + end MUniFinFun. + + module FunRO = { + var f : in_t -> out_t + + proc init() : unit = { + FunRO.f <$ (dfun dout)%MUniFinFun; + } + + proc get(x : in_t) : out_t = { + return FunRO.f x; + } + + proc set(x : in_t, y : out_t) : unit = { + FunRO.f <- fun (z : in_t) => if z = x then y else FunRO.f z; + } + + proc sample(x : in_t) : unit = {} + + proc rem(x : in_t) : unit = {} + }. + + lemma FinRO_FunRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ), + equiv[ MainD(D, FinRO).distinguish ~ MainD(D, FunRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_FinRO_FunRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ) &m (x : d_in_t) + (p : d_out_t -> bool), + Pr[MainD(D, FinRO).distinguish(x) @ &m : p res] = Pr[MainD(D, FunRO).distinguish(x) @ &m : p res]. + end FinEager. + end RO_H. + + module type Ideal_RO = { + proc get(x : RO_H.in_t) : RO_H.out_t {} + } + + module type ROAdv_T(O : Ideal_RO) = { + proc guess(sd : W8.t Array32.t, t : polyvec, uv : polyvec * poly) : bool {O.get} + } + + module MLWE_RO(Adv : ROAdv_T, O : RO_H.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var _A : RO_H.out_t; + var u0 : polyvec; + var u1 : polyvec; + var t : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + O.init(); + sd <$ srand; + s <$ dshort; + e <$ dshort; + _A <@ O.get(sd); + _A <- if tr then (trmx _A)%Matrix_.Matrix else _A; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + t <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ Adv(O).guess(sd, t, if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + + theory MLWE_vs_MLWE_ROM. + module B(A : ROAdv_T, O : RO_H.RO) = { + var _sd : W8.t Array32.t + + var __A : polymat + + module FakeRO = { + proc get(sd : W8.t Array32.t) : polymat = { + var _Ares : polymat; + + _Ares <- B.__A; + if (sd <> B._sd) { + O.sample(sd); + _Ares <@ O.get(sd); + } + + return _Ares; + } + } + + proc guess(_A : polymat, t : polyvec, uv : polyvec * poly) : bool = { + var sd : W8.t Array32.t; + var b : bool; + + sd <$ srand; + B._sd <- sd; + B.__A <- _A; + O.init(); + b <@ A(B(A, O).FakeRO).guess(sd, t, uv); + + return b; + } + }. + + module Bt(A : ROAdv_T, O : RO_H.RO) = { + var _sd : W8.t Array32.t + + var __A : polymat + + module FakeRO = { + proc get(sd : W8.t Array32.t) : polymat = { + var _Ares : polymat; + + _Ares <- Bt.__A; + if (sd <> Bt._sd) { + O.sample(sd); + _Ares <@ O.get(sd); + } + + return _Ares; + } + } + + proc guess(_A : polymat, t : polyvec, uv : polyvec * poly) : bool = { + var sd : W8.t Array32.t; + var b : bool; + + sd <$ srand; + Bt._sd <- sd; + Bt.__A <- (trmx _A)%Matrix_.Matrix; + O.init(); + b <@ A(Bt(A, O).FakeRO).guess(sd, t, uv); + + return b; + } + }. + + lemma MLWE_RO_equiv: + forall (b : bool) &m (A(O : Ideal_RO) <: ROAdv_T{+all mem, -RO_H.LRO, -B} ), + Pr[MLWE_RO(A, RO_H.LRO).main(false, b) @ &m : res] = Pr[MLWE(B(A, RO_H.LRO)).main(b) @ &m : res]. + + lemma MLWE_RO_equiv_t: + forall (b : bool) &m (A(O : Ideal_RO) <: ROAdv_T{+all mem, -RO_H.LRO, -Bt} ), + Pr[MLWE_RO(A, RO_H.LRO).main(true, b) @ &m : res] = Pr[MLWE(Bt(A, RO_H.LRO)).main(b) @ &m : res]. + end MLWE_vs_MLWE_ROM. + end MLWE_ROM. + + theory MLWE_SMP. + theory RO_SMP. + type in_t. + + type out_t. + + op dout : in_t -> out_t distr. + + type d_in_t. + + type d_out_t. + + module type RO = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + } + + module type RO_Distinguisher(G : RO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.rem, G.sample} + } + + module MainD(D : RO_Distinguisher, RO0 : RO) = { + proc distinguish(x : d_in_t) : d_out_t = { + var r : d_out_t; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + module type ROmap = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + + proc restrK() : (in_t, out_t) SmtMap.fmap {} + } + + module type FRO = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + + proc queried(x : in_t, f : PROM.flag) : bool {} + + proc allKnown() : (in_t, out_t) SmtMap.fmap {} + } + + module type FRO_Distinguisher(G : FRO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.rem, G.sample, G.queried, G.allKnown} + } + + abstract theory MkRO. + module RO = { + var m : (in_t, out_t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : in_t, y : out_t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : in_t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + RO.get(x); + } + + proc restrK() : (in_t, out_t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : in_t) : out_t = RO.get + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc rem(x : in_t) : unit = RO.rem + + proc sample(x : in_t) : unit = {} + }. + end MkRO. + + module RO = { + var m : (in_t, out_t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : in_t, y : out_t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : in_t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + RO.get(x); + } + + proc restrK() : (in_t, out_t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : in_t) : out_t = RO.get + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc rem(x : in_t) : unit = RO.rem + + proc sample(x : in_t) : unit = {} + }. + + module FRO = { + var m : (in_t, out_t * PROM.flag) SmtMap.fmap + + proc init() : unit = { + FRO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \in FRO.m)%SmtMap) + r <- (oget (FRO.m.[x])%SmtMap).`1; + FRO.m.[x] <- (r, PROM.Known); + + return r; + } + + proc set(x : in_t, y : out_t) : unit = { + FRO.m.[x] <- (y, PROM.Known); + } + + proc rem(x : in_t) : unit = { + FRO.m <- (rem FRO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + if ((x \notin FRO.m)%SmtMap) + FRO.m.[x] <- (c, PROM.Unknown); + } + + proc queried(x : in_t, f : PROM.flag) : bool = { + return (x \in FRO.m)%SmtMap /\ ((FRO.m.[x])%SmtMap \is f)%PROM; + } + + proc allKnown() : (in_t, out_t) SmtMap.fmap = { + return (restr PROM.Known FRO.m)%SmtMap; + } + }. + + lemma RO_FRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_get: + equiv[ RO.get ~ FRO.get : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> ={res} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_sample: + equiv[ RO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(RO).distinguish ~ D(FRO).distinguish : + ={arg, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_init_ll: islossless RO.init. + + lemma FRO_init_ll: islossless FRO.init. + + lemma FRO_in_dom_ll: islossless FRO.queried. + + lemma FRO_restrK_ll: islossless FRO.allKnown. + + lemma RO_set_ll: islossless RO.set. + + lemma FRO_set_ll: islossless FRO.set. + + lemma RO_get_ll: (forall (x : in_t), is_lossless (dout x)) => islossless RO.get. + + lemma FRO_get_ll: (forall (x : in_t), is_lossless (dout x)) => islossless FRO.get. + + lemma RO_sample_ll: (forall (x : in_t), is_lossless (dout x)) => islossless RO.sample. + + lemma FRO_sample_ll: (forall (x : in_t), is_lossless (dout x)) => islossless FRO.sample. + + module type RM_Distinguisher(G : ROmap) = { + proc distinguish(_ : d_in_t) : d_out_t {G.get, G.sample, G.restrK} + } + + module MainRM(D : RM_Distinguisher, RO0 : ROmap) = { + proc distinguish(x : d_in_t) : d_out_t = { + var r : d_out_t; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + lemma fcoll_bound ['rT]: + forall (f : out_t -> 'rT) (Pc : real), + 0%r <= Pc => + (forall (x1 x2 : in_t) (y : 'rT), y \in dmap (dout x1) f => mu1 (dmap (dout x2) f) y <= Pc) => + forall (q : int), + 0 <= q => + forall (D(G : ROmap) <: RM_Distinguisher{+all mem, -RO} ) &m (z : d_in_t), + Pr[MainRM(D, RO).distinguish(z) @ &m : (fcoll f RO.m)%SmtMap /\ (fsize RO.m)%SmtMap <= q] <= + (q * (q - 1))%r / 2%r * Pc. + + theory FullEager. + abstract theory EagerCore. + axiom dout_ll: forall (x : in_t), is_lossless (dout x). + + module type Orcl = { + proc f(x : in_t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : in_t list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : in_t list, l2 : in_t list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : in_t, l : in_t list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : in_t, t2 : in_t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : in_t, t2 : in_t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : in_t list) (i : in_t) (s2 : in_t list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : in_t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : in_t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : in_t, y : out_t) : unit = FRO.set + + proc rem(x : in_t) : unit = FRO.rem + + proc sample(x : in_t) : unit = FRO.sample + + proc queried(x : in_t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (in_t, out_t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : in_t) (mx1 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : in_t) (mx1 mx2 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : in_t) (r1 : out_t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + end FullEager. + + abstract theory FinEager. + abstract theory EagerCore. + axiom dout_ll: forall (x : in_t), is_lossless (dout x). + + module type Orcl = { + proc f(x : in_t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : in_t list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : in_t list, l2 : in_t list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : in_t, l : in_t list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : in_t, t2 : in_t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : in_t, t2 : in_t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : in_t list) (i : in_t) (s2 : in_t list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : in_t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : in_t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : in_t, y : out_t) : unit = FRO.set + + proc rem(x : in_t) : unit = FRO.rem + + proc sample(x : in_t) : unit = FRO.sample + + proc queried(x : in_t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (in_t, out_t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : in_t) (mx1 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : in_t) (mx1 mx2 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : in_t) (r1 : out_t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + theory FinFrom. + type t = in_t. + + op enum : t list. + + op card : int = size enum. + + axiom enum_spec: forall (x : t), count (pred1 x) enum = 1. + + lemma enumP: forall (x : t), x \in enum. + + lemma enum_uniq: uniq enum. + + lemma card_gt0: 0 < card. + + lemma count_mem: forall (xs : t list), uniq xs => count (mem xs) enum = size xs. + + lemma is_finite_for: (is_finite_for predT enum)%Finite. + + lemma is_finite: Finite.finite_type. + + lemma perm_eq_enum_to_seq: perm_eq enum ((to_seq predT))%Finite. + + lemma card_size_to_seq: card = size ((to_seq predT))%Finite. + end FinFrom. + + module FinRO = { + proc rem(x : in_t) : unit = RO.rem + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc init() : unit = { + var l : FinFrom.t list; + + l <- FinFrom.enum; + RO.init(); + while (l <> []){ + RO.sample(head witness l); + l <- behead l; + } + } + + proc get(x : in_t) : out_t = { + return oget (RO.m.[x])%SmtMap; + } + + proc sample(x : in_t) : unit = {} + }. + + module type FinRO_Distinguisher(G : RO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.sample} + } + + lemma RO_FinRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ MainD(D, RO).distinguish ~ MainD(D, FinRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_RO_FinRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO} ) &m (x : d_in_t) (p : + d_out_t -> bool), + Pr[MainD(D, RO).distinguish(x) @ &m : p res] = Pr[MainD(D, FinRO).distinguish(x) @ &m : p res]. + + theory MUniFinFun. + op mfun ['u] (d : in_t -> 'u distr) (f : in_t -> 'u) : real = + (big predT (fun (x : in_t) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma mfunE ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mfun d f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + lemma isdistr_dfun ['u]: forall (d : in_t -> 'u distr), isdistr (mfun d). + + op dfun ['u] (d : in_t -> 'u distr) : (in_t -> 'u) distr = mk (mfun d). + + lemma dfun1E ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mu1 (dfun d) f = (big predT (fun (x : in_t) => mu1 (d x) (f x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun1E_djoin ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), + mu1 (dfun d) f = mu1 (djoinmap d FinFrom.enum) (map f FinFrom.enum). + + op tofun ['u] (us : 'u list) (x : in_t) : 'u = nth witness us (index x FinFrom.enum). + + op offun ['u] (f : in_t -> 'u) : 'u list = map f FinFrom.enum. + + lemma offunK ['u]: cancel offun tofun. + + lemma tofunK ['u]: forall (us : 'u list), size us = FinFrom.card => offun (tofun us) = us. + + lemma dfun_dmap ['u]: forall (d : in_t -> 'u distr), dfun d = dmap (djoinmap d FinFrom.enum) tofun. + + lemma dfun_supp ['u]: + forall (d : in_t -> 'u distr) (f : in_t -> 'u), f \in dfun d <=> forall (x : in_t), f x \in d x. + + lemma dfun_fu ['u]: forall (d : in_t -> 'u distr), (forall (x : in_t), is_full (d x)) => is_full (dfun d). + + lemma dfun_uni ['u]: + forall (d : in_t -> 'u distr), (forall (x : in_t), is_uniform (d x)) => is_uniform (dfun d). + + lemma dfun_funi ['u]: + forall (d : in_t -> 'u distr), + (forall (x : in_t), is_uniform (d x)) => (forall (x : in_t), is_full (d x)) => is_funiform (dfun d). + + lemma dfunE_djoin ['u]: + forall (du : in_t -> 'u distr) (E : (in_t -> 'u) -> bool), + mu (dfun du) E = mu (djoinmap du FinFrom.enum) (E \o tofun). + + lemma dfunE ['u]: + forall (du : in_t -> 'u distr) (p : in_t -> 'u -> bool), + mu (dfun du) (fun (f : in_t -> 'u) => forall (x : in_t), p x (f x)) = + (big predT (fun (x : in_t) => mu (du x) (p x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma weight_dfun ['u]: + forall (df : in_t -> 'u distr), + weight (dfun df) = (big predT (fun (x : in_t) => weight (df x)) FinFrom.enum)%StdBigop.Bigreal.BRM. + + lemma dfun_ll ['u]: + forall (d : in_t -> 'u distr), (forall (x : in_t), is_lossless (d x)) => is_lossless (dfun d). + + lemma dlet_dfun ['u, 'v]: + forall (d1 : in_t -> 'u distr) (d2 : in_t -> 'u -> 'v distr), + dlet (dfun d1) (fun (f1 : in_t -> 'u) => dfun (fun (x : in_t) => d2 x (f1 x))) = + dfun (fun (x : in_t) => dlet (d1 x) (fun (x1 : 'u) => d2 x x1)). + + lemma dfun_unit ['u]: forall (f : in_t -> 'u), dfun (fun (x : in_t) => dunit (f x)) = dunit f. + + lemma dmap_dfun ['u, 'v]: + forall (d : in_t -> 'u distr) (F : in_t -> 'u -> 'v), + dmap (dfun d) (fun (f : in_t -> 'u) (x : in_t) => F x (f x)) = + dfun (fun (x : in_t) => dmap (d x) (fun (fx : 'u) => F x fx)). + + lemma dfun1E_fix1 ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t) (f : in_t -> 'u), + mu1 (dfun d) f = mu1 (d x0) (f x0) * mu1 (dfun d.[x0 <- dunit (f x0)]) f. + + lemma dlet_dfun_fupdate_ll ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t) (v : 'u), + dlet (dfun d.[x0 <- dunit v]) (fun (f : in_t -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + dfun d. + + lemma dfunE_dlet_fix1 ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t), dfun d = dlet (d x0) (fun (v : 'u) => dfun d.[x0 <- dunit v]). + + lemma dlet_dfun_update ['u]: + forall (d : in_t -> 'u distr) (x0 : in_t), + dlet (dfun d) (fun (f : in_t -> 'u) => dmap (d x0) (fun (y : 'u) => f.[x0 <- y])) = + (weight (d x0) \cdot dfun d). + + lemma dfun_projE ['u]: + forall (df : in_t -> 'u distr) (x : in_t), + dmap (dfun df) (fun (f : in_t -> 'u) => f x) = (weight (dfun df) / weight (df x) \cdot df x). + + lemma eq_from_dfun_proj_eq ['u]: + forall (df1 df2 : in_t -> 'u distr), + (forall (x : in_t), is_lossless (df1 x)) => + (forall (x : in_t), is_lossless (df2 x)) => + (forall (x : in_t), + dmap (dfun df1) (fun (f : in_t -> 'u) => f x) = dmap (dfun df2) (fun (f : in_t -> 'u) => f x)) => + df1 == df2. + + lemma dfun_prod1E ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr) (f : + in_t -> 'u) (g : in_t -> 'v), + mu1 (dfun (fun (x : in_t) => df x `*` dg x)) (fun (x : in_t) => (f x, g x)) = + mu1 (dfun df) f * mu1 (dfun dg) g. + + lemma dprod_funE ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr), + dfun df `*` dfun dg = + dmap (dfun (fun (x : in_t) => df x `*` dg x)) + (fun (fg : in_t -> 'u * 'v) => ((fun (p : 'u * 'v) => p.`1) \o fg, (fun (p : 'u * 'v) => p.`2) \o fg)). + + lemma dfun_prodE ['u, 'v]: + forall (df : in_t -> 'u distr) (dg : in_t -> 'v distr), + dfun (fun (x : in_t) => df x `*` dg x) = + dmap (dfun df `*` dfun dg) (fun (fg : (in_t -> 'u) * (in_t -> 'v)) (x : in_t) => (fg.`1 x, fg.`2 x)). + + lemma dfun_condE ['u]: + forall (X : in_t -> bool) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dmap (dfun dt `*` dfun df) + (fun (tf : (in_t -> 'u) * (in_t -> 'u)) (x : in_t) => if X x then tf.`1 x else tf.`2 x) = + dfun (fun (x : in_t) => if X x then dt x else df x). + + lemma dlet_dfun_cond ['u]: + forall (dX : in_t -> bool distr) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dX x)) => + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dlet (dfun dX) (fun (X : in_t -> bool) => dfun (fun (x : in_t) => if X x then dt x else df x)) = + dfun (fun (x : in_t) => dlet (dX x) (fun (b : bool) => if b then dt x else df x)). + + lemma dfun_dcondE ['u]: + forall (dX : in_t -> bool distr) (dt df : in_t -> 'u distr), + (forall (x : in_t), is_lossless (dX x)) => + (forall (x : in_t), is_lossless (dt x)) => + (forall (x : in_t), is_lossless (df x)) => + dlet (dfun dX) + (fun (X : in_t -> bool) => + dmap (dfun dt `*` dfun df) + (fun (tf : (in_t -> 'u) * (in_t -> 'u)) (x : in_t) => if X x then tf.`1 x else tf.`2 x)) = + dfun (fun (x : in_t) => (mu1 (dX x) true \cdot dt x) + (mu1 (dX x) false \cdot df x)). + end MUniFinFun. + + module FunRO = { + var f : in_t -> out_t + + proc init() : unit = { + FunRO.f <$ (dfun dout)%MUniFinFun; + } + + proc get(x : in_t) : out_t = { + return FunRO.f x; + } + + proc set(x : in_t, y : out_t) : unit = { + FunRO.f <- fun (z : in_t) => if z = x then y else FunRO.f z; + } + + proc sample(x : in_t) : unit = {} + + proc rem(x : in_t) : unit = {} + }. + + lemma FinRO_FunRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ), + equiv[ MainD(D, FinRO).distinguish ~ MainD(D, FunRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + + lemma pr_FinRO_FunRO_D: + (forall (x : in_t), is_lossless (dout x)) => + forall (D(G : RO) <: FinRO_Distinguisher{+all mem, -RO, -FRO, -FunRO} ) &m (x : d_in_t) + (p : d_out_t -> bool), + Pr[MainD(D, FinRO).distinguish(x) @ &m : p res] = Pr[MainD(D, FunRO).distinguish(x) @ &m : p res]. + end FinEager. + end RO_SMP. + + module type SMP_RO = { + proc get(x : RO_SMP.in_t) : RO_SMP.out_t {} + } + + module type Sampler(O : SMP_RO) = { + proc sampleA(sd : W8.t Array32.t) : polymat {O.get} + + proc sampleAT(sd : W8.t Array32.t) : polymat {O.get} + } + + module type PSampler = { + proc sampleA(sd : W8.t Array32.t) : polymat {} + + proc sampleAT(sd : W8.t Array32.t) : polymat {} + } + + module type SAdv_T(O : SMP_RO) = { + proc interact(sd : W8.t Array32.t, t : polyvec) : unit {O.get} + + proc guess(uv : polyvec * poly) : bool {O.get} + } + + module MLWE_SMP(Adv : SAdv_T, S : Sampler, O : RO_SMP.RO) = { + proc main(tr : bool, b : bool) : bool = { + var sd : W8.t Array32.t; + var s : polyvec; + var e : polyvec; + var _A : polymat; + var u0 : polyvec; + var u1 : polyvec; + var t : polyvec; + var e' : poly; + var v0 : poly; + var v1 : poly; + var b' : bool; + + O.init(); + sd <$ srand; + t <$ duni; + Adv(O).interact(sd, t); + if (tr) + _A <@ S(O).sampleAT(sd); + else + _A <@ S(O).sampleA(sd); + s <$ dshort; + e <$ dshort; + u0 <- ((_A *^ s)%Matrix_.Matrix + e)%Vector; + u1 <$ duni; + e' <$ dshort_R; + v0 <- ((t `<*>` s) &+ e')%MLWE_; + v1 <$ duni_R; + b' <@ Adv(O).guess(if b then (u1, v1) else (u0, v0)); + + return b'; + } + }. + end MLWE_SMP. + + theory SMP_vs_ROM. + theory MLWE_SMP. + theory RO_SMP. + type in_t = W8.t Array32.t. + + type out_t = polymat. + + op dout (_ : W8.t Array32.t) : polymat distr = duni_matrix. + + type d_in_t = bool. + + type d_out_t = bool. + + module type RO = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + } + + module type RO_Distinguisher(G : RO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.rem, G.sample} + } + + module MainD(D : RO_Distinguisher, RO0 : RO) = { + proc distinguish(x : d_in_t) : d_out_t = { + var r : d_out_t; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + module type ROmap = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + + proc restrK() : (in_t, out_t) SmtMap.fmap {} + } + + module type FRO = { + proc init() : unit {} + + proc get(x : in_t) : out_t {} + + proc set(x : in_t, y : out_t) : unit {} + + proc rem(x : in_t) : unit {} + + proc sample(x : in_t) : unit {} + + proc queried(x : in_t, f : PROM.flag) : bool {} + + proc allKnown() : (in_t, out_t) SmtMap.fmap {} + } + + module type FRO_Distinguisher(G : FRO) = { + proc distinguish(_ : d_in_t) : d_out_t {G.init, G.get, G.set, G.rem, G.sample, G.queried, G.allKnown} + } + + abstract theory MkRO. + module RO = { + var m : (in_t, out_t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : in_t, y : out_t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : in_t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + RO.get(x); + } + + proc restrK() : (in_t, out_t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : in_t) : out_t = RO.get + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc rem(x : in_t) : unit = RO.rem + + proc sample(x : in_t) : unit = {} + }. + end MkRO. + + module RO = { + var m : (in_t, out_t) SmtMap.fmap + + proc init() : unit = { + RO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin RO.m)%SmtMap) + RO.m.[x] <- r; + + return oget (RO.m.[x])%SmtMap; + } + + proc set(x : in_t, y : out_t) : unit = { + RO.m.[x] <- y; + } + + proc rem(x : in_t) : unit = { + RO.m <- (rem RO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + RO.get(x); + } + + proc restrK() : (in_t, out_t) SmtMap.fmap = { + return RO.m; + } + }. + + module LRO = { + proc init() : unit = RO.init + + proc get(x : in_t) : out_t = RO.get + + proc set(x : in_t, y : out_t) : unit = RO.set + + proc rem(x : in_t) : unit = RO.rem + + proc sample(x : in_t) : unit = {} + }. + + module FRO = { + var m : (in_t, out_t * PROM.flag) SmtMap.fmap + + proc init() : unit = { + FRO.m <- SmtMap.empty; + } + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \in FRO.m)%SmtMap) + r <- (oget (FRO.m.[x])%SmtMap).`1; + FRO.m.[x] <- (r, PROM.Known); + + return r; + } + + proc set(x : in_t, y : out_t) : unit = { + FRO.m.[x] <- (y, PROM.Known); + } + + proc rem(x : in_t) : unit = { + FRO.m <- (rem FRO.m x)%SmtMap; + } + + proc sample(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + if ((x \notin FRO.m)%SmtMap) + FRO.m.[x] <- (c, PROM.Unknown); + } + + proc queried(x : in_t, f : PROM.flag) : bool = { + return (x \in FRO.m)%SmtMap /\ ((FRO.m.[x])%SmtMap \is f)%PROM; + } + + proc allKnown() : (in_t, out_t) SmtMap.fmap = { + return (restr PROM.Known FRO.m)%SmtMap; + } + }. + + lemma RO_FRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_get: + equiv[ RO.get ~ FRO.get : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> ={res} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_sample: + equiv[ RO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_FRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(RO).distinguish ~ D(FRO).distinguish : + ={arg, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (noflags FRO.m{2})%SmtMap]. + + lemma RO_init_ll: islossless RO.init. + + lemma FRO_init_ll: islossless FRO.init. + + lemma FRO_in_dom_ll: islossless FRO.queried. + + lemma FRO_restrK_ll: islossless FRO.allKnown. + + lemma RO_set_ll: islossless RO.set. + + lemma FRO_set_ll: islossless FRO.set. + + lemma RO_get_ll: (forall (x : in_t), is_lossless (dout x)) => islossless RO.get. + + lemma FRO_get_ll: (forall (x : in_t), is_lossless (dout x)) => islossless FRO.get. + + lemma RO_sample_ll: (forall (x : in_t), is_lossless (dout x)) => islossless RO.sample. + + lemma FRO_sample_ll: (forall (x : in_t), is_lossless (dout x)) => islossless FRO.sample. + + module type RM_Distinguisher(G : ROmap) = { + proc distinguish(_ : d_in_t) : d_out_t {G.get, G.sample, G.restrK} + } + + module MainRM(D : RM_Distinguisher, RO0 : ROmap) = { + proc distinguish(x : d_in_t) : d_out_t = { + var r : d_out_t; + + RO0.init(); + r <@ D(RO0).distinguish(x); + + return r; + } + }. + + lemma fcoll_bound ['rT]: + forall (f : out_t -> 'rT) (Pc : real), + 0%r <= Pc => + (forall (x1 x2 : in_t) (y : 'rT), y \in dmap (dout x1) f => mu1 (dmap (dout x2) f) y <= Pc) => + forall (q : int), + 0 <= q => + forall (D(G : ROmap) <: RM_Distinguisher{+all mem, -RO} ) &m (z : d_in_t), + Pr[MainRM(D, RO).distinguish(z) @ &m : (fcoll f RO.m)%SmtMap /\ (fsize RO.m)%SmtMap <= q] <= + (q * (q - 1))%r / 2%r * Pc. + + theory FullEager. + abstract theory EagerCore. + axiom dout_ll: forall (x : in_t), is_lossless (dout x). + + module type Orcl = { + proc f(x : in_t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : in_t list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : in_t list, l2 : in_t list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : in_t, l : in_t list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : in_t, t2 : in_t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : in_t, t2 : in_t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : in_t list) (i : in_t) (s2 : in_t list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : in_t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : in_t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : in_t, y : out_t) : unit = FRO.set + + proc rem(x : in_t) : unit = FRO.rem + + proc sample(x : in_t) : unit = FRO.sample + + proc queried(x : in_t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (in_t, out_t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : in_t) (mx1 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : in_t) (mx1 mx2 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2 ==> + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap = mx1 /\ (FRO.m{2}.[x1])%SmtMap = mx2]. + + lemma I_f_set: + forall (x1 : in_t) (r1 : out_t), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap ==> + (FRO.m{1}.[x1])%SmtMap = None /\ FRO.m{2} = (FRO.m{1}.[x1 <- (r1, PROM.Known)])%SmtMap]. + + lemma eager_get: + eager[ RRO.resample();, FRO.get ~ RRO.get, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_set: + eager[ RRO.resample();, FRO.set ~ RRO.set, RRO.resample(); : ={x, y} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_rem: + eager[ RRO.resample();, FRO.rem ~ RRO.rem, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_sample: + eager[ RRO.resample();, FRO.sample ~ RRO.sample, RRO.resample(); : ={arg, FRO.m} ==> ={res, FRO.m}]. + + lemma eager_queried: + eager[ RRO.resample();, FRO.queried ~ RRO.queried, RRO.resample( + ); : ={x, f} /\ ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_allKnown: + eager[ RRO.resample();, FRO.allKnown ~ RRO.allKnown, RRO.resample(); : ={FRO.m} ==> ={res, FRO.m}]. + + lemma eager_D: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + eager[ RRO.resample();, D(FRO).distinguish ~ D(RRO).distinguish, RRO.resample( + ); : ={glob D, FRO.m, arg} ==> ={FRO.m, glob D} /\ ={res}]. + + module Eager(D : FRO_Distinguisher) = { + proc main1(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(FRO).distinguish(x); + + return b; + } + + proc main2(x : d_in_t) : d_out_t = { + var b : d_out_t; + + FRO.init(); + b <@ D(RRO).distinguish(x); + RRO.resample(); + + return b; + } + }. + + lemma Eager_1_2: + forall (D(G : FRO) <: FRO_Distinguisher{+all mem, -FRO} ), + equiv[ Eager(D).main1 ~ Eager(D).main2 : ={glob D, arg} ==> ={res, FRO.m, glob D}]. + + lemma LRO_RRO_init: equiv[ RO.init ~ FRO.init : true ==> RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_get: + equiv[ RO.get ~ RRO.get : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_set: + equiv[ RO.set ~ FRO.set : + ={x, y} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_rem: + equiv[ RO.rem ~ FRO.rem : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_sample: + equiv[ LRO.sample ~ FRO.sample : + ={arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + + lemma LRO_RRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + equiv[ D(LRO).distinguish ~ D(RRO).distinguish : + ={glob D, arg} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap ==> + ={res, glob D} /\ RO.m{1} = (restr PROM.Known FRO.m{2})%SmtMap]. + end EagerCore. + + lemma RO_LRO_D: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ D(RO).distinguish ~ D(LRO).distinguish : ={glob D, RO.m, arg} ==> ={res, glob D}]. + + lemma RO_LRO: + forall (D(G : RO) <: RO_Distinguisher{+all mem, -RO, -FRO} ), + (forall (x : in_t), is_lossless (dout x)) => + equiv[ MainD(D, RO).distinguish ~ MainD(D, LRO).distinguish : ={glob D, arg} ==> ={res, glob D}]. + end FullEager. + + abstract theory FinEager. + abstract theory EagerCore. + axiom dout_ll: forall (x : in_t), is_lossless (dout x). + + module type Orcl = { + proc f(x : in_t) : unit {} + } + + module Iter(O : Orcl) = { + proc iter(l : in_t list) : unit = { + while (l <> []){ + O.f(head witness l); + l <- drop 1 l; + } + } + + proc iters(l1 : in_t list, l2 : in_t list) : unit = { + Iter(O).iter(l1); + Iter(O).iter(l2); + } + + proc iter_1s(t : in_t, l : in_t list) : unit = { + O.f(t); + Iter(O).iter(l); + } + + proc iter_12(t1 : in_t, t2 : in_t) : unit = { + O.f(t1); + O.f(t2); + } + + proc iter_21(t1 : in_t, t2 : in_t) : unit = { + O.f(t2); + O.f(t1); + } + }. + + lemma iter_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter. + + lemma iters_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iters. + + lemma iter_1s_ll: forall (O <: Orcl), islossless O.f => islossless Iter(O).iter_1s. + + lemma iter_cat: + forall (O <: Orcl), + equiv[ Iter(O).iter ~ Iter(O).iters : ={glob O} /\ arg{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_cat_cat: + forall (O <: Orcl), + equiv[ Iter(O).iters ~ Iter(O).iters : ={glob O} /\ l1{1} ++ l2{1} = l1{2} ++ l2{2} ==> ={glob O}]. + + lemma iter_12_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t1{1}; t2{1}] ==> ={glob O}]. + + lemma iter_21_eq: + forall (O <: Orcl), + equiv[ Iter(O).iter_21 ~ Iter(O).iter : ={glob O} /\ arg{2} = [t2{1}; t1{1}] ==> ={glob O}]. + + lemma iter_swap: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + forall (s1 : in_t list) (i : in_t) (s2 : in_t list), + equiv[ Iter(O).iter ~ Iter(O).iter : + arg{1} = i :: s1 ++ s2 /\ arg{2} = s1 ++ i :: s2 /\ ={glob O} ==> ={glob O}]. + + lemma iter_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter ~ Iter(O).iter : perm_eq arg{1} arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iters_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iters ~ Iter(O).iter : perm_eq (l1{1} ++ l2{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter1_perm: + forall (O <: Orcl), + equiv[ Iter(O).iter_12 ~ Iter(O).iter_21 : ={glob O, t1, t2} ==> ={glob O}] => + equiv[ Iter(O).iter_1s ~ Iter(O).iter : perm_eq (t{1} :: l{1}) arg{2} /\ ={glob O} ==> ={glob O}]. + + lemma iter_inv: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> (glob O) -> bool), + equiv[ O.f ~ O.f : ={arg} /\ P arg{1} /\ Inv (glob O){1} (glob O){2} ==> Inv (glob O){1} (glob O){2}] => + equiv[ Iter(O).iter ~ Iter(O).iter : + ={arg} /\ (forall (x : in_t), x \in arg{1} => P x) /\ Inv (glob O){1} (glob O){2} ==> + Inv (glob O){1} (glob O){2}]. + + lemma iter_inv_HL: + forall (O <: Orcl) (P : in_t -> bool) (Inv : (glob O) -> bool), + hoare[ O.f : P arg /\ Inv (glob O) ==> Inv (glob O)] => + hoare[ Iter(O).iter : (forall (x : in_t), x \in arg => P x) /\ Inv (glob O) ==> Inv (glob O)]. + + module RRO = { + proc init() : unit = FRO.init + + proc get(x : in_t) : out_t = { + var r : out_t; + + r <$ dout x; + if ((x \notin FRO.m)%SmtMap \/ ((FRO.m.[x])%SmtMap \is PROM.Unknown)%PROM) + FRO.m.[x] <- (r, PROM.Known); + + return (oget (FRO.m.[x])%SmtMap).`1; + } + + proc set(x : in_t, y : out_t) : unit = FRO.set + + proc rem(x : in_t) : unit = FRO.rem + + proc sample(x : in_t) : unit = FRO.sample + + proc queried(x : in_t, f : PROM.flag) : bool = FRO.queried + + proc allKnown() : (in_t, out_t) SmtMap.fmap = FRO.allKnown + + module I = { + proc f(x : in_t) : unit = { + var c : out_t; + + c <$ dout x; + FRO.m.[x] <- (c, PROM.Unknown); + } + } + + proc resample() : unit = { + Iter(RRO.I).iter((elems ((fdom ((restr PROM.Unknown FRO.m))%SmtMap))%SmtMap)%FSet); + } + }. + + lemma RRO_resample_ll: islossless RRO.resample. + + lemma eager_init: eager[ RRO.resample();, FRO.init ~ RRO.init, RRO.resample(); : ={FRO.m} ==> ={FRO.m}]. + + lemma iter_perm2: equiv[ Iter(RRO.I).iter_12 ~ Iter(RRO.I).iter_21 : ={FRO.m, t1, t2} ==> ={FRO.m}]. + + lemma I_f_neq: + forall (x1 : in_t) (mx1 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg, FRO.m} /\ x1 <> arg{1} /\ (FRO.m{1}.[x1])%SmtMap = mx1 ==> + ={FRO.m} /\ (FRO.m{1}.[x1])%SmtMap = mx1]. + + lemma I_f_eqex: + forall (x1 : in_t) (mx1 mx2 : (out_t * PROM.flag) option), + equiv[ RRO.I.f ~ RRO.I.f : + ={arg} /\ + x1 <> arg{1} /\ + (eq_except (pred1 x1) FRO.m{1} FRO.m{2})%SmtMap /\ + (FRO.m{1}.[x1])%SmtMap \ No newline at end of file