(* Otway Rees protocol.
   Version of Abadi and Needham, Prudent engineering practice...

A -> B: (A, B, Na)
B -> S: (A, B, Na, Nb)
S -> B: ({ Na, A, B, k }_kA, { Nb, A, B, k }_kB)
B -> A: { Na, A, B, k }_kA

Attack
I -> B : (A,B,v217)
B -> S : (A,B,v217,Nb[(A,B,v217),v215])
S -> B : (...., encrypt((Nb[(A,B,v217),v215],A,B,k[(A,B,v217,Nb[(A,B,v217),v215]),v219]),kB[]))
B says end(B)

Attack
A -> I : (A, B, Na)
I -> S : (A, B, Na, v217)
S -> I : ({ Na, A, B, k }_kA, {v217, , A, B, k }_kB)
I -> A : { Na, A, B, k }_kA

*)

free c: channel.

type key.
type host.
type nonce.

(* Shared key encryption *)

fun encrypt(bitstring,key): bitstring.
reduc forall x: bitstring, y: key; decrypt(encrypt(x,y),y) = x.

(* Secrecy assumptions *)

not attacker(new Kas).
not attacker(new Kbs).

(* 2 honest host names A and B *)

free A, B: host.

(* the table host names/keys 
   The key table consists of pairs 
   (host, key shared between the host and the server) *)
table keys(host, key).

(* Queries *)

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

event endAparam(host,host).
event endBparam(host,host).
event beginAparam(host,host).
event beginBparam(host,host).
event endAkey(host, host, key).
event beginAkey(host, host, key).
event endBkey(host, host, key).
event beginBkey(host, host, key).

query x: host, y: host; inj-event(endAparam(x,y)) ==> inj-event(beginAparam(x,y)).
query x: host, y: host, z: key; inj-event(endAkey(x,y,z)) ==> inj-event(beginAkey(x,y,z)).
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)).

(* Role of the initiator with identity xA and key kA shared with S *)

let processInitiator =
        (* 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, xB0: host));
	if xA = A || xA = B then
	get keys(=xA, kA) in
	(* Real start of the role *)
	new Na: nonce;
	event beginBparam(xA, xB0);
        out(c, (xA, xB0, Na));
	in(c, m: bitstring);
	let (=Na, =xA, =xB0, k: key) = decrypt(m, kA) in
	event beginBkey(xA, xB0, k);
	(* OK *)
	if xB0 = A || xB0 = B then 
	event endAparam(xA, xB0);
        event endAkey(xA, xB0, k);
	out(c, encrypt(secretA, k)).


(* Role of the responder with identity xB and key kB shared with S *)

let processResponder =
        (* The attacker starts the responder by choosing identity xB.
	   We check that xB is honest (i.e. is A or B)
	   and get its corresponding key. *)
        in(c, xB: host);
	if xB = A || xB = B then
	get keys(=xB, kB) in
	(* Real start of the role *)
	in(c, (xA1: host, =xB, m2: bitstring)); 
	event beginAparam(xA1, xB); 
	new Nb: nonce; 
	out(c, (xA1, xB, m2, Nb)); 
	in(c, (m3: bitstring, m4: bitstring)); 
	let (=Nb, =xA1, =xB, k: key) = decrypt(m4, kB) in 
	event beginAkey(xA1, xB, k); 
	out(c, m3);
	(* OK *)
	if xA1 = A || xA1 = B then
	event endBparam(xA1, xB);
	event endBkey(xA1, xB, k);
	out(c, encrypt(secretB, k)).

(* Server *)

let processS = 
    	in(c, (xA1: host, xB1: host, Na1: nonce, Nb1: nonce));
    	get keys(=xA1, kA1) in
	get keys(=xB1, kB1) in
	new k: key;
        out(c, (encrypt((Na1, xA1, xB1, k), kA1), 
                encrypt((Nb1, xA1, xB1, k), kB1))).

(* Key registration *)

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

(* Start process *)

process
	new Kas: key; new Kbs: key;
	insert keys(A, Kas);
	insert keys(B, Kbs);
	(
          (* Launch an unbounded number of sessions of the initiator *)
          (!processInitiator) | 
          (* Launch an unbounded number of sessions of the responder *)
          (!processResponder) |
          (* Launch an unbounded number of sessions of the server *)
          (!processS) |
          (* Key registration process *)
	  (!processK)
        )


(* EXPECTPV
RESULT not attacker(secretA[]) is true.
RESULT not attacker(secretB[]) is true.
RESULT inj-event(endAparam(x,y)) ==> inj-event(beginAparam(x,y)) is false.
RESULT (even event(endAparam(x,y)) ==> event(beginAparam(x,y)) is false.)
RESULT inj-event(endAkey(x,y,z)) ==> inj-event(beginAkey(x,y,z)) is false.
RESULT (even event(endAkey(x,y,z)) ==> event(beginAkey(x,y,z)) 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.176s (user 0.124s + system 0.052s), max rss 9832K
END *)
