(* Lee et al. election scheme (ICISC'03).
   Proof of receipt-freeness with two honest voters *)

free a,b,c: bitstring.

free ch,chVa,chVb,ch1,ch2,c1,chc: channel.

type pkey.
type skey.
type seed.
type dvp_t.
type check_t.

fun penc(bitstring, pkey, seed): bitstring. (* probabilistic encryption *)
fun rencrypt(bitstring, seed): bitstring. (* re-encryption *)
fun f(seed, seed): seed. (* function combining random coins*)
fun sign(bitstring, skey): bitstring. (* digital signature *)
fun dvp(bitstring, bitstring, seed, pkey): dvp_t. (* designated verifier proof *)
fun fake_dvp(bitstring, bitstring, seed, skey): dvp_t.

const ok: check_t. (* check dvp ok *)
fun pk(skey):pkey.        (* get public key from private key *)

reduc forall m: bitstring, sk: skey, r: seed; 
      decrypt(penc(m, pk(sk), r), sk) = m.
reduc forall m: bitstring, sk: skey; 
      checksign(sign(m, sk), pk(sk)) = m.
equation forall m: bitstring, sk: skey, r1: seed, r2: seed; 
	 rencrypt(penc(m, pk(sk), r1), r2) = penc(m, pk(sk), f (r1, r2)).

reduc forall x: bitstring, r: seed, sk: skey;
      checkdvp(dvp(x, rencrypt(x, r), r, pk(sk)), x, rencrypt(x, r), pk(sk)) = ok;
      forall x: bitstring, y: bitstring, z: seed, skv: skey;
      checkdvp(fake_dvp(x, y, z, skv), x, y, pk(skv)) = ok.

(* Voter with secret key skv, vote v, talking to his trusted device, of public key pubkd, on channel chD *) 

let processV(chV: channel, chD1: channel, chD2: channel, pubkt: pkey, pubkd: pkey, skv: skey, v: bitstring) =
	(*sync 1;*)
	new r: seed;
	let e = penc (v, pubkt, r) in
	out(chD1, e);
	in(chD2, (re: bitstring, sd: bitstring, dvpV: dvp_t));
	if checkdvp(dvpV, e, re, pk(skv)) = ok then
	if checksign(sd, pubkd) = re then
	out(chV,sign(sd,skv)).

(* First component of the next process is V^chc, voting fst(v),
   second component is V' voting snd(v), and trying to make it look like vfake 
   (we have vfake = fst(v)) *)

let processVchcV'(chV: channel, chD1: channel, chD2: channel, pubkt: pkey, pubkd: pkey, skv: skey, v: bitstring, vfake: bitstring) =
	(*sync 1;*)
	out(chc, (pubkt, pubkd, skv));
	new r: seed;
	out(chc, r);
	let e = penc (v, pubkt, r) in
	out(chD1, e);
	in(chD2 , (re: bitstring, sd: bitstring, dvpV: dvp_t));
	new r': seed;
	let fkdvp = fake_dvp(penc(vfake, pubkt, r), re, r', skv) in
	out(chc, (re, sd, diff[dvpV, fkdvp])); 
	if checkdvp(dvpV, e, re, pk(skv)) = ok then
	if checksign(sd, pubkd) = re then
	out(chV,sign(sd,skv)).

(* Trusted device with secret key skd, talking to the voter with public key pubkv on channel chD *)   

let processD(chD1: channel, chD2: channel, skd: skey, pubkv: pkey) =
	(* sync 1;*)
	in(chD1, enc: bitstring);
	new r1: seed;
	let reenc = rencrypt (enc, r1) in
	let signD = sign(reenc, skd) in
	let dvpD = dvp(enc, reenc, r1, pubkv) in
	out(chD2,(reenc, signD, dvpD)).

(* Mixer & Tallier *)

let processM(skt: skey, pubkv: pkey, pubkd: pkey) =
        (* Mixer *)
	(*sync 1;*)
	in(ch1,sig: bitstring);
	let enc = checksign(checksign(sig,pubkv),pubkd) in
	sync 2;
	new r2: seed;
	let mixedvote = rencrypt(enc, r2) in
	out(ch2,mixedvote);
	(* Tallier *)
	out(ch,decrypt(mixedvote,skt)).

process 
	(* private keys *)
	new skt: skey;
	new skva: skey; new skvb: skey; 
	new skda: skey; new skdb: skey; 

	(* public keys *)
	let pkt = pk(skt) in
	let pkva = pk(skva) in
	let pkvb = pk(skvb) in
	let pkda = pk(skda) in
	let pkdb = pk(skdb) in
	
	(* public key disclosure *)
	out(ch,pkt);
	out(ch,pkva); out(ch,pkvb); 
	out(ch,pkda); out(ch,pkdb); 
	
	(* private channels *)
	new chDa1: channel; new chDa2: channel; 
	new chDb1: channel; new chDb2: channel; 

	( 
		(* mixers and talliers *)
		processM(skt, pkva, pkda) | 
		processM(skt, pkvb, pkdb) | 

		(* voters *)
		processV(chVa, chDa1, chDa2, pkt, pkda, skva, choice[a,b]) |
		processVchcV'(chVb, chDb1, chDb2, pkt, pkdb, skvb, choice[b,a], b) |

		(* trusted devices *)
		processD(chDa1, chDa2, skda, pkva) |
		processD(chDb1, chDb2, skdb, pkvb) 
	)

(* EXPECTPV
RESULT Observational equivalence is true.
0.252s (user 0.248s + system 0.004s), max rss 15448K
END *)
