(* SKEME *)

free c: channel.

type nonce.
type pkey.
type skey.
type key.
type exponent.

fun nonce_to_bitstring(nonce): bitstring [data, typeConverter].

(* Probabilistic public key encryption *)

type seed.
fun pk(skey): pkey.
fun internal_encrypt(bitstring, pkey, seed): bitstring.
reduc forall x: bitstring, y: skey, r: seed; 
        decrypt(internal_encrypt(x,pk(y),r),y) = x.
letfun encrypt(x: bitstring, y:pkey) = new r: seed; internal_encrypt(x,y,r).

(* Hash function *)

fun hash(bitstring):key.

(* Diffie-Hellman *)

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

(* Keyed hash function *)

fun keyhash(bitstring, key):bitstring.

(* Probabilistic shared key encryption *)

type sseed.
fun internal_sencrypt(bitstring,key,sseed): bitstring.
reduc forall x: bitstring, y: key, r: sseed; sdecrypt(internal_sencrypt(x,y,r),y) = x.
letfun sencrypt(x: bitstring, y: key) = new r: sseed; internal_sencrypt(x,y,r).

(* Secrecy assumptions *)

not attacker(new skA).
not attacker(new skB).
not attacker(new x).
not attacker(new y).

free secretA, secretB: bitstring [private].
noninterf secretA, secretB.
noninterf secretA.
noninterf secretB.

(* A *)

let processA(skA: skey, pkA: pkey, pkB: pkey) = 
        in(c, pkX: pkey);
        new x: exponent; 
	let gx = exp(g,x) in
	new Ka: nonce;
        out(c, (encrypt((pkA, Ka), pkX), gx));
        in(c, (m1: bitstring, m2: bitstring, m3: bitstring));
	let nonce_to_bitstring(Kb) = decrypt(m1, skA) in
        let K0 = hash((Ka, Kb)) in
	if m3 = keyhash((gx, m2, pkX, pkA), K0) then
        let sh = hash(exp(m2, x)) in 
        out(c, keyhash((m2, gx, pkA, pkX), K0));
	(* OK *)
        if pkX = pkB then 
	out(c, sencrypt(secretA, sh)).

(* B *)

let processB(skB: skey, pkB: pkey, pkA: pkey) = 
        in(c, (m1: bitstring, m2: bitstring));
        let (pkAr: pkey, Ka: nonce) = decrypt(m1, skB) in
        new Kb: nonce;
        let K0 = hash((Ka, Kb)) in
	new y: exponent;
	let gy = exp(g,y) in
        let sh = hash(exp(m2, y)) in
	out(c, (encrypt(nonce_to_bitstring(Kb), pkAr), gy, keyhash((m2, gy, pkB, pkAr), K0)));
        in(c, m3: bitstring);
	if m3 = keyhash((gy, m2, pkAr, pkB), K0) then
        (* OK *)
	if pkAr = pkA then 
	out(c, sencrypt(secretB, sh)).


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


(* EXPECTPV
RESULT Non-interference secretA, secretB is true.
RESULT Non-interference secretA is true.
RESULT Non-interference secretB is true.
0.300s (user 0.256s + system 0.044s), max rss 11240K
END *)
