free c.
(* SKEME *)

(* Public key cryptography *)

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

(* Hash function *)

fun hash/1.

(* Diffie-Hellman *)

fun f/2.
fun g/1.
equation f(x,g(y)) = f(y,g(x)).

(* Keyed hash function *)

fun keyhash/2.

(* Shared-key cryptography *)

fun sencrypt/2.
reduc sdecrypt(sencrypt(x,y),y) = x.

(* Secrecy assumptions *)

not attacker:skA.
not attacker:skB.
not attacker:x[].
not attacker:y[].

private free secretA, secretB.
query attacker:secretA;
      attacker:secretB.
query evinj:endAparam(t) ==> evinj:beginAparam(t).
query evinj:endAkey(t,z) ==> evinj:beginAkey(t,z).
query evinj:endBparam(t) ==> evinj:beginBparam(t).
query evinj:endBkey(t,z) ==> evinj:beginBkey(t,z).

(* A *)

let processA = in(c, pkX);
		event beginBparam(pkX);
               new x; new Ka;
                out(c, (encrypt((pkA, Ka), pkX), g(x)));
                in(c, (m1, m2, m3));
		let Kb = decrypt(m1, skA) in
                let K0 = hash((Ka, Kb)) in
		if m3 = keyhash((g(x), m2, pkX, pkA), K0) then
                 let sh = hash(f(x, m2)) in 
                 event beginBkey(pkX, sh);
                 out(c, keyhash((m2, g(x), pkA, pkX), K0));
		 (* OK *)
                 if pkX = pkB then event endAparam(pkA); event endAkey(pkA, sh); out(c, sencrypt(secretA, sh)).

(* B *)

let processB = in(c, (m1, m2));
               let (pkAr, Ka) = decrypt(m1, skB) in
		event beginAparam(pkAr);
               new Kb;
               let K0 = hash((Ka, Kb)) in
	       new y;
               let sh = hash(f(y, m2)) in
	        event beginAkey(pkAr, sh);
	       out(c, (encrypt(Kb, pkAr), g(y), keyhash((m2, g(y), pkB, pkAr), K0)));
               in(c, m3);
	       if m3 = keyhash((g(y), m2, pkAr, pkB), K0) then
               (* OK *)
		if pkAr = pkA then event endBparam(pkB); event endBkey(pkB, sh); out(c, sencrypt(secretB, sh)).


process 

new skA; let pkA = pk(skA) in
out(c, pkA);
new skB; let pkB = pk(skB) in
out(c, pkB);
((!processA) | (!processB))


(* EXPECTPV
RESULT not attacker:secretA[] is true.
RESULT not attacker:secretB[] is true.
RESULT evinj:endAparam(t) ==> evinj:beginAparam(t) is true.
RESULT evinj:endAkey(t,z) ==> evinj:beginAkey(t,z) is true.
RESULT evinj:endBparam(t) ==> evinj:beginBparam(t) is true.
RESULT evinj:endBkey(t,z) ==> evinj:beginBkey(t,z) is true.
0.180s (user 0.152s + system 0.028s), max rss 10312K
END *)
