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

(* declaring additional types; type bitstring is a default type *)
type pkey.
type skey.


(* declaring private names *)
free ska, skb, skc:skey [private]. 


(* Public key cryptography *)
fun pk(skey):pkey.		        (* public key*)
fun aenc(bitstring, pkey):bitstring. 	(* asymmetric encryption *)
fun adec(bitstring, skey):bitstring.	(* asymmetric decryption *)
fun hash(bitstring): bitstring.         (* hashing function *)

(* equational theory for pk encryption *)
equation forall x:bitstring, y:skey; adec(aenc(x,pk(y)),y) = x.

(* events *)
event acceptI(pkey, pkey, bitstring, bitstring).
event startR(pkey, pkey, bitstring, bitstring).
event acceptR(pkey, pkey, bitstring, bitstring).
event startI(pkey, pkey, bitstring, bitstring).

(* queries *)
(*query pk1:pkey, pk2:pkey, x:bitstring, y:bitstring; event(acceptI(pk1, pk2, x, y)) ==> event(startR(pk2, pk1, x, y)).
query pk1:pkey, pk2:pkey, x:bitstring, y:bitstring; event(acceptR(pk1, pk2, x, y)) ==> event(startI(pk2, pk1, x, y)).*)
query x:bitstring, y:bitstring; inj-event(acceptI(pk(ska), pk(skb), x, y)) ==> inj-event(startR(pk(skb), pk(ska), x, y)).
(*query pk1:pkey, pk2:pkey, x:bitstring, y:bitstring; inj-event(acceptR(pk1, pk2, x, y)) ==> inj-event(startI(pk2, pk1, x, y)).
query attacker(new ak).
query attacker(new bk).*)

(* initiator process *)
let Pia(ski:skey, pkr:pkey) = 
	 new ak:bitstring;
	 out(c, aenc((pk(ski),ak), pkr));
	 in(c, x:bitstring);
	 let (=pkr, mr:bitstring, =hash(ak)) = adec(x, ski) in
	 event startI(pk(ski), pkr, ak, mr);
	 out(c, hash(mr));
	 if (pkr = pk(ska) || pkr = pk(skb)) then
	 event acceptI(pk(ski), pkr, ak, mr).

(* initiator process *)
let Pib(ski:skey, pkr:pkey) = 
	 new bk:bitstring;
	 out(c, aenc((pk(ski),bk), pkr));
	 in(c, x:bitstring);
	 let (=pkr, mr:bitstring, =hash(bk)) = adec(x, ski) in
	 event startI(pk(ski), pkr, bk, mr);
	 out(c, hash(mr));
	 if (pkr = pk(ska) || pkr = pk(skb)) then
	 event acceptI(pk(ski), pkr, bk, mr).

(* initiator process *)
let Pic(ski:skey, pkr:pkey) = 
	 new k:bitstring;
	 out(c, aenc((pk(ski),k), pkr));
	 in(c, x:bitstring);
	 let (=pkr, mr:bitstring, =hash(k)) = adec(x, ski) in
	 out(c, hash(mr)).
	 

(* responder process *)
let Pra(sk:skey)= (* fill in the process *)
	in(c, x:bitstring);
	let (xpka:pkey, xk:bitstring) = adec(x,sk) in (
	new ma:bitstring;
	event startR(pk(sk), xpka, xk, ma);
	out(c, aenc((pk(sk), ma, hash(xk)), xpka));
	in(c, y:bitstring);
	if adec(y,sk) = hash(ma) then event acceptR(pk(sk), xpka, xk, ma)).

 let Prb(sk:skey)= 
	in(c, x:bitstring);
	let (xpka:pkey, xk:bitstring) = adec(x,sk) in (
	new mb:bitstring;
	event startR(pk(sk), xpka, xk, mb); 
	out(c, aenc((pk(sk), mb, hash(xk)), xpka));
	in(c, y:bitstring);
	if adec(y,sk) = hash(mb) then event acceptR(pk(sk), xpka, xk, mb)).


(* main process *)
process  ( !Pia(ska, pk(skb)) | !Pic(ska, pk(skc)) | !Pib(skb, pk(ska)) | !Pic(skb, pk(skc)) | !Pra(ska) | !Prb(skb) | out(c, pk(ska)) | out(c, pk(skb)) | out(c, skc))


(* EXPECTPV
RESULT inj-event(acceptI(pk(ska[]),pk(skb[]),x_6,y_2)) ==> inj-event(startR(pk(skb[]),pk(ska[]),x_6,y_2)) is true.
0.012s (user 0.012s + system 0.000s), max rss 8852K
END *)
