free c:channel.  (* declaring a free channel name *)

type id.
type pkey.
type skey.
type key.

free a, b, d, s: id. (* declaring identities as free names*)

event start(id, id, key).
event end(id, id, key).
event sec(id, id, bitstring).

not attacker(sk(b)).
not attacker(sk(a)).

type stamp.
event Precise(stamp,bitstring).
event PreciseId(stamp,id).

axiom st:stamp, x:bitstring, x':bitstring;
	event(Precise(st,x)) && event(Precise(st,x')) ==> x = x' [removeEvents].

axiom st:stamp, x:id, x':id;
	event(PreciseId(st,x)) && event(PreciseId(st,x')) ==> x = x' [removeEvents,keepEvents].
(* Public key cryptography *)

fun pk(id):pkey.			(* public key*)
fun aenc(bitstring, pkey):bitstring.	(* asymmetric encryption *)
(* fun adec(bitstring, skey):bitstring.	(* asymmetric decryption *)*)
fun senc(bitstring, key):bitstring.	(* symmetric encryption *)
(* fun sdec(bitstring, key):bitstring.	(* symmetric decryption *)*)

fun sk(id):skey [private]. (* sk is a private function, i.e. cannot be applied by the adversary *)
fun symk(id, id):key [private].

reduc forall x:bitstring, y:id ; adec(aenc(x, pk(y)), sk(y)) = x.
reduc forall x:bitstring, y:key ; sdec(senc(x, y), y) = x.

(* queries should be added here *)
query attacker(new k).
query i:id, r:id, k:key ; inj-event(end(i, r, k)) ==> inj-event(start(i, r, k)).
query i:id, r:id, n:bitstring ; event(sec(i, r, n)) && attacker(n) ==> i = d || r = d.

(* initiator process *)
let Pi(i1:id, r1:id) =

	 (* Step 1 *)
	 new k:key;
	 event start(i1, r1, k);
	 out(c, (i1, senc((r1, k), symk(i1, s))));

	 new st[]:stamp;
	 in(c, x:bitstring);
	 event Precise(st,x);
	 let nb:bitstring = sdec(adec(x, sk(i1)), k) in
	 (* End step 3 *)

	 (* Step 4 *)
	 out(c, aenc(nb, pk(r1)));

	 (* Step 5 *)
	 new na:bitstring;
	 out(c, aenc(senc(na, k), pk(r1)));

	 new st[]:stamp;
	 in(c, x:bitstring);
	 event Precise(st,x);
	 if na = adec(x, sk(i1)) then
	 event sec(i1, r1, na).
	 (* End step 6 *)


(* responder process *)
let Pr(r2:id) =

	 new st[]:stamp;
	 in(c, x:bitstring);
	 event Precise(st,x);
	 let (i:id, k:key) = sdec(x, symk(r2, s)) in
	 (* End step 2 *)

	 (* Step 3 *)
	 new nb:bitstring;
	 out(c, aenc(senc(nb, k), pk(i)));

	 new st[]:stamp;
	 in(c, x:bitstring);
	 event Precise(st,x);
	 if nb = adec(x, sk(r2)) then
	 event sec(i, r2, nb);
	 (* End step 4 *)

	 new st[]:stamp;
	 in(c, x:bitstring);
	 event Precise(st,x);
	 let na:bitstring = sdec(adec(x, sk(r2)), k) in
	 event end(i, r2, k);
	 (* End step 5 *)

	 (* Step 6 *)
	 out(c, aenc(na, pk(i))).


(* server process *)
let Ps =
	 new st[]:stamp;
	 in(c, (i:id, x:bitstring));
	 event Precise(st,(i,x));
	 let (r:id, k:key) = sdec(x, symk(i, s)) in
	 (* End step 1 *)

	 (* Step 2 *)
	 out(c, senc((i, k), symk(r, s))).


(* main process *)
process (!
	new st1[]:stamp;
	in(c, x1:id);
	event PreciseId(st1,x1);
	new st2[]:stamp;
	in(c, y1:id);
	event PreciseId(st2,y1);
	Pi(x1,y1)) | (!
	new st3[]:stamp;
	in(c, y2:id);
	event PreciseId(st3,y2);
	Pr(y2)) | !Ps | out(c, sk(d))

(* EXPECTPV
Error: The option keepEvents cannot be declared at the same time as removeEvents.
xtime: error in child process (status : 2)
END *)
