(* Signed Diffie-Hellman
	A -> B : { e^n0 }skA, pkA    (n0 fresh)
        B -> A : { e^n1 }skB         (n1 fresh)
          A and B compute the key as k = (e^n0)^n1 = (e^n1)^n0
        A -> B : { s }k

 *)

free c: channel.

type pkey.
type skey.
type G.
type exponent.

(* Signatures *)

fun sign(G, skey): bitstring.
fun pk(skey):pkey.
reduc forall m: G, k: skey; getmess(sign(m,k)) = m.
reduc forall m: G, k: skey; checksign(sign(m,k), pk(k)) = m.

(* Shared key encryption *)

fun enc(bitstring, G): bitstring.
reduc forall x: bitstring, y: G; dec(enc(x,y),y) = x.

(* Diffie-Hellman *)

const g: G.
fun exp(G, exponent): G.
equation forall x: exponent, y: exponent; exp(exp(g, x), y) = exp(exp(g, y), x).

(* Test whether s is secret *)

free s: bitstring [private].
query attacker(s).

(* The process *)

let p0(skA: skey, pkA: pkey, pkB: pkey) = 
         in(c, pkX: pkey);
	 new n0: exponent; 
         out(c,(sign(exp(g, n0), skA), pkA));
	 in(c,m1: bitstring); 
	 let x1 = checksign(m1, pkX) in
	 let k = exp(x1, n0) in 
  	 if pkX = pkB then
	 out(c, enc(s,k)).

let p1(skB: skey, pkB: pkey, pkA: pkey) = 
         in(c, (m0: bitstring, pkY: pkey));
	 let x0 = checksign(m0, pkY) in
	 new n1: exponent; 
         let k = exp(x0, n1) in 
         out(c,sign(exp(g, n1), skB));
         in (c,m: bitstring); 
         let s2 = dec(m,k) in 0.

process 
	new skA: skey;
	let pkA = pk(skA) in
	out(c, pkA);
	new skB: skey;
	let pkB = pk(skB) in
	out(c, pkB);
	((!p0(skA, pkA, pkB)) | (!p1(skB, pkB, pkA)))

(* EXPECTPV
RESULT not attacker(s[]) is true.
0.020s (user 0.016s + system 0.004s), max rss 8704K
END *)
