swh:1:snp:f92f30fa3090eae65efe8d40c3d27dad22819990
Tip revision: 955e909402cf7a5dc3dc55e4de13bbf373edd920 authored by Pierre-Yves Strub on 30 July 2015, 08:20:28 UTC
NewList: last_ -> last.
NewList: last_ -> last.
Tip revision: 955e909
PSS.ec
timeout 0.
(** This proof is naively adapted from
"Making RSA-PSS secure against non-random faults"
As such, it may contain some weird quirks that are
due to that more complex context.
**)
require import Pair.
require import Option.
require import Int.
require import Real.
require import Distr.
(*---*) import Dprod.
(*---*) import Dapply.
require import FMap.
require import ABitstring.
require import FSet.
(*---*) import Interval.
require ISet.
(*---*) import ISet.Finite.
require import Sum. (* for the FEL tactic *)
(*---*) import Monoid.Mrplus.
require List.
(* Generics that we clone later *)
require AWord.
require ROM.
require PKS.
require OW.
(** We want to build a public-key signature scheme
from arbitrary-length bitstrings to
fixed-(k)-length bitstrings *)
type message = bitstring.
op k: int.
axiom leq4_k: 4 <= k.
type signature.
type padded = signature.
op signature: padded distr.
clone import AWord as Signature with
type word <- signature,
op length <- k,
op Dword.dword <- signature
proof
leq0_length by smt.
op from_signature: signature -> bitstring = Signature.to_bits.
op to_signature: bitstring -> signature = Signature.from_bits.
lemma signature_finite: finite ISet.univ<:signature> by [].
(* We only convert to and from integers on full k-length bitstrings.
In the absence of a concrete implementation of the conversion,
we axiomatize the additional property we need for our proof to
go through: that integers below 2^(k - 1) are exactly those
whose representation have a leading zero. *)
op i2osp = Signature.from_int.
op os2ip = Signature.to_int.
lemma os2ip_bnd x: 0 <= os2ip x < 2^k.
proof. by rewrite /os2ip -to_from from_to; smt. qed.
lemma can_i2osp_os2ip x: i2osp (os2ip x) = x
by [].
lemma pcan_os2ip_i2osp x:
0 <= x < 2^k =>
os2ip (i2osp x) = x
by [].
axiom msb0_bnd s:
sub (from_signature s) 0 1 = zeros 1 <=>
os2ip s < 2^(k - 1).
(*** We rely on RSA (we may be able to generalize, but it
looks like the proof requires very strong homomorphic
and invertibility properties). We axiomatize RSA as
abstractly as possible below. *)
(** Keys are left partially abstract *)
type pkey.
op p_n: pkey -> int.
op p_e: pkey -> int.
axiom finite_pkey : finite (ISet.univ <:pkey>).
type skey.
op s_d: skey -> int.
op s_p: skey -> int.
op s_q: skey -> int.
op s_n: skey -> int = fun sk,
s_p sk * s_q sk.
axiom finite_skey : finite (ISet.univ <:skey>).
(** They are generated by sampling in this distribution *)
type keypair = pkey * skey.
op keypairs: keypair distr.
axiom keypairsL: mu keypairs True = 1%r.
(* Necessary conditions for (pk,sk) to be a valid keypair:
* - the public and private modulus are identical, and
* - the modulus is exactly k bits long *)
axiom valid_keypairs pk sk:
support keypairs (pk,sk) =>
p_n pk = s_n sk /\
2^(k - 1) <= p_n pk < 2^k.
(** The permutations *)
op rsap pk (m:int) =
let e = p_e pk in
let n = p_n pk in
(m^e) %% n axiomatized by rsapE.
op rsas sk (m:int) =
let d = s_d sk in
let n = s_n sk in
(m^d) %% n axiomatized by rsasE.
(** Validity conditions and axioms *)
op rsap_dom pk m =
0 <= m < p_n pk.
op rsas_dom sk m =
0 <= m < s_n sk.
axiom rsap_range pk m:
rsap_dom pk m =>
rsap_dom pk (rsap pk m).
axiom rsas_range sk m:
rsas_dom sk m =>
rsas_dom sk (rsas sk m).
axiom rsap_rsas pk sk m:
support keypairs (pk,sk) =>
rsap_dom pk m =>
rsap pk (rsas sk m) = m.
axiom rsas_rsap pk sk m:
support keypairs (pk,sk) =>
rsap_dom pk m =>
rsas sk (rsap pk m) = m.
lemma rsap_rsas_dom pk sk:
support keypairs (pk,sk) =>
rsap_dom pk = rsas_dom sk.
proof.
move=> vkeys; apply valid_keypairs in vkeys.
move: vkeys=> [eq_N _].
by rewrite /rsas_dom -eq_N.
qed.
lemma rsap_range_sk pk sk m:
support keypairs (pk,sk) =>
rsas_dom sk m =>
rsas_dom sk (rsap pk m).
proof.
move=> vkeys.
rewrite -(rsap_rsas_dom pk) // => m_in_dom.
by rewrite rsap_range.
qed.
lemma rsas_range_pk pk sk m:
support keypairs (pk,sk) =>
rsap_dom pk m =>
rsap_dom pk (rsas sk m).
proof.
move=> vkeys.
rewrite (rsap_rsas_dom pk sk) // => m_in_dom.
by rewrite rsas_range.
qed.
(** Additional properties of RSA. *)
op invertible: pkey -> int -> bool.
axiom card_noninvertible pk sk:
support keypairs (pk,sk) =>
card (filter (!(invertible pk)) (interval 0 (p_n pk - 1))) = s_p sk + s_q sk - 1.
(* We also need a bound on the probability of sampling a
non-invertible element (from Z/nZ) in [0..2^(k - 1)).
Non-invertible elemtns in Z/nZ are multiples of p and
multiples of q. There are at most 2^(k - 1) /% p
multiples of p in [0..2^(k - 1)) (and symmetrically for
q), and we can upper-bound 2^(k - 1) /% p by 2^(k /% 2 - 1).
The bound we use is twice that (multiples of p and
multiples of q). *)
axiom card_noninvertible_2k1 pk sk:
support keypairs (pk,sk) =>
card (filter (!(invertible pk)) (interval 0 (2^(k - 1) - 1))) <= 2^(k /% 2).
op inv: pkey -> int -> int.
op mulzp: pkey -> int -> int -> int.
op ( ** ) (x y:int) pk = mulzp pk x y.
axiom inv_range pk m:
rsap_dom pk m =>
rsap_dom pk (inv pk m).
axiom mulzp_range pk x y:
rsap_dom pk x =>
rsap_dom pk y =>
rsap_dom pk ((x ** y) pk).
axiom mulzp1 (x:int) (pk:pkey):
rsap_dom pk x =>
(1 ** x) pk = x.
axiom mulzpV (x:int) (pk:pkey):
rsap_dom pk x =>
invertible pk x =>
(x ** (inv pk x)) pk = 1.
axiom mulzpC x y pk:
rsap_dom pk x =>
rsap_dom pk y =>
(x ** y) pk = (y ** x) pk.
axiom mulzpA x y z pk:
rsap_dom pk x =>
rsap_dom pk y =>
rsap_dom pk z =>
(((x ** y) pk) ** z) pk = (x ** ((y ** z) pk)) pk.
axiom rsap_morphism_mulzp (x y:int) (pk:pkey):
rsap_dom pk x =>
rsap_dom pk y =>
rsap pk ((x ** y) pk) = ((rsap pk x) ** (rsap pk y)) pk.
axiom rsas_morphism_mulzp (x y:int) (pk:pkey) (sk:skey):
support keypairs (pk,sk) =>
rsap_dom pk x =>
rsap_dom pk y =>
rsas sk ((x ** y) pk) = ((rsas sk x) **(rsas sk y)) pk.
axiom invertible_mulzp x y pk:
(invertible pk x /\ invertible pk y) <=> invertible pk ((x ** y) pk).
axiom invertible_inv x pk:
invertible pk x <=> invertible pk (inv pk x).
(* The following axioms are consequences... They are left as axioms because
proving them is not the object of this effort. *)
axiom rsap_dom1 pk: rsap_dom pk 1.
axiom rsap_morphism0 (pk:pkey):
rsap pk 0 = 0.
axiom rsap_morphism1 (pk:pkey):
rsap pk 1 = 1.
axiom invertible_rsap (pk:pkey) (sk:skey) x:
support keypairs (pk,sk) =>
invertible pk x <=> invertible pk (rsap pk x).
axiom invertible_rsas (pk:pkey) (sk:skey) x:
support keypairs (pk,sk) =>
invertible pk x <=> invertible pk (rsas sk x).
axiom rsap_morphism_inv (x:int) (pk:pkey):
rsap_dom pk x =>
rsap pk (inv pk x) = inv pk (rsap pk x).
axiom rsas_morphism_inv (x:int) (pk:pkey) (sk:skey):
support keypairs (pk,sk) =>
rsap_dom pk x =>
rsas sk (inv pk x) = inv pk (rsas sk x).
(** The challenge distribution *)
op challenge (pk:pkey) = [0..p_n pk - 1].
lemma challenge_valid pk c:
in_supp c (challenge pk) <=> rsap_dom pk c
by [].
lemma challengeL pk sk:
support keypairs (pk,sk) =>
mu (challenge pk) True = 1%r.
proof.
move=> vkp; apply valid_keypairs in vkp.
cut:= Dinter.weight_def 0 (p_n pk - 1).
rewrite /challenge /weight (_: 0 <= p_n pk - 1) //=.
smt. (* 0 <= p_n pk - 1 *)
qed.
(* A lemma on the probability of sampling a challenge
whose most significant bit is 0 *)
lemma mu_challenge0 pk sk:
support keypairs (pk,sk) =>
mu (challenge pk) (fun x, mem x (interval 0 (2^(k - 1) - 1))) = (2 ^ (k - 1))%r / (p_n pk)%r.
proof.
move=> vkeys.
rewrite /challenge -Dinter_uni.dinter_is_dinter. (* Oh, look! Finite sets! *)
rewrite Dinter_uni.mu_def_nz; first smt.
rewrite Interval.card_interval_max (_: max (p_n pk - 1 - 0 + 1) 0 = p_n pk); first smt.
cut ->: filter (fun x, mem x (interval 0 (2^(k - 1) - 1))) (interval 0 (p_n pk - 1)) =
interval 0 (2^(k - 1) - 1).
by apply set_ext=> x; rewrite mem_filter /= !mem_interval; smt.
by rewrite card_interval_max (_: max (2^(k - 1) - 1 - 0 + 1) 0 = 2^(k - 1)); first smt.
qed.
clone OW as RSA with
type t <- int,
type pkey <- pkey,
type skey <- skey,
op keypairs <- keypairs,
op challenge <- challenge,
op f <- rsap,
op finv <- rsas
proof
keypairsL by apply keypairsL,
challengeL by (move=> pk; case=> sk; apply challengeL),
challengeU by (move=> pk _; apply Dinter.dinterU),
finv_correct,
fofinv by smt,
finvof by smt.
realize finv_correct.
move=> pk sk m vkp; cut:= vkp=> vkp'; apply valid_keypairs in vkp.
case=> x; rewrite /f_rng /f_dom=> [vch ->].
rewrite rsas_rsap // /rsap_dom.
smt.
qed.
(*-*) import RSA.
(* Additional Facts useful for this particular setting of RSA *)
(* factor things *)
axiom s_p_bounds (sk:skey):
valid_skey sk =>
2^(k /% 2 - 1) <= s_p sk < 2^(k /% 2).
axiom s_q_bounds (sk:skey):
valid_skey sk =>
2^(k /% 2 - 1) <= s_q sk < 2^(k /% 2).
lemma rsap_dom_in_2k pk:
valid_pkey pk =>
rsap_dom pk <= (fun x, 0 <= x < 2^k)
by [].
lemma rsas_dom_in_2k sk:
valid_skey sk =>
rsas_dom sk <= (fun x, 0 <= x < 2^k)
by [].
(** We instantiate the PKS library with the appropriate types to define security *)
clone import PKS as PKSa
with
type pkey <- pkey,
type skey <- skey,
type message <- message,
type signature <- signature.
import NM_CMA.
module UF_CMA = NM_CMA.
(** Now we can set things up for the definition of PSS *)
(* Setting up the random salts *)
op k0:int.
axiom leq0_k0: 0 < k0.
type salt.
op salt: salt distr.
clone import AWord as Salt with
type word <- salt,
op length <- k0,
op Dword.dword <- salt
proof
leq0_length by smt.
op from_salt: salt -> bitstring = Salt.to_bits.
op to_salt: bitstring -> salt = Salt.from_bits.
(* Setting up H *)
op kh:int.
axiom leq0_kh: 0 < kh.
type htag.
op htag: htag distr.
axiom htagL: mu htag True = 1%r.
axiom htagU: isuniform htag.
axiom htagF x: support htag x.
axiom mux_htag: mu htag ((=) witness) = 1%r / (2^kh)%r.
clone import AWord as HTag with
type word <- htag,
op length <- kh,
op Dword.dword <- htag
proof
leq0_length by smt.
op from_htag: htag -> bitstring = HTag.to_bits.
op to_htag: bitstring -> htag = HTag.from_bits.
lemma htag_finite: finite ISet.univ<:htag> by [].
(* A note on using "message * salt" as domain for H.
This is my way of forcing the bad guy to call H with at least k0 bits.
It is easy to see the following if we let the adversary call H with less than k0 bits:
- the two oracles can be separated with no possibility of query-level collision,
- the probability of a response-level collision is the same. *)
clone ROM.Lazy as Ht with
type from <- message * salt,
type to <- htag,
op dsample <- fun (x:message * salt), htag.
module H = Ht.RO.
(* Setting up G *)
op kg:int.
axiom lt_0_kg: 0 < kg.
type gtag.
op gtag: gtag distr.
axiom gtagL: mu gtag True = 1%r.
axiom gtagU: isuniform gtag.
axiom gtagF x: support gtag x.
axiom mux_gtag: mu gtag ((=) witness) = 1%r / (2^kg)%r.
clone import AWord as GTag with
type word <- gtag,
op length <- kg,
op Dword.dword <- gtag
proof
leq0_length by smt.
op from_gtag: gtag -> bitstring = GTag.to_bits.
op to_gtag: bitstring -> gtag = GTag.from_bits.
lemma gtag_finite: finite ISet.univ<:gtag> by [].
clone ROM.LazyEager as Gt with
type from <- htag,
type to <- gtag,
op dsample <- fun (x:htag), gtag.
module G = Gt.Lazy.RO.
module Ge = Gt.Eager.RO.
(** Constraints on lengths k, k0, kh, kg *)
axiom k_constraints:
k0 <= kg /\
k = 1 + kh + kg.
(** PSS96 definition *)
op Delta: int distr. (* we need to know things about this *)
axiom DeltaL: mu Delta True = 1%r.
module PSS96(H:Ht.Types.Oracle,G1:Gt.Types.Oracle): Scheme = {
proc init(): unit = {
H.init();
G.init();
}
proc keygen(): (pkey * skey) = {
var pk, sk;
(pk,sk) = $keypairs;
return (pk,sk);
}
proc sign(sk:skey, m:message): signature = {
var r, w, g, rs, em;
var s;
r = $salt;
w = H.o(m,r);
g = G.o(w);
rs = g ^ (to_gtag (from_salt r || zeros (kg - k0)));
em = to_signature (zeros 1 || (from_htag w) || (from_gtag rs));
s = i2osp (rsas sk (os2ip em));
return s;
}
proc verify(pk:pkey, m:message, s:signature): bool = {
var re;
var y, b, w, rs, g, ga, r, w';
re = false;
if (0 <= os2ip s < p_n pk) {
y = from_signature (i2osp (rsap pk (os2ip s)));
b = sub y 0 1;
w = to_htag (sub y 1 kh);
rs = to_gtag (sub y (1 + kh) kg);
g = G.o(w);
r = sub (from_gtag (g ^ rs)) 0 k0;
ga = sub (from_gtag (g ^ rs)) k0 (kg - k0);
w' = H.o(m,to_salt r);
re = (b = zeros 1 /\ w' = w /\ ga = zeros (kg - k0));
}
return re;
}
}.
(* Some types to make things easier on ourselves *)
type hmap = (message * salt,bool * htag * int) map.
type gmap = (htag,gtag) map.
(* CMA adversary with 2 random oracles *)
module type PSS_AdvCMA (H:Ht.Types.ARO,G:Gt.Types.ARO,O:AdvOracles) = {
proc forge(pk:pkey): message * signature
}.
(* Bounding the adversary *)
op qH:int.
axiom leq0_qH: 0 <= qH.
op qG:int.
axiom leq0_qG: 0 < qG.
op qS:int.
axiom leq0_qS: 0 <= qS.
module Bounded_PSS_Adv(A:PSS_AdvCMA,H:Ht.Types.ARO,G:Gt.Types.ARO,O:AdvOracles) = {
var cH, cG, cS, cF:int
module Hc = {
proc o(mr:message * salt): htag = {
var r = witness;
if (cH < qH) {
r = H.o(mr);
cH = cH + 1;
}
return r;
}
}
module Gc = {
proc o(w:htag): gtag = {
var r = witness;
if (cG < qG) {
r = G.o(w);
cG = cG + 1;
}
return r;
}
}
module Oc = {
proc sign(m:message): signature = {
var r = witness;
if (cS < qS) {
r = O.sign(m);
cS = cS + 1;
}
return r;
}
}
module Ac = A(Hc,Gc,Oc)
proc forge(pk:pkey): message * signature = {
var r;
cH = 0;
cG = 0;
cS = 0;
cF = 0;
r = Ac.forge(pk);
return r;
}
}.
(** Defining complex distributions for the proof: we will switch
from sampling in the uniform distribution over the
preimage of [0..2^(k - 1)[ by the function simul defined
below to sampling in challenge until the value is within
the preimage of [0..2^(k - 1)[ as desired. *)
op simul (pk:pkey) (sk:skey) (b:bool) (x u:int) =
if b then ((rsap pk x) ** (rsap pk u)) pk
else rsap pk u.
(* For simplicity in the formal proof, we will instead
consider the uniform distribution on the image of
[0..2^(k - 1)[ by the inverse permutation (when it is
a permutation) defined as follows. This is strictly
equivalent (and can be proved equivalent). *)
op simul_inv (pk:pkey) (sk:skey) (b:bool) (x z:int) =
if b then ((inv pk x) ** (rsas sk z)) pk
else rsas sk z.
op sigd (pk:pkey) (sk:skey) (b:bool) (x:int): int distr =
dapply (simul_inv pk sk b x) ([0..2^(k - 1) - 1]).
(* We prove lots of lemmas that are useful along the way. *)
lemma simul_range (pk:pkey) (sk:skey) (b:bool) (x u:int):
support keypairs (pk,sk) =>
rsap_dom pk x =>
rsap_dom pk u =>
rsap_dom pk (simul pk sk b x u).
proof.
move=> vkeys x_bnd u_bnd.
case b; rewrite /simul /=; last by move=> _; apply rsap_range; smt.
by move=> _; rewrite mulzp_range ?rsap_range.
qed.
lemma simul_inv_range (pk:pkey) (sk:skey) (b:bool) (x u:int):
support keypairs (pk,sk) =>
rsap_dom pk x =>
rsap_dom pk u =>
rsap_dom pk (simul_inv pk sk b x u).
proof.
move=> vkeys x_bnd u_bnd.
case b; rewrite /simul_inv /=; last by move=> _; apply rsas_range_pk; smt.
by move=> _; rewrite mulzp_range 1:inv_range // 1:rsas_range_pk.
qed.
lemma simulK (pk:pkey) (sk:skey) (b:bool) (x u:int):
support keypairs (pk,sk) =>
rsap_dom pk x =>
rsap_dom pk u =>
(b => invertible pk x) =>
simul_inv pk sk b x (simul pk sk b x u) = u.
proof.
move=> vkeys x_bnd u_bnd x_nonzero.
rewrite /simul /simul_inv; case b; last by rewrite rsas_rsap.
move=> bt; rewrite rsas_morphism_mulzp ?rsap_range //.
apply x_nonzero in bt.
rewrite !rsas_rsap //.
rewrite -mulzpA 1:inv_range //.
by rewrite (mulzpC _ x) 1:inv_range // mulzpV // mulzp1.
qed.
lemma simul_invK (pk:pkey) (sk:skey) (b:bool) (x u:int):
support keypairs (pk,sk) =>
rsap_dom pk x =>
rsap_dom pk u =>
(b => invertible pk x) =>
simul pk sk b x (simul_inv pk sk b x u) = u.
proof.
move=> vkeys x_bnd u_bnd x_nonzero.
rewrite /simul /simul_inv; case b; last by rewrite rsap_rsas.
move=> bt; rewrite rsap_morphism_mulzp ?rsap_range // 1:inv_range // 1:rsas_range_pk //.
apply x_nonzero in bt.
rewrite !rsap_rsas //.
rewrite -mulzpA ?rsap_range // 1:inv_range //.
by rewrite -rsap_morphism_mulzp // 1:inv_range // mulzpV // rsap_morphism1 mulzp1.
qed.
lemma supp_sigd pk sk b x s:
valid_keys (pk,sk) =>
rsap_dom pk x =>
(b => invertible pk x) =>
in_supp s (sigd pk sk b x) <=> (rsap_dom pk s /\ 0 <= simul pk sk b x s < 2^(k - 1)).
proof.
rewrite /valid_keys=> vkeys x_bnd x_nonzero.
rewrite /sigd supp_def.
split.
elim=> y [->]; rewrite Dinter.supp_def=> supp_y.
by rewrite simul_invK //; first 2 smt.
move=> [supp_s supp_simul]; exists (simul pk sk b x s); split.
by rewrite simulK.
by rewrite Dinter.supp_def; smt.
qed.
lemma sigdX_interX pk sk b x u:
valid_keys (pk,sk) =>
support (sigd pk sk b x) u =>
rsap_dom pk x =>
(b => invertible pk x) =>
mu (sigd pk sk b x) ((=) u) = mu [0..2^(k - 1) - 1] ((=) (simul pk sk b x u)).
proof.
move=> vkeys supp_u x_bnd x_nonzero; rewrite /sigd.
rewrite mu_def (mu_support (fun x0, u = simul_inv pk sk b x x0)) (mu_support ((=) (simul pk sk b x u))).
apply mu_eq=> y /=; rewrite eq_iff /Pred.(/\) /=; split=> //.
by move=> [->] sup_y; split=> //; rewrite simul_invK //; first smt.
by move=> [<-] sup_y; split=> //; rewrite simulK //; first smt.
qed.
lemma sigdU pk sk b x:
valid_keys (pk,sk) =>
rsap_dom pk x =>
(b => invertible pk x) =>
isuniform (sigd pk sk b x).
proof.
move=> vkeys x_bnd x_nonzero s0 s1 supp_s0 supp_s1.
rewrite /mu_x.
do 2!rewrite sigdX_interX //.
by apply Dinter.dinterU; rewrite /in_supp /mu_x -sigdX_interX.
qed.
lemma sigdL pk sk b x: mu (sigd pk sk b x) True = 1%r.
proof. by rewrite /sigd -/(weight _) Dapply.lossless Dinter.weight_def; smt. qed.
lemma sigdX pk sk b x u:
valid_keys (pk,sk) =>
rsap_dom pk x =>
support (sigd pk sk b x) u =>
(b => invertible pk x) =>
mu (sigd pk sk b x) ((=) u) = 1%r/(2^(k - 1))%r.
proof.
move=> vkeys x_bnd u_bnd x_nonzero.
rewrite sigdX_interX //.
by rewrite -/(mu_x _ _) Dinter.mu_x_def_in; smt.
qed.
lemma mu_challenge_in_pim pk sk b x:
support keypairs (pk,sk) =>
rsap_dom pk x =>
(b => invertible pk x) =>
mu (challenge pk)
(fun y, 0 <= simul pk sk b x y < 2^(k - 1))
= (2^(k - 1))%r / (p_n pk)%r.
proof.
move=> vkeys x_in_dom x_invertible.
pose f:= fun y, simul pk sk b x y.
pose finv:= fun y, simul_inv pk sk b x y.
cut ->: (fun y, 0 <= simul pk sk b x y < 2^(k - 1)) = Fun.preim f (fun x, 0 <= x < 2^(k - 1))
by beta.
rewrite -Dapply.dapply_preim.
cut supp_eq: support (dapply f (challenge pk)) = support (challenge pk).
apply fun_ext=> x'; rewrite /challenge /support Dapply.supp_def eq_iff; split.
move=> [y [->]]; rewrite -/(rsap_dom _ _) !Dinter.supp_def.
cut H2: forall x y, 0 <= x <= y - 1 = 0 <= x < y by smt.
by rewrite !H2 -/(rsap_dom _ _) -/(rsap_dom _ _) /f=> {H2}; apply simul_range.
rewrite Dinter.supp_def.
cut H2: forall x y, 0 <= x <= y - 1 = 0 <= x < y by smt.
rewrite H2 -/(rsap_dom _ _)=> x'_in_dom.
exists (finv x'); split.
by rewrite /f /finv simul_invK=> //.
by rewrite Dinter.supp_def H2 -/(rsap_dom _ _) /finv; apply simul_inv_range.
cut ->: dapply f (challenge pk) = challenge pk.
apply uniform_unique=> //.
by rewrite Dapply.mu_def.
move=> x' y'; progress.
rewrite !Dapply.mu_x_def (mu_support (fun x0, x' = f x0)) (mu_support (fun x0, y' = f x0)).
cut invert: forall x, support (challenge pk) x =>
((fun x0, x = f x0) /\ support (challenge pk))
= ((=) (finv x) /\ support (challenge pk)).
progress; rewrite /Pred.(/\) /= -fun_ext=> x1 /=; rewrite eq_iff; split.
by move=> [-> x1_in_supp]; rewrite /f /finv simulK //; smt.
by move=> [<- x1_in_supp]; rewrite /f /finv simul_invK //; smt.
rewrite invert 1:-supp_eq 1:/support //.
rewrite invert 1:-supp_eq 1:/support //.
rewrite -!mu_support.
apply challengeU; first smt.
by cut:= H; rewrite -/(support _ _) supp_eq /support !challenge_valid /finv simul_inv_range //; smt.
by cut:= H0; rewrite -/(support _ _) supp_eq /support !challenge_valid /finv simul_inv_range //; smt.
by apply challengeU; exists sk.
cut ->: (fun x, 0 <= x < 2^(k - 1)) = (fun x, mem x (interval 0 (2^(k - 1) - 1))).
by apply fun_ext=> z //=; rewrite mem_interval; smt.
by apply (mu_challenge0 _ sk).
qed.
(*** Some useful notations *)
op pi3_1 (x:'a * 'b * 'c) = x.`1.
op pi3_2 (x:'a * 'b * 'c) = x.`2.
op pi3_3 (x:'a * 'b * 'c) = x.`3.
(*** The inverter *)
type caller = [ Adv | Sig ].
module Inverter(A:PSS_AdvCMA): RSA.Inverter = {
var h:(message * salt,htag * caller * int) map
var g:(htag,gtag * caller) map
var pk:pkey
var ystar:int
var cH, cG, cS, cF:int
module G = {
proc o(w:htag): gtag = {
var r;
r = $gtag;
if (!mem w (dom g)) g.[w] = (r,Adv);
return fst (oget g.[w]);
}
}
module Ga = {
proc o(w:htag): gtag = {
var r;
if (cG < qG) {
r = G.o(w);
cG = cG + 1;
}
else r = witness;
return r;
}
}
module Ha = {
proc o(mr:message * salt): htag = {
var m, r, w, st, u, z, i;
(m,r) = mr;
u = 0;
w = witness;
st = witness;
i = 0;
z = 2^k - 1;
if (cH < qH) {
if (invertible pk ystar) {
while (!0 <= z < 2^(k - 1) /\ i < k0 + 1) {
u = $challenge pk;
z = (ystar ** (rsap pk u)) pk;
w = to_htag (sub (from_signature (i2osp z)) 1 kh);
st = to_gtag (sub (from_signature (i2osp z)) (1 + kh) kg);
i = i + 1;
}
st = st ^ (to_gtag (from_salt r || zeros (kg - k0)));
if (0 <= z < 2^(k - 1) /\ invertible pk u) {
if (!mem (m,r) (dom h)) {
if (!mem w (dom g)) {
h.[(m,r)] = (w,Adv,u);
g.[w] = (st,Adv);
}
} else
w = pi3_1 (oget h.[(m,r)]);
} else
w = witness;
}
cH = cH + 1;
}
return w;
}
}
module S = {
proc sign(m:message): signature = {
var r, w, st, u, z, i, s;
s = witness;
i = 0;
z = 2^k - 1;
r = $salt;
u = 0;
w = witness;
st = witness;
if (cS < qS) {
while (!0 <= z < 2^(k - 1) /\ i < k0 + 1) {
u = $challenge pk;
z = rsap pk u;
w = to_htag (sub (from_signature (i2osp z)) 1 kh);
st = to_gtag (sub (from_signature (i2osp z)) (1 + kh) kg);
i = i + 1;
}
st = st ^ (to_gtag (from_salt r || zeros (kg - k0)));
if (0 <= z < 2^(k - 1)) {
if (!mem (m,r) (dom h)) {
if (!mem w (dom g)) {
h.[(m,r)] = (w,Sig,u);
g.[w] = (st,Sig);
}
s = i2osp u;
}
}
cS = cS + 1;
}
return s;
}
}
module A = A(Ha,Ga,S)
proc invert(pk:pkey, c:int): int = {
var m, s, y, w, st, w', st', r, c', u, x;
Inverter.h = FMap.empty;
Inverter.g = FMap.empty;
Inverter.pk = pk;
Inverter.ystar = c;
Inverter.cF = 0;
Inverter.cS = 0;
Inverter.cG = 0;
Inverter.cH = 0;
u = 1;
(m,s) = A.forge(pk);
y = from_signature (i2osp (rsap pk (os2ip s)));
w = to_htag (sub y 1 kh);
st = to_gtag (sub y (1 + kh) kg);
st' = G.o(w);
r = to_salt (sub (from_gtag (st ^ st')) 0 k0);
w' = $htag;
if (mem (m,r) (dom h))
(w',c',u) = oget h.[(m,r)];
x = ((os2ip s) ** (inv pk u)) pk;
return x;
}
}.
(*** Games and transitions *)
section.
declare module A: PSS_AdvCMA {H,G,Ge,Wrap(PSS96(H,G)),Bounded_PSS_Adv,
Inverter}.
axiom A_forge_ll (H <: Ht.Types.ARO {A}) (G <: Gt.Types.ARO {A}) (O <: AdvOracles {A}):
islossless H.o =>
islossless G.o =>
islossless O.sign =>
islossless A(H,G,O).forge.
(* This is the memory that is used by the concrete adversary during the proof *)
(* In the final transition, we rewrite the concrete adversary into an
adversary against OW, and therefore drop the secret key from its state. *)
local module Mem = {
var pk:pkey
var sk:skey
var qs:(message * signature) set
var fs:(message * signature) set
var cH, cG, cS:int
proc init(ks:pkey * skey): unit = {
(pk,sk) = ks;
qs = FSet.empty;
fs = FSet.empty;
cH = 0;
cG = 0;
cS = 0;
}
}.
(* The oracle H is split between adversary queries,
and queries made by the signature oracle *)
module type SplitOracle = {
proc init(ks:pkey * skey):unit { }
proc o(c:caller,m:message,r:salt):htag option
proc v(m:message,r:salt):htag (* for use by the experiment *)
}.
(** Since the proof mostly works by modifying H,
with some eager-style interactions with G,
we will mostly reason in terms of a concrete
adversary trying to distinguish various
implementations of G and H through the signing
oracle. This should allow some abstraction in the
proof, and in particular in the two eager steps
on G. **)
module type Gadv (H:SplitOracle, G:Gt.Types.ARO) = {
proc main (ks:pkey * skey): bool {H.o G.o H.v}
}.
local module Gen (Gadv:Gadv, H:SplitOracle, G:Gt.Types.Oracle) = {
module GA = Gadv(H,G)
proc main () : bool = {
var keys, b;
keys = $keypairs;
G.init();
H.init(keys);
b = GA.main(keys);
return b;
}
}.
local module GAdv(H:SplitOracle, G:Gt.Types.ARO) = {
(* Wrapping a split oracle for direct use by the adversary *)
module Ha = {
proc o(m:message * salt): htag = {
var r = None;
if (Mem.cH < qH) {
r = H.o(Adv,fst m,snd m);
Mem.cH = Mem.cH + 1;
}
return oget r;
}
}
(* Wrapping a split oracle for use by the signing oracle *)
module Hs = {
proc o(m:message * salt): htag option = {
var r;
r = H.o(Sig,fst m,snd m);
return r;
}
}
(* Counting queries to G *)
module Ga = {
proc o(w:htag): gtag = {
var r = witness;
if (Mem.cG < qG) {
r = G.o(w);
Mem.cG = Mem.cG + 1;
}
return r;
}
}
(* Signing oracle *)
module S = {
proc sign(m:message): signature = {
var r, wo, w, g, rs, em;
var s = witness;
if (Mem.cS < qS) {
r = $salt;
wo = Hs.o(m,r);
if (wo = None)
s = witness;
else {
w = oget wo;
g = G.o(w);
rs = g ^ (to_gtag (from_salt r || zeros (kg - k0)));
em = to_signature (zeros 1 || (from_htag w) || (from_gtag rs));
s = i2osp (rsas Mem.sk (os2ip em));
Mem.qs = add (m,s) Mem.qs;
}
Mem.cS = Mem.cS + 1;
}
return s;
}
proc fresh(m:message, s:signature): bool = {
return !mem (m,s) Mem.qs;
}
}
module A = A(Ha, Ga, S)
proc main (ks:pkey*skey): bool = {
var m, s;
var y, w, st, g, rs, r, w';
Mem.init(ks);
(m,s) = A.forge(Mem.pk);
y = from_signature (i2osp (rsap Mem.pk (os2ip s)));
w = to_htag (sub y 1 kh);
st = to_gtag (sub y (1 + kh) kg);
g = G.o(w);
rs = from_gtag (st ^ g);
r = to_salt (sub rs 0 k0);
w' = H.v(m,r);
return sub y 0 1 = zeros 1 /\ w = w' /\ sub rs k0 (kg - k0) = zeros (kg - k0) /\
0 <= os2ip s < p_n Mem.pk /\ !mem (m,s) Mem.qs;
}
}.
(* Generic losslessness lemmas for wrapped split oracles *)
local lemma GAdvHa_o_ll (H <: SplitOracle {GAdv}) (G <: Gt.Types.ARO {GAdv}):
islossless H.o => islossless GAdv(H,G).Ha.o.
proof. by move=> H_o_ll; proc; sp; if=> //; wp; call H_o_ll. qed.
local lemma GAdvHs_o_ll (H <: SplitOracle {GAdv}) (G <: Gt.Types.ARO {GAdv}):
islossless H.o => islossless GAdv(H,G).Hs.o.
proof. by intros=> H_o_ll; proc; call H_o_ll. qed.
local lemma GAdvGa_o_ll (H <: SplitOracle {GAdv}) (G <: Gt.Types.ARO {GAdv}):
islossless G.o => islossless GAdv(H,G).Ga.o.
proof. by move=> G_o_ll; proc; sp; if=> //; wp; call G_o_ll. qed.
(* Generic losslessness lemma for refactored PSS *)
local lemma GAdv_main_ll (H <: SplitOracle {GAdv}) (G <: Gt.Types.ARO {GAdv}):
islossless H.o => islossless G.o => islossless H.v => islossless GAdv(H, G).main.
proof.
move=> H_o_ll G_o_ll H_v_ll; proc.
call H_v_ll.
wp; call G_o_ll.
wp; call (A_forge_ll (<:GAdv(H,G).Ha) (<:GAdv(H,G).Ga) (<:GAdv(H,G).S) _ _ _).
by apply (GAdvHa_o_ll H G _)=> //.
by apply (GAdvGa_o_ll H G _)=> //.
proc; sp; if=> //; seq 2: true=> //.
by call (GAdvHs_o_ll H G _)=> //; rnd; skip; smt.
if; first by wp.
wp; call G_o_ll.
by wp.
by call (_: true)=> //; wp.
qed.
(** A module to store the globals used
in most variants of H, along with
some useful equality predicates *)
local module Hmem = {
var pk:pkey
var sk:skey
var xstar:int
var ystar:int
proc init(ks:pkey*skey): unit = {
(pk,sk) = ks;
xstar = $challenge pk;
ystar = rsap pk xstar;
}
}.
local module Hmap = {
var m:(message * salt,htag * caller * int) map
proc init(ks:pkey*skey) : unit = {
Hmem.init(ks);
m = FMap.empty;
}
}.
(* We pass on as much information as possible to make later proofs easier. *)
local equiv Hmem_init:
Hmem.init ~ Hmem.init: ={ks} /\ valid_keys ks{2} ==>
={glob Hmem} /\
valid_keys (Hmem.pk,Hmem.sk){2} /\
rsap_dom Hmem.pk{2} Hmem.xstar{2} /\
rsap_dom Hmem.pk{2} Hmem.ystar{2} /\
Hmem.ystar{2} = rsap Hmem.pk{2} Hmem.xstar{2} /\
Hmem.xstar{2} = rsas Hmem.sk{2} Hmem.ystar{2}.
proof.
proc; auto.
move=> &1 &2 /=; elim/tuple2_ind (ks{2})=> ks2 pk sk ks2_def [ks1_def vkeys] /=; subst.
by progress; expect 5 smt.
qed.
lemma divrM (r1 r2 r3:real): 0%r <= r1 => 0%r < r3 <= r2 => r1 / r2 <= r1 / r3.
proof.
move=> lt0r1 [lt0r3 ler3r2].
rewrite !(div_def r1) ~3:smt !(Real.Comm.Comm r1); apply mulrMle=> //.
cut ->: 1%r / r2 = inv r2 by smt.
cut ->: 1%r / r3 = inv r3 by smt.
by apply inv_le; first smt.
qed.
local lemma Hmem_init_bad:
phoare [Hmem.init: support keypairs ks ==>
!invertible Hmem.pk Hmem.xstar] <= (1%r/(2^(k /% 2 - 2))%r).
proof.
proc; wp; rnd (!invertible Hmem.pk); wp; skip=> //= &hr.
elim/tuple2_ind (ks{hr})=> ks pk sk ks_def //=; progress=> //.
cut vpk: valid_pkey pk by exists sk.
rewrite /challenge -Dinter_uni.dinter_is_dinter.
rewrite Dinter_uni.mu_def_nz; first by apply (Int.Trans _ (2^(k-1) - 1)); smt.
rewrite (card_noninvertible pk sk) //.
rewrite card_interval_max (_: max (p_n pk - 1 - 0 + 1) 0 = p_n pk); first smt.
cut p_q_2k: s_p sk + s_q sk - 1 <= 2^(k /% 2 + 1) by smt.
cut k2_pq: 2^(k - 1) <= p_n pk by smt.
apply (Trans _ ((2^(k /% 2 + 1))%r/(2^(k - 1))%r)).
apply (Trans _ ((s_p sk + s_q sk - 1)%r/(2 ^ (k - 1))%r )).
apply divrM; last smt.
cut: 0 < s_p sk + s_q sk - 1; last smt.
cut: 0 < s_p sk /\ 0 < s_q sk; last by smt.
cut H0: forall (x y:int), 0 <= x => 0 <= y => 0 < x * y => 0 < x /\ 0 < y by smt.
apply H0.
by cut: 2^(k /%2 - 1) <= s_p sk; smt.
by cut: 2^(k /%2 - 1) <= s_q sk; smt.
smt.
cut H0: forall (x y z:real), 0%r < z => x <= y => x / z <= y / z by smt.
by apply H0; smt.
rewrite Real.PowerInt.pow_div_den; first smt.
cut ->: k - 1 - (k /% 2 + 1) = k - k /% 2 - 2 by smt.
cut leq: k /% 2 - 2 <= k - k /% 2 - 2 by smt.
apply (_: forall (x y:real), 0%r < x <= y => 1%r / y <= 1%r / x); first smt.
split; first smt.
by rewrite FromInt.from_intMle; smt.
qed.
local equiv Hmap_init:
Hmap.init ~ Hmap.init: ={ks} /\ valid_keys ks{2} ==>
={glob Hmap} /\
valid_keys (Hmem.pk,Hmem.sk){2} /\
rsap_dom Hmem.pk{2} Hmem.xstar{2} /\
rsap_dom Hmem.pk{2} Hmem.ystar{2} /\
Hmem.ystar{2} = rsap Hmem.pk{2} Hmem.xstar{2} /\
Hmem.xstar{2} = rsas Hmem.sk{2} Hmem.ystar{2}.
proof. by proc; wp; call Hmem_init. qed.
(** Partial equalities between simple and extended maps *)
(* extending a single output map to triple output *)
pred hmap_preq (m0:('a,'b) map) (m1:('a,'b * 'c * 'd) map) =
forall x, m0.[x] = omap pi3_1 m1.[x].
lemma hmap_preq_dom m0 (m1:('a,'b * 'c * 'd) map) x:
hmap_preq m0 m1 =>
mem x (dom m0) <=> mem x (dom m1).
proof.
rewrite /hmap_preq=> m0_m1.
cut: !mem x (dom m0) <=> !mem x (dom m1); last smt.
by rewrite !mem_dom /= m0_m1 none_omap.
qed.
lemma hmap_preq_get m0 (m1:('a,'b * 'c * 'd) map) x:
hmap_preq m0 m1 =>
m0.[x] = omap pi3_1 m1.[x].
proof. by move=> hmap_eq; cut:= hmap_eq x. qed.
(* extending a single output map to pair output *)
pred gmap_preq (m0:('a,'b) map) (m1:('a,'b * 'c) map) = forall x,
m0.[x] = omap fst m1.[x].
lemma gmap_preq_dom m0 (m1:('a,'b * 'c) map) x:
gmap_preq m0 m1 =>
mem x (dom m0) <=> mem x (dom m1).
proof.
rewrite /gmap_preq=> m0_m1.
cut: !mem x (dom m0) <=> !mem x (dom m1); last smt.
by rewrite !mem_dom /= m0_m1 none_omap.
qed.
lemma gmap_preq_get m0 (m1:('a,'b * 'c) map) x:
gmap_preq m0 m1 =>
m0.[x] = omap fst m1.[x].
proof. by move=> gmap_eq; cut:= gmap_eq x. qed.
lemma gmap_preq_set m0 (m1:('a,'b * 'c) map) x y z:
gmap_preq m0 m1 =>
gmap_preq m0.[x <- y] m1.[x <- (y,z)].
proof.
move=> gmap_eq x0; case (x = x0).
by move=> ->; rewrite !get_set_eq.
by move=> neq; rewrite !get_set_neq 3:gmap_eq.
qed.
(* partial equality of maps with range triples where we consider
only the first two elements *)
pred hmap_peq (m0 m1:('a,'b * 'c * 'd) map) = forall x,
(omap (fun (x:'b * 'c * 'd), (x.`1,x.`2)) m1.[x] = omap (fun (x:'b * 'c * 'd), (x.`1,x.`2)) m0.[x]).
lemma hmap_peq_dom (m0 m1:('a,'b * 'c * 'd) map) x:
hmap_peq m0 m1 =>
mem x (dom m0) <=> mem x (dom m1).
proof.
move=> hmap_peq; cut: !mem x (dom m0) <=> !mem x (dom m1); last smt.
rewrite !mem_dom /=.
cut:= hmap_peq x.
by case (m0.[x])=> //=; [apply none_omap | case (m1.[x])].
qed.
lemma hmap_peq_set (m0 m1:('a,'b * 'c * 'd) map) x b c d d':
hmap_peq m0 m1 =>
hmap_peq m0.[x <- (b,c,d)] m1.[x <- (b,c,d')].
proof.
move=> hmap_peq x0.
case (x = x0).
by move=> <-; rewrite !get_set_eq.
by move=> neq_x0_x; rewrite !get_set_neq // hmap_peq.
qed.
lemma hmap_peq_get1 (m0 m1:('a,'b * 'c * 'd) map) x:
hmap_peq m0 m1 =>
pi3_1 (oget m0.[x]) = pi3_1 (oget m1.[x]).
proof.
move=> hmap_peq; cut:= hmap_peq x.
by case (m0.[x])=> //=; case (m1.[x])=> //=; smt.
qed.
lemma hmap_peq_get2 (m0 m1:('a,'b * 'c * 'd) map) x:
hmap_peq m0 m1 =>
pi3_2 (oget m0.[x]) = pi3_2 (oget m1.[x]).
proof.
move=> hmap_peq; cut:= hmap_peq x.
by case (m0.[x])=> //=; case (m1.[x])=> //=; smt.
qed.
(** Zeroth Transition:
We rewrite PSS into an adversary against Gen with G and a trivial split oracle H0. *)
(* More or less Coron's Game0 *)
local module H0: SplitOracle = {
proc init(ks:pkey*skey): unit = {
Hmap.init(ks);
}
proc o(c:caller,m:message,r:salt):htag option = {
var w;
w = $htag;
if (!mem (m,r) (dom Hmap.m))
Hmap.m.[(m,r)] = (w,c,2^k - 1);
return omap pi3_1 Hmap.m.[(m,r)];
}
proc v(m:message,r:salt):htag = {
var w;
w = $htag;
if (mem (m,r) (dom Hmap.m))
w = pi3_1 (oget Hmap.m.[(m,r)]);
return w;
}
}.
local module Game0 = Gen(GAdv,H0,G).
local equiv PSS96_Game0_H: H.o ~ H0.o:
x{1} = (m,r){2} /\ hmap_preq H.m{1} Hmap.m{2} ==> res{2} = Some res{1} /\ hmap_preq H.m{1} Hmap.m{2}.
proof.
proc; inline H0.o.
case ((mem x (dom Ht.RO.m)){1}).
rcondf{1} 2; first by progress; rnd.
rcondf{2} 2; first by progress; rnd; skip; progress; rewrite -(hmap_preq_dom Ht.RO.m{m}).
conseq* (_: _ ==> hmap_preq H.m{1} Hmap.m{2}).
progress.
rewrite H; cut: mem (m,r){2} (dom Hmap.m{2}) by rewrite -(hmap_preq_dom Ht.RO.m{1}).
by rewrite mem_dom; case (Hmap.m.[(m,r)]{2}).
by rnd.
rcondt{1} 2; first by progress ;rnd.
rcondt{2} 2; first by progress; rnd; skip; progress; rewrite -(hmap_preq_dom Ht.RO.m{m}).
conseq* (_: _ ==> mem (m,r){2} (dom Hmap.m){2} /\ hmap_preq H.m{1} Hmap.m{2}).
progress.
by rewrite H2; cut:= H1; rewrite mem_dom; case (m_R.[(m,r)]{2}).
wp; rnd; skip; progress.
by rewrite dom_set mem_add; right.
rewrite /hmap_preq=> x; case ((m,r){2} = x)=> [<- | neq_mr_x].
by rewrite !get_set_eq.
by rewrite !get_set_neq 3:H.
qed.
local equiv PSS96_Game0_Hv: H.o ~ H0.v:
x{1} = (m,r){2} /\ hmap_preq H.m{1} Hmap.m{2} ==> ={res}.
proof. by proc; inline H0.o; wp; rnd; skip; progress; smt. qed.
local equiv PSS96_Game0_equiv:
UF_CMA(Wrap(PSS96(H,G)),Bounded_PSS_Adv(A,H,G)).main ~ Game0.main: ={glob A} ==> ={res}.
proof.
proc.
inline Gen(GAdv,H0,Gt.Lazy.RO).GA.main Wrap(PSS96(Ht.RO,Gt.Lazy.RO)).fresh
Wrap(PSS96(Ht.RO,Gt.Lazy.RO)).verify PSS96(Ht.RO,Gt.Lazy.RO).verify
UF_CMA(Wrap(PSS96(Ht.RO,Gt.Lazy.RO)),Bounded_PSS_Adv(A,Ht.RO,Gt.Lazy.RO)).A.forge.
seq 8 6: (={glob G, m, s} /\
hmap_preq H.m{1} Hmap.m{2} /\
Wrap.qs{1} = Mem.qs{2} /\
Wrap.pk{1} = Mem.pk{2} /\
Wrap.sk{1} = Mem.sk{2} /\
Bounded_PSS_Adv.cH{1} = Mem.cH{2} /\
Bounded_PSS_Adv.cG{1} = Mem.cG{2} /\
Bounded_PSS_Adv.cS{1} = Mem.cS{2}).
wp; call (_: ={glob G} /\
hmap_preq H.m{1} Hmap.m{2} /\
Wrap.qs{1} = Mem.qs{2} /\
Wrap.pk{1} = Mem.pk{2} /\
Wrap.sk{1} = Mem.sk{2} /\
Bounded_PSS_Adv.cH{1} = Mem.cH{2} /\
Bounded_PSS_Adv.cG{1} = Mem.cG{2} /\
Bounded_PSS_Adv.cS{1} = Mem.cS{2}).
(* sign *)
proc; inline Wrap(PSS96(Ht.RO,Gt.Lazy.RO)).sign
PSS96(Ht.RO,Gt.Lazy.RO).sign.
sp; if=> //.
inline GAdv(H0,Gt.Lazy.RO).Hs.o.
swap{1} 4 -3.
seq 5 4: (={glob G, m} /\
m0{1} = m{2} /\
r0{1} = r{2} /\
hmap_preq H.m{1} Hmap.m{2} /\
Wrap.qs{1} = Mem.qs{2} /\
Wrap.pk{1} = Mem.pk{2} /\
Wrap.sk{1} = Mem.sk{2} /\
sk{1} = Wrap.sk{1} /\
wo{2} = Some w{1} /\
Bounded_PSS_Adv.cH{1} = Mem.cH{2} /\
Bounded_PSS_Adv.cG{1} = Mem.cG{2} /\
Bounded_PSS_Adv.cS{1} = Mem.cS{2}).
by wp; call PSS96_Game0_H; wp; rnd; wp.
rcondf{2} 1=> //.
by wp; call (_: ={glob G}); auto; smt.
(* G *)
by proc; sp; if=> //; wp; call (_: ={glob G}); auto; smt.
(* H *)
proc*.
inline Bounded_PSS_Adv(A,Ht.RO,Gt.Lazy.RO,Wrap(PSS96(Ht.RO,Gt.Lazy.RO))).Hc.o
GAdv(H0,Gt.Lazy.RO).Ha.o.
sp; if=> //; last by wp.
by wp; call PSS96_Game0_H; skip; progress; rewrite -pairS.
(* Initialization code *)
inline Wrap(PSS96(Ht.RO,Gt.Lazy.RO)).init PSS96(Ht.RO,Gt.Lazy.RO).keygen PSS96(Ht.RO,Gt.Lazy.RO).init
Mem.init Ht.RO.init H0.init Hmap.init Hmem.init.
swap{1} 4 -3.
swap{1} 3 -1.
wp; rnd{2} True; wp.
call (Gt.Lazy.RO_init_eq).
rnd.
skip; progress.
by apply challengeL; rewrite /valid_pkey; exists pk2skL.`2; smt.
smt.
sp; if{1}; first last.
by inline *; auto; progress; smt.
wp; call PSS96_Game0_Hv=> //=.
wp; call (_: ={glob G}); first by sim.
wp; skip; progress.
by rewrite xorwC.
by rewrite (eq_sym result_R0) xorwC eq_iff; smt.
qed.
local lemma PSS96_Game0 &m:
Pr[UF_CMA(Wrap(PSS96(H,G)),Bounded_PSS_Adv(A,H,G)).main() @ &m: res] = Pr[Game0.main() @ &m: res]
by byequiv PSS96_Game0_equiv.
(*** Game1: - Eliminate collisions on calls to H and G
made by the signing oracles.
- Anticipate calls to G on H's output and
returns both results to signing oracles.
- Split G between adversary queries and
queries made by signing oracles. *)
(** We first define new global state and wrappers *)
local module Gmap = {
var m:(htag,gtag * caller) map
proc init(): unit = {
m = FMap.empty;
}
}.
module type SplitOracle1 = {
proc init(ks:pkey * skey):unit { }
proc o(c:caller,m:message,r:salt):(htag * gtag) option
proc v(m:message,r:salt):htag
}.
module type Gadv1 (H:SplitOracle1, G:Gt.Types.ARO) = {
proc main (ks:pkey * skey): bool {H.o G.o H.v}
}.
local module Gen1 (Gadv:Gadv1, H:SplitOracle1, G:Gt.Types.Oracle) = {
module GA = Gadv(H,G)
proc main () : bool = {
var keys, b;
keys = $keypairs;
G.init();
H.init(keys);
b = GA.main(keys);
return b;
}
}.
local module GAdv1(H:SplitOracle1, G:Gt.Types.ARO) = {
(* Wrapping a split oracle for direct use by the adversary *)
module Ha = {
proc o(m:message * salt): htag = {
var r = None;
if (Mem.cH < qH) {
r = H.o(Adv,fst m,snd m);
Mem.cH = Mem.cH + 1;
}
return oget (omap fst r);
}
}
(* Wrapping a split oracle for use by the signing oracle *)
module Hs = {
proc o(m:message * salt): (htag*gtag) option = {
var r;
r = H.o(Sig,fst m,snd m);
return r;
}
}
(* Counting queries to G *)
module Ga = {
proc o(w:htag): gtag = {
var r = witness;
if (Mem.cG < qG) {
r = G.o(w);
Mem.cG = Mem.cG + 1;
}
return r;
}
}
(* Signing oracle *)
module S = {
proc sign(m:message): signature = {
var r, wo, w, g, rs, em;
var s = witness;
if (Mem.cS < qS) {
r = $salt;
wo = Hs.o(m,r);
if (wo = None)
s = witness;
else {
(w,g) = oget wo;
rs = g ^ (to_gtag (from_salt r || zeros (kg - k0)));
em = to_signature (zeros 1 || (from_htag w) || (from_gtag rs));
s = i2osp (rsas Mem.sk (os2ip em));
Mem.qs = add (m,s) Mem.qs;
}
Mem.cS = Mem.cS + 1;
}
return s;
}
proc fresh(m:message, s:signature): bool = {
return !mem (m,s) Mem.qs;
}
}
module A = A(Ha, Ga, S)
proc main (ks:pkey*skey): bool = {
var m, s;
var y, w, st, g, rs, r, w';
Mem.init(ks);
(m,s) = A.forge(Mem.pk);
y = from_signature (i2osp (rsap Mem.pk (os2ip s)));
w = to_htag (sub y 1 kh);
st = to_gtag (sub y (1 + kh) kg);
g = G.o(w);
rs = from_gtag (st ^ g);
r = to_salt (sub rs 0 k0);
w' = H.v(m,r);
return sub y 0 1 = zeros 1 /\ w = w' /\ sub rs k0 (kg - k0) = zeros (kg - k0) /\
0 <= os2ip s < p_n Mem.pk /\ !mem (m,s) Mem.qs;
}
}.
local lemma GAdv1Ha_o_ll (H <: SplitOracle1 {GAdv}) (G <: Gt.Types.ARO {GAdv}):
islossless H.o => islossless GAdv1(H,G).Ha.o.
proof. by move=> H_o_ll; proc; sp; if=> //; wp; call H_o_ll. qed.
local lemma GAdv1Hs_o_ll (H <: SplitOracle1 {GAdv}) (G <: Gt.Types.ARO {GAdv}):
islossless H.o => islossless GAdv1(H,G).Hs.o.
proof. by intros=> H_o_ll; proc; call H_o_ll. qed.
local lemma GAdv1Ga_o_ll (H <: SplitOracle1 {GAdv}) (G <: Gt.Types.ARO {GAdv}):
islossless G.o => islossless GAdv1(H,G).Ga.o.
proof. by move=> G_o_ll; proc; sp; if=> //; wp; call G_o_ll. qed.
local lemma GAdv1_main_ll (H <: SplitOracle1 {GAdv}) (G <: Gt.Types.ARO {GAdv}):
islossless H.o => islossless G.o => islossless H.v => islossless GAdv1(H, G).main.
proof.
intros=> H_o_ll G_o_ll H_v_ll; proc.
call H_v_ll.
wp; call G_o_ll.
wp; call (A_forge_ll (<:GAdv1(H,G).Ha) (<:GAdv1(H,G).Ga) (<:GAdv1(H,G).S) _ _ _).
by apply (GAdv1Ha_o_ll H G _)=> //.
by apply (GAdv1Ga_o_ll H G _)=> //.
proc; sp; if=> //; seq 2: true=> //.
by call (GAdv1Hs_o_ll H G _)=> //; rnd; skip; smt.
if; first by wp.
by wp.
by call (_: true)=> //; wp.
qed.
(** We can now define Game1 *)
local module G1 = {
proc init = Gmap.init
proc o(w:htag): gtag = {
var g;
g = $gtag;
if (!mem w (dom Gmap.m)) Gmap.m.[w] = (g,Adv);
return fst (oget Gmap.m.[w]);
}
}.
local module H1: SplitOracle1 = {
proc init = H0.init
proc o(c:caller,m:message,r:salt): (htag *gtag) option = {
var w, st, ho;
w = $htag;
st = $gtag;
st = st ^ (GTag.from_bits (from_salt r || zeros (kg - k0)));
if (!mem (m,r) (dom Hmap.m)) {
if (!mem w (dom Gmap.m)) {
Hmap.m.[(m,r)] = (w,c,2^k - 1);
Gmap.m.[w] = (st,c);
}
ho = Some(w,st);
} else {
w = oget (omap pi3_1 Hmap.m.[(m,r)]);
ho = if c = Adv then Some(w,witness) else None;
}
return ho;
}
proc v = H0.v
}.
local module Game1 = Gen1(GAdv1,H1,G1).
(** Game0.1: Anticipate calls to G in H *)
local module H0'1:SplitOracle = {
proc init = H0.init
proc o(c:caller,m:message,r:salt): htag option = {
var w, st;
w = $htag;
if (!mem (m,r) (dom Hmap.m)) {
Hmap.m.[(m,r)] = (w,c,2^k - 1);
st = G.o(w);
}
return omap pi3_1 Hmap.m.[(m,r)];
}
proc v = H0.v
}.
local module Game0'1 = Gen(GAdv,H0'1,G).
(* We refactor PSS into a distinguisher for random oracles *)
(* - Game0_D0 refactors *
* - D0_D0e applies the generic eager transformation (lazy to eager) *
* - D0e_D1e adds the call to G in H (in the eager world) *
* - D1e_D1 applies the generic eager transformation (eager to lazy) *
* - D1_Game1'1 defactors *)
(* Game0_1'1 combines all 5 steps into a single abstract equiv *)
module type PSplitOracle (G:Gt.Types.ARO) = {
proc init(ks:pkey * skey): unit
proc o(c:caller,m:message,r:salt): htag option {G.o}
proc v(m:message,r:salt): htag {}
}.
local module H0e (G:Gt.Types.ARO): PSplitOracle(G) = {
proc init(ks:pkey*skey): unit = {
Hmap.init(ks);
}
proc o(c:caller,m:message,r:salt):htag option = {
var w;
w = $htag;
if (!mem (m,r) (dom Hmap.m))
Hmap.m.[(m,r)] = (w,c,2^k - 1);
return omap pi3_1 Hmap.m.[(m,r)];
}
proc v(m:message,r:salt):htag = {
var w;
w = $htag;
if (mem (m,r) (dom Hmap.m))
w = pi3_1 (oget Hmap.m.[(m,r)]);
return w;
}
}.
local module H0'1e (G:Gt.Types.ARO): PSplitOracle(G) = {
proc init = H0.init
proc o(c:caller,m:message,r:salt): htag option = {
var w, st;
w = $htag;
if (!mem (m,r) (dom Hmap.m)) {
Hmap.m.[(m,r)] = (w,c,2^k - 1);
st = G.o(w);
}
return omap pi3_1 Hmap.m.[(m,r)];
}
proc v = H0.v
}.
local module D (Ga:Gadv, H:PSplitOracle, G:Gt.Types.ARO) = {
module H = H(G)
module Ga = Ga(H,G)
proc distinguish(): bool = {
var b:bool;
var ks:pkey * skey;
ks = $keypairs;
H.init(ks);
b = Ga.main(ks);
return b;
}
}.
local equiv Game0_D0 (Ga <: Gadv {G,Hmap}):
Gen(Ga,H0,G).main ~ Gt.Eager.Types.IND(G,D(Ga,H0e)).main: ={glob Ga} ==> ={res}.
proof.
proc.
inline Gt.Eager.Types.IND(Gt.Lazy.RO,D(Ga,H0e)).D.distinguish; swap{1} 1 1.
by sim.
qed.
local equiv D0_D0e (Ga <: Gadv {G,Ge,Hmap}):
Gt.Eager.Types.IND(G,D(Ga,H0e)).main ~ Gt.Eager.Types.IND(Ge,D(Ga,H0e)).main: ={glob D(Ga,H0e)} ==> ={res}.
proof. by apply (Gt.eagerRO (D(Ga,H0e)) _ _); [apply htag_finite | apply gtagL]. qed.
local equiv D0e_D1e (Ga <: Gadv {Ge,Hmap}):
Gt.Eager.Types.IND(Ge,D(Ga,H0e)).main ~ Gt.Eager.Types.IND(Ge,D(Ga,H0'1e)).main: ={glob D(Ga,H0e)} ==> ={res}.
proof.
proc; call (_: ={glob Gt.Eager.RO, glob D(Ga,H0e)} /\ forall x, mem x (dom Ge.m{1})).
call (_: ={glob Gt.Eager.RO, glob Hmap} /\ forall x, mem x (dom Ge.m{1})).
(* H *)
proc; case ((mem (m,r) (dom Hmap.m)){2}).
rcondf{1} 2; first by progress; rnd.
rcondf{2} 2; first by progress; rnd.
by rnd; skip; smt.
rcondt{1} 2; first by progress; rnd.
rcondt{2} 2; first by progress; rnd.
by inline Gt.Eager.RO.o; wp; rnd; skip; smt.
(* G *)
by proc; auto.
(* Hv *)
by conseq* (_: ={glob Hmap, m, r} ==> ={glob Hmap, res})=> //; sim.
call (_: ={ks} ==> ={glob Hmap}); first by sim.
by rnd; skip; smt.
call (_: true ==> ={glob Ge} /\ forall x, mem x (dom Ge.m){1}).
by conseq* (Gt.Eager.RO_init_eq _) (Gt.Eager.RO_init_full _); last 2 apply htag_finite.
by skip; smt.
qed.
local equiv D0'1e_D0'1 (Ga <: Gadv {G,Ge}):
Gt.Eager.Types.IND(Ge,D(Ga,H0'1e)).main ~ Gt.Eager.Types.IND(G,D(Ga,H0'1e)).main: ={glob D(Ga,H0'1e)} ==> ={res}.
proof.
symmetry.
conseq* (_: ={glob D(Ga,H0'1e)} ==> ={res}).
done.
smt. (* should be trivial but substitution sucks *)
apply (Gt.eagerRO (D(Ga,H0'1e)) _ _).
by apply htag_finite.
by apply gtagL.
qed.
local equiv D0'1_Game0'1 (Ga <: Gadv {G,Hmap}):
Gt.Eager.Types.IND(G,D(Ga,H0'1e)).main ~ Gen(Ga,H0'1,G).main: ={glob Ga} ==> ={res}.
proof.
proc.
inline Gt.Eager.Types.IND(Gt.Lazy.RO,D(Ga,H0'1e)).D.distinguish; swap{1} 1 1.
by sim.
qed.
local equiv Game0_0'1_abstract (Ga <: Gadv {G,Ge,Hmap}):
Gen(Ga,H0,G).main ~ Gen(Ga,H0'1,G).main: ={glob Ga} ==> ={res}.
proof.
bypr (res{1}) (res{2})=> // &1 &2 a eq_Ga.
apply (eq_trans _ Pr[Gt.Eager.Types.IND(G,D(Ga,H0e)).main() @ &1: a = res]);
first by byequiv (Game0_D0 Ga).
apply (eq_trans _ Pr[Gt.Eager.Types.IND(Ge,D(Ga,H0e)).main() @ &1: a = res]);
first by byequiv (D0_D0e Ga).
apply (eq_trans _ Pr[Gt.Eager.Types.IND(Ge,D(Ga,H0'1e)).main() @ &1: a = res]);
first by byequiv (D0e_D1e Ga).
apply (eq_trans _ Pr[Gt.Eager.Types.IND(G,D(Ga,H0'1e)).main() @ &1: a = res]);
first by byequiv (D0'1e_D0'1 Ga).
by byequiv (D0'1_Game0'1 Ga).
qed.
local lemma Game0_0'1 &m:
Pr[Game0.main() @ &m: res] = Pr[Game0'1.main() @ &m: res].
proof.
byequiv (_: ={glob GAdv} ==> ={res})=> //.
by apply (Game0_0'1_abstract GAdv).
qed.
(** H returns both w and st, unless called directly by the adversary *)
local module HG0'1:SplitOracle1 = {
proc init = H0.init
proc o(c:caller,m:message,r:salt): (htag *gtag) option = {
var w, st;
w = $htag;
if (!mem (m,r) (dom Hmap.m)) {
Hmap.m.[(m,r)] = (w,c,2^k - 1);
st = G.o(w);
} else {
w = oget (omap pi3_1 Hmap.m.[(m,r)]);
if (c = Adv) st = witness;
else st = G.o(w);
}
return Some (w,st);
}
proc v = H0.v
}.
local module GameG0'1 = Gen1(GAdv1,HG0'1,G).
local lemma Game0'1_G0'1 &m: Pr[Game0'1.main() @ &m: res] = Pr[GameG0'1.main() @ &m: res].
proof.
byequiv (_: ={glob A} ==> ={res})=> //; proc.
seq 3 3: (={glob A, glob Hmap, glob G, keys} /\
Hmap.m{1} = FMap.empty /\
G.m{1} = FMap.empty);
first by inline*; auto.
call (_: ={glob A, glob Hmap, glob G, ks} /\
Hmap.m{1} = FMap.empty /\
G.m{1} = FMap.empty ==>
={res})=> //; proc.
conseq* (_: _ ==> ={y, w, w', rs, m, s, Mem.pk, Mem.qs})=> //.
sim.
seq 1 1: (={glob A, glob Hmap, glob G, glob Mem, ks} /\
Hmap.m{1} = FMap.empty /\
G.m{1} = FMap.empty);
first by inline*; auto.
call (_: ={glob Hmap, glob G, glob Mem} /\
(forall x,
mem x (dom Hmap.m{2}) =>
mem (pi3_1 (oget Hmap.m.[x])){2} (dom G.m{2}))).
(* sign *)
proc; sp; if=> //.
inline *.
seq 1 1: (={glob Hmap, glob G, glob Mem, m, r, s} /\
(forall x,
mem x (dom Hmap.m{2}) =>
mem (pi3_1 (oget Hmap.m.[x])){2} (dom G.m{2})));
first by rnd.
rcondf{1} 9.
progress; case (mem (m,r) (dom Hmap.m)).
by rcondf 6; auto; progress; smt.
by rcondt 6; auto; progress; smt.
rcondf{2} 9.
progress; case (mem (m,r) (dom Hmap.m)).
rcondf 6; first by auto.
by rcondf 7; auto.
by rcondt 6; auto.
case ((mem (m,r) (dom Hmap.m)){2}).
rcondf{1} 6; first by auto.
rcondf{2} 6; first by auto.
rcondf{2} 7; first by auto.
by auto; progress; smt.
rcondt{1} 6; first by auto.
rcondt{2} 6; first by auto.
rcondf{1} 16.
auto; progress.
by rewrite -pairS get_set_eq oget_omap_some // mem_dom /oget /pi3_1 /= get_set_eq.
by rewrite -pairS get_set_eq oget_omap_some.
wp; rnd{1} True; auto; progress.
smt.
by rewrite -pairS get_set_eq /oget /pi3_1 /= get_set_eq /oget /oget.
by rewrite -pairS get_set_eq /oget /pi3_1 /= get_set_eq /oget /oget.
move: H8; rewrite -pairS=> H10; case ((m,r){2} = x1).
by move=> <-; rewrite get_set_eq /oget /pi3_1 /= mem_dom get_set_eq.
move=> neq_mr_x1; rewrite get_set_neq // mem_dom get_set_neq.
by cut:= H10; rewrite mem_dom get_set_neq //; smt.
rewrite -mem_dom; apply H.
by cut:= H10; rewrite mem_dom get_set_neq //; smt.
smt.
by rewrite -pairS !get_set_eq /oget /pi3_1.
by rewrite -pairS !get_set_eq /oget /pi3_1.
move: H8; rewrite -pairS=> H10; case ((m,r){2} = x1).
by move=> <-; rewrite get_set_eq /oget /pi3_1.
move=> neq_mr_x1; rewrite get_set_neq //.
apply H.
by cut:= H10; rewrite mem_dom get_set_neq //; smt.
(* Ga *)
by proc; inline *; sp; if=> //; auto; progress; smt.
(* Ha *)
proc; sp; if=> //.
inline H0'1.o HG0'1.o; case ((mem m (dom Hmap.m)){1}).
rcondf{1} 5; first by auto; smt.
rcondf{2} 5; first by auto; smt.
by rcondt{2} 6; auto.
rcondt{1} 5; first by auto; smt.
rcondt{2} 5; first by auto; smt.
inline G.o; auto; progress.
by rewrite -pairS get_set_eq.
move: H7; rewrite -pairS=> H9; case (m{2} = x0).
by move=> <-; rewrite get_set_eq /oget /pi3_1 /= mem_dom get_set_eq.
move=> neq_m_x0; rewrite get_set_neq // mem_dom get_set_neq.
by cut:= H9; rewrite mem_dom get_set_neq //; smt.
rewrite -mem_dom; apply H.
by cut:= H9; rewrite mem_dom get_set_neq //; smt.
by rewrite -pairS get_set_eq.
move: H7; rewrite -pairS=> H7; case (m{2} = x0).
by move=> <-; rewrite get_set_eq.
move=> neq_m_x0; rewrite get_set_neq //; apply H.
by cut:= H7; rewrite mem_dom get_set_neq //; smt.
by skip; smt.
qed.
(** Game0'2: - Abort if H calls G on a non-fresh input,
with Pr[Game1'2: H1'2.badg] <= (qS + qF + qH) * (qS + qF + qH + qG)%r * 2^-kh
- Abort if one of the signing oracles queries H
on some non-fresh input despite freshly sampling r,
with Pr[Game1'2: H1'2.bad] <= (qS + qF) * (qS + qF + qH) * 2^-k0 *)
local module H0'2: SplitOracle1 = {
var badg, badh:bool
proc init(ks:pkey * skey): unit = {
Hmap.init(ks);
badg = false;
badh = false;
}
proc o(c:caller,m:message,r:salt): (htag *gtag) option = {
var w, st, ho;
w = $htag;
st = $gtag;
st = st ^ (GTag.from_bits (from_salt r || zeros (kg - k0)));
badg = badg \/ mem w (dom Gmap.m);
badh = badh \/ (mem (m,r) (dom Hmap.m) /\ !c = Adv);
if (!mem (m,r) (dom Hmap.m)) {
if (!mem w (dom Gmap.m)) {
Hmap.m.[(m,r)] = (w,c,2^k - 1);
Gmap.m.[w] = (st,c);
}
ho = Some(w,st);
} else {
w = oget (omap pi3_1 Hmap.m.[(m,r)]);
ho = if c = Adv then Some(w,witness) else None;
}
return ho;
}
proc v = H0.v
}.
local module Game0'2 = Gen1(GAdv1,H0'2,G1).
(* proving near-equivalence of ROM programming *)
local equiv GameG0'1_0'2_H: HG0'1.o ~ H0'2.o:
!H0'2.badg{2} /\ !H0'2.badh{2} /\
={glob Hmap, c, m, r} /\ G.m{1} = FMap.map fst Gmap.m{2} ==>
!(H0'2.badg{2} \/ H0'2.badh{2}) =>
( ={glob Hmap, res} /\ G.m{1} = FMap.map fst Gmap.m{2}).
proof.
proc.
swap{2} [4..5] -2.
seq 1 3 : (={glob Hmap, c, m, r, w} /\ G.m{1} = FMap.map fst Gmap.m{2} /\
(H0'2.badg = mem w (dom Gmap.m)){2} /\
(H0'2.badh = (mem (m,r) (dom Hmap.m) /\ ! c = Adv)){2});
first by auto; smt.
case (H0'2.badg{2} \/ H0'2.badh{2}).
conseq * (_ : _ ==> true)=> //.
by wp; rnd{2}; inline G.o; if{1}; wp; [ | sp; if{1}]; auto; progress; expect 3 smt.
case ((mem (m,r) (dom Hmap.m)){1}).
rcondf{1} 1=> //; rcondf{2} 3; first by auto; smt.
sp; elim * => wL; if{1}; first by auto; smt.
by inline G.o; auto; smt.
rcondt{1} 1=> //; rcondt{2} 3; first by auto; smt.
inline *; swap{1} 3 -2.
case (mem w{2} (dom Gmap.m{2})); first by conseq * (_: _ ==> true); auto; smt.
rcondt{2} 3; first by auto; smt.
rcondt{1} 4.
auto;progress.
move: H0; rewrite !mem_dom get_map; smt.
wp; rnd ((^) (GTag.from_bits (from_salt r || zeros (kg - k0)))){2}; auto; progress; try algebra.
by apply gtagU; apply gtagF.
by apply gtagF.
by rewrite get_set_eq /oget /=; congr; split=> //; algebra.
by rewrite map_set /fst /=; congr; algebra.
qed.
local lemma GameG0'1_0'2_abstract (Ga <: Gadv1 {G, HG0'1, H0'2}):
(forall (H <: SplitOracle1 {Ga}) (G <: Gt.Types.ARO {Ga}),
islossless H.o => islossless G.o => islossless H.v => islossless Ga(H,G).main) =>
equiv [Gen1(Ga,HG0'1,G).main ~ Gen1(Ga,H0'2,G1).main:
={glob Ga} ==> !(H0'2.badg \/ H0'2.badh){2}=> ={res}].
proof.
intros=> Ga_main_ll; proc.
call (_: (H0'2.badg \/ H0'2.badh), ={glob Hmap} /\ G.m{1} = FMap.map fst Gmap.m{2}).
(* H *)
by conseq* GameG0'1_0'2_H; smt.
progress; proc; seq 1: true=> //;
first by auto; smt.
if=> //.
by call (Gt.Lazy.RO_o_ll _); [apply gtagL | auto].
sp; if; first by auto.
by call (Gt.Lazy.RO_o_ll _); first apply gtagL.
by progress; proc; auto; smt.
(* G *)
conseq * (_ : _ ==> ={res} /\ G.m{1} = map fst Gmap.m{2})=> //.
proc.
seq 1 1 : (x{1} = w{2} /\ y{1} = g{2} /\ G.m{1} = map fst Gmap.m{2});first by auto.
if.
by progress [-split]; rewrite !mem_dom get_map; smt.
by auto; smt.
by auto; smt.
by move=> _ _; conseq* (Gt.Lazy.RO_o_ll _); apply gtagL.
by move=> _ /=; proc; auto; smt.
(* Hv *)
by proc; auto.
by progress; proc; auto; smt.
by progress; proc; auto; smt.
by inline *; auto; smt.
qed.
(* end proof *)
(* computing the probability of badg *)
local module H0'2'badg: SplitOracle1 = {
var cHs:int
proc init(ks:pkey * skey): unit = {
Hmap.init(ks);
H0'2.badg = false;
cHs = 0;
}
proc o(c:caller,m:message,r:salt): (htag *gtag) option = {
var w, st, ho;
if (cHs < qH + qS /\ card (dom Gmap.m) < qH + qG + qS) {
w = $htag;
st = $gtag;
st = st ^ (GTag.from_bits (from_salt r || zeros (kg - k0)));
H0'2.badg = H0'2.badg \/ mem w (dom Gmap.m);
if (!mem (m,r) (dom Hmap.m)) {
if (!mem w (dom Gmap.m)) {
Hmap.m.[(m,r)] = (w,c,2^k - 1);
Gmap.m.[w] = (st,c);
}
ho = Some(w,st);
} else {
w = oget (omap pi3_1 Hmap.m.[(m,r)]);
ho = if c = Adv then Some(w,witness) else None;
}
cHs = cHs + 1;
}
return ho;
}
proc v = H0.v
}.
local module Game0'2'badg = Gen1(GAdv1, H0'2'badg, G1).
local equiv H0'2_H0'2'badg : H0'2.o ~ H0'2'badg.o :
={c, m, r, glob Hmap, glob Gmap, glob Mem, H0'2.badg} /\
H0'2'badg.cHs{2} = Mem.cH{1} + Mem.cS{1} /\
Mem.cG{1} <= qG /\ Mem.cH{1} + Mem.cS{1} < qH + qS /\
card (dom Gmap.m{1}) <= Mem.cG{1} + Mem.cH{1} + Mem.cS{1} ==>
={res, glob Hmap, glob Gmap, glob Mem, H0'2.badg} /\
H0'2'badg.cHs{2} = Mem.cH{1} + Mem.cS{1} + 1 /\
card (dom Gmap.m{1}) <= Mem.cG{1} + Mem.cH{1} + Mem.cS{1} + 1.
proof.
proc; rcondt{2} 1; first by auto; smt.
seq 5 4: (={w, st, c, m, r, glob Hmap, glob Gmap, glob Mem, H0'2.badg} /\
H0'2'badg.cHs{2} = Mem.cH{1} + Mem.cS{1} /\
Mem.cG{1} <= qG /\ Mem.cH{1} + Mem.cS{1} < qH + qS /\
card (dom Gmap.m{1}) <= Mem.cG{1} + Mem.cH{1} + Mem.cS{1});
first by auto.
by if=> //; auto; progress; expect 3 smt.
qed.
local lemma Game0'2_0'2'badg &m:
Pr[Game0'2.main() @ &m: H0'2.badg] = Pr[Game0'2'badg.main() @ &m: H0'2.badg /\ H0'2'badg.cHs <= qS + qH].
proof.
byequiv (_: ={glob GAdv} ==> ={H0'2.badg} /\ H0'2'badg.cHs{2} <= qS + qH)=> //.
proc.
inline Gen1(GAdv1, H0'2, G1).GA.main Gen1(GAdv1, H0'2'badg, G1).GA.main; auto.
call (_: true ==> true); first by proc; auto.
wp; call (_: true ==> true); first by proc; auto.
wp.
call (_: ={glob Hmap, glob Gmap, glob Mem, H0'2.badg} /\
H0'2'badg.cHs{2} = Mem.cH{1} + Mem.cS{1} /\
card (dom Gmap.m){1} <= Mem.cG{1} + Mem.cH{1} + Mem.cS{1} /\
Mem.cG{1} <= qG /\
Mem.cH{1} <= qH /\
Mem.cS{1} <= qS).
(* sign *)
proc; sp=> //; if=> //.
seq 2 2: (={glob Hmap, glob Gmap, glob Mem, H0'2.badg, wo, s, r, m} /\
H0'2'badg.cHs{2} = Mem.cH{1} + Mem.cS{1} + 1 /\
card (dom Gmap.m){1} <= Mem.cG{1} + Mem.cH{1} + Mem.cS{1} + 1 /\
Mem.cG{1} <= qG /\
Mem.cH{1} <= qH /\
Mem.cS{1} < qS);
last by if; auto; smt.
inline GAdv1(H0'2, G1).Hs.o GAdv1(H0'2'badg, G1).Hs.o; wp.
by call H0'2_H0'2'badg; auto; smt.
(* Ga *)
by proc; inline *; sp; if=> //; auto; smt.
(* Ha *)
by proc; sp; if=> //; wp; call H0'2_H0'2'badg; auto; smt.
by inline *; auto; smt.
qed.
local lemma Game0'2'badg &m:
Pr[Game0'2'badg.main() @ &m: H0'2.badg /\ H0'2'badg.cHs <= qS + qH]
<= (qS + qH)%r * (qS + qH + qG - 1)%r / (2^kh)%r.
proof.
fel 3 (H0'2'badg.cHs)
(fun x, (qS + qH + qG - 1)%r / (2^kh)%r)
(qS + qH)
(H0'2.badg)
[H0'2'badg.o: (H0'2'badg.cHs < qS + qH /\ card (dom Gmap.m) < qH + qG + qS)] => //.
(*-*) rewrite /int_sum (Monoid.Mrplus.NatMul.sum_const ((qS + qH + qG - 1)%r /(2^kh)%r)) //.
rewrite /intval card_interval_max (_: qS + qH - 1 - 0 + 1 = qS + qH); first smt.
rewrite /max (_: qS + qH < 0 = false) /=; first smt.
by rewrite /Monoid.Mrplus.NatMul.( * ); smt.
(*-*) by inline *; auto.
(*-*) proc.
cut bnd_pos: 0%r <= (qS + qH + qG - 1)%r / (2^kh)%r.
cut leq: 0%r <= (qS + qH + qG - 1)%r by smt.
cut lt: 0%r < (2^kh)%r by smt.
cut H: forall (x y z:real), x <= y => 0%r < z => x / z <= y / z by smt.
cut ->: 0%r = 0%r / (2^kh)%r by smt.
smt.
if; last by hoare.
wp.
seq 1: (mem w (dom Gmap.m)) ((qS + qH + qG - 1)%r / (2 ^ kh)%r) 1%r _ 0%r (!H0'2.badg) => //;
first by auto.
rnd; skip; progress.
rewrite -/(cpMem _) (mu_cpMem _ _ (1%r / (2^kh)%r)); first smt.
cut ->: forall x, x * (1%r / (2^kh)%r) = x / (2^kh)%r by smt.
by apply (_: forall (x y z:real), x <= y => 0%r < z => x / z <= y / z); smt.
(*-*) by hoare; conseq * (_ : _ ==> true) => //; smt.
(*-*) by progress; proc; rcondt 1; auto; smt.
(*-*) by progress; proc; rcondf 1; auto; smt.
qed.
(* end computation *)
(* computing the probability Pr[Game0'2.main() @ &m: H0'2.badh] *)
local module Game0'2'badh = {
var cHs:int
module H = {
proc init = H0'2.init
proc o(c:caller,m:message,r:salt): (htag *gtag) option = {
var w, st, ho;
H0'2.badh = H0'2.badh \/ (mem (m,r) (dom Hmap.m) /\ !c = Adv);
w = $htag;
st = $gtag;
st = st ^ (GTag.from_bits (from_salt r || zeros (kg - k0)));
if (!mem (m,r) (dom Hmap.m)) {
if (!mem w (dom Gmap.m)) {
Hmap.m.[(m,r)] = (w,c,2^k - 1);
Gmap.m.[w] = (st,c);
}
ho = Some(w,st);
} else {
w = oget (omap pi3_1 Hmap.m.[(m,r)]);
ho = if c = Adv then Some(w,witness) else None;
}
return ho;
}
proc v = H0.v
}
module Hss = {
proc o (c:caller,m:message): (htag * gtag * salt) option = {
var r;
var wo = None;
var ho = None;
if (cHs < qS /\ card (dom Hmap.m) < qH + qS) {
r = $salt;
wo = H.o(c,m,r);
ho = omap (fun x, let (a,b) = x in (a,b,r)) wo;
cHs = cHs + 1;
}
return ho;
}
}
module Ha = {
proc o (mr:message * salt): htag = {
var m, r, st;
var w = witness;
if (Mem.cH < qH) {
(m,r) = mr;
w = $htag;
st = $gtag;
if (!mem (m,r) (dom Hmap.m)) {
if (!mem w (dom Gmap.m)) {
Hmap.m.[(m,r)] = (w,Adv,2^k - 1);
Gmap.m.[w] = (st ^ (GTag.from_bits (from_salt r || zeros (kg - k0))), Adv);
}
} else
w = oget (omap pi3_1 Hmap.m.[(m,r)]);
Mem.cH = Mem.cH + 1;
}
return w;
}
}
module Ga = {
proc o (w:htag): gtag = {
var g = witness;
if (Mem.cG < qG) {
g = G1.o(w);
Mem.cG = Mem.cG + 1;
}
return g;
}
}
module S = {
proc sign(m:message): signature = {
var r, wo, w, g, rs, em;
var s = witness;
if (Mem.cS < qS) {
wo = Hss.o(Sig,m);
if (wo = None)
s = witness;
else {
(w,g,r) = oget wo;
rs = g ^ (to_gtag (from_salt r || zeros (kg - k0)));
em = to_signature (zeros 1 || (from_htag w) || (from_gtag rs));
s = i2osp (rsas Mem.sk (os2ip em));
Mem.qs = add (m,s) Mem.qs;
}
Mem.cS = Mem.cS + 1;
}
return s;
}
proc fresh(m:message, s:signature): bool = {
return !mem (m,s) Mem.qs;
}
}
module A = A(Ha,Ga,S)
proc main(): unit = {
var ks, m, s;
ks = $keypairs;
G1.init();
H0'2.init(ks);
Mem.init(ks);
cHs = 0;
(m, s) = A.forge(Mem.pk);
}
}.
local lemma Game0'2_0'2'badh &m:
Pr[Game0'2.main() @ &m: H0'2.badh] = Pr[Game0'2'badh.main() @ &m: H0'2.badh /\ Game0'2'badh.cHs <= qS].
proof.
byequiv (_: ={glob GAdv} ==> ={H0'2.badh} /\ Game0'2'badh.cHs{2} <= qS)=> //.
proc; inline Gen1(GAdv1,H0'2,G1).GA.main.
wp; call{1} (_: true ==> true); first by proc; auto; smt.
wp; call{1} (_: true ==> true); first by proc; auto; smt.
wp.
call (_: ={glob Hmap, glob Gmap, glob Mem, H0'2.badh} /\
Game0'2'badh.cHs{2} = Mem.cS{1} /\
card (dom Hmap.m){1} <= Mem.cH{1} + Mem.cS{1} /\
Mem.cG{1} <= qG /\
Mem.cH{1} <= qH /\
Mem.cS{1} <= qS).
(* sign *)
proc; sp=> //; if=> //.
seq 2 1: (={glob Hmap, glob Gmap, glob Mem, H0'2.badh, s, m} /\
wo{2} = omap (fun x, let (a,b) = x in (a,b,r{1})) wo{1} /\
Game0'2'badh.cHs{2} = Mem.cS{1} + 1 /\
card (dom Hmap.m){1} <= Mem.cH{1} + Mem.cS{1} + 1 /\
Mem.cG{1} <= qG /\
Mem.cH{1} <= qH /\
Mem.cS{1} < qS).
inline GAdv1(H0'2,G1).Hs.o Game0'2'badh.Hss.o; wp.
rcondt{2} 5; first by auto; smt.
wp; call (_: ={glob Hmap, glob Gmap, glob Mem, H0'2.badh, c, m, r} /\
c{2} = Sig /\
Game0'2'badh.cHs{2} = Mem.cS{1} /\
card (dom Hmap.m){1} <= Mem.cH{1} + Mem.cS{1} /\
Mem.cG{1} <= qG /\
Mem.cH{1} <= qH /\
Mem.cS{1} < qS ==>
={glob Hmap, glob Gmap, Mem.cG, H0'2.badh, res} /\
Game0'2'badh.cHs{2} = Mem.cS{1} /\
card (dom Hmap.m){1} <= Mem.cH{1} + Game0'2'badh.cHs{2} + 1 /\
Mem.cS{1} < qS).
by proc; auto; progress; smt.
by auto; progress; smt.
by if; auto; progress; smt.
(* Ga *)
proc; sp; if=> //.
wp; call (_: ={glob Gmap, glob Mem, w} ==>
={glob Gmap, glob Mem, res}).
by proc; auto; smt.
by skip; smt.
(* Ha *)
by proc; inline *; sp; if=> //; auto; progress; expect 22 smt.
by inline *; auto; smt.
qed.
local lemma Game0'2'badh &m:
Pr[Game0'2'badh.main() @ &m: H0'2.badh /\ Game0'2'badh.cHs <= qS]
<= qS%r * (qS + qH)%r / (2^k0)%r.
proof.
fel 5 (Game0'2'badh.cHs)
(fun x, (qS + qH)%r / (2^k0)%r)
qS
(H0'2.badh)
[Game0'2'badh.Hss.o: (Game0'2'badh.cHs < qS /\ card (dom Hmap.m) < qH + qS)].
(*-*) rewrite /int_sum (Monoid.Mrplus.NatMul.sum_const ((qS + qH)%r / (2^k0)%r)) //.
rewrite /intval card_interval_max (_: qS - 1 - 0 + 1 = qS); first smt.
rewrite /max (_: qS < 0 = false) /=; first smt.
by rewrite /Monoid.Mrplus.NatMul.( * ); smt.
(*-*) smt.
(*-*) inline Mem.init.
by wp; call (_: true ==> !H0'2.badh);
first proc; wp; call (_: true).
(*-*) proc.
cut H: forall (x y z:real), x <= y => 0%r < z => x / z <= y / z by smt.
cut bnd1_pos: 0%r <= 1%r / (2^k0)%r.
cut leq: 0%r <= 1%r by smt.
cut lt: 0%r < (2^k0)%r by smt.
cut ->: 0%r = 0%r / (2^k0)%r by smt.
smt.
cut bnd_pos: 0%r <= (qS + qH)%r / (2^k0)%r.
cut leq: 0%r <= (qS + qH)%r by smt.
cut lt: 0%r < (2^k0)%r by smt.
cut ->: 0%r = 0%r / (2^k0)%r by smt.
smt.
sp; if.
seq 1: (mem (m,r) (dom Hmap.m) /\ c <> Adv) ((qH + qS)%r/(2^k0)%r) (1%r) _ 0%r (!H0'2.badh)=> //.
by rnd.
rnd (fun r, mem (m,r) (dom Hmap.m)); skip; progress.
cut <-: mu ((Dunit.dunit m{hr}) * salt) (cpMem (dom Hmap.m{hr}))
= mu salt (fun r, mem (m{hr},r) (dom Hmap.m{hr})).
cut ->: mu ((Dunit.dunit m{hr}) * salt) (cpMem (dom Hmap.m{hr}))
= mu ((Dunit.dunit m{hr}) * salt) (fun x, (fun a, a = m{hr}) (fst x) /\
(fun b, mem (m{hr},b) (dom Hmap.m{hr})) (snd x)).
rewrite mu_support /support /Pred.(/\) /cpMem; apply mu_eq=> x //=.
by rewrite Dprod.supp_def Dunit.supp_def; smt.
by rewrite Dprod.mu_def Dunit.mu_def /charfun /=.
apply (Trans _ ((card (dom Hmap.m{hr}))%r * (1%r / (2^k0)%r))).
apply mu_cpMem_le.
move=> x mem_x; rewrite /mu_x.
cut ->: mu ((Dunit.dunit m{hr}) * salt) ((=) x)
= mu ((Dunit.dunit m{hr}) * salt) (fun y, (fun a, a = fst x) (fst y) /\
(fun b, b = snd x) (snd y))
by (apply mu_eq; smt).
rewrite Dprod.mu_def Dunit.mu_def /charfun /=.
case (m{hr} = fst x).
pose r:= snd x; cut ->: (fun x, x = r) = ((=) r) by (apply fun_ext; smt).
by rewrite -/(mu_x _ _); smt.
smt.
cut ->: forall x, x * (1%r / (2^k0)%r) = x / (2^k0)%r by smt.
by apply H; smt.
hoare; inline Game0'2'badh.H.o; auto; smt.
smt.
by hoare.
(*-*) progress; proc; rcondt 3; first by auto.
seq 3: (Game0'2'badh.cHs < qS /\
card (dom Hmap.m) < qH + qS /\
c = Game0'2'badh.cHs);
first by rnd; wp.
inline Game0'2'badh.H.o; auto; smt.
(*-*) by progress; proc; rcondf 3; auto.
qed.
(* Game 0.1 -> Game 0.2 *)
local lemma Game0'1_0'2 &m:
Pr[Game0'1.main() @ &m: res]
<= Pr[Game0'2.main() @ &m: res]
+ (qS + qH)%r * (qS + qH + qG - 1)%r / (2^kh)%r
+ qS%r * (qS + qH)%r / (2^k0)%r.
proof.
rewrite (Game0'1_G0'1 &m).
cut: Pr[GameG0'1.main() @ &m: res] <= Pr[Game0'2.main() @ &m: res \/ H0'2.badg \/ H0'2.badh].
byequiv (_: ={glob GAdv1} ==> !(H0'2.badg \/ H0'2.badh){2} => ={res}); last 2 smt.
by (apply (GameG0'1_0'2_abstract GAdv1); apply GAdv1_main_ll).
rewrite Pr [mu_or] Pr [mu_or] (Game0'2_0'2'badg &m) (Game0'2_0'2'badh &m).
by cut:= Game0'2'badg &m; cut:= Game0'2'badh &m; smt.
qed.
(* end computation *)
(**** Current status *)
local lemma PSS96_Game1 &m:
Pr[UF_CMA(Wrap(PSS96(H,G)),Bounded_PSS_Adv(A,H,G)).main() @ &m: res]
<= Pr[Game1.main() @ &m: res]
+ (qS + qH)%r * (qS + qH + qG - 1)%r / (2^kh)%r
+ qS%r * (qS + qH)%r / (2^k0)%r.
proof.
rewrite (PSS96_Game0 &m) (Game0_0'1 &m).
cut <-: Pr[Game0'2.main() @ &m: res] = Pr[Game1.main() @ &m: res].
by byequiv (_: ={glob A} ==> ={res})=> //; sim.
by apply (Game0'1_0'2 &m).
qed.
(*** Game2: Sample the signature then fix the random oracles
1) to make verification succeed when called by signature oracle,
2) to let the adversary break one-way otherwise. *)
local module H2: SplitOracle1 = {
proc init = H0.init
proc o(c:caller,m:message,r:salt): (htag * gtag) option = {
var w, st, u, z, ho;
u = 0;
z = 0;
ho = None;
if (invertible Hmem.pk Hmem.xstar \/ c <> Adv) {
u = $sigd Hmem.pk Hmem.sk (c = Adv) Hmem.xstar;
z = if c = Adv
then (Hmem.ystar ** (rsap Hmem.pk u)) Hmem.pk
else rsap Hmem.pk u;
w = to_htag (sub (from_signature (i2osp z)) 1 kh);
st = to_gtag (sub (from_signature (i2osp z)) (1 + kh) kg);
st = st ^ (to_gtag (from_salt r || zeros (kg - k0)));
if (!mem (m,r) (dom Hmap.m)) {
if (!mem w (dom Gmap.m)) {
Hmap.m.[(m,r)] = (w,c,u);
Gmap.m.[w] = (st,c);
}
ho = Some (w,st);
} else {
w = oget (omap pi3_1 Hmap.m.[(m,r)]);
ho = if c = Adv then Some(w,witness) else None;
}
}
return ho;
}
proc v = H0.v
}.
local module Game2 = Gen1(GAdv1,H2,G1).
(* We push the sampling operations out of H
so we can reason about them abstractly.
At the same time, we let Ha do work only when
the (global) one-way challenge is invertible. *)
module type H1Sample = {
proc sample(c:caller): htag * gtag
}.
local module GenH1(S:H1Sample): SplitOracle1 = {
proc init = H0.init
proc o(c:caller,m:message,r:salt): (htag * gtag) option = {
var w, st, ho;
ho = None;
if (invertible Hmem.pk Hmem.xstar \/ c <> Adv) {
(w,st) = S.sample(c);
st = st ^ (to_gtag (from_salt r || zeros (kg - k0)));
if (!mem (m,r) (dom Hmap.m)) {
if (!mem w (dom Gmap.m)) {
Hmap.m.[(m,r)] = (w,c,2^k - 1);
Gmap.m.[w] = (st,c);
}
ho = Some (w,st);
} else {
w = oget (omap pi3_1 Hmap.m.[(m,r)]);
ho = if c = Adv then Some(w,witness) else None;
}
}
return ho;
}
proc v = H0.v
}.
local module SampleWST = {
proc sample(c:caller): htag * gtag = {
var w, st;
w = $htag;
st = $gtag;
return (w,st);
}
}.
local module Game1'1 = Gen1(GAdv1,GenH1(SampleWST),G1).
(* proof of refactoring (upto sampling a non-invertible one-way challenge) *)
local equiv Game1_1'1_H: H1.o ~ GenH1(SampleWST).o:
={glob Hmap, glob Gmap, c, m, r} /\
(c{2} = Adv => invertible Hmem.pk{2} Hmem.xstar{2}) ==>
={glob Hmap, glob Gmap, res}.
proof.
proc.
rcondt{2} 2; first by auto; smt.
by inline SampleWST.sample; auto.
qed.
local lemma Game1_1'1_abstract (Ga <: Gadv1 {Gmap,Hmap}):
(forall (H <: SplitOracle1 {Ga}) (G <: Gt.Types.ARO {Ga}),
islossless H.o => islossless G.o => islossless H.v => islossless Ga(H,G).main) =>
equiv [Gen1(Ga,H1,G1).main ~ Gen1(Ga,GenH1(SampleWST),G1).main:
={glob Ga} ==>
={res} \/ !invertible Hmem.pk{2} Hmem.xstar{2}].
proof.
move=> GAdv_main_ll; proc.
seq 3 3: (={glob Ga, glob Gmap, glob Hmap, keys});
first by sim.
case (!invertible Hmem.pk{2} Hmem.xstar{2}).
call{1} (GAdv_main_ll H1 G1 _ _ _).
by proc; auto; smt.
by proc; auto; smt.
by proc; auto; smt.
call{2} (GAdv_main_ll (GenH1(SampleWST)) G1 _ _ _).
by proc; inline SampleWST.sample; sp; if=> //; auto; smt.
by proc; auto; smt.
by proc; auto; smt.
by skip; smt.
call (_: ={glob Gmap, glob Hmap} /\ invertible Hmem.pk{2} Hmem.xstar{2}).
by conseq* Game1_1'1_H.
by conseq* (_: _ ==> ={glob Gmap, res}); last sim.
by conseq* (_: _ ==> ={res}); last sim.
by inline *; auto.
qed.
(* computation of probability of sampling a non-invertible element *)
local lemma Game1'1_bad &m:
Pr[Game1'1.main() @ &m: !invertible Hmem.pk Hmem.xstar] <= 1%r/(2^(k /% 2 - 2))%r.
proof.
byphoare (_: true ==> !invertible Hmem.pk Hmem.xstar)=> //.
proc.
seq 3: (!invertible Hmem.pk Hmem.xstar) (1%r/(2^(k /% 2 - 2))%r) 1%r _ 0%r=> //.
inline GenH1(SampleWST).init Hmap.init G1.init Gmap.init.
wp; call Hmem_init_bad.
by auto.
by hoare; call (_: true).
qed.
(* end of computation *)
(* end of refactoring proof *)
local module SampleU = {
proc sample(c:caller): htag * gtag = {
var u, z, w, st;
u = $sigd Hmem.pk Hmem.sk (c = Adv) Hmem.xstar;
z = simul Hmem.pk Hmem.sk (c = Adv) Hmem.xstar u;
w = to_htag (sub (from_signature (i2osp z)) 1 kh);
st = to_gtag (sub (from_signature (i2osp z)) (1 + kh) kg);
return (w,st);
}
}.
local module Game1'2 = Gen1(GAdv1,GenH1(SampleU),G1).
(* proof of distribution equality *)
local equiv Game1'1_1'2_sample: SampleWST.sample ~ SampleU.sample:
={c} /\
(c{2} = Adv => invertible Hmem.pk{2} Hmem.xstar{2}) /\
Hmem.ystar{2} = rsap Hmem.pk{2} Hmem.xstar{2} /\
rsap_dom Hmem.pk{2} Hmem.xstar{2} /\
valid_keys (Hmem.pk,Hmem.sk){2} ==>
={res}.
proof.
bypr (res{1}) (res{2})=> //.
progress.
cut ->: Pr[SampleWST.sample(c{1}) @ &1: a = res] = 1%r/(2^(kg + kh))%r.
byphoare (_: true ==> a = res)=> //; proc.
seq 1: (w = a.`1) (1%r/(2^kh)%r) (1%r/(2^kg)%r) _ 0%r=> //.
rnd; skip; progress.
cut ->: (fun x, x = a.`1) = ((=) a.`1) by apply fun_ext; smt.
smt.
by rnd ((=) a.`2); skip; progress; smt.
by hoare; rnd; skip; smt.
progress; cut ->: (2^kh)%r * (2^kg)%r = (2^kh * 2^kg)%r by smt.
by rewrite pow_add; smt.
rewrite Logic.eq_sym.
byphoare (_: (c = Adv => invertible Hmem.pk Hmem.xstar) /\
Hmem.ystar = rsap Hmem.pk Hmem.xstar /\
rsap_dom Hmem.pk Hmem.xstar /\
valid_keys (Hmem.pk,Hmem.sk) ==>
a = res)=> //.
proc; elim/tuple2_ind a=> a w0 st0 a_def.
wp 2.
conseq* (_: _ ==>
z = os2ip (to_signature (zeros 1 || from_htag w0 || from_gtag st0)))
(_: _ ==> 0 <= z < 2^(k-1)) => //;first by (auto;smt).
progress; rewrite /from_htag /to_htag /from_gtag /to_gtag /from_signature
/to_signature; last 2 by smt.
cut [_ <-] := msb0_bnd (i2osp z0);first by smt.
rewrite /from_signature HTag.pcan_to_from 1:smt.
rewrite GTag.pcan_to_from 1:smt.
rewrite sub_app_sub //;first 3 smt.
rewrite app_sub;first 3 smt.
by rewrite Signature.can_from_to; smt.
wp; rnd; skip; progress.
rewrite /sigd mu_def /=.
rewrite mu_support /Pred.(/\) /=.
pose x0:= os2ip (to_signature (zeros 1 || from_htag w0 || from_gtag st0)).
cut x0_bnd: 0 <= x0 < 2^(k - 1) by smt.
cut ->: (fun x, simul Hmem.pk{hr} Hmem.sk{hr} (c{hr} = Adv) Hmem.xstar{hr}
(simul_inv Hmem.pk{hr} Hmem.sk{hr} (c{hr} = Adv) Hmem.xstar{hr} x)
= x0 /\ support [0..2^(k - 1) - 1] x)
= ((=) x0).
apply fun_ext=> x /=; rewrite eq_iff; split.
by rewrite andC=> [supp_x]; rewrite simul_invK //; first smt.
by move=> <-; rewrite simul_invK //; smt.
by rewrite -/(mu_x _ _) Dinter.mu_x_def_in; smt.
qed.
local equiv Game1'1_1'2_H: GenH1(SampleWST).o ~ GenH1(SampleU).o:
={glob Hmap, glob Gmap, c, m, r} /\
Hmem.ystar{2} = rsap Hmem.pk{2} Hmem.xstar{2} /\
rsap_dom Hmem.pk{2} Hmem.xstar{2} /\
valid_keys (Hmem.pk,Hmem.sk){2} ==>
={glob Hmap, glob Gmap, res}.
proof. by proc; sp; if=> //; wp; call Game1'1_1'2_sample; auto; progress; elim H1=> //=; smt. qed.
local equiv Game1'1_1'2_abstract (Ga <: Gadv1 {Gmap,Hmap}):
Gen1(Ga,GenH1(SampleWST),G1).main ~ Gen1(Ga,GenH1(SampleU),G1).main:
={glob Ga} ==> ={res}.
proof.
proc.
call (_: ={glob Hmap, glob Gmap} /\
Hmem.ystar{2} = rsap Hmem.pk{2} Hmem.xstar{2} /\
rsap_dom Hmem.pk{2} Hmem.xstar{2} /\
valid_keys (Hmem.pk,Hmem.sk){2}).
by conseq* Game1'1_1'2_H.
by proc; auto.
by proc; auto.
by inline*; auto; smt.
qed.
(* proof of defactoring and storing u in Hmap *)
local equiv Game1'2_2_H: GenH1(SampleU).o ~ H2.o:
={glob Hmem, glob Gmap, c, m, r} /\
Hmem.ystar{2} = rsap Hmem.pk{2} Hmem.xstar{2} /\
hmap_peq Hmap.m{1} Hmap.m{2} ==>
={glob Hmem, glob Gmap, res} /\ hmap_peq Hmap.m{1} Hmap.m{2}.
proof.
proc; inline SampleU.sample.
sp; if=> //.
seq 7 5: (={w, ho, u, z, st, glob Hmem, glob Gmap, c, m, r} /\
Hmem.ystar{2} = rsap Hmem.pk{2} Hmem.xstar{2} /\
hmap_peq Hmap.m{1} Hmap.m{2});
first by auto.
if => //.
progress.
by rewrite -(hmap_peq_dom Hmap.m{1}).
by rewrite (hmap_peq_dom _ Hmap.m{2}).
auto; progress.
by apply hmap_peq_set.
auto; progress.
cut indom1: mem (m,r){2} (dom Hmap.m){1} by smt.
cut:= indom1; rewrite (hmap_peq_dom _ Hmap.m{2}) //.
move: indom1.
rewrite !mem_dom /oget /pi3_1.
cut:= H (m,r){2}.
case (Hmap.m{1}.[(m,r)]{2})=> //=.
case (Hmap.m.[(m,r)]{2})=> //=.
smt.
qed.
local equiv Game1'2_2_abstract (Ga <: Gadv1 {Gmap,Hmap}):
Gen1(Ga,GenH1(SampleU),G1).main ~ Gen1(Ga,H2,G1).main:
={glob Ga} ==> ={res}.
proof.
proc.
call (_: ={glob Hmem, glob Gmap} /\
Hmem.ystar{2} = rsap Hmem.pk{2} Hmem.xstar{2} /\
hmap_peq Hmap.m{1} Hmap.m{2}).
by conseq* Game1'2_2_H.
by conseq* (_: _ ==> ={glob Gmap, res})=> //; sim.
by proc; auto; smt.
inline GenH1(SampleU).init H2.init.
call Hmap_init.
wp; call (_: true ==> ={glob Gmap}); first by sim.
by rnd; skip; smt.
qed.
(* end of proof *)
(**** Current status *)
local lemma PSS96_Game2 &m:
Pr[UF_CMA(Wrap(PSS96(H,G)),Bounded_PSS_Adv(A,H,G)).main() @ &m: res]
<= Pr[Game2.main() @ &m: res]
+ (qS + qH)%r * (qS + qH + qG - 1)%r / (2^kh)%r
+ qS%r * (qS + qH)%r / (2^k0)%r
+ 1%r / (2^(k /% 2 - 2))%r.
proof.
cut <-: Pr[Game1'2.main() @ &m: res] = Pr[Game2.main() @ &m: res].
by byequiv (Game1'2_2_abstract GAdv1).
cut <-: Pr[Game1'1.main() @ &m: res] = Pr[Game1'2.main() @ &m: res].
by byequiv (Game1'1_1'2_abstract GAdv1).
cut: Pr[Game1.main() @ &m: res] <= Pr[Game1'1.main() @ &m: res \/ !invertible Hmem.pk Hmem.xstar].
byequiv (_: ={glob GAdv} ==> ={res} \/ !invertible Hmem.pk{2} Hmem.xstar{2}); last 2 smt.
by apply (Game1_1'1_abstract GAdv1 _); apply GAdv1_main_ll.
by rewrite Pr [mu_or]; smt.
qed.
(* Game 3: cleaning the games up and preparing for the reduction *)
local module GAdv3(H:SplitOracle1, G:Gt.Types.ARO) = {
(* Wrapping a split oracle for direct use by the adversary *)
module Ha = {
proc o(m:message * salt): htag = {
var r = None;
if (Mem.cH < qH) {
r = H.o(Adv,fst m,snd m);
Mem.cH = Mem.cH + 1;
}
return oget (omap fst r);
}
}
(* Wrapping a split oracle for use by the signing oracle *)
module Hs = {
proc o(m:message * salt): (htag*gtag) option = {
var r;
r = H.o(Sig,fst m,snd m);
return r;
}
}
(* Counting queries to G *)
module Ga = {
proc o(w:htag): gtag = {
var r = witness;
if (Mem.cG < qG) {
r = G.o(w);
Mem.cG = Mem.cG + 1;
}
return r;
}
}
(* Signing oracle *)
module S = {
proc sign(m:message): signature = {
var r, wo, w, g, rs, em;
var s = witness;
if (Mem.cS < qS) {
r = $salt;
wo = Hs.o(m,r);
if (wo = None)
s = witness;
else {
(w,g) = oget wo;
rs = g ^ (to_gtag (from_salt r || zeros (kg - k0)));
em = to_signature (zeros 1 || (from_htag w) || (from_gtag rs));
s = i2osp (rsas Mem.sk (os2ip em));
Mem.qs = add (m,s) Mem.qs;
}
Mem.cS = Mem.cS + 1;
}
return s;
}
proc fresh(m:message, s:signature): bool = {
return !mem (m,s) Mem.qs;
}
}
module A = A(Ha, Ga, S)
var lucky:bool
proc main (ks:pkey*skey): bool = {
var m, s;
var y, w, st, g, rs, r, w';
Mem.init(ks);
(m,s) = A.forge(Mem.pk);
y = from_signature (i2osp (rsap Mem.pk (os2ip s)));
w = to_htag (sub y 1 kh);
st = to_gtag (sub y (1 + kh) kg);
g = G.o(w);
rs = from_gtag (st ^ g);
r = to_salt (sub rs 0 k0);
lucky = !mem (m,r) (dom Hmap.m);
w' = H.v(m,r);
return sub y 0 1 = zeros 1 /\ w = w' /\ sub rs k0 (kg - k0) = zeros (kg - k0) /\
0 <= os2ip s < p_n Mem.pk /\ !mem (m,s) Mem.qs;
}
}.
local module H3: SplitOracle1 = {
proc init = Hmap.init
proc o(c:caller,m:message,r:salt): (htag*gtag) option = {
var w, st, u, z, ho;
u = 0;
z = 0;
ho = None;
if (invertible Hmem.pk Hmem.xstar \/ c <> Adv) {
u = $sigd Hmem.pk Hmem.sk (c = Adv) Hmem.xstar;
z = if c = Adv
then (Hmem.ystar ** (rsap Hmem.pk u)) Hmem.pk
else rsap Hmem.pk u;
w = to_htag (sub (from_signature (i2osp z)) 1 kh);
st = to_gtag (sub (from_signature (i2osp z)) (1 + kh) kg);
st = st ^ (to_gtag (from_salt r || zeros (kg - k0)));
if (!mem (m,r) (dom Hmap.m)) {
if (!mem w (dom Gmap.m)) {
Hmap.m.[(m,r)] = (w,c,u);
Gmap.m.[w] = (st,c);
}
ho = Some (w,st);
}
else {
w = oget (omap pi3_1 Hmap.m.[(m,r)]);
ho = if c = Adv then Some(w,witness) else None;
}
}
return ho;
}
proc v = H0.v
}.
local module Game3 = Gen1(GAdv3,H3,G1).
local lemma Game2_3 &m:
Pr[Game2.main() @ &m: res] = Pr[Game3.main() @ &m: res].
proof.
byequiv (_: ={glob A} ==> ={res})=> //.
by proc; inline *; sim.
qed.
(* Game 3.1: abort if a u-value sampled by H during a query made by the adversary is non-invertible *)
local module H3'1: SplitOracle1 = {
var bad:bool
proc init(ks:keypair): unit = {
Hmap.init(ks);
bad = false;
}
proc o(c:caller,m:message,r:salt): (htag*gtag) option = {
var w, st, u, z, ho;
u = 0;
z = 0;
ho = None;
if (invertible Hmem.pk Hmem.xstar \/ c <> Adv) {
u = $sigd Hmem.pk Hmem.sk (c = Adv) Hmem.xstar;
z = if c = Adv
then (Hmem.ystar ** (rsap Hmem.pk u)) Hmem.pk
else rsap Hmem.pk u;
w = to_htag (sub (from_signature (i2osp z)) 1 kh);
st = to_gtag (sub (from_signature (i2osp z)) (1 + kh) kg);
st = st ^ (to_gtag (from_salt r || zeros (kg - k0)));
bad = bad \/ (c = Adv /\ !invertible Hmem.pk u);
if (c <> Adv \/ invertible Hmem.pk u) {
if (!mem (m,r) (dom Hmap.m)) {
if (!mem w (dom Gmap.m)) {
Hmap.m.[(m,r)] = (w,c,u);
Gmap.m.[w] = (st,c);
}
ho = Some (w,st);
}
else {
w = oget (omap pi3_1 Hmap.m.[(m,r)]);
ho = if c = Adv then Some(w,witness) else None;
}
}
}
return ho;
}
proc v = H0.v
}.
local module Game3'1 = Gen1(GAdv3,H3'1,G1).
local equiv Game3_3'1_H: H3.o ~ H3'1.o:
!H3'1.bad{2} /\ ={glob Hmap, glob Gmap, c, m, r} ==>
!H3'1.bad{2} => ={glob Hmap, glob Gmap, res}.
proof.
proc; sp; if=> //.
seq 5 5: (!H3'1.bad{2} /\
(c{1} = Adv => invertible Hmem.pk{1} Hmem.xstar{1}) /\
={glob Hmap, glob Gmap, c, m, r, u, z, w, st, ho} /\ ho{1} = None);
first by auto.
case ((c = Adv /\ !invertible Hmem.pk u){2}).
by rcondf{2} 2; auto; smt.
rcondt{2} 2; first by auto; smt.
sp 0 1; if; first smt.
by sim.
by wp.
qed.
local equiv Game3_3'1: Game3.main ~ Game3'1.main:
={glob A} ==> !H3'1.bad{2} => ={res}.
proof.
proc.
call (_: !H3'1.bad{2} /\ ={glob A, glob Hmap, glob Gmap, ks} /\
support keypairs ks{1} ==>
!H3'1.bad{2} => ={res}).
proc.
inline H3.v H3'1.v G1.o; auto.
call (_: H3'1.bad,
={glob Hmap, glob Gmap, glob Mem} /\
support keypairs (Mem.pk,Mem.sk){1},
={Mem.pk, Mem.sk} /\
support keypairs (Mem.pk,Mem.sk){1}).
by progress; apply (A_forge_ll H G O).
proc; sp; if=> //.
inline GAdv3(H3,G1).Hs.o GAdv3(H3'1,G1).Hs.o.
wp; call Game3_3'1_H.
by auto; smt.
by progress; proc; inline*; sp; if=> //; rcondt 9; auto; smt.
by progress; proc; inline*; sp; if=> //; rcondt 9; auto; smt.
by progress; proc; inline*; sp; if=> //; auto; smt.
by progress; proc; inline*; sp; if=> //; auto; smt.
by progress; proc; inline *; sp; if=> //; auto; smt.
by progress; proc; inline *; sp; if=> //; sp; if=> //; auto.
by progress; proc; inline *; sp; if=> //; sp; if=> //; auto; smt.
by progress; proc; inline *; sp; if=> //; sp; if=> //; auto; smt.
by inline *; auto; smt.
by inline *; auto.
qed.
(* computing the probability of H3'1.bad *)
local module Game3'1'bad = {
module Ha = {
proc o(mr:message * salt): htag = {
var m, r, w, st, u, z, h;
(m,r) = mr;
u = 0;
z = 0;
h = witness;
if (Mem.cH < qH) {
if (invertible Hmem.pk Hmem.xstar /\
support keypairs (Hmem.pk,Hmem.sk) /\
0 <= Hmem.xstar < p_n Hmem.pk) {
u = $sigd Hmem.pk Hmem.sk true Hmem.xstar;
z = simul Hmem.pk Hmem.sk true Hmem.xstar u;
w = to_htag (sub (from_signature (i2osp z)) 1 kh);
st = to_gtag (sub (from_signature (i2osp z)) (1 + kh) kg);
st = st ^ (to_gtag (from_salt r || zeros (kg - k0)));
H3'1.bad = H3'1.bad \/ !invertible Hmem.pk u;
if (invertible Hmem.pk u) {
if (!mem (m,r) (dom Hmap.m)) {
if (!mem w (dom Gmap.m)) {
Hmap.m.[(m,r)] = (w,Adv,u);
Gmap.m.[w] = (st,Adv);
}
h = w;
}
else {
w = oget (omap pi3_1 Hmap.m.[(m,r)]);
h = w;
}
}
}
Mem.cH = Mem.cH + 1;
}
return h;
}
}
module Ga = {
proc o(w:htag): gtag = {
var r = witness;
if (Mem.cG < qG) {
r = G1.o(w);
Mem.cG = Mem.cG + 1;
}
return r;
}
}
module Hs = {
proc o(m:message,r:salt): (htag * gtag) option = {
var w, st, u, z, ho;
u = 0;
z = 0;
ho = None;
u = $sigd Hmem.pk Hmem.sk false Hmem.xstar;
z = rsap Hmem.pk u;
w = to_htag (sub (from_signature (i2osp z)) 1 kh);
st = to_gtag (sub (from_signature (i2osp z)) (1 + kh) kg);
st = st ^ (to_gtag (from_salt r || zeros (kg - k0)));
if (!mem (m,r) (dom Hmap.m)) {
if (!mem w (dom Gmap.m)) {
Hmap.m.[(m,r)] = (w,Sig,u);
Gmap.m.[w] = (st,Sig);
}
ho = Some (w,st);
}
else {
w = oget (omap pi3_1 Hmap.m.[(m,r)]);
ho = None;
}
return ho;
}
}
module S = {
proc sign(m:message): signature = {
var r, wo, w, g, rs, em;
var s = witness;
if (Mem.cS < qS) {
r = $salt;
wo = Hs.o(m,r);
if (wo = None)
s = witness;
else {
(w,g) = oget wo;
rs = g ^ (to_gtag (from_salt r || zeros (kg - k0)));
em = to_signature (zeros 1 || (from_htag w) || (from_gtag rs));
s = i2osp (rsas Mem.sk (os2ip em));
Mem.qs = add (m,s) Mem.qs;
}
Mem.cS = Mem.cS + 1;
}
return s;
}
proc fresh(m:message, s:signature): bool = {
return !mem (m,s) Mem.qs;
}
}
module A = A(Ha, Ga, S)
var lucky:bool
proc main (): unit = {
var m, s, ks;
ks = $keypairs;
G1.init();
H3'1.init(ks);
Mem.init(ks);
(m,s) = A.forge(Mem.pk);
}
}.
local equiv Game3'1_3'1'bad: Game3'1.main ~ Game3'1'bad.main: ={glob A} ==>
H3'1.bad{1} = (H3'1.bad /\ Mem.cH <= qH){2}.
proof.
proc.
inline Gen1(GAdv3,H3'1,G1).GA.main.
inline H3'1.v G1.o; auto.
conseq* (_: _ ==> ={H3'1.bad} /\ Mem.cH{2} <= qH); first smt.
call (_: ={glob H3'1, glob Gmap, glob Mem} /\
Mem.cH{2} <= qH /\
support keypairs (Hmem.pk,Hmem.sk){1} /\
rsap_dom Hmem.pk{1} Hmem.xstar{1} /\
Hmem.ystar{1} = rsap Hmem.pk{1} Hmem.xstar{1}).
proc; sp; if=> //; auto.
call (_: ={glob H3'1, glob Gmap} /\ m{1} = (m,r){2} ==> ={glob H3'1, glob Gmap, res}).
proc; inline H3'1.o.
rcondt{1} 7; first by auto.
rcondt{1} 13; first by auto.
by auto.
by auto.
by proc; inline *; sp; if=> //; auto.
proc; sp; if=> //.
inline H3'1.o; sp; if=> //; first smt.
by auto; progress; expect 26 smt.
by auto; progress; smt.
by inline *; auto; progress; smt.
qed.
local lemma Pr_Game3'1'bad &m:
Pr[Game3'1'bad.main() @ &m: H3'1.bad /\ Mem.cH <= qH] <= qH%r * 1%r/(2^(k /% 2 - 1))%r.
proof.
fel 4 (Mem.cH)
(fun x, 1%r / (2^(k /% 2 - 1))%r)
(qH)
(H3'1.bad)
[Game3'1'bad.Ha.o: (Mem.cH < qH)].
(*-*) rewrite /int_sum (Monoid.Mrplus.NatMul.sum_const (1%r / (2^(k /% 2 - 1))%r)) //.
rewrite /intval card_interval_max (_: qH - 1 - 0 + 1 = qH); first smt.
rewrite /max (_: qH < 0 = false) /=; first smt.
by rewrite /Monoid.Mrplus.NatMul.( * ); smt.
(*-*) smt.
(*-*) by inline *; auto.
(*-*) cut H: forall (x y z:real), x <= y => 0%r < z => x / z <= y / z by smt.
cut bnd1_pos: 0%r <= 1%r / (2^(k /% 2 - 1))%r.
cut leq: 0%r <= 1%r by smt.
cut lt: 0%r < (2^(k /% 2 - 1))%r by smt.
cut ->: 0%r = 0%r / (2^(k /% 2 - 1))%r by smt.
smt.
proc.
sp; if=> //; last by hoare; skip; smt.
sp; if=> //; last by hoare; auto.
seq 1: (!invertible Hmem.pk u) (1%r / (2^(k /% 2 - 1))%r) 1%r _ 0%r (!H3'1.bad)=> //.
by rnd.
rnd; skip; progress.
rewrite /sigd.
pose f:= simul Hmem.pk{hr} Hmem.sk{hr} true Hmem.xstar{hr}.
pose finv:= simul_inv Hmem.pk{hr} Hmem.sk{hr} true Hmem.xstar{hr}.
cut ->: mu (dapply finv [0..2^(k - 1) -1]) (fun x, !invertible Hmem.pk{hr} x)
= mu [0..2^(k - 1) - 1] (fun x, !invertible Hmem.pk{hr} x).
rewrite Dapply.mu_def /=.
rewrite (mu_support (fun x, !invertible Hmem.pk{hr} x))
(mu_support (fun x, !invertible Hmem.pk{hr} (finv x))).
apply mu_eq=> x; rewrite /Pred.(/\) /= eq_iff; split.
by rewrite /finv /simul_inv /= -invertible_mulzp -invertible_inv H3 -invertible_rsas.
by progress; rewrite /finv /simul_inv /= -invertible_mulzp -invertible_inv H3 -invertible_rsas.
rewrite -Dinter_uni.dinter_is_dinter Dinter_uni.mu_def_nz; first smt.
cut ->: (fun x, !invertible Hmem.pk{hr} x) = !invertible Hmem.pk{hr} by apply fun_ext.
apply (Trans _ ((2^(k /% 2))%r / (2^(k - 1))%r)).
rewrite card_interval_max /max (_: 2^(k - 1) - 1 - 0 + 1 = 2^(k - 1)); first smt.
cut ->: 2^(k - 1) < 0 = false by smt.
simplify.
cut:= card_noninvertible_2k1 Hmem.pk{hr} Hmem.sk{hr} _=> // bound_den.
by apply (_: forall (x y z:real), 0%r < z => x <= y => x / z <= y / z)=> //; smt.
rewrite Real.PowerInt.pow_div_den; first smt.
apply (_: forall (x y:real), 0%r < x <= y => 1%r / y <= 1%r / x); first smt.
split; first smt.
by rewrite FromInt.from_intMle; smt.
by hoare; auto; smt.
(*-*) progress; proc; rcondt 5; first by auto.
by sp; if; auto; progress; expect 4 smt.
(*-*) progress; proc; rcondf 5; auto.
qed.
(** We refactor the game into an adversary against the sampling of u in H
then use the GenDice generic argument to replace the restricted sampling
with a (non-PTIME) loop *)
module type LoopSampling = {
proc sample(i:pkey * skey * bool * int, dflt:int): int
}.
local module GenH3(S:LoopSampling) = {
proc init = Hmap.init
proc o(c:caller,m:message,r:salt): (htag*gtag) option = {
var w, st, u, z, ho;
u = 0;
z = 0;
ho = None;
if (invertible Hmem.pk Hmem.xstar \/ c <> Adv) {
u = S.sample((Hmem.pk,Hmem.sk,c = Adv,Hmem.xstar),2^k - 1);
z = if c = Adv
then (Hmem.ystar ** (rsap Hmem.pk u)) Hmem.pk
else rsap Hmem.pk u;
w = to_htag (sub (from_signature (i2osp z)) 1 kh);
st = to_gtag (sub (from_signature (i2osp z)) (1 + kh) kg);
st = st ^ (to_gtag (from_salt r || zeros (kg - k0)));
if (c <> Adv \/ invertible Hmem.pk u) {
if (!mem (m,r) (dom Hmap.m)) {
if (!mem w (dom Gmap.m)) {
Hmap.m.[(m,r)] = (w,c,u);
Gmap.m.[w] = (st,c);
}
ho = Some (w,st);
}
else {
w = oget (omap pi3_1 Hmap.m.[(m,r)]);
ho = if c = Adv then Some(w,witness) else None;
}
}
}
return ho;
}
proc v = H0.v
}.
local module Direct = {
proc sample(i:pkey * skey * bool * int, dflt:int): int = {
var r, pk, sk, b, x;
(pk,sk,b,x) = i;
r = $sigd pk sk b x;
return r;
}
}.
local equiv Game3'1_GenH3_H: H3'1.o ~ GenH3(Direct).o:
={glob Hmap, glob Gmap, c, m, r} ==>
={glob Hmap, glob Gmap, res}.
proof.
proc; inline Direct.sample; sp; if=> //.
sim.
by rnd; wp.
qed.
local equiv Game3'1_Direct:
Gen1(GAdv3,H3'1,G1).main ~ Gen1(GAdv3,GenH3(Direct),G1).main: ={glob A} ==> ={res}.
proof.
proc.
inline Gen1(GAdv3,H3'1,G1).GA.main Gen1(GAdv3,GenH3(Direct),G1).GA.main; sim.
call (_: ={glob Hmap, glob Gmap, glob Mem}).
proc; sp; if=> //; sp.
inline GAdv3(H3'1,G1).Hs.o GAdv3(GenH3(Direct),G1).Hs.o.
auto; call Game3'1_GenH3_H.
by auto.
by sim.
proc; sp; if=> //; sp.
auto; call Game3'1_GenH3_H.
by auto.
by inline *; auto.
qed.
local module Loop = {
proc sample(i:pkey * skey * bool * int, dflt:int): int = {
var u, z, pk, sk, b, x;
u = 0;
z = dflt;
(pk,sk,b,x) = i;
while (!0 <= z < 2^(k - 1))
{
u = $challenge pk;
z = simul pk sk b x u;
}
return u;
}
}.
local lemma Direct_Loop pk' sk' b' x' d a &1 &2:
valid_keys (pk',sk') =>
rsap_dom pk' x' =>
(b' => invertible pk' x') =>
!0 <= d < 2^(k - 1) =>
Pr[Direct.sample((pk',sk',b',x'),d) @ &1: res = a]
= Pr[Loop.sample((pk',sk',b',x'),d) @ &2: res = a].
proof.
progress.
case (exists r', 0 <= r' < 2^(k - 1) /\ a = simul_inv pk' sk' b' x' r').
move=> [r'] [valid_r'] ->.
cut ->: Pr[Direct.sample((pk',sk',b',x'),d) @ &1: res = simul_inv pk' sk' b' x' r']
= 1%r/(2^(k - 1))%r.
byphoare (_: i = (pk',sk',b',x') ==> res = simul_inv pk' sk' b' x' r')=> //.
proc; inline *; wp; rnd (fun r, simul pk' sk' b' x' r = r'); wp; skip; progress.
rewrite /sigd Dapply.mu_def /= mu_support.
cut ->: ((fun r, simul pk' sk' b' x' (simul_inv pk' sk' b' x' r) = r') /\ support [0..2^(k - 1) - 1])
= ((=) r').
rewrite /Pred.(/\) /= -fun_ext=> y /=.
rewrite eq_iff; split.
by progress; rewrite simul_invK //; first smt.
progress.
by rewrite simul_invK //; first smt.
smt.
rewrite -/(mu_x _ _) Dinter.mu_x_def.
cut ->: in_supp r' [0..2^(k - 1) - 1] by smt.
smt.
by rewrite simulK //; smt.
by rewrite simul_invK //; smt.
apply Logic.eq_sym.
pose bdt:= (2^(k - 1))%r.
cut bdt_pos: 0 < 2^(k - 1) by smt.
cut bdt_posr: 0%r < (2^(k - 1))%r by smt.
byphoare (_: i = (pk',sk',b',x') /\ dflt = d ==> res = simul_inv pk' sk' b' x' r')=> //; proc.
pose bd:= mu_x (challenge pk') r'.
cut bd_pos: 0%r < bd by smt.
cut d_uni: forall x, in_supp x (challenge pk') => mu_x (challenge pk') x = bd.
by move=> y Hy; rewrite /bd; apply challengeU=> //; smt.
cut Hdiff: bdt <> (Real.zero)%Real by smt.
sp.
conseq (_: (pk,sk,b,x){hr} = (pk',sk',b',x') /\ !0 <= z < 2^(k - 1) ==>
u = simul_inv pk' sk' b' x' r': = (if 0 <= z < 2^(k - 1)
then charfun ((=) (simul_inv pk' sk' b' x' r')) u
else 1%r / bdt)) => //;
first smt.
while ((pk,sk,b,x){hr} = (pk',sk',b',x'))
(if 0 <= z < 2^(k - 1) then 0 else 1) 1 (bdt * bd) => //.
progress; cut ->: 0 <= z < 2^(k - 1) by smt.
rewrite /= /charfun; cut ->: (simul_inv pk sk b x r' = u){hr} = false by smt.
smt.
smt.
move=> Hw.
alias 3 z0 = z.
phoare split bd ((1%r - bdt*bd) * (1%r/bdt)): (r' = z0).
move=> &hr [H5 H6]; rewrite (_: 0 <= z{hr} < 2^(k - 1) = false) 1:neqF //=.
cut {1}->: bd = bd * bdt / bdt.
cut ->: bd * bdt / bdt = bd * (bdt / bdt) by smt.
cut ->: bdt / bdt = 1%r by smt.
smt.
smt.
seq 3: (r' = z0) bd 1%r _ 0%r (z0 = z /\
simul_inv pk' sk' b' x' z0 = u /\
(pk,sk,b,x) = (pk',sk',b',x'))=> //.
by auto; progress; rewrite simulK //; smt.
wp; rnd; skip; progress.
cut ->: mu (challenge pk{hr}) (fun y, r' = simul pk sk b x y){hr}
= mu (challenge pk{hr}) ((=) (simul_inv pk sk b x r')){hr}.
rewrite (mu_support (fun y, r' = simul pk sk b x y){hr})
(mu_support ((=) (simul_inv pk sk b x r'){hr})).
apply mu_eq=> y; rewrite /Pred.(/\) /= eq_iff; split.
by progress; rewrite simulK //; smt.
by progress; rewrite simul_invK //; smt.
by rewrite /bd /mu_x; apply challengeU; smt.
conseq* Hw; progress=> //.
cut ->: 0 <= z{hr} < 2^(k - 1) by smt.
by rewrite /charfun.
hoare; conseq* (_: _ ==> true) => //.
by progress;rewrite -nand;left.
seq 3: (0 <= z0 < 2^(k - 1)) _ 0%r (1%r - bdt * bd) (1%r/bdt)
((pk,sk,b,x) = (pk',sk',b',x') /\ 0 <= r' < 2^(k - 1) /\ z0 = z /\
simul_inv pk' sk' b' x' z = u)=> //.
by auto; progress; rewrite simulK //; smt.
case (r' = z0); first by conseq* (_: _ ==> false).
conseq* Hw; progress=> //.
cut ->: 0 <= z{hr} < 2^(k - 1) by smt.
rewrite /= /charfun.
cut simul_inj: (simul_inv pk sk b x r' = simul_inv pk sk b x z => r' = z){hr}.
cut {2}<-:= simul_invK pk{hr} sk{hr} b{hr} x{hr} r'=> //; first smt.
cut {2}<-:= simul_invK pk{hr} sk{hr} b{hr} x{hr} z{hr}=> //; first smt.
by move=> ->.
smt.
phoare split ! 1%r (bdt*bd); wp; rnd=> //.
by skip; smt.
skip; progress; rewrite /bdt /bd {2}/challenge Dinter.mu_x_def_in; first smt.
by rewrite mu_challenge_in_pim //; smt.
by conseq * Hw => //; smt.
by conseq * (_: _ ==> true)=> //; auto; smt.
progress; first smt.
wp; rnd; skip; progress.
cut ->: 0 <= z{hr} < 2^(k - 1) = false by smt.
rewrite /=; cut ->: (fun y, (if 0 <= simul pk sk b x y < 2^(k - 1) then 0 else 1) < 1){hr}
= (fun y, 0 <= simul pk sk b x y < 2^(k - 1)){hr}.
by apply fun_ext=> y /=; smt.
rewrite /bdt /bd {1}/challenge Dinter.mu_x_def_in; first smt.
by rewrite mu_challenge_in_pim //; smt.
cut ->: (!exists r', 0 <= r' < 2^(k - 1) /\ a = simul_inv pk' sk' b' x' r')
<=> (forall r', !0 <= r' < 2^(k - 1) \/ a <> simul_inv pk' sk' b' x' r').
by split; smt.
move=> r'_constraints.
cut ->: Pr[Direct.sample((pk',sk',b',x'),d) @ &1: res = a] = 0%r.
byphoare (_: i = (pk',sk',b',x') /\ dflt = d ==> res = a)=> //=.
hoare; proc; rnd; wp; skip=> //= &hr [i_def d_def] r; subst=> //=.
rewrite /sigd Dapply.supp_def.
smt.
apply Logic.eq_sym.
byphoare (_: i = (pk',sk',b',x') /\ dflt = d ==> res = a)=> //=.
hoare; proc.
while (valid_keys (pk,sk) /\ rsap_dom pk x /\ (b => invertible pk x) /\
(0 <= z < 2^(k - 1) => u = simul_inv pk sk b x z)).
by auto; progress; rewrite simulK //; smt.
wp; skip; progress.
smt.
cut:= H8 _; first smt.
smt.
qed.
local equiv Direct_Loop_equiv: Direct.sample ~ Loop.sample:
={i, dflt} /\ !0 <= dflt{1} < 2^(k - 1) /\
let (pk',sk',b',x') = i {1} in
valid_keys (pk',sk') /\
rsap_dom pk' x' /\
(b' => invertible pk' x') ==>
={res}.
proof.
bypr (res{1}) (res{2})=> //.
move=> &1 &2 a.
elim/tuple4_ind (i{2})=> i2 pk' sk' b' x' i2_def.
generalize (dflt{2})=> d.
move=> [[->] ->] [d_invalid] //= [vkeys [vx' invx']].
cut:= Direct_Loop pk' sk' b' x' d a &1 &2 _ _ _ _=> //.
cut ->: Pr[Direct.sample((pk',sk',b',x'),d) @ &1: res = a]
= Pr[Direct.sample((pk',sk',b',x'),d) @ &1: a = res]
by rewrite Pr [mu_eq].
by move=> ->; rewrite Pr [mu_eq].
qed.
local equiv GenH3Direct_Loop: GenH3(Direct).o ~ GenH3(Loop).o:
={glob Hmap, glob Gmap, c, m, r} /\
valid_keys (Hmem.pk,Hmem.sk){2} /\ rsap_dom Hmem.pk{2} Hmem.xstar{2} ==>
={glob Hmap, glob Gmap, res}.
proof.
proc*; inline GenH3(Direct).o GenH3(Loop).o; sp; if=> //; first smt.
auto; call Direct_Loop_equiv.
by skip; progress; expect 27 smt.
by auto.
qed.
local lemma GameH3Direct_Loop &m:
Pr[Gen1(GAdv3,GenH3(Direct),G1).main() @ &m: res]
= Pr[Gen1(GAdv3,GenH3(Loop),G1).main() @ &m: res].
proof.
byequiv (_: ={glob A} ==> ={res})=> //.
proc.
inline Gen1(GAdv3,GenH3(Direct),G1).GA.main Gen1(GAdv3,GenH3(Loop),G1).GA.main
GenH3(Direct).v GenH3(Loop).v G1.o; auto.
call (_: ={glob Hmap, glob Gmap, glob Mem} /\
valid_keys (Hmem.pk,Hmem.sk){2} /\
rsap_dom Hmem.pk{2} Hmem.xstar{2}).
proc; inline GAdv3(GenH3(Direct),G1).Hs.o GAdv3(GenH3(Loop),G1).Hs.o; sp; if=> //; auto.
by call GenH3Direct_Loop; auto.
by proc; inline *; sp; if=> //; auto.
proc; sp; if=> //; auto.
by call GenH3Direct_Loop; auto.
by inline *; auto; smt.
qed.
local module H3'2: SplitOracle1 = {
proc init = Hmap.init
proc o(c:caller,m:message,r:salt): (htag * gtag) option = {
var w, st, u, z, ho, i, b;
u = 0;
z = 2^k - 1;
i = 0;
ho = None;
w = witness;
st = witness;
b = false;
if (invertible Hmem.pk Hmem.xstar \/ c <> Adv) {
while (!b) {
u = $challenge Hmem.pk;
z = if c = Adv
then (Hmem.ystar ** (rsap Hmem.pk u)) Hmem.pk
else rsap Hmem.pk u;
w = to_htag (sub (from_signature (i2osp z)) 1 kh);
st = to_gtag (sub (from_signature (i2osp z)) (1 + kh) kg);
b = b \/ 0 <= z < 2^(k - 1);
i = i + 1;
}
st = st ^ (to_gtag (from_salt r || zeros (kg - k0)));
if (c <> Adv \/ invertible Hmem.pk u) {
if (!mem (m,r) (dom Hmap.m)) {
if (!mem w (dom Gmap.m)) {
Hmap.m.[(m,r)] = (w,c,u);
Gmap.m.[w] = (st,c);
}
ho = Some (w,st);
} else {
w = oget (omap pi3_1 Hmap.m.[(m,r)]);
ho = if c = Adv then Some(w,witness) else None;
}
}
}
return ho;
}
proc v(m:message,r:salt):htag = {
var h;
h = $htag;
if (mem (m,r) (dom Hmap.m)) h = pi3_1 (oget Hmap.m.[(m,r)]);
return h;
}
}.
local module Game3'2 = Gen1(GAdv3,H3'2,G1).
local lemma H3'2_o_ll:
phoare [H3'2.o: support keypairs (Hmem.pk,Hmem.sk) /\
rsap_dom Hmem.pk Hmem.xstar /\
Hmem.ystar = rsap Hmem.pk Hmem.xstar ==>
true] = 1%r.
proof.
proc; sp; wp.
if=> //; wp.
while (valid_keys (Hmem.pk,Hmem.sk) /\
rsap_dom Hmem.pk Hmem.xstar /\
(c = Adv => invertible Hmem.pk Hmem.xstar) /\
Hmem.ystar = rsap Hmem.pk Hmem.xstar)
(if !b then 1 else 0) 1 ((2^(k-1))%r/(p_n Hmem.pk)%r)=> //.
smt.
progress; seq 6: (valid_keys (Hmem.pk,Hmem.sk) /\
rsap_dom Hmem.pk Hmem.xstar /\
(c = Adv => invertible Hmem.pk Hmem.xstar) /\
Hmem.ystar = rsap Hmem.pk Hmem.xstar)=> //.
by auto; smt.
by hoare; auto.
by auto; smt.
split.
progress.
cut lt_div: forall (x y z:real), 0%r < z => x < y => x / z < y / z by smt.
cut pos_inv: forall x, 0%r < x => 0%r < 1%r/x.
by move=> x lt0x; cut ->: 0%r = 0%r/x; smt.
cut ->: forall x y, x / y = x * 1%r/y by smt.
cut pos_mul: forall x y, 0%r < x => 0%r < y => 0%r < x * y by smt.
by apply (pos_mul ((2^(k-1))%r) (1%r/(p_n Hmem.pk{hr})%r)); smt.
move=> z; conseq* (_: _: =((2^(k - 1))%r/(p_n Hmem.pk)%r)).
wp; rnd; skip; progress.
move: H2; rewrite -neqF=> -> //=.
cut ->: (fun x, (if !0 <= (if c{hr} = Adv
then ((rsap Hmem.pk Hmem.xstar) ** (rsap Hmem.pk x)) Hmem.pk
else rsap Hmem.pk x)
< 2^(k - 1)
then 1 else 0) < 1){hr} =
(fun x, (if (fun y, 0 <= simul Hmem.pk Hmem.sk (c = Adv) Hmem.xstar y < 2^(k - 1)){hr} x
then 0 else 1) < 1){hr}.
by rewrite /simul -fun_ext=> x //=; smt.
cut ->: forall (P:int -> bool), (fun x, (if P x then 0 else 1) < 1) = P.
by move=> P; apply fun_ext=> x /=; smt.
by apply mu_challenge_in_pim.
qed.
local equiv GenH3Loop_H3'2: GenH3(Loop).o ~ H3'2.o:
={glob Hmap, glob Gmap, c, m, r} /\
valid_keys (Hmem.pk,Hmem.sk){2} /\
Hmem.ystar{2} = (rsap Hmem.pk Hmem.xstar){2} ==>
={glob Hmap, glob Gmap, res}.
proof.
proc.
sp; if=> //.
seq 1 1: (={glob Hmap, glob Gmap, u, ho, c, m, r} /\
(c{1} = Adv => invertible Hmem.pk{2} Hmem.xstar{2}) /\
Hmem.ystar{2} = rsap Hmem.pk{2} Hmem.xstar{2} /\
valid_keys (Hmem.pk,Hmem.sk){2} /\
z{2} = (simul Hmem.pk Hmem.sk (c = Adv) Hmem.xstar u){2} /\
w{2} = to_htag (sub (from_signature (i2osp z)) 1 kh){2} /\
st{2} = to_gtag (sub (from_signature (i2osp z)) (1 + kh) kg){2}).
inline Loop.sample; wp.
while (pk{1} = Hmem.pk{2} /\ sk{1} = Hmem.sk{2} /\ b{1} = (c{2} = Adv) /\
x{1} = Hmem.xstar{2} /\ u0{1} = u{2} /\ z0{1} = z{2} /\
Hmem.ystar{2} = rsap Hmem.pk{2} Hmem.xstar{2} /\
b{2} = 0 <= z{2} < 2^(k - 1) /\
(0 <= z{2} < 2^(k - 1) =>
z{2} = (simul Hmem.pk Hmem.sk (c = Adv) Hmem.xstar u){2} /\
w{2} = to_htag (sub (from_signature (i2osp z)) 1 kh){2} /\
st{2} = to_gtag (sub (from_signature (i2osp z)) (1 + kh) kg){2})).
auto; progress; first last; last 2 smt.
by cut:= H4; rewrite /simul; smt.
by wp; skip; progress; expect 8 smt.
seq 4 1: (={glob Hmap, glob Gmap, u, ho, c, z, w, st, m, r});
first by wp.
by sim.
qed.
local equiv Game3'1Loop_3'2: Gen1(GAdv3,GenH3(Loop),G1).main ~ Game3'2.main:
={glob A} ==> ={res}.
proof.
proc; seq 3 3: (={glob A, glob Hmap, glob Gmap, keys} /\
keys{2} = (Hmem.pk,Hmem.sk){2} /\
valid_keys (Hmem.pk,Hmem.sk){2} /\
rsap_dom Hmem.pk{2} Hmem.xstar{2} /\
Hmem.ystar{2} = (rsap Hmem.pk Hmem.xstar){2}).
by inline*; auto; smt.
inline*; auto.
call (_: ={glob Hmap, glob Gmap, glob Mem} /\
support keypairs (Hmem.pk,Hmem.sk){2} /\
rsap_dom Hmem.pk{2} Hmem.xstar{2} /\
Hmem.ystar{2} = rsap Hmem.pk{2} Hmem.xstar{2} /\
Mem.pk{2} = Hmem.pk{2}).
proc; inline GAdv3(GenH3(Loop),G1).Hs.o GAdv3(H3'2,G1).Hs.o; sp; if=> //.
wp; call GenH3Loop_H3'2.
by auto.
by proc; inline*; sp; if=> //; auto.
by proc; sp; if=> //; wp; call GenH3Loop_H3'2; auto.
by auto.
qed.
(** We stop the loop early to ensure that the simulator is p.p.t. *)
local module H3'3: SplitOracle1 = {
var bad:bool
proc init(ks:keypair): unit = {
Hmap.init(ks);
bad = false;
}
proc o(c:caller,m:message,r:salt): (htag * gtag) option = {
var w, st, u, z, ho, i, b;
u = 0;
z = 2^k - 1;
i = 0;
ho = None;
w = witness;
st = witness;
b = false;
if (invertible Hmem.pk Hmem.xstar \/ c <> Adv) {
while (!b /\ i < k0 + 1) {
u = $challenge Hmem.pk;
z = if c = Adv
then (Hmem.ystar ** (rsap Hmem.pk u)) Hmem.pk
else rsap Hmem.pk u;
w = to_htag (sub (from_signature (i2osp z)) 1 kh);
st = to_gtag (sub (from_signature (i2osp z)) (1 + kh) kg);
b = b \/ 0 <= z < 2^(k - 1);
i = i + 1;
}
st = st ^ (to_gtag (from_salt r || zeros (kg - k0)));
if (0 <= z < 2^(k - 1) /\ (c <> Adv \/ invertible Hmem.pk u)) {
if (!mem (m,r) (dom Hmap.m)) {
if (!mem w (dom Gmap.m)) {
Hmap.m.[(m,r)] = (w,c,u);
Gmap.m.[w] = (st,c);
}
ho = Some (w,st);
} else {
w = oget (omap pi3_1 Hmap.m.[(m,r)]);
ho = if c = Adv then Some(w,witness) else None;
}
} else bad = true;
}
return ho;
}
proc v(m:message,r:salt):htag = {
var h;
h = $htag;
if (mem (m,r) (dom Hmap.m)) h = pi3_1 (oget Hmap.m.[(m,r)]);
return h;
}
}.
local module Game3'3 = Gen1(GAdv3,H3'3,G1).
local equiv Game3'2_3'3_H: H3'2.o ~ H3'3.o:
!H3'3.bad{2} /\ ={glob Hmap, glob Gmap, c, m, r} /\
valid_keys (Hmem.pk,Hmem.sk){1} /\
rsap_dom Hmem.pk{1} Hmem.xstar{1} /\
Hmem.ystar{1} = rsap Hmem.pk{1} Hmem.xstar{1} ==>
!H3'3.bad{2} => ={glob Hmap, glob Gmap, res}.
proof.
proc.
sp; if=> //.
splitwhile {1} 1 : (i < k0 + 1).
seq 1 1: (={glob Hmap, glob Gmap, u, z, i, ho, w, st, b, c, m, r} /\
valid_keys (Hmem.pk,Hmem.sk){1} /\
(c{1} = Adv => invertible Hmem.pk{1} Hmem.xstar{1}) /\
rsap_dom Hmem.pk{1} Hmem.xstar{1} /\
Hmem.ystar{1} = rsap Hmem.pk{1} Hmem.xstar{1} /\
b{1} = 0 <= z{1} < 2^(k - 1)).
while (={Hmem.pk, Hmem.ystar, u, z, i, w, st, b, c} /\
b{1} = 0 <= z{1} < 2^(k - 1));
first by auto; progress; smt.
by auto; smt.
case (b{1}); first by rcondf{1} 1; auto; smt.
(** Reduce to proving termination of the remainder of the loop *)
wp; conseq* (_: valid_keys (Hmem.pk,Hmem.sk){1} /\
rsap_dom Hmem.pk{1} Hmem.xstar{1} /\
(c{1} = Adv => invertible Hmem.pk{1} Hmem.xstar{1}) /\
Hmem.ystar{1} = rsap Hmem.pk{1} Hmem.xstar{1} ==> true)
(_: valid_keys (Hmem.pk,Hmem.sk){1} /\
rsap_dom Hmem.pk{1} Hmem.xstar{1} /\
(c{1} = Adv => invertible Hmem.pk{1} Hmem.xstar{1}) /\
Hmem.ystar{1} = rsap Hmem.pk{1} Hmem.xstar{1} ==>
true: =1%r)=> //; first smt.
while (valid_keys (Hmem.pk,Hmem.sk){1} /\
rsap_dom Hmem.pk{1} Hmem.xstar{1} /\
(c{1} = Adv => invertible Hmem.pk{1} Hmem.xstar{1}) /\
Hmem.ystar{1} = rsap Hmem.pk{1} Hmem.xstar{1})
(if !b{1} then 1 else 0) 1 ((2^(k-1))%r/(p_n Hmem.pk)%r)=> //.
smt.
progress; seq 6: (valid_keys (Hmem.pk,Hmem.sk){1} /\
rsap_dom Hmem.pk{1} Hmem.xstar{1} /\
(c{1} = Adv => invertible Hmem.pk{1} Hmem.xstar{1}) /\
Hmem.ystar{1} = rsap Hmem.pk{1} Hmem.xstar{1})=> //.
by auto; smt.
by hoare; auto.
by auto; smt.
split.
progress.
cut lt_div: forall (x y z:real), 0%r < z => x < y => x / z < y / z by smt.
cut pos_inv: forall x, 0%r < x => 0%r < 1%r/x.
by move=> x lt0x; cut ->: 0%r = 0%r/x; smt.
cut ->: forall x y, x / y = x * 1%r/y by smt.
cut pos_mul: forall x y, 0%r < x => 0%r < y => 0%r < x * y by smt.
apply (pos_mul ((2^(k-1))%r) (1%r/(p_n Hmem.pk{1})%r)); first smt.
smt.
move=> z; conseq* (_: _: =((2^(k - 1))%r/(p_n Hmem.pk)%r)).
wp; rnd; skip; progress.
move: H2; rewrite -neqF=> -> //=.
cut ->: (fun x, (if !0 <= (if c{1} = Adv
then ((rsap Hmem.pk Hmem.xstar) ** (rsap Hmem.pk x)) Hmem.pk
else rsap Hmem.pk x)
< 2^(k - 1)
then 1 else 0) < 1){1} =
(fun x, (if (fun y, 0 <= simul Hmem.pk Hmem.sk (c = Adv) Hmem.xstar y < 2^(k - 1)){1} x
then 0 else 1) < 1){1}.
by rewrite /simul -fun_ext=> x //=; smt.
cut ->: forall (P:int -> bool), (fun x, (if P x then 0 else 1) < 1) = P.
by move=> P; apply fun_ext=> x /=; smt.
by apply mu_challenge_in_pim.
qed.
local equiv GenH3'2_Game3'3: Game3'2.main ~ Game3'3.main:
={glob A} ==> ={res} \/ H3'3.bad{2}.
proof.
proc.
seq 3 3: (={glob A, glob Hmap, glob Gmap, keys} /\
!H3'3.bad{2} /\
support keypairs keys{1} /\
support keypairs (Hmem.pk,Hmem.sk){1} /\
keys{1} = (Hmem.pk,Hmem.sk){1} /\
rsap_dom Hmem.pk{1} Hmem.xstar{1} /\
Hmem.ystar{1} = rsap Hmem.pk{1} Hmem.xstar{1}).
by inline*; auto; smt.
inline*; auto.
call (_: H3'3.bad,
={glob Hmap, glob Gmap, glob Mem} /\
support keypairs (Hmem.pk,Hmem.sk){2} /\
rsap_dom Hmem.pk{2} Hmem.xstar{2} /\
Hmem.ystar{2} = rsap Hmem.pk{2} Hmem.xstar{2} /\
Mem.pk{2} = Hmem.pk{2},
={glob Hmem, Mem.pk} /\
support keypairs (Hmem.pk,Hmem.sk){2} /\
rsap_dom Hmem.pk{2} Hmem.xstar{2} /\
Hmem.ystar{2} = rsap Hmem.pk{2} Hmem.xstar{2} /\
Mem.pk{2} = Hmem.pk{2}).
by progress; apply (A_forge_ll H G O).
(* sign upto *)
proc; inline GAdv3(H3'2,G1).Hs.o GAdv3(H3'3,G1).Hs.o; sp; if=> //; auto.
call Game3'2_3'3_H.
by auto; smt.
(* Bad => sign{1} *)
progress; proc; inline GAdv3(H3'2,G1).Hs.o; sp; if=> //; auto.
call H3'2_o_ll.
by auto; smt.
(* Bad => sign{2} *)
progress; proc; inline*; sp; if=> //; rcondt 13; auto.
while (0 <= i <= k0 + 1 /\ valid_keys (Hmem.pk,Hmem.sk)) (k0 + 1 - i).
by progress; auto; smt.
by auto; smt.
(* Ga *)
by proc; inline *; sp; if=> //; auto.
by progress; proc; inline *; sp; if=> //; auto; smt.
by progress; proc; inline *; sp; if=> //; auto; smt.
(* Ha upto *)
proc; inline GAdv3(H3'2,G1).Ha.o GAdv3(H3'3,G1).Ha.o; sp; if=> //; auto.
call Game3'2_3'3_H.
by auto; smt.
(* Bad => Ha{1} *)
progress; proc; inline GAdv3(H3'2,G1).Ha.o; sp; if=> //; auto.
call H3'2_o_ll.
by auto; smt.
(* Bad => Ha{2} *)
progress; proc; inline*; sp; if=> //; sp; if=> //; auto.
while (0 <= i <= k0 + 1 /\ valid_keys (Hmem.pk,Hmem.sk)) (k0 + 1 - i).
by progress; auto; smt.
by auto; smt.
by auto; smt.
qed.
(* computing the probability of bad in Game3'3 (a call to H fails to sample a valid
signature in k0 + 1 attempts) *)
local module H3'3bad: SplitOracle1 = {
var cH:int
proc init(ks:keypair): unit = {
Hmap.init(ks);
H3'3.bad = false;
cH = 0;
}
proc o(c:caller,m:message,r:salt): (htag * gtag) option = {
var w, st, u, z, ho, i, b;
u = 0;
z = 2^k - 1;
i = 0;
ho = None;
w = witness;
st = witness;
b = false;
if (cH < qH + qS) {
if (Hmem.ystar = rsap Hmem.pk Hmem.xstar /\ support keypairs (Hmem.pk, Hmem.sk) /\
rsap_dom Hmem.pk Hmem.xstar /\
(invertible Hmem.pk Hmem.xstar \/ c <> Adv)) {
while (!b /\ i < k0 + 1) {
u = $challenge Hmem.pk;
z = if c = Adv
then (Hmem.ystar ** (rsap Hmem.pk u)) Hmem.pk
else rsap Hmem.pk u;
w = to_htag (sub (from_signature (i2osp z)) 1 kh);
st = to_gtag (sub (from_signature (i2osp z)) (1 + kh) kg);
b = b \/ 0 <= z < 2^(k - 1);
i = i + 1;
}
st = st ^ (to_gtag (from_salt r || zeros (kg - k0)));
if (0 <= z < 2^(k - 1) /\ (c <> Adv \/ invertible Hmem.pk u)) {
if (!mem (m,r) (dom Hmap.m)) {
if (!mem w (dom Gmap.m)) {
Hmap.m.[(m,r)] = (w,c,u);
Gmap.m.[w] = (st,c);
}
ho = Some (w,st);
} else {
w = oget (omap pi3_1 Hmap.m.[(m,r)]);
ho = if c = Adv then Some(w,witness) else None;
}
} else H3'3.bad = true;
}
cH = cH + 1;
}
return ho;
}
proc v = H3'3.v
}.
local module Game3'3bad = Gen1(GAdv3,H3'3bad,G1).
local equiv H3'3o : H3'3.o ~ H3'3bad.o :
={arg, glob Hmap, glob Gmap, H3'3.bad} /\
(Hmem.ystar = rsap Hmem.pk Hmem.xstar){2} /\ support keypairs (Hmem.pk, Hmem.sk){2} /\
(rsap_dom Hmem.pk Hmem.xstar){2} /\
(H3'3bad.cH = Mem.cH + Mem.cS){2} /\ (Mem.cH + Mem.cS){2} < qH + qS ==>
={res,glob Hmap, glob Gmap, H3'3.bad} /\ (H3'3bad.cH = Mem.cH + Mem.cS + 1){2}.
proof.
proc;inline *;sp;rcondt{2} 1; first by intros _;skip;progress.
if => //;last by auto.
wp 3 3; conseq * (_: _ ==> ={ho,glob Hmap, glob Gmap, H3'3.bad}) => //;sim.
qed.
local lemma Game3'3_3'3bad &m:
Pr[Game3'3.main() @ &m: H3'3.bad] =
Pr[Game3'3bad.main() @ &m: H3'3.bad /\ H3'3bad.cH <= qH + qS].
proof.
byequiv (_: ={glob A} ==> ={H3'3.bad} /\ H3'3bad.cH{2} <= qH + qS) => //;proc.
inline Gen1(GAdv3, H3'3, G1).GA.main Gen1(GAdv3, H3'3bad, G1).GA.main.
seq 6 6 : (={m,s,glob A, glob Hmap, glob Gmap, glob Mem, H3'3.bad} /\
(Hmem.ystar = rsap Hmem.pk Hmem.xstar){2} /\ support keypairs (Hmem.pk, Hmem.sk){2} /\
(rsap_dom Hmem.pk Hmem.xstar){2} /\ H3'3bad.cH{2} <= qH + qS);
last by sim (: ={glob Hmap, glob Gmap, glob Mem}) /(H3'3bad.cH{2} <= qH + qS) : (={H3'3.bad}).
call (_: ={glob Hmap, glob Gmap, glob Mem, H3'3.bad} /\
(Hmem.ystar = rsap Hmem.pk Hmem.xstar){2} /\ support keypairs (Hmem.pk, Hmem.sk){2} /\
(rsap_dom Hmem.pk Hmem.xstar){2} /\
(H3'3bad.cH = Mem.cH + Mem.cS){2} /\ Mem.cH{2} <= qH /\ Mem.cS{2} <= qS).
proc;sp;if => //;wp; inline GAdv3(H3'3, G1).Hs.o GAdv3(H3'3bad, G1).Hs.o;wp.
call H3'3o;wp;rnd;skip;progress;smt.
by sim (: ={glob Gmap}) / ((Hmem.ystar = rsap Hmem.pk Hmem.xstar){2} /\ support keypairs (Hmem.pk, Hmem.sk){2} /\
(rsap_dom Hmem.pk Hmem.xstar){2} /\
H3'3bad.cH{2} = Mem.cH{2} + Mem.cS{2} /\ Mem.cH{2} <= qH /\ Mem.cS{2} <= qS) :
(={res,glob Hmap, glob Gmap, glob Mem, H3'3.bad}).
by proc;sp;if => //;wp;call H3'3o;skip;progress;smt.
inline *;auto;progress. smt. smt. smt. smt. smt. smt.
qed.
(* computing the probability of bad in Game3'3 (a call to H fails to sample a valid
signature in k0 + 1 attempts) *)
local lemma Game3'3'bad &m:
Pr[Game3'3bad.main() @ &m: H3'3.bad /\ H3'3bad.cH <= qH + qS] <= (qH + qS)%r * 1%r/(2^(k0+1))%r.
proof.
fel 3 (H3'3bad.cH)
(fun x, 1%r/ (2^(k0+1))%r)
(qH + qS)
(H3'3.bad)
[H3'3bad.o: (H3'3bad.cH < qH + qS)] => //.
rewrite /int_sum (Monoid.Mrplus.NatMul.sum_const (1%r /(2^(k0 + 1))%r)) //.
rewrite /intval card_interval_max (_: qH + qS - 1 - 0 + 1 = qH + qS); first smt.
rewrite /max (_: qH + qS < 0 = false) /=; first smt.
by rewrite /Monoid.Mrplus.NatMul.( * ); smt.
by inline *;auto.
cut Hpos : (0%r <= 1%r / (2 ^ (k0+1))%r).
cut lt_div: forall (x y z:real), 0%r < z => x < y => x / z < y / z by smt.
cut pos_inv: forall x, 0%r < x => 0%r < 1%r/x.
by move=> x lt0x; cut ->: 0%r = 0%r/x; smt.
cut pos_mul: forall x y, 0%r < x => 0%r < y => 0%r < x * y by smt.
smt.
proc;sp;if;last by hoare.
if;last by hoare;auto.
wp => /=;simplify.
conseq * (_: _ ==> !b : <= (1%r/(2^(k0+1-i))%r))
(_: _ ==> (0 <= z < 2 ^ (k - 1) /\ (c <> Adv \/ invertible Hmem.pk u)) <=> b)=> //.
by while (0 <= z < 2 ^ (k - 1) => b); auto;smt.
by progress; rewrite -not_def=> /H11.
while (0 <= i <= k0 + 1 /\ (c = Adv => invertible Hmem.pk Hmem.xstar) /\
rsap_dom Hmem.pk Hmem.xstar /\
Hmem.ystar = rsap Hmem.pk Hmem.xstar /\ support keypairs (Hmem.pk, Hmem.sk)) => //.
intros Hrec;exists * i;elim * => i0.
seq 6 : (!b) (1%r/2%r) (1%r / (2 ^ (k0+1 - (i0 + 1)))%r) _ 0%r
(i = i0 + 1 /\ 0 <= i0 <= k0 /\ (c = Adv => invertible Hmem.pk Hmem.xstar) /\
rsap_dom Hmem.pk Hmem.xstar /\
Hmem.ystar = rsap Hmem.pk Hmem.xstar /\ support keypairs (Hmem.pk, Hmem.sk)) => //;first by auto;smt.
wp => /=;rnd;skip;progress.
rewrite (_:b{hr} = false)=> /=;first by smt.
cut -> : ((fun (x:int),
! 0 <= (if c{hr} = Adv then ( ** ) (rsap Hmem.pk{hr} Hmem.xstar{hr}) (rsap Hmem.pk{hr} x) Hmem.pk{hr}
else rsap Hmem.pk{hr} x) < 2 ^ (k - 1)) =
(!(fun (x:int), 0 <= simul Hmem.pk{hr} Hmem.sk{hr} (c{hr} = Adv) Hmem.xstar{hr} x < 2^(k-1)))).
by apply fun_ext => x;rewrite /Pred.([!]) /simul.
rewrite mu_not (_ : mu (challenge Hmem.pk{hr}) True = 1%r);first by smt.
cut -> := mu_challenge_in_pim Hmem.pk{hr} Hmem.sk{hr} (c{hr} = Adv) Hmem.xstar{hr} _ _ _ => //.
cut : (1%r/2%r <= (2 ^ (k - 1))%r / (p_n Hmem.pk{hr})%r); last smt.
apply (_:forall x1 x2 y1 y2, 0%r < y1 => 0%r < y2 => x1 * y2 <= x2 * y1 => x1/y1 <= x2/y2) => //=.
intros x1 x2 y1 y2 Hy1 Hy2 Hm.
apply (_: forall (x y z:real), 0%r < z => x*z <= y => x <= y / z) => //; first by smt.
by cut -> : ((x1 / y1) * y2 = (x1*y2) / y1);smt.
by smt.
rewrite {2} (_:2 = 2^1); first by rewrite (_: 1 = 0 + 1) // powS // pow0.
rewrite (_:(2 ^ (k - 1))%r * (2 ^ 1)%r = (2 ^ (k - 1) * 2 ^ 1)%r); first smt.
by rewrite pow_add //;smt.
by conseq * Hrec => //;smt.
rcondf 1 => //;first by auto;smt.
by hoare.
progress.
rewrite (_ : (2%r * (2 ^ (k0 + 1 - (i{hr} + 1)))%r) = (2 ^ (k0 + 1 - i{hr}))%r) //.
rewrite {1}(_: 2 = (2^1)); first by rewrite (_: 1 = 0 + 1) // powS // pow0.
cut ->: (2^1)%r * (2 ^ (k0 + 1 - (i{hr} + 1)))%r = (2^1 * 2^(k0 + 1 - (i{hr} + 1)))%r.
smt.
by rewrite pow_add //;smt.
by skip;smt.
by intros c;proc;sp;rcondt 1 => //;wp;conseq * (_ : _ ==> true)=> //;smt.
by intros b c;proc;sp;rcondf 1.
qed.
(* end of computation *)
local lemma Game3'2_3'3 &m:
Pr[Game3'2.main() @ &m: res]
<= Pr[Game3'3.main() @ &m: res]
+ (qH + qS)%r * 1%r/(2^(k0+1))%r.
proof.
cut: Pr[Game3'2.main() @ &m: res] <= Pr[Game3'3.main() @ &m: res \/ H3'3.bad].
by byequiv GenH3'2_Game3'3=> //; smt.
rewrite Pr [mu_or].
rewrite (Game3'3_3'3bad &m).
cut := Game3'3'bad &m; smt.
qed.
(** Game4: Minor cleanup to facilitate the reduction *)
local module H4: SplitOracle1 = {
proc init = Hmap.init
proc o(c:caller,m:message,r:salt): (htag * gtag) option = {
var w, st, u, z, ho, i;
u = 0;
z = 2^k - 1;
i = 0;
ho = None;
w = witness;
st = witness;
if (invertible Hmem.pk Hmem.xstar \/ c <> Adv) {
while (!0 <= z < 2^(k - 1) /\ i < k0 + 1) {
u = $challenge Hmem.pk;
z = if c = Adv
then (Hmem.ystar ** (rsap Hmem.pk u)) Hmem.pk
else rsap Hmem.pk u;
w = to_htag (sub (from_signature (i2osp z)) 1 kh);
st = to_gtag (sub (from_signature (i2osp z)) (1 + kh) kg);
i = i + 1;
}
st = st ^ (to_gtag (from_salt r || zeros (kg - k0)));
if (0 <= z < 2^(k - 1) /\ (c <> Adv \/ invertible Hmem.pk u)) {
if (!mem (m,r) (dom Hmap.m)) {
if (!mem w (dom Gmap.m)) {
Hmap.m.[(m,r)] = (w,c,u);
Gmap.m.[w] = (st,c);
}
ho = Some (w,st);
} else {
w = oget (omap pi3_1 Hmap.m.[(m,r)]);
ho = if c = Adv then Some(w,witness) else None;
}
}
}
return ho;
}
proc v(m:message,r:salt):htag = {
var h;
h = $htag;
if (mem (m,r) (dom Hmap.m)) h = pi3_1 (oget Hmap.m.[(m,r)]);
return h;
}
}.
local module Game4 = Gen1(GAdv3,H4,G1).
local lemma Game3'3_4 &m: Pr[Game3'3.main() @ &m: res] = Pr[Game4.main() @ &m: res].
proof.
byequiv (_: ={glob A} ==> ={res})=> //.
proc; inline *; auto.
call (_: ={glob Mem, glob Hmap, glob Gmap}).
proc; inline *; sp; if=> //.
rcondt{1} 13; first by auto.
rcondt{2} 12; first by auto.
wp; while (={glob Hmap, z, i, u, w0, st, c} /\
0 <= i{1} <= k0 + 1 /\
b{1} = (0 <= z{2} < 2^(k - 1))).
by auto; progress; expect 5 smt.
by auto; progress; expect 4 smt.
by proc; inline *; sp; if=> //; auto; smt.
proc; inline *; sp; if=> //; case (!invertible Hmem.pk{1} Hmem.xstar{1}).
rcondf{1} 11; first by auto.
rcondf{2} 10; first by auto.
by auto.
rcondt{1} 11; first by auto.
rcondt{2} 10; first by auto.
wp; while (={glob Hmap, z, i, u, w, st, c} /\
0 <= i{1} <= k0 + 1 /\
b{1} = (0 <= z{2} < 2^(k - 1))).
by auto; progress; expect 5 smt.
by auto; progress; expect 4 smt.
by auto.
qed.
(**** Current status *)
local lemma Game3_4 &m:
Pr[Game3.main() @ &m: res]
<= Pr[Game4.main() @ &m: res]
+ qH%r * 1%r/(2^(k /% 2 - 1))%r
+ (qH + qS)%r * 1%r/(2^(k0+1))%r.
proof.
rewrite -(Game3'3_4 &m).
cut: Pr[Game3.main() @ &m: res]
<= Pr[Game3'1.main() @ &m: res]
+ qH%r * 1%r/(2^(k /% 2 - 1))%r.
cut: Pr[Game3.main() @ &m: res]
<= Pr[Game3'1.main() @ &m: res \/ H3'1.bad].
by byequiv Game3_3'1=> //; smt.
rewrite Pr [mu_or].
cut ->: Pr[Game3'1.main() @ &m: H3'1.bad]
= Pr[Game3'1'bad.main() @ &m: H3'1.bad /\ Mem.cH <= qH]
by byequiv Game3'1_3'1'bad.
by cut:= Pr_Game3'1'bad &m; smt.
cut ->: Pr[Game3'1.main() @ &m: res]
= Pr[Gen1(GAdv3,GenH3(Direct),G1).main() @ &m: res].
by byequiv Game3'1_Direct.
rewrite (GameH3Direct_Loop &m).
cut ->: Pr[Gen1(GAdv3,GenH3(Loop),G1).main() @ &m: res]
= Pr[Game3'2.main() @ &m: res].
by byequiv Game3'1Loop_3'2.
cut:= Game3'2_3'3 &m.
smt.
qed.
local lemma PSS96_Game4 &m:
Pr[UF_CMA(Wrap(PSS96(H,G)),Bounded_PSS_Adv(A,H,G)).main() @ &m: res]
<= Pr[Game4.main() @ &m: res]
+ (qS + qH)%r * (qS + qH + qG - 1)%r / (2^kh)%r
+ qS%r * (qS + qH)%r / (2^k0)%r
+ 1%r / (2^(k /% 2 - 2))%r
+ qH%r * 1%r/(2^(k /% 2 - 1))%r
+ (qH + qS)%r * 1%r/(2^(k0+1))%r.
proof.
cut:= PSS96_Game2 &m.
rewrite (Game2_3 &m).
smt.
qed.
(** We now build the inverter. Note that we now check whether ystar is
invertible (not xstar) before doing computations in H *)
local module LocalInverter(A:PSS_AdvCMA): RSA.Inverter = {
module G = {
proc o(w:htag): gtag = {
var r;
r = $gtag;
if (!mem w (dom Gmap.m)) Gmap.m.[w] = (r,Adv);
return fst (oget Gmap.m.[w]);
}
}
module Ga = {
proc o(w:htag): gtag = {
var r;
if (Mem.cG < qG) {
r = G.o(w);
Mem.cG = Mem.cG + 1;
}
else r = witness;
return r;
}
}
module Ha = {
proc o(mr:message * salt): htag = {
var m, r, w, st, u, z, i;
(m,r) = mr;
u = 0;
w = witness;
st = witness;
i = 0;
z = 2^k - 1;
if (Mem.cH < qH) {
if (invertible Hmem.pk Hmem.ystar) {
while (!0 <= z < 2^(k - 1) /\ i < k0 + 1) {
u = $challenge Hmem.pk;
z = (Hmem.ystar ** (rsap Hmem.pk u)) Hmem.pk;
w = to_htag (sub (from_signature (i2osp z)) 1 kh);
st = to_gtag (sub (from_signature (i2osp z)) (1 + kh) kg);
i = i + 1;
}
st = st ^ (to_gtag (from_salt r || zeros (kg - k0)));
if (0 <= z < 2^(k - 1) /\ invertible Hmem.pk u) {
if (!mem (m,r) (dom Hmap.m)) {
if (!mem w (dom Gmap.m)) {
Hmap.m.[(m,r)] = (w,Adv,u);
Gmap.m.[w] = (st,Adv);
}
} else
w = pi3_1 (oget Hmap.m.[(m,r)]);
} else
w = witness;
}
Mem.cH = Mem.cH + 1;
}
return w;
}
}
module S = {
proc sign(m:message): signature = {
var r, w, st, u, z, i, s;
s = witness;
i = 0;
z = 2^k - 1;
r = $salt;
u = 0;
w = witness;
st = witness;
if (Mem.cS < qS) {
while (!0 <= z < 2^(k - 1) /\ i < k0 + 1) {
u = $challenge Hmem.pk;
z = rsap Hmem.pk u;
w = to_htag (sub (from_signature (i2osp z)) 1 kh);
st = to_gtag (sub (from_signature (i2osp z)) (1 + kh) kg);
i = i + 1;
}
st = st ^ (to_gtag (from_salt r || zeros (kg - k0)));
if (0 <= z < 2^(k - 1)) {
if (!mem (m,r) (dom Hmap.m)) {
if (!mem w (dom Gmap.m)) {
Hmap.m.[(m,r)] = (w,Sig,u);
Gmap.m.[w] = (st,Sig);
}
s = i2osp u;
}
}
Mem.cS = Mem.cS + 1;
}
return s;
}
}
module A = A(Ha,Ga,S)
proc invert(pk:pkey, c:int): int = {
var m, s, y, w, st, w', st', r, c', u, x;
Hmap.m = FMap.empty;
Hmem.pk = pk;
Hmem.ystar = c;
Gmap.m = FMap.empty;
Mem.cS = 0;
Mem.cG = 0;
Mem.cH = 0;
Mem.qs = FSet.empty;
u = 1;
(m,s) = A.forge(pk);
y = from_signature (i2osp (rsap pk (os2ip s)));
w = to_htag (sub y 1 kh);
st = to_gtag (sub y (1 + kh) kg);
st' = G.o(w);
r = to_salt (sub (from_gtag (st ^ st')) 0 k0);
w' = $htag;
if (mem (m,r) (dom Hmap.m))
(w',c',u) = oget Hmap.m.[(m,r)];
x = ((os2ip s) ** (inv pk u)) pk;
return x;
}
}.
local equiv SignEq: GAdv3(H4,G1).S.sign ~ LocalInverter(A).S.sign:
={glob Gmap, Hmap.m, Mem.cS, Hmem.pk, Hmem.ystar, m} /\
valid_keys (Hmem.pk,Mem.sk){1} /\
Hmem.ystar{1} = rsap Hmem.pk{1} Hmem.xstar{1} ==>
={glob Gmap, Hmap.m, Mem.cS, res}.
proof.
proc.
case (Mem.cS{1} < qS).
rcondt{1} 2; first by auto.
rcondt{2} 8; first by auto.
inline GAdv3(H4,G1).Hs.o H4.o.
rcondt{1} 13; first by auto.
wp; while (={Hmem.pk, s, r, u, m, st, z, i} /\
w0{1} = w{2} /\
c{1} = Sig /\
rsap_dom Hmem.pk{2} u{2} /\
valid_keys (Hmem.pk,Mem.sk){1} /\
(0 <= z{1} < 2^(k - 1) =>
z{2} = rsap Hmem.pk{2} u{2} /\
w{2} = to_htag (sub (from_signature (i2osp z)) 1 kh){2} /\
st{2} = to_gtag (sub (from_signature (i2osp z)) (1 + kh) kg)){2}).
by auto; progress; expect 2 smt.
auto; progress; first 4 smt.
rewrite /oget /snd /= -xorwA xorwK xorw0.
cut:= H7 _=> // [[->]] [->] ->; rewrite /from_signature /to_signature.
rewrite /from_htag /to_htag HTag.pcan_to_from; first smt.
rewrite /from_gtag /to_gtag GTag.pcan_to_from; first smt.
cut ->: (sub (to_bits (i2osp (rsap Hmem.pk u_R))){2} 1 kh ||
sub (to_bits (i2osp (rsap Hmem.pk u_R))){2} (1 + kh) kg) =
sub (to_bits (i2osp (rsap Hmem.pk u_R))){2} 1 (kh + kg)
by smt.
cut:= msb0_bnd (i2osp (rsap Hmem.pk{2} u_R)); rewrite /from_signature; elim=> _ <-.
by rewrite pcan_os2ip_i2osp; smt.
cut ->: (sub (to_bits (i2osp (rsap Hmem.pk u_R))){2} 0 1 ||
sub (to_bits (i2osp (rsap Hmem.pk u_R))){2} 1 (kh + kg)) =
to_bits (i2osp (rsap Hmem.pk u_R)){2}
by smt.
rewrite Signature.can_from_to pcan_os2ip_i2osp; first smt.
by rewrite rsas_rsap; first 2 smt.
smt.
smt.
smt.
rewrite /oget /snd /= -xorwA xorwK xorw0.
cut:= H7 _=> // [[->]] [->] ->; rewrite /from_signature /to_signature.
rewrite /from_htag /to_htag HTag.pcan_to_from; first smt.
rewrite /from_gtag /to_gtag GTag.pcan_to_from; first smt.
cut ->: (sub (to_bits (i2osp (rsap Hmem.pk u_R))){2} 1 kh ||
sub (to_bits (i2osp (rsap Hmem.pk u_R))){2} (1 + kh) kg) =
sub (to_bits (i2osp (rsap Hmem.pk u_R))){2} 1 (kh + kg)
by smt.
cut:= msb0_bnd (i2osp (rsap Hmem.pk{2} u_R)); rewrite /from_signature; elim=> _ <-.
by rewrite pcan_os2ip_i2osp; smt.
cut ->: (sub (to_bits (i2osp (rsap Hmem.pk u_R))){2} 0 1 ||
sub (to_bits (i2osp (rsap Hmem.pk u_R))){2} 1 (kh + kg)) =
to_bits (i2osp (rsap Hmem.pk u_R)){2}
by smt.
rewrite Signature.can_from_to pcan_os2ip_i2osp; first smt.
by rewrite rsas_rsap; first 2 smt.
smt.
smt.
smt.
smt.
smt.
by rcondf{1} 2; [auto | rcondf{2} 8; auto]; progress; smt.
qed.
local hoare SignInv: GAdv3(H4,G1).S.sign:
valid_keys (Hmem.pk,Hmem.sk) /\
Hmem.ystar = rsap Hmem.pk Hmem.xstar /\
Hmem.pk = Mem.pk /\
Hmem.sk = Mem.sk /\
rsap_dom Hmem.pk Hmem.xstar /\
(forall m r,
mem (m,r) (dom Hmap.m) =>
let (w,c,u) = oget Hmap.m.[(m,r)] in
mem w (dom Gmap.m) /\
rsap_dom Hmem.pk u /\
let (st,c') = oget Gmap.m.[w] in
c = c' /\
(c = Adv =>
invertible Hmem.pk u /\
let y = (Hmem.ystar ** rsap Hmem.pk u) Hmem.pk in
zeros 1 = sub (from_signature (i2osp y)) 0 1 /\
w = to_htag (sub (from_signature (i2osp y)) 1 kh) /\
st ^ (to_gtag (from_salt r || zeros (kg - k0))) =
to_gtag (sub (from_signature (i2osp y)) (1 + kh) kg)) /\
(c = Sig =>
let y = rsap Hmem.pk u in
zeros 1 = sub (from_signature (i2osp y)) 0 1 /\
w = to_htag (sub (from_signature (i2osp y)) 1 kh) /\
st ^ (to_gtag (from_salt r || zeros (kg - k0))) =
to_gtag (sub (from_signature (i2osp y)) (1 + kh) kg) /\
mem (m,i2osp u) Mem.qs)) ==>
(forall m r,
mem (m,r) (dom Hmap.m) =>
let (w,c,u) = oget Hmap.m.[(m,r)] in
mem w (dom Gmap.m) /\
rsap_dom Hmem.pk u /\
let (st,c') = oget Gmap.m.[w] in
c = c' /\
(c = Adv =>
invertible Hmem.pk u /\
let y = (Hmem.ystar ** rsap Hmem.pk u) Hmem.pk in
zeros 1 = sub (from_signature (i2osp y)) 0 1 /\
w = to_htag (sub (from_signature (i2osp y)) 1 kh) /\
st ^ (to_gtag (from_salt r || zeros (kg - k0))) =
to_gtag (sub (from_signature (i2osp y)) (1 + kh) kg)) /\
(c = Sig =>
let y = rsap Hmem.pk u in
zeros 1 = sub (from_signature (i2osp y)) 0 1 /\
w = to_htag (sub (from_signature (i2osp y)) 1 kh) /\
st ^ (to_gtag (from_salt r || zeros (kg - k0))) =
to_gtag (sub (from_signature (i2osp y)) (1 + kh) kg) /\
mem (m,i2osp u) Mem.qs)).
proof.
proc; sp; if=> //.
inline GAdv3(H4,G1).Hs.o H4.o.
rcondt 12; first by auto.
seq 12: (c = Sig /\ m = m1 /\ r = r1 /\
s = witness /\ ho = None /\
valid_keys (Hmem.pk,Hmem.sk) /\
Hmem.ystar = rsap Hmem.pk Hmem.xstar /\
Hmem.pk = Mem.pk /\
Hmem.sk = Mem.sk /\
rsap_dom Hmem.pk Hmem.xstar /\
(0 <= z < 2^(k - 1) => rsap_dom Hmem.pk u) /\
(forall m r,
mem (m,r) (dom Hmap.m) =>
let (w,c,u) = oget Hmap.m.[(m,r)] in
mem w (dom Gmap.m) /\
rsap_dom Hmem.pk u /\
let (st,c') = oget Gmap.m.[w] in
c = c' /\
(c = Adv =>
invertible Hmem.pk u /\
let y = (Hmem.ystar ** rsap Hmem.pk u) Hmem.pk in
zeros 1 = sub (from_signature (i2osp y)) 0 1 /\
w = to_htag (sub (from_signature (i2osp y)) 1 kh) /\
st ^ (to_gtag (from_salt r || zeros (kg - k0))) =
to_gtag (sub (from_signature (i2osp y)) (1 + kh) kg)) /\
(c = Sig =>
let y = rsap Hmem.pk u in
zeros 1 = sub (from_signature (i2osp y)) 0 1 /\
w = to_htag (sub (from_signature (i2osp y)) 1 kh) /\
st ^ (to_gtag (from_salt r || zeros (kg - k0))) =
to_gtag (sub (from_signature (i2osp y)) (1 + kh) kg) /\
mem (m,i2osp u) Mem.qs)) /\
(0 <= z < 2^(k - 1) =>
z = rsap Hmem.pk u /\
zeros 1 = sub (from_signature (i2osp z)) 0 1 /\
w0 = to_htag (sub (from_signature (i2osp z)) 1 kh) /\
st = to_gtag (sub (from_signature (i2osp z)) (1 + kh) kg))).
while (c = Sig /\
valid_pkey Hmem.pk /\
(0 <= z < 2^(k - 1) =>
rsap_dom Hmem.pk u /\
z = rsap Hmem.pk u /\
zeros 1 = sub (from_signature (i2osp z)) 0 1 /\
w0 = to_htag (sub (from_signature (i2osp z)) 1 kh) /\
st = to_gtag (sub (from_signature (i2osp z)) (1 + kh) kg))).
by auto; progress; expect 3 smt.
by auto; progress; expect 12 smt.
sp 1; elim* => st0; if=> //; last by rcondt 3; auto.
if=> //; last by rcondt 5; auto.
if=> //; last first.
rcondf 4; first by auto.
wp; skip; progress [-split].
cut:= H2 m2 r2 _ => //; rewrite H9 /= => [x1_in_G].
cut:= x1_in_G; rewrite mem_dom /oget; case (Gmap.m.[x1]{hr})=> //= [[w st]].
by progress; cut:= H12 _=> //; progress; rewrite mem_add; left.
rcondf 6; first by auto.
wp; skip; progress.
case ((m1,r1){hr} = (m2,r2)).
elim=> m_eq r_eq; subst; move: H9; rewrite get_set_eq /oget /= => [->].
by rewrite mem_dom get_set_eq.
move=> neq; move: H9; rewrite get_set_neq // => hmr.
move: H8; rewrite mem_dom get_set_neq // -mem_dom=> dom_hmr.
cut:= H2 m2 r2 _=> //; rewrite hmr /=; progress.
by rewrite mem_dom get_set_neq 2:-mem_dom; first smt.
case ((m1,r1){hr} = (m2,r2)).
by elim=> m_eq r_eq; subst; move: H9; rewrite get_set_eq /oget /=; smt.
move=> neq; move: H9; rewrite get_set_neq // => hmr.
move: H8; rewrite mem_dom get_set_neq // -mem_dom=> dom_hmr.
by cut:= H2 m2 r2 _=> //; rewrite hmr /=; smt.
case ((m1,r1){hr} = (m2,r2)).
by elim=> m_eq r_eq; subst; move: H9; rewrite get_set_eq /oget /=; smt.
move=> neq; move: H9; rewrite get_set_neq // => hmr.
move: H8; rewrite mem_dom get_set_neq // -mem_dom=> dom_hmr.
by cut:= H2 m2 r2 _=> //; rewrite hmr /=; smt.
case ((m1,r1){hr} = (m2,r2)).
elim=> m_eq r_eq; subst; move: H9; rewrite get_set_eq /oget /= => [w_eq] [c_eq u_eq]; subst.
by move: H10; rewrite get_set_eq /oget.
move=> neq; move: H9; rewrite get_set_neq // => hmr.
move: H8; rewrite mem_dom get_set_neq // -mem_dom=> dom_hmr.
cut:= H2 m2 r2 _=> //; rewrite hmr /=; progress.
move: H10 H11; rewrite get_set_neq; first smt.
by move=> -> /=.
move: H9; case ((m1,r1){hr} = (m2,r2)).
by elim=> [m_eq r_eq]; subst; rewrite get_set_eq /oget.
move=> neq; rewrite get_set_neq // => hmr.
move: H8; rewrite mem_dom get_set_neq // -mem_dom=> dom_hmr.
cut:= H2 m2 r2 _=> //; rewrite hmr /=; progress.
move: H10 H11; rewrite get_set_neq; first smt.
by move=> -> /=; smt.
move: H9; case ((m1,r1){hr} = (m2,r2)).
by elim=> [m_eq r_eq]; subst; rewrite get_set_eq /oget.
move=> neq; rewrite get_set_neq // => hmr.
move: H8; rewrite mem_dom get_set_neq // -mem_dom=> dom_hmr.
cut:= H2 m2 r2 _=> //; rewrite hmr /=; progress.
move: H10 H11; rewrite get_set_neq; first smt.
by move=> -> /=; smt.
move: H9; case ((m1,r1){hr} = (m2,r2)).
by elim=> [m_eq r_eq]; subst; rewrite get_set_eq /oget.
move=> neq; rewrite get_set_neq // => hmr.
move: H8; rewrite mem_dom get_set_neq // -mem_dom=> dom_hmr.
cut:= H2 m2 r2 _=> //; rewrite hmr /=; progress.
move: H10 H11; rewrite get_set_neq; first smt.
by move=> -> /=; smt.
move: H9; case ((m1,r1){hr} = (m2,r2)).
by elim=> [m_eq r_eq]; subst; rewrite get_set_eq /oget.
move=> neq; rewrite get_set_neq // => hmr.
move: H8; rewrite mem_dom get_set_neq // -mem_dom=> dom_hmr.
cut:= H2 m2 r2 _=> //; rewrite hmr /=; progress.
move: H10 H11; rewrite get_set_neq; first smt.
by move=> ->.
move: H9; case ((m1,r1){hr} = (m2,r2)).
by elim=> [m_eq r_eq]; subst; rewrite get_set_eq /oget /= => [w_eq u_eq]; subst; smt.
move=> neq; rewrite get_set_neq // => hmr.
move: H8; rewrite mem_dom get_set_neq // -mem_dom=> dom_hmr.
cut:= H2 m2 r2 _=> //; rewrite hmr /=; progress.
move: H10 H11; rewrite get_set_neq; first smt.
by move=> ->.
move: H9; case ((m1,r1){hr} = (m2,r2)).
by elim=> [m_eq r_eq]; subst; rewrite get_set_eq /oget /= => [w_eq u_eq]; subst; smt.
move=> neq; rewrite get_set_neq // => hmr.
move: H8; rewrite mem_dom get_set_neq // -mem_dom=> dom_hmr.
cut:= H2 m2 r2 _=> //; rewrite hmr /=; progress.
move: H10 H11; rewrite get_set_neq; first smt.
by move=> ->.
move: H9; case ((m1,r1){hr} = (m2,r2)).
elim=> [m_eq r_eq]; subst; rewrite get_set_eq /oget /= => [w_eq u_eq]; subst.
move: H10; rewrite get_set_eq /oget /= => [st_eq c'_eq]; subst.
by rewrite -xorwA xorwK xorw0; smt.
move=> neq; rewrite get_set_neq // => hmr.
move: H8; rewrite mem_dom get_set_neq // -mem_dom=> dom_hmr.
cut:= H2 m2 r2 _=> //; rewrite hmr /=; progress.
move: H10 H11; rewrite get_set_neq; first smt.
by move=> ->.
rewrite /oget /= -xorwA xorwK xorw0.
case ((m1,r1){hr} = (m2,r2)).
elim=> [m_eq r_eq]; subst; move: H9; rewrite get_set_eq /oget /= => [w_eq u_eq]; subst.
move: H10; rewrite get_set_eq /oget /= => [st_eq c'_eq]; subst.
cut:= H3 _; first by split.
move=> [z_def [-> [-> ->]]].
rewrite /to_htag /from_htag HTag.pcan_to_from; first smt.
rewrite /to_gtag /from_gtag GTag.pcan_to_from; first smt.
cut ->: (sub (from_signature (i2osp z)) 0 1 ||
sub (from_signature (i2osp z)) 1 kh ||
sub (from_signature (i2osp z)) (1 + kh) kg){hr} =
from_signature (i2osp z){hr}.
cut ->: (sub (from_signature (i2osp z)) 1 kh ||
sub (from_signature (i2osp z)) (1 + kh) kg){hr} =
sub (from_signature (i2osp z)){hr} 1 (kh + kg)
by smt.
smt.
rewrite /to_signature /from_signature Signature.can_from_to.
rewrite pcan_os2ip_i2osp; first smt.
by rewrite z_def rsas_rsap; smt.
move=> neq.
move: H8; rewrite mem_dom get_set_neq // -mem_dom=> dmrh.
move: H9; rewrite get_set_neq // => hmr.
cut:= H2 m2 r2 _ => //; rewrite hmr /=; progress.
move: H10 H11; rewrite get_set_neq; first smt.
move=> -> //=.
by rewrite mem_add; progress; left.
qed.
local equiv HaEq: GAdv3(H4,G1).Ha.o ~ LocalInverter(A).Ha.o:
={glob Gmap, Hmap.m, Mem.cH, Hmem.pk, Hmem.ystar} /\ m{1} = mr{2} /\
valid_keys (Hmem.pk,Mem.sk){1} /\
Hmem.ystar{1} = rsap Hmem.pk{1} Hmem.xstar{1} ==>
={glob Gmap, Hmap.m, Mem.cH, res}.
proof.
proc; sp; if=> //.
inline H4.o.
case (!invertible Hmem.pk{1} Hmem.xstar{1}).
rcondf{1} 10; first by auto.
rcondf{2} 1; first by auto; progress; smt.
by auto.
rcondt{1} 10; first by auto.
rcondt{2} 1; first by auto; smt.
seq 10 1: (={glob Gmap, Hmap.m, Mem.cH, Hmem.pk, Hmem.ystar, u, w, st, z} /\
m{1} = (m0,r0){1} /\ m{1} = (m,r){2} /\
ho{1} = None /\
c{1} = Adv /\
valid_keys (Hmem.pk,Mem.sk){1} /\
(0 <= z{1} < 2^(k - 1) =>
rsap_dom Hmem.pk{2} u{2} /\
z{2} = (Hmem.ystar ** (rsap Hmem.pk u)){2} Hmem.pk{2} /\
w{2} = to_htag (sub (from_signature (i2osp z)) 1 kh){2} /\
st{2} = to_gtag (sub (from_signature (i2osp z)) (1 + kh) kg)){2}).
while (={Hmem.pk, Hmem.ystar, u, w, st, z, i} /\
c{1} = Adv /\
rsap_dom Hmem.pk{2} u{2} /\
valid_keys (Hmem.pk,Mem.sk){1} /\
(0 <= z{1} < 2^(k - 1) =>
z{2} = (Hmem.ystar ** (rsap Hmem.pk u)){2} Hmem.pk{2} /\
w{2} = to_htag (sub (from_signature (i2osp z)) 1 kh){2} /\
st{2} = to_gtag (sub (from_signature (i2osp z)) (1 + kh) kg)){2}).
by auto; progress; expect 2 smt.
by auto; progress; expect 7 smt.
sp 1 1; elim* => st0 st1.
if=> //; last by auto.
if=> //; last by auto; progress; smt.
if=> //; last by auto.
by wp.
qed.
local hoare HaInv: GAdv3(H4,G1).Ha.o:
valid_keys (Hmem.pk,Hmem.sk) /\
Hmem.ystar = rsap Hmem.pk Hmem.xstar /\
Hmem.pk = Mem.pk /\
Hmem.sk = Mem.sk /\
rsap_dom Hmem.pk Hmem.xstar /\
(forall m r,
mem (m,r) (dom Hmap.m) =>
let (w,c,u) = oget Hmap.m.[(m,r)] in
mem w (dom Gmap.m) /\
rsap_dom Hmem.pk u /\
let (st,c') = oget Gmap.m.[w] in
c = c' /\
(c = Adv =>
invertible Hmem.pk u /\
let y = (Hmem.ystar ** rsap Hmem.pk u) Hmem.pk in
zeros 1 = sub (from_signature (i2osp y)) 0 1 /\
w = to_htag (sub (from_signature (i2osp y)) 1 kh) /\
st ^ (to_gtag (from_salt r || zeros (kg - k0))) =
to_gtag (sub (from_signature (i2osp y)) (1 + kh) kg)) /\
(c = Sig =>
let y = rsap Hmem.pk u in
zeros 1 = sub (from_signature (i2osp y)) 0 1 /\
w = to_htag (sub (from_signature (i2osp y)) 1 kh) /\
st ^ (to_gtag (from_salt r || zeros (kg - k0))) =
to_gtag (sub (from_signature (i2osp y)) (1 + kh) kg) /\
mem (m,i2osp u) Mem.qs)) ==>
(forall m r,
mem (m,r) (dom Hmap.m) =>
let (w,c,u) = oget Hmap.m.[(m,r)] in
mem w (dom Gmap.m) /\
rsap_dom Hmem.pk u /\
let (st,c') = oget Gmap.m.[w] in
c = c' /\
(c = Adv =>
invertible Hmem.pk u /\
let y = (Hmem.ystar ** rsap Hmem.pk u) Hmem.pk in
zeros 1 = sub (from_signature (i2osp y)) 0 1 /\
w = to_htag (sub (from_signature (i2osp y)) 1 kh) /\
st ^ (to_gtag (from_salt r || zeros (kg - k0))) =
to_gtag (sub (from_signature (i2osp y)) (1 + kh) kg)) /\
(c = Sig =>
let y = rsap Hmem.pk u in
zeros 1 = sub (from_signature (i2osp y)) 0 1 /\
w = to_htag (sub (from_signature (i2osp y)) 1 kh) /\
st ^ (to_gtag (from_salt r || zeros (kg - k0))) =
to_gtag (sub (from_signature (i2osp y)) (1 + kh) kg) /\
mem (m,i2osp u) Mem.qs)).
proof.
proc; sp; if=> //.
inline GAdv3(H4,G1).Hs.o H4.o.
case (!invertible Hmem.pk Hmem.xstar); first by rcondf 10; auto.
rcondt 10; first by auto.
seq 10: (c = Adv /\
ho = None /\
valid_keys (Hmem.pk,Hmem.sk) /\
Hmem.ystar = rsap Hmem.pk Hmem.xstar /\
Hmem.pk = Mem.pk /\
Hmem.sk = Mem.sk /\
rsap_dom Hmem.pk Hmem.xstar /\
(0 <= z < 2^(k - 1) => rsap_dom Hmem.pk u) /\
(forall m r,
mem (m,r) (dom Hmap.m) =>
let (w,c,u) = oget Hmap.m.[(m,r)] in
mem w (dom Gmap.m) /\
rsap_dom Hmem.pk u /\
let (st,c') = oget Gmap.m.[w] in
c = c' /\
(c = Adv =>
invertible Hmem.pk u /\
let y = (Hmem.ystar ** rsap Hmem.pk u) Hmem.pk in
zeros 1 = sub (from_signature (i2osp y)) 0 1 /\
w = to_htag (sub (from_signature (i2osp y)) 1 kh) /\
st ^ (to_gtag (from_salt r || zeros (kg - k0))) =
to_gtag (sub (from_signature (i2osp y)) (1 + kh) kg)) /\
(c = Sig =>
let y = rsap Hmem.pk u in
zeros 1 = sub (from_signature (i2osp y)) 0 1 /\
w = to_htag (sub (from_signature (i2osp y)) 1 kh) /\
st ^ (to_gtag (from_salt r || zeros (kg - k0))) =
to_gtag (sub (from_signature (i2osp y)) (1 + kh) kg) /\
mem (m,i2osp u) Mem.qs)) /\
(0 <= z < 2^(k - 1) =>
z = (Hmem.ystar ** rsap Hmem.pk u) Hmem.pk /\
zeros 1 = sub (from_signature (i2osp z)) 0 1 /\
w = to_htag (sub (from_signature (i2osp z)) 1 kh) /\
st = to_gtag (sub (from_signature (i2osp z)) (1 + kh) kg))).
while (c = Adv /\
(0 <= z < 2^(k - 1) =>
rsap_dom Hmem.pk u /\
z = (Hmem.ystar ** rsap Hmem.pk u) Hmem.pk /\
zeros 1 = sub (from_signature (i2osp z)) 0 1 /\
w = to_htag (sub (from_signature (i2osp z)) 1 kh) /\
st = to_gtag (sub (from_signature (i2osp z)) (1 + kh) kg))).
by auto; progress; expect 3 smt.
by auto; progress; expect 11 smt.
sp 1; elim* => st0; if=> //; last by auto.
if=> //; last by auto.
if=> //; last by auto.
wp; skip; progress.
case ((m0,r0){hr} = (m1,r1)).
elim=> m_eq r_eq; subst; move: H10; rewrite get_set_eq /oget /= => [->].
by rewrite mem_dom get_set_eq.
move=> neq; move: H10; rewrite get_set_neq // => hmr.
move: H9; rewrite mem_dom get_set_neq // -mem_dom=> dom_hmr.
cut:= H2 m1 r1 _=> //; rewrite hmr /=; progress.
by rewrite mem_dom get_set_neq 2:-mem_dom; first smt.
case ((m0,r0){hr} = (m1,r1)).
by elim=> m_eq r_eq; subst; move: H10; rewrite get_set_eq /oget; smt.
move=> neq; move: H10; rewrite get_set_neq //.
move: H9; rewrite mem_dom get_set_neq // -mem_dom=> H9 hmr.
by cut:= H2 m1 r1 _=> //; rewrite hmr /=; progress; smt.
case ((m0,r0){hr} = (m1,r1)).
by elim=> m_eq r_eq; subst; move: H10; rewrite get_set_eq /oget; smt.
move=> neq; move: H10; rewrite get_set_neq //.
move: H9; rewrite mem_dom get_set_neq // -mem_dom=> H9 hmr.
by cut:= H2 m1 r1 _=> //; rewrite hmr /=; progress; smt.
case ((m0,r0){hr} = (m1,r1)).
elim=> m_eq r_eq; subst; move: H10; rewrite get_set_eq /oget /= => [w_eq [c_eq u_eq]]; subst.
by move: H11; rewrite get_set_eq /oget.
move=> neq; move: H10; rewrite get_set_neq // => hmr.
move: H9; rewrite mem_dom get_set_neq // -mem_dom=> dom_hmr.
cut:= H2 m1 r1 _=> //; rewrite hmr /=; progress.
move: H11 H12; rewrite get_set_neq; first smt.
by move=> ->.
move: H10; case ((m0,r0){hr} = (m1,r1)).
by elim=> [m_eq r_eq]; subst; rewrite get_set_eq /oget; cut:= H3 _.
move=> neq; rewrite get_set_neq // => hmr.
move: H9; rewrite mem_dom get_set_neq // -mem_dom=> dom_hmr.
cut:= H2 m1 r1 _=> //; rewrite hmr /=; progress.
move: H11 H12; rewrite get_set_neq; first smt.
by move=> ->.
move: H10; case ((m0,r0){hr} = (m1,r1)).
by elim=> [m_eq r_eq]; subst; rewrite get_set_eq /oget /= => [w_eq u_eq]; subst; smt.
move=> neq; rewrite get_set_neq // => hmr.
move: H9; rewrite mem_dom get_set_neq // -mem_dom=> dom_hmr.
cut:= H2 m1 r1 _=> //; rewrite hmr /=; progress.
move: H11 H12; rewrite get_set_neq; first smt.
by move=> ->; rewrite /rsap_dom.
move: H10; case ((m0,r0){hr} = (m1,r1)).
elim=> [m_eq r_eq]; subst; rewrite get_set_eq /oget /= => [w_eq u_eq]; subst.
move: H11; rewrite get_set_eq /oget /= => [st_eq c'_eq]; subst.
by cut:= H3 _.
move=> neq; rewrite get_set_neq // => hmr.
move: H9; rewrite mem_dom get_set_neq // -mem_dom=> dom_hmr.
cut:= H2 m1 r1 _=> //; rewrite hmr /=; progress.
move: H11 H12; rewrite get_set_neq; first smt.
by move=> ->.
move: H10; case ((m0,r0){hr} = (m1,r1)).
elim=> [m_eq r_eq]; subst; rewrite get_set_eq /oget /= => [w_eq u_eq]; subst.
move: H11; rewrite get_set_eq /oget /= => [st_eq c'_eq]; subst.
by rewrite -xorwA xorwK xorw0; cut:= H3 _.
move=> neq; rewrite get_set_neq // => hmr.
move: H9; rewrite mem_dom get_set_neq // -mem_dom=> dom_hmr.
cut:= H2 m1 r1 _=> //; rewrite hmr /=; progress.
move: H11 H12; rewrite get_set_neq; first smt.
by move=> ->.
move: H10; case ((m0,r0){hr} = (m1,r1)).
by elim=> [m_eq r_eq]; subst; rewrite get_set_eq /oget.
move=> neq; rewrite get_set_neq // => hmr.
move: H9; rewrite mem_dom get_set_neq // -mem_dom=> dom_hmr.
cut:= H2 m1 r1 _=> //; rewrite hmr /=; progress.
move: H11 H12; rewrite get_set_neq; first smt.
by move=> ->.
move: H10; case ((m0,r0){hr} = (m1,r1)).
by elim=> [m_eq r_eq]; subst; rewrite get_set_eq /oget.
move=> neq; rewrite get_set_neq // => hmr.
move: H9; rewrite mem_dom get_set_neq // -mem_dom=> dom_hmr.
cut:= H2 m1 r1 _=> //; rewrite hmr /=; progress.
move: H11 H12; rewrite get_set_neq; first smt.
by move=> ->.
move: H10; case ((m0,r0){hr} = (m1,r1)).
by elim=> [m_eq r_eq]; subst; rewrite get_set_eq /oget.
move=> neq; rewrite get_set_neq // => hmr.
move: H9; rewrite mem_dom get_set_neq // -mem_dom=> dom_hmr.
cut:= H2 m1 r1 _=> //; rewrite hmr /=; progress.
move: H11 H12; rewrite get_set_neq; first smt.
by move=> ->.
move: H10; case ((m0,r0){hr} = (m1,r1)).
by elim=> [m_eq r_eq]; subst; rewrite get_set_eq /oget.
move=> neq; rewrite get_set_neq // => hmr.
move: H9; rewrite mem_dom get_set_neq // -mem_dom=> dom_hmr.
cut:= H2 m1 r1 _=> //; rewrite hmr /=; progress.
move: H11 H12; rewrite get_set_neq; first smt.
by move=> ->.
qed.
(** The adversary wins if he is lucky, or if he knows the result of H
for the (m,r) used in the signature he returns *)
local lemma Game4_split &m:
Pr[Game4.main() @ &m: res] = Pr[Game4.main() @ &m: res /\ GAdv3.lucky]
+ Pr[Game4.main() @ &m: res /\ !GAdv3.lucky].
proof.
cut ->: Pr[Game4.main() @ &m: res] = Pr[Game4.main() @ &m: (res /\ GAdv3.lucky) \/ (res /\ !GAdv3.lucky)].
by rewrite Pr [mu_eq]; first smt.
rewrite Pr [mu_or].
cut ->: Pr[Game4.main() @ &m: (res /\ GAdv3.lucky) /\ (res /\ !GAdv3.lucky)] = Pr[Game4.main() @ &m: false].
by rewrite Pr [mu_eq]; first smt.
by rewrite Pr [mu_false].
qed.
(** Bounding the adversary's luck *)
local lemma Pr_Game4_lucky &m:
Pr[Game4.main() @ &m: res /\ GAdv3.lucky] <= 1%r/(2^kh)%r.
proof.
byphoare (_: true ==> res /\ GAdv3.lucky)=> //; proc.
inline Gen1(GAdv3,H4,G1).GA.main.
seq 12: (mem (m,r) (dom Hmap.m)) 1%r 0%r 1%r (1%r/(2^kh)%r)=> //.
by hoare; inline H4.v; rcondt 5; auto; smt.
inline H4.v; rcondf 5; first by auto.
by wp; rnd ((=) w); wp; skip; progress; smt.
qed.
local hoare GInv w0: G1.o:
w = w0 /\
(forall m r,
mem (m,r) (dom Hmap.m) =>
let (w,c,u) = oget Hmap.m.[(m,r)] in
mem w (dom Gmap.m) /\
rsap_dom Hmem.pk u /\
let (st,c') = oget Gmap.m.[w] in
c = c' /\
(c = Adv =>
invertible Hmem.pk u /\
let y = (Hmem.ystar ** rsap Hmem.pk u) Hmem.pk in
zeros 1 = sub (from_signature (i2osp y)) 0 1 /\
w = to_htag (sub (from_signature (i2osp y)) 1 kh) /\
st ^ (to_gtag (from_salt r || zeros (kg - k0))) =
to_gtag (sub (from_signature (i2osp y)) (1 + kh) kg)) /\
(c = Sig =>
let y = rsap Hmem.pk u in
zeros 1 = sub (from_signature (i2osp y)) 0 1 /\
w = to_htag (sub (from_signature (i2osp y)) 1 kh) /\
st ^ (to_gtag (from_salt r || zeros (kg - k0))) =
to_gtag (sub (from_signature (i2osp y)) (1 + kh) kg) /\
mem (m,i2osp u) Mem.qs)) ==>
omap fst Gmap.m.[w0] = Some res /\
(forall m r,
mem (m,r) (dom Hmap.m) =>
let (w,c,u) = oget Hmap.m.[(m,r)] in
mem w (dom Gmap.m) /\
rsap_dom Hmem.pk u /\
let (st,c') = oget Gmap.m.[w] in
c = c' /\
(c = Adv =>
invertible Hmem.pk u /\
let y = (Hmem.ystar ** rsap Hmem.pk u) Hmem.pk in
zeros 1 = sub (from_signature (i2osp y)) 0 1 /\
w = to_htag (sub (from_signature (i2osp y)) 1 kh) /\
st ^ (to_gtag (from_salt r || zeros (kg - k0))) =
to_gtag (sub (from_signature (i2osp y)) (1 + kh) kg)) /\
(c = Sig =>
let y = rsap Hmem.pk u in
zeros 1 = sub (from_signature (i2osp y)) 0 1 /\
w = to_htag (sub (from_signature (i2osp y)) 1 kh) /\
st ^ (to_gtag (from_salt r || zeros (kg - k0))) =
to_gtag (sub (from_signature (i2osp y)) (1 + kh) kg) /\
mem (m,i2osp u) Mem.qs)).
proof.
proc; case (mem w (dom Gmap.m)).
by rcondf 2; auto; progress; cut:= H0; rewrite mem_dom /fst /oget; case (Gmap.m.[w]{hr}).
rcondt 2; first by auto.
auto; progress; (* Good opportunity for progress* *)
last 9 by (cut:= H m r _=> //; rewrite H3 => [x2_sig] [x_in_G];
(cut neq_w_x: w{hr} <> x1 by smt);
cut:= H4; rewrite get_set_neq /rsap_dom // => ->).
by rewrite /fst /oget get_set_eq.
cut:= H m r _=> //; rewrite H3 => [x2_sig] [x_in_G].
cut neq_w_x: w{hr} <> x1 by smt.
by rewrite mem_dom get_set_neq // -mem_dom.
cut:= H m r _=> //; rewrite H3 => [x2_sig] [x_in_G].
cut neq_w_x: w{hr} <> x1 by smt.
smt.
cut:= H m r _=> //; rewrite H3 => [x2_sig] [x_in_G].
cut neq_w_x: w{hr} <> x1 by smt.
smt.
qed.
local equiv G_LocalG w0: G1.o ~ LocalInverter(A).G.o:
={Hmem.pk, Gmap.m, w} /\
w{1} = w0 /\
(forall m r,
mem (m,r) (dom Hmap.m){1} =>
let (w,c,u) = oget Hmap.m.[(m,r)]{1} in
mem w (dom Gmap.m){1} /\
rsap_dom Hmem.pk{1} u /\
let (st,c') = oget Gmap.m.[w]{1} in
c = c' /\
(c = Adv =>
invertible Hmem.pk{1} u /\
let y = (Hmem.ystar ** rsap Hmem.pk u){1} Hmem.pk{1} in
zeros 1 = sub (from_signature (i2osp y)) 0 1 /\
w = to_htag (sub (from_signature (i2osp y)) 1 kh) /\
st ^ (to_gtag (from_salt r || zeros (kg - k0))) =
to_gtag (sub (from_signature (i2osp y)) (1 + kh) kg)) /\
(c = Sig =>
let y = rsap Hmem.pk{1} u in
zeros 1 = sub (from_signature (i2osp y)) 0 1 /\
w = to_htag (sub (from_signature (i2osp y)) 1 kh) /\
st ^ (to_gtag (from_salt r || zeros (kg - k0))) =
to_gtag (sub (from_signature (i2osp y)) (1 + kh) kg) /\
mem (m,i2osp u) Mem.qs{1})) ==>
={Gmap.m, res} /\
omap fst Gmap.m.[w0]{1} = Some res{1} /\
(forall m r,
mem (m,r) (dom Hmap.m){1} =>
let (w,c,u) = oget Hmap.m.[(m,r)]{1} in
mem w (dom Gmap.m){1} /\
rsap_dom Hmem.pk{1} u /\
let (st,c') = oget Gmap.m.[w]{1} in
c = c' /\
(c = Adv =>
invertible Hmem.pk{1} u /\
let y = (Hmem.ystar ** rsap Hmem.pk u){1} Hmem.pk{1} in
zeros 1 = sub (from_signature (i2osp y)) 0 1 /\
w = to_htag (sub (from_signature (i2osp y)) 1 kh) /\
st ^ (to_gtag (from_salt r || zeros (kg - k0))) =
to_gtag (sub (from_signature (i2osp y)) (1 + kh) kg)) /\
(c = Sig =>
let y = rsap Hmem.pk{1} u in
zeros 1 = sub (from_signature (i2osp y)) 0 1 /\
w = to_htag (sub (from_signature (i2osp y)) 1 kh) /\
st ^ (to_gtag (from_salt r || zeros (kg - k0))) =
to_gtag (sub (from_signature (i2osp y)) (1 + kh) kg) /\
mem (m,i2osp u) Mem.qs{1})).
proof.
conseq* (_: ={Hmem.pk, Gmap.m, w} ==> ={Gmap.m, res}) (GInv w0)=> //.
by sim.
qed.
lemma equal_components s1 s2:
sub (from_signature s1) 0 1 = sub (from_signature s2) 0 1 =>
to_htag (sub (from_signature s1) 1 kh) = to_htag (sub (from_signature s2) 1 kh) =>
to_gtag (sub (from_signature s1) (1 + kh) kg) = to_gtag (sub (from_signature s2) (1 + kh) kg) =>
s1 = s2.
proof.
rewrite /from_signature=> b0_eq h_eq g_eq.
apply ((_: forall (x y:message), `|x| = kh => `|y| = kh => to_htag x = to_htag y => x = y) _ _ _ _) in h_eq;
first 3 by try apply length_sub; smt.
apply ((_: forall (x y:message), `|x| = kg => `|y| = kg => to_gtag x = to_gtag y => x = y) _ _ _ _) in g_eq;
first 3 by try apply length_sub; smt.
cut decomp1: forall (s:signature), s = to_signature (sub (to_bits s) 0 1 || sub (to_bits s) 1 (kh + kg))
by smt.
cut decomp2: forall (s:signature), sub (to_bits s) 1 (kh + kg) =
(sub (to_bits s) 1 kh || sub (to_bits s) (1 + kh) kg)
by smt.
by rewrite decomp1 decomp2 b0_eq h_eq g_eq -decomp2 -decomp1.
qed.
lemma i2osp_inj x y pk:
valid_pkey pk =>
rsap_dom pk x => rsap_dom pk y =>
i2osp x = i2osp y => x = y.
proof.
move=> vpk x_dom y_dom.
rewrite -{2}(pcan_os2ip_i2osp x); first smt.
rewrite -{2}(pcan_os2ip_i2osp y); first smt.
by move=> ->.
qed.
lemma rsap_inj x y pk sk:
valid_keys (pk,sk) =>
rsap_dom pk x => rsap_dom pk y =>
rsap pk x = rsap pk y => x = y.
proof.
move=> vpk x_dom y_dom.
rewrite -{2}(rsas_rsap pk sk x) //.
rewrite -{2}(rsas_rsap pk sk y) //.
by move=> ->.
qed.
lemma fcongr (f:'a -> 'b) x y:
x = y => f x = f y
by move=> ->.
(** Reduction: if the adversary wins in a clever way, then he can invert the one-way challenge *)
local equiv reduction:
Game4.main ~ OW(LocalInverter(A)).main:
={glob A} ==> res{1} /\ !GAdv3.lucky{1} => res{2}.
proof.
proc; inline Gen1(GAdv3,H4,G1).GA.main LocalInverter(A).invert.
seq 6 14: (={Mem.cH, Mem.cG, Mem.cS, Gmap.m, Hmap.m, Hmem.pk, Hmem.ystar, m, s} /\
valid_keys (Hmem.pk,Hmem.sk){1} /\
x{2} = Hmem.xstar{1} /\
Hmem.ystar{1} = rsap Hmem.pk{1} Hmem.xstar{1} /\
pk0{2} = Mem.pk{1} /\
Hmem.pk{1} = Mem.pk{1} /\
Hmem.sk{1} = Mem.sk{1} /\
rsap_dom Hmem.pk{1} Hmem.xstar{1} /\
(forall m r,
mem (m,r) (dom Hmap.m){1} =>
let (w,c,u) = oget Hmap.m.[(m,r)]{1} in
mem w (dom Gmap.m){1} /\
rsap_dom Hmem.pk{1} u /\
let (st,c') = oget Gmap.m.[w]{1} in
c = c' /\
(c = Adv =>
invertible Hmem.pk{1} u /\
let y = (Hmem.ystar{1} ** rsap Hmem.pk{1} u) Hmem.pk{1} in
zeros 1 = sub (from_signature (i2osp y)) 0 1 /\
w = to_htag (sub (from_signature (i2osp y)) 1 kh) /\
st ^ (to_gtag (from_salt r || zeros (kg - k0))) =
to_gtag (sub (from_signature (i2osp y)) (1 + kh) kg)) /\
(c = Sig =>
let y = rsap Hmem.pk{1} u in
zeros 1 = sub (from_signature (i2osp y)) 0 1 /\
w = to_htag (sub (from_signature (i2osp y)) 1 kh) /\
st ^ (to_gtag (from_salt r || zeros (kg - k0))) =
to_gtag (sub (from_signature (i2osp y)) (1 + kh) kg) /\
mem (m,i2osp u) Mem.qs{1}))).
wp; call (_: ={Mem.cH, Mem.cG, Mem.cS, Gmap.m, Hmap.m, Hmem.pk, Hmem.ystar} /\
valid_keys (Hmem.pk,Hmem.sk){1} /\
Hmem.ystar{1} = rsap Hmem.pk{1} Hmem.xstar{1} /\
Hmem.pk{1} = Mem.pk{1} /\
Hmem.sk{1} = Mem.sk{1} /\
rsap_dom Hmem.pk{1} Hmem.xstar{1} /\
(forall m r,
mem (m,r) (dom Hmap.m){1} =>
let (w,c,u) = oget Hmap.m.[(m,r)]{1} in
mem w (dom Gmap.m){1} /\
rsap_dom Hmem.pk{1} u /\
let (st,c') = oget Gmap.m.[w]{1} in
c = c' /\
(c = Adv =>
invertible Hmem.pk{1} u /\
let y = (Hmem.ystar{1} ** rsap Hmem.pk{1} u) Hmem.pk{1} in
zeros 1 = sub (from_signature (i2osp y)) 0 1 /\
w = to_htag (sub (from_signature (i2osp y)) 1 kh) /\
st ^ (to_gtag (from_salt r || zeros (kg - k0))) =
to_gtag (sub (from_signature (i2osp y)) (1 + kh) kg)) /\
(c = Sig =>
let y = rsap Hmem.pk{1} u in
zeros 1 = sub (from_signature (i2osp y)) 0 1 /\
w = to_htag (sub (from_signature (i2osp y)) 1 kh) /\
st ^ (to_gtag (from_salt r || zeros (kg - k0))) =
to_gtag (sub (from_signature (i2osp y)) (1 + kh) kg) /\
mem (m,i2osp u) Mem.qs{1}))).
(* Sign... we combine hoare invariant and a "simple" relational equivalence *)
by conseq* SignEq SignInv=> //.
(* Ga *)
proc; sp; if=> //; last by wp.
by exists* w{1}; elim* => w0; wp; call (G_LocalG w0).
(* Ha *)
by conseq* HaEq HaInv=> //.
by inline*; auto; progress; smt.
(* After the adversary call...
We know the invariant, and we know that (m,s){1} and (m,s){2} are equal *)
seq 3 3: (={Mem.cH, Mem.cG, Mem.cS, Gmap.m, Hmap.m, Hmem.pk, Hmem.ystar, m, s, y, w, st} /\
y{1} = from_signature (i2osp (rsap Mem.pk (os2ip s))){1} /\
w{1} = to_htag (sub y 1 kh){1} /\
st{1} = to_gtag (sub y (1 + kh) kg){1} /\
x{2} = Hmem.xstar{1} /\
valid_keys (Hmem.pk,Hmem.sk){1} /\
Hmem.ystar{1} = rsap Hmem.pk{1} Hmem.xstar{1} /\
pk0{2} = Mem.pk{1} /\
Hmem.pk{1} = Mem.pk{1} /\
Hmem.sk{1} = Mem.sk{1} /\
rsap_dom Hmem.pk{1} Hmem.xstar{1} /\
(forall m r,
mem (m,r) (dom Hmap.m){1} =>
let (w,c,u) = oget Hmap.m.[(m,r)]{1} in
mem w (dom Gmap.m){1} /\
rsap_dom Hmem.pk{1} u /\
let (st,c') = oget Gmap.m.[w]{1} in
c = c' /\
(c = Adv =>
invertible Hmem.pk{1} u /\
let y = (Hmem.ystar{1} ** rsap Hmem.pk{1} u) Hmem.pk{1} in
zeros 1 = sub (from_signature (i2osp y)) 0 1 /\
w = to_htag (sub (from_signature (i2osp y)) 1 kh) /\
st ^ (to_gtag (from_salt r || zeros (kg - k0))) =
to_gtag (sub (from_signature (i2osp y)) (1 + kh) kg)) /\
(c = Sig =>
let y = rsap Hmem.pk{1} u in
zeros 1 = sub (from_signature (i2osp y)) 0 1 /\
w = to_htag (sub (from_signature (i2osp y)) 1 kh) /\
st ^ (to_gtag (from_salt r || zeros (kg - k0))) =
to_gtag (sub (from_signature (i2osp y)) (1 + kh) kg) /\
mem (m,i2osp u) Mem.qs{1})));
first by wp.
seq 1 1: (={Mem.cH, Mem.cG, Mem.cS, Gmap.m, Hmap.m, Hmem.pk, Hmem.ystar, m, s, y, w, st} /\
y{1} = from_signature (i2osp (rsap Mem.pk (os2ip s))){1} /\
w{1} = to_htag (sub y 1 kh){1} /\
st{1} = to_gtag (sub y (1 + kh) kg){1} /\
g{1} = st'{2} /\
omap fst Gmap.m.[w]{1} = Some g{1} /\
x{2} = Hmem.xstar{1} /\
valid_keys (Hmem.pk,Hmem.sk){1} /\
Hmem.ystar{1} = rsap Hmem.pk{1} Hmem.xstar{1} /\
pk0{2} = Mem.pk{1} /\
Hmem.pk{1} = Mem.pk{1} /\
Hmem.sk{1} = Mem.sk{1} /\
rsap_dom Hmem.pk{1} Hmem.xstar{1} /\
(forall m r,
mem (m,r) (dom Hmap.m){1} =>
let (w,c,u) = oget Hmap.m.[(m,r)]{1} in
mem w (dom Gmap.m){1} /\
rsap_dom Hmem.pk{1} u /\
let (st,c') = oget Gmap.m.[w]{1} in
c = c' /\
(c = Adv =>
invertible Hmem.pk{1} u /\
let y = (Hmem.ystar{1} ** rsap Hmem.pk{1} u) Hmem.pk{1} in
zeros 1 = sub (from_signature (i2osp y)) 0 1 /\
w = to_htag (sub (from_signature (i2osp y)) 1 kh) /\
st ^ (to_gtag (from_salt r || zeros (kg - k0))) =
to_gtag (sub (from_signature (i2osp y)) (1 + kh) kg)) /\
(c = Sig =>
let y = rsap Hmem.pk{1} u in
zeros 1 = sub (from_signature (i2osp y)) 0 1 /\
w = to_htag (sub (from_signature (i2osp y)) 1 kh) /\
st ^ (to_gtag (from_salt r || zeros (kg - k0))) =
to_gtag (sub (from_signature (i2osp y)) (1 + kh) kg) /\
mem (m,i2osp u) Mem.qs{1}))).
by exists* w{1}; elim* => w0; call (G_LocalG w0).
inline H4.v; wp; rnd; wp; skip; progress.
move: H5 H7; pose r:= to_salt (sub (from_gtag (to_gtag (sub (from_signature (i2osp (rsap Mem.pk{1} (os2ip s{2})))) (1 + kh) kg) ^ st'{2})) 0 k0); move=> H5 H7.
cut:= H2 m{2} r _=> //; move: H5 H7.
rewrite mem_dom /oget /pi3_1; case (Hmap.m.[(m,r)]{2})=> //= hmr.
elim/tuple3_ind hmr=> hmr w c u //=.
case c=> //= hmr_def.
(* c = Adv *)
move=> w_def []; cut:= H; rewrite w_def mem_dom /oget /fst; case (Gmap.m.[w]{2})=> //= stc'.
elim/tuple2_ind stc'=> stc' st c' stc'_def //= st'_def [c'_def] [u_inv].
apply someI in st'_def.
rewrite -H6 -w_def -H8 /r.
rewrite /from_salt /to_salt Salt.pcan_to_from.
by rewrite length_sub /from_gtag /to_gtag /from_signature=> //; smt.
rewrite app_sub=> //; first 3 smt.
rewrite /to_gtag /from_gtag GTag.can_from_to -/(to_gtag _) st'_def xorwC -xorwA xorwK xorw0.
move=> [u_in_ZN] [b0_eq] [w_eq st_eq].
cut: i2osp (rsap Mem.pk (os2ip s{2})){1} =
i2osp (((rsap Mem.pk Hmem.xstar) ** (rsap Mem.pk u)) Mem.pk){1}
by apply equal_components.
rewrite -rsap_morphism_mulzp; first 2 smt.
move=> sig_bits_eq.
apply (i2osp_inj _ _ Mem.pk{1} _ _ _) in sig_bits_eq; first 3 smt.
apply (rsap_inj _ _ Mem.pk{1} Mem.sk{1} _ _ _) in sig_bits_eq; first 3 smt.
apply (fcongr (fun x, (x ** (inv Mem.pk{1} u)) Mem.pk{1})) in sig_bits_eq; move: sig_bits_eq=> /=.
rewrite mulzpA; first 3 smt.
rewrite mulzpV; first 2 smt.
rewrite (mulzpC _ 1); first 2 smt.
rewrite mulzp1; first smt.
smt.
(* c = Sig: ignore the goal and prove s{2} = i2osp u *)
move=> w_def []; cut:= H; rewrite w_def mem_dom /oget /fst; case (Gmap.m.[w]{2})=> //= stc'.
elim/tuple2_ind stc'=> stc' st c' stc'_def //= st'_def [c'_def].
apply someI in st'_def.
rewrite -H6 -w_def -H8 /r.
rewrite /from_salt /to_salt Salt.pcan_to_from.
by rewrite length_sub /from_gtag /to_gtag/ from_signature=> //; smt.
rewrite app_sub=> //; first 3 smt.
rewrite /to_gtag /from_gtag GTag.can_from_to -/(to_gtag _) st'_def xorwC -xorwA xorwK xorw0.
move=> [_] [b0_eq] [w_eq] [st_eq unfresh].
cut: i2osp (rsap Mem.pk (os2ip s{2})){1} =
i2osp (rsap Mem.pk u){1}
by apply equal_components.
move=> sig_bits_eq.
apply (i2osp_inj _ _ Mem.pk{1} _ _ _) in sig_bits_eq; first 3 smt.
apply (rsap_inj _ _ Mem.pk{1} Mem.sk{1} _ _ _) in sig_bits_eq; first 3 smt.
apply (fcongr i2osp) in sig_bits_eq.
by move: sig_bits_eq unfresh H11; rewrite can_i2osp_os2ip=> -> ->.
qed.
local lemma OW_Local_Global &m:
Pr[OW(LocalInverter(A)).main() @ &m: res] = Pr[OW(Inverter(A)).main() @ &m: res].
proof. by byequiv (_: ={glob A} ==> ={res})=> //; sim. qed.
lemma conclusion &m:
Pr[UF_CMA(Wrap(PSS96(H,G)),Bounded_PSS_Adv(A,H,G)).main() @ &m: res]
<= Pr[OW(Inverter(A)).main() @ &m: res]
+ (qS + qH)%r * (qS + qH + qG - 1)%r / (2^kh)%r
+ qS%r * (qS + qH)%r / (2^k0)%r
+ 1%r / (2^(k /% 2 - 2))%r
+ qH%r * 1%r/(2^(k /% 2 - 1))%r
+ (qH + qS)%r * 1%r/(2^(k0+1))%r
+ 1%r/(2^kh)%r.
proof.
cut:= PSS96_Game4 &m.
rewrite (Game4_split &m).
cut: Pr[Game4.main() @ &m: res /\ !GAdv3.lucky] <= Pr[OW(Inverter(A)).main() @ &m: res].
by rewrite -(OW_Local_Global &m); byequiv reduction.
cut:= Pr_Game4_lucky &m.
smt.
qed.
end section.