(* Denning-Sacco
   In this model, I separate the keys for encryption and the
   keys for signature, which complicates the model. *)

free c: channel.

type host.
type key.
type pkey.
type skey.
type spkey.
type sskey.
type tag.

(* Public key encryption *)

fun pk(skey): pkey.
fun encrypt(bitstring, pkey): bitstring.
reduc forall x: bitstring, y: skey; decrypt(encrypt(x,pk(y)),y) = x.

(* Signatures *)

fun spk(sskey): spkey.
fun sign(bitstring, sskey): bitstring.
reduc forall m: bitstring, k: sskey; getmess(sign(m,k)) = m.
reduc forall m: bitstring, k: sskey; checksign(sign(m,k), spk(k)) = m.

(* Shared key encryption *)

fun sencrypt(bitstring,key): bitstring.
reduc forall x: bitstring, y: key; sdecrypt(sencrypt(x,y),y) = x.

(* Secrecy assumptions *)

not attacker(new skA).
not attacker(new skB).
not attacker(new skS).

(* Tags *)

const c0, c1, c2, c3: tag [data].

(* 2 honest host names A and B *)

free A, B: host.

(* the table host names/encryption keys 
   The key table consists of pairs (host, public key) *)
table enc_keys(host, pkey).

(* the table host names/signature verification keys 
   The key table consists of pairs (host, public key) *)
table sign_keys(host, spkey).

(* Queries *)

free secretA, secretB: bitstring [private].
query attacker(secretA);
      attacker(secretB).

event endBparam(host,host).
event beginBparam(host,host).
event endBkey(host, host, key).
event beginBkey(host, host, key).

query x: host, y: host; inj-event(endBparam(x,y)) ==> inj-event(beginBparam(x,y)).
query x: host, y: host, z: key; inj-event(endBkey(x,y,z)) ==> inj-event(beginBkey(x,y,z)).

(* Initiator *)

let processInitiator(pkS: spkey, xA: host, skA: sskey, pkA: spkey) =
	in(c, (cert1: bitstring, cert2: bitstring));
	let (=c0, =xA, =pkA) = checksign(cert1, pkS) in
	let (=c1, h2: host, pk2: pkey) = checksign(cert2, pkS) in
	event beginBparam(xA, h2);
	new k: key;
	event beginBkey(xA, h2, k);
	out(c, (cert1, cert2, encrypt((c2, sign((c3, k), skA)), pk2)));
	if h2 = B then
	out(c, sencrypt(secretA, k)).

(* Responder *)

let processResponder(pkS: spkey,  xB: host, skB: skey, pkB: pkey) =
	in(c, (cert1: bitstring, cert2: bitstring, km: bitstring));
	let (=c0, h1: host, pk1: spkey) = checksign(cert1, pkS) in
	let (=c1, =xB, =pkB) = checksign(cert2, pkS) in
	let (=c2, ks: bitstring) = decrypt(km,skB) in
	let (=c3, k:key) = checksign(ks, pk1) in
	if h1 = A then
	event endBparam(h1, xB);
	event endBkey(h1, xB, k);
	out(c, sencrypt(secretB, k)).

(* Server *)

let processS(skS: sskey) =
	in(c, (h1: host,h2: host));
	get sign_keys(=h1, pk1) in
	get enc_keys(=h2, pk2) in
	out(c, (sign((c0, h1, pk1), skS), sign((c1, h2, pk2), skS))).

(* Signature key registration *)

let process_signK =
        in(c, (h: host, pk: spkey));
	if h <> A then insert sign_keys(h, pk).

(* Encryption key registration *)

let process_encK =
        in(c, (h: host, pk: pkey));
	if h <> B then insert enc_keys(h, pk).

(* Main process *)

process new skA: sskey; 
	let pkA = spk(skA) in
        out(c, pkA);
	insert sign_keys(A, pkA);
        new skB: skey; 
	let pkB = pk(skB) in
        out(c, pkB);
	insert enc_keys(B, pkB);
	new skS: sskey; 
	let pkS = spk(skS) in
	out(c, pkS);
	((!processInitiator(pkS, A, skA, pkA)) | 
	 (!processResponder(pkS, B, skB, pkB)) |
	 (!processS(skS)) |
	 (!process_signK) | (!process_encK))

(* EXPECTPV
RESULT not attacker(secretA[]) is true.
RESULT not attacker(secretB[]) is false.
RESULT inj-event(endBparam(x,y)) ==> inj-event(beginBparam(x,y)) is false.
RESULT (even event(endBparam(x,y)) ==> event(beginBparam(x,y)) is false.)
RESULT inj-event(endBkey(x,y,z)) ==> inj-event(beginBkey(x,y,z)) is false.
RESULT (even event(endBkey(x,y,z)) ==> event(beginBkey(x,y,z)) is false.)
0.084s (user 0.056s + system 0.028s), max rss 9564K
END *)
