(* Helios without revote: Peter Roenne's attack does not work anymore. *)
(* Of course, if A or B revotes then the attack works again *)
(* Just three voters A, B, C*)

type skey.
type pkey.
type vkey.
type nonce.

free c:channel.

(* Symmetric cryptography *)
fun senc(bitstring,skey):bitstring.
reduc forall m:bitstring, k:skey; sdec(senc(m,k),k) = m.

(* Public key cryptography *)

fun pk(skey):pkey.
fun aenc(bitstring,pkey):bitstring.
reduc forall m:bitstring, k:skey; adec(aenc(m,pk(k)),k) = m.

(* Signatures *)

fun sign(bitstring,skey):bitstring.
fun vk(skey):vkey.
reduc forall x:bitstring, y:skey; checksign(sign(x,y),vk(y)) = x.

type stamp.
event RecordVoter(stamp,vkey).

free k:skey [private]. (* election key *)
free sk_A:skey [private]. (* models an authenticated channel *)
free sk_B:skey [private].
free sk_C:skey.
free k':skey [private]. (* auxiliary key to encode a state *)

free cand1:bitstring. (* candidate 1 *)
free cand2:bitstring. (* candidate 2 *)

let setup =
  out(c, vk(sk_A)) ;
  out(c, vk(sk_B)) ;
  out(c, pk(k)) ;
  out(c, sk_C).

let voterA =
  in(c,x_rs:bitstring); (* Takes some randomnes from the server as well. *)
  new r:bitstring;
  let vote = choice [cand1,cand2] in
  out (c, sign(aenc((vote,x_rs,r),pk(k)), sk_A)).

let voterB =
  in(c,x_rs:bitstring); (* Takes some randomnes from the server as well. *)
  new r:bitstring;
  let vote = choice [cand2,cand1] in
  out (c, sign(aenc((vote,x_rs,r),pk(k)), sk_B)).

let badboard =
  in(c,x0:bitstring);
  let (y1:bitstring,y2:bitstring) = sdec(x0,k') in (* expects the two honest ciphertexts to do weeding *)
  in(c,x:bitstring);
  let y:bitstring = checksign(x,vk(sk_C)) in
  if y = y1 then 0
  else
    if y = y2 then 0
    else
      let (v:bitstring,xrs:bitstring,r:bitstring) = adec(y,k) in
      out(c,v).

set preciseActions = true.


(* The board accepts any ballot with a valid signature but the restriction makes sure the board receives exactly the vote of Alice and the vote of Bob, in any order. *)
(* Restriction : x1:vkey, x2:vkey, st:stamp, st':stamp; event RecordVoter(st,x1) && event RecordVoter(st',x2) ==> st = st' || ((x1 = vk(sk_A)) && (x2 = vk(sk_B))) || ((x1 = vk(sk_B)) && (x2 = vk(sk_A))) *)

(* We prove that the restriction is possible *)
(* 1 - First case : We assume the restriction on the left side and we show that the restriction holds on the right side. *)

(*
axiom st:stamp, st':stamp, x1:vkey, x1':vkey, x2:vkey, x2':vkey;
  event(RecordVoter(st,diff[x1,x1'])) && event(RecordVoter(st',diff[x2,x2'])) ==> st = st' || (x1 = vk(sk_A) && x2 = vk(sk_B)) || (x1 = vk(sk_B) && x2 = vk(sk_A)).

lemma st:stamp, st':stamp, x1:vkey, x1':vkey, x2:vkey, x2':vkey;
  event(RecordVoter(st,diff[x1,x1'])) && event(RecordVoter(st',diff[x2,x2'])) ==>
    (st <> st' && (x1 <> vk(sk_A) || x2 <> vk(sk_B)) && (x1 <> vk(sk_B) || x2 <> vk(sk_A))) ||
    st = st' || (x1' = vk(sk_A) && x2' = vk(sk_B)) || (x1' = vk(sk_B) && x2' = vk(sk_A)).
*)

(* 2 - Second case : We assume the restriction on the right side and we show that the restriction holds on the left side. *)

(*
axiom st:stamp, st':stamp, x1:vkey, x1':vkey, x2:vkey, x2':vkey;
  event(RecordVoter(st,diff[x1,x1'])) && event(RecordVoter(st',diff[x2,x2'])) ==> st = st' || (x1' = vk(sk_A) && x2' = vk(sk_B)) || (x1' = vk(sk_B) && x2' = vk(sk_A)).

lemma st:stamp, st':stamp, x1:vkey, x1':vkey, x2:vkey, x2':vkey;
  event(RecordVoter(st,diff[x1,x1'])) && event(RecordVoter(st',diff[x2,x2'])) ==>
    (st <> st' && (x1' <> vk(sk_A) || x2' <> vk(sk_B)) && (x1' <> vk(sk_B) || x2' <> vk(sk_A))) ||
    st = st' || (x1 = vk(sk_A) && x2 = vk(sk_B)) || (x1 = vk(sk_B) && x2 = vk(sk_A)).
*)

(* 3 - The equivalence proof *)

axiom st:stamp, st':stamp, x1:vkey, x1':vkey, x2:vkey, x2':vkey;
  event(RecordVoter(st,diff[x1,x1'])) && event(RecordVoter(st',diff[x2,x2'])) ==> st = st' || (x1 = vk(sk_A) && x2 = vk(sk_B)) || (x1 = vk(sk_B) && x2 = vk(sk_A)).

axiom st:stamp, st':stamp, x1:vkey, x1':vkey, x2:vkey, x2':vkey;
  event(RecordVoter(st,diff[x1,x1'])) && event(RecordVoter(st',diff[x2,x2'])) ==> st = st' || (x1' = vk(sk_A) && x2' = vk(sk_B)) || (x1' = vk(sk_B) && x2' = vk(sk_A)).


let board =
  in (c,(x1:bitstring,verifkey1:vkey)) ;
  in (c,(x2:bitstring,verifkey2:vkey)) ;
  let y1 = checksign(x1,verifkey1) in new st:stamp; event RecordVoter(st,verifkey1);
  let y2 = checksign(x2,verifkey2) in new st:stamp; event RecordVoter(st,verifkey2);
  let (v1:bitstring,xr1s:bitstring,r1:bitstring) = adec(y1, k) in
  let (v2:bitstring,xr2s:bitstring,r2:bitstring) = adec(y2, k) in
  out(c,senc((y1,y2),k')); (* records the two honest ciphertexts *)
  (out(c,choice[v1,v2]) | out(c,choice[v2,v1])).

process
  setup | voterA | voterB | board | badboard
