(* Dreier, Lafourcade, & Lakhnech (2011) variant of the Lee et al. 
   election scheme. 
   Failed proof of ballot secrecy with two honest voters and
   one dishonest voter. This failure comes from a vote copying
   attack formalized by Dreier, Lafourcade, & Lakhnech, and 
   already mentioned for the original protocol by Lee et al.

   We made some changes with respect to the scripts given by
   Dreier, Lafourcade, & Lakhnech (2011):

   * We replaced the key distribution on private channels
   by processes taking the keys as arguments. This change exploits
   the new parametric processes of ProVerif to write more elegant
   code, and to avoid a false attack due to abstractions in the 
   treatment of private channels.

   * The secret keys of the trusted devices are skda/skdb/skdc, 
   not skva/skvb/skvc.

   * The channel between the voter and the trusted device is private.

   * We modified the definition of cryptographic primitives, for 
   several reasons:
     - We had to change the equation checksign(sign(m, sk), pk(sk)) = m
   into a destructor definition. This is important, because with an equation, 
   the test
          if checksign(sig, pubkv) = enc then
   does not really play its role: it is easy for the adversary to
   make it succeed by setting enc = checksign(sig, pubkv), even when
   sig is not a signature.
     - ProVerif does not accept the equations as written by Dreier, 
   Lafourcade, & Lakhnech because, when orienting the given equations from 
   left to right, the obtained rewrite system is not convergent.
   We solved the problem by defining checkdvp as a destructor
   (while rencrypt is still defined by an equation).
     - We use two different symbols for normal Designed Verifier
   Proofs (dvp) built by the hardware devices and for fake ones
   (fake_dvp) that the voter might build. The computations made in
   these two cases are different, and the types of the function
   symbols are also different. (The last argument of dvp is a public
   key, while the last argument of fake_dvp is a private key.)

   * We had to change the code of mixer and tallier, so that the
   tallier always decrypts the votes produced by the mixer. We do
   that by merging the mixer and tallier into a single process. The
   mixer publishes the mixed votes (to model the bulletin board), and
   the tallier immediately decrypts them, without reading them on a
   public channel.  Otherwise, the adversary could change the votes
   that are decrypted by the tallier, in particular use votes before
   mixing.  *)

free a,b,c: bitstring.

free ch,chVa,chVb,ch1,ch2,c1: 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, chD: channel, pubkt: pkey, pubkd: pkey, skv: skey, v: bitstring) =
	(*sync 1;*)
	new r: seed;
	let e = penc (v, pubkt, r) in
	out(chD, sign(e, skv));
	in(chD , (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)).

(* Voter under the control of the attacker *)

let processVc1c2(chD: channel, skv: skey) =
 	(* leak all secrets of the voter: private key & channel to trusted device,
	   so that the adversary can implement the voter *)
	out(c1, skv);
	out(c1, chD).

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

let processD(chD: channel, skd: skey, pubkv: pkey) =
	(* sync 1;*)
	in(chD, sig: bitstring);
	let enc = checksign(sig, pubkv) in
	new r1: seed;
	let reenc = rencrypt (enc, r1) in
	let signD = sign(reenc, skd) in
	let dvpD = dvp(enc, reenc, r1, pubkv) in
	out(chD,(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)).

    (* I use the variant "nosync" for voter c 
       to reduce the number of swapping possibilities *)

let processMnosync(skt: skey, pubkv: pkey, pubkd: pkey) =
        (* Mixer *)
	in(ch1,sig: bitstring);
	let enc = checksign(checksign(sig,pubkv),pubkd) in
	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 skvc: skey;
	new skda: skey; new skdb: skey; new skdc: skey;

	(* public keys *)
	let pkt = pk(skt) in
	let pkva = pk(skva) in
	let pkvb = pk(skvb) in
	let pkvc = pk(skvc) in
	let pkda = pk(skda) in
	let pkdb = pk(skdb) in
	let pkdc = pk(skdc) in
	
	(* public key disclosure *)
	out(ch,pkt);
	out(ch,pkva); out(ch,pkvb); out(ch,pkvc);
	out(ch,pkda); out(ch,pkdb); out(ch,pkdc);
	
	(* private channels *)
	new chDa: channel; new chDb: channel; new chDc: channel;

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

		(* voters *)
		processV(chVa, chDa, pkt, pkda, skva, choice[a,b]) |
		processV(chVb, chDb, pkt, pkdb, skvb, choice[b,a]) |
		processVc1c2(chDc, skvc) |

		(* trusted devices *)
		processD(chDa, skda, pkva) |
		processD(chDb, skdb, pkvb) |
		processD(chDc, skdc, pkvc)
	)

(* EXPECTPV
RESULT Observational equivalence cannot be proved.
RESULT Observational equivalence cannot be proved.
0.560s (user 0.556s + system 0.004s), max rss 17656K
END *)
