free c.
(* 

? -> A : pkB
A -> B : { N_A, A }_pkB
B -> S : (B,A)
S -> B : { A, pkA }_sS
B -> A : { N_A, N_B, pkB }_pkA
A -> B : { N_B }_pkB

*)

(* Public key cryptography *)

fun pk/1.
fun encrypt/2.
reduc decrypt(encrypt(x,pk(y)),y) = x.

(* Host *)

fun host/1.
private reduc getkey(host(x)) = x.

(* Signatures *)

fun sign/2.
reduc checksign(sign(x,y),pk(y)) = x.
reduc getmess(sign(x,y)) = x.

authquery Aparam/1.
authquery Afull/6.
authquery Bparam/1.
authquery Bfull/6.

let processA =  in(c,hostX); begin Bparam(hostX); out(c, (hostA, hostX));
		in(c, ms); let (pkX,hostX2) = checksign(ms,pkS) in
                if hostX2 = hostX then
		new Na; 
                out(c, encrypt((Na, hostA), pkX));
                in(c, m); let (Na2, NX2, hostX2) = decrypt(m, skA) in
                          if Na2 = Na then
                             if hostX2 = hostX then
			       begin Bfull(Na, hostA, hostX, pkX, pkA, NX2);
                               out(c, encrypt(NX2, pkX));
			       if hostX = hostB then
				 end Aparam(hostA);
				 end Afull(Na, hostA, hostX, pkX, pkA, NX2).

let processB = in(c, m); let (NY, hostY) = decrypt(m, skB) in
			 begin Aparam(hostY);
                         out(c, (hostB, hostY));
			 in(c,ms);
                         let (pkY,hostY2) = checksign(ms,pkS) in
			 if hostY = hostY2 then
			 new Nb;
			 begin Afull(NY, hostY, hostB, pkB, pkY, Nb);
			 out(c, encrypt((NY, Nb, hostB), pkY));
			  in(c, m3);
                          let Nb2 = decrypt(m3, skB) in
                          if Nb2 = Nb then 
			  (* OK *)
                            if hostY = hostA then
			      end Bparam(hostB);
			      end Bfull(NY, hostY, hostB, pkB, pkA, Nb).

let processS =  in(c,m); 
	        let (a,b) = m in
		let sb = getkey(b) in
                out(c,sign((pk(sb),b),skS)).

process new skA; let pkA = pk(skA) in
        out(c, pkA);
        new skB; let pkB = pk(skB) in
        out(c, pkB);
	new skS; let pkS = pk(skS) in
	out(c, pkS);
	let hostA = host(skA) in
	out(c, hostA);
	let hostB = host(skB) in
	out(c, hostB);
	((!processA) | (!processB) | (!processS))
