(* 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 *)
(* equation f(x,g(y)) = f(y,g(x)). *)

fun g/1.
fun h1/2.
fun h0/2.
reduc f(y, g(x)) = h1(x,y);
      f(x, g(y)) = h1(x,y);
      f(x, y) = h0(x,y).

(* Keyed hash function *)

fun keyhash/2.

query secretA.
query secretB.

(* A *)

let processA = in(c, pkX);
               new x; new Ka;
               (out(c, (encrypt((pkA, Ka), pkX), g(x))) |
                in(c, m);
	        let (m1, m2, m3) = m in
		let Kb = decrypt(m1, skA) in
                let K0 = hash((Ka, Kb)) in
		if m3 = keyhash((g(x), m2, pkX, pkA), K0) then
                (out(c, keyhash((m2, g(x), pkA, pkX), K0)) |
                 let sh = hash(f(x, m2)) in 
                 if pkX = pkB then out(c, encrypt(secretA, pk(sh))))).

(* B *)

let processB = in(c, m);
               let (m1, m2) = m in
               let (pkAr, Ka) = decrypt(m1, skB) in
               new Kb;
               let K0 = hash((Ka, Kb)) in
	       new y;
	       (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
               let sh = hash(f(y, m2)) in
               if pkAr = pkA then out(c, encrypt(secretB, pk(sh)))).


process 

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

