(* 

Needham-Schroeder public key protocol
Corrected version by Lowe

	   ? -> A : pkB
Message 1: A -> S : (A, B)
Message 2: S -> A : { B, pkB }_skS
Message 3: A -> B : { N_A, A }_pkB
Message 4: B -> S : (B, A)
Message 5: S -> B : { A, pkA }_skS
Message 6: B -> A : { N_A, N_B, B }_pkA
Message 7: A -> B : { N_B }_pkB

The heart of the protocol is messages 3, 6, 7.

*)

free c: channel.

type host.
type nonce.
type pkey.
type skey.
type spkey.
type sskey.

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

(* 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,nonce): bitstring.
reduc forall x: bitstring, y: nonce; sdecrypt(sencrypt(x,y),y) = x.

(* Secrecy assumptions *)

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

(* 2 honest host names A and B *)

free A, B: host.

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

(* Queries *)

free secretANa, secretANb, secretBNa, secretBNb: bitstring [private].
noninterf secretANa, secretANb, secretBNa, secretBNb.
noninterf secretANa, secretANb.
noninterf secretBNa, secretBNb.

(* Role of the initiator with identity xA and secret key skxA *)

let processInitiator(pkS: spkey, skA: skey, skB: skey) =
        (* The attacker starts the initiator by choosing identity xA,
	   and its interlocutor xB0.
	   We check that xA is honest (i.e. is A or B)
	   and get its corresponding key.
	*)
        in(c, (xA: host, hostX: host));
	if xA = A || xA = B then
	let skxA = if xA = A then skA else skB in
	let pkxA = pk(skxA) in
	(* Real start of the role *)
	(* Message 1: Get the public key certificate for the other host *)
	out(c, (xA, hostX));
	(* Message 2 *)
	in(c, ms: bitstring); 
	let (pkX: pkey, =hostX) = checksign(ms,pkS) in
        (* Message 3 *)
	new Na: nonce; 
        out(c, encrypt((Na, xA), pkX));
        (* Message 6 *)
        in(c, m: bitstring); 
        let (=Na, NX2: nonce, =hostX) = decrypt(m, skA) in
	(* Message 7 *)
        out(c, encrypt(nonce_to_bitstring(NX2), pkX));
        (* OK *)
	if hostX = B || hostX = A then
	out(c, sencrypt(secretANa, Na));
	out(c, sencrypt(secretANb, NX2)).

(* Role of the responder with identity xB and secret key skxB *)

let processResponder(pkS: spkey, skA: skey, skB: skey) =
        (* The attacker starts the responder by choosing identity xB.
	   We check that xB is honest (i.e. is A or B). *)
        in(c, xB: host);
	if xB = A || xB = B then
	let skxB = if xB = A then skA else skB in
	let pkxB = pk(skxB) in
	(* Real start of the role *)
        (* Message 3 *)
	in(c, m: bitstring);
	let (NY: nonce, hostY: host) = decrypt(m, skxB) in
	(* Message 4: Get the public key certificate for the other host *)
        out(c, (xB, hostY));
	(* Message 5 *)
	in(c,ms: bitstring);
        let (pkY: pkey,=hostY) = checksign(ms,pkS) in
        (* Message 6 *)
	new Nb: nonce;
	out(c, encrypt((NY, Nb, xB), pkY));
        (* Message 7 *)
	in(c, m3: bitstring);
        if nonce_to_bitstring(Nb) = decrypt(m3, skB) then
	(* OK *)
        if hostY = A || hostY = B then
	out(c, sencrypt(secretBNa, NY));
	out(c, sencrypt(secretBNb, Nb)).


(* Server *)

let processS(skS: sskey) =  
        in(c,(a: host, b: host)); 
	get keys(=b, sb) in
        out(c,sign((sb,b),skS)).

(* Key registration *)

let processK =
        in(c, (h: host, k: pkey));
        if h <> A && h <> B then insert keys(h,k).

(* Start process *)

process new skA: skey; 
	let pkA = pk(skA) in
        out(c, pkA);
	insert keys(A, pkA);
        new skB: skey; 
	let pkB = pk(skB) in
        out(c, pkB);
	insert keys(B, pkB);
	new skS: sskey; 
	let pkS = spk(skS) in
	out(c, pkS);
	(
          (* Launch an unbounded number of sessions of the initiator *)
          (!processInitiator(pkS, skA, skB)) | 
          (* Launch an unbounded number of sessions of the responder *)
          (!processResponder(pkS, skA, skB)) |
          (* Launch an unbounded number of sessions of the server *)
          (!processS(skS)) |
          (* Key registration process *)
	  (!processK)
        )


(* EXPECTPV
RESULT Non-interference secretANa, secretANb, secretBNa, secretBNb cannot be proved.
RESULT Non-interference secretANa, secretANb is true.
RESULT Non-interference secretBNa, secretBNb is true.
0.432s (user 0.348s + system 0.084s), max rss 10820K
END *)
