(* Version with possibly dishonest participants.
   Using two series of function symbols to prevent repeated applications
   of the same cryptographic rule.
*)

param NA, NB, NS, NK.

type nonce [large,fixed].
type host [bounded].
type key [fixed].
type maxenc [bounded].

(* A function f(x1...xn) is declared [data] when:
   - it is injective
   - the values of x1...xn are computable in polynomial time from f(x1...xn) *)

fun concat1(nonce, nonce, host, host):maxenc [data].
fun concat2(nonce, key):maxenc [data].

equation forall y:nonce,z:nonce,t:host,u:host, y2:nonce,z2:key; concat1(y,z,t,u) <> concat2(y2,z2).

(* Shared-key encryption (IND-CPA + INT-CTXT Stream cipher) *)

proba Penc(time, number).
proba Pencctxt(time, number, number, length).

expand IND_CPA_INT_CTXT_sym_enc(key, maxenc, bitstring, enc, dec, injbot, Z, Penc, Pencctxt).

(* The function Z returns for each bitstring, a bitstring
   of the same length, consisting only of zeroes. *)
const Zconcat1:maxenc.
const Zconcat2:maxenc.
equation forall y:nonce,z:nonce,t:host,u:host; 
	Z(concat1(y,z,t,u)) = Zconcat1.
equation forall y:nonce,z:key; 
	Z(concat2(y,z)) = Zconcat2.

table keys(host, key).

(* Queries *)

channel c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13,
	start, cAK, cBK.

const A : host.
const B : host.
fun acceptA(host):bitstring.
fun acceptB(host):bitstring.

event partA(host, bitstring).
event partB(host, bitstring).
event partB2(host, bitstring).
event fullA(host, key, bitstring).
event fullB(host, key, bitstring).

query secret keyB.
query secret keyA.
query x:bitstring;
	event(partB(A,x)) ==> event(partA(B,x)) public_vars keyA, keyB.
query x:bitstring;
	inj-event(partB(A,x)) ==> inj-event(partA(B,x)) public_vars keyA, keyB.
query k:key, x:bitstring;
	inj-event(fullA(B,k,x)) ==> inj-event(fullB(A,k,x)) public_vars keyA, keyB.
query k:key, x:bitstring;
	inj-event(fullA(B,k,x)) ==> inj-event(partB2(A,x)) public_vars keyA, keyB.
query k:key, x:bitstring;
	inj-event(fullA(B,k,x)) ==> inj-event(partB(A,x)) public_vars keyA, keyB.
query k:key, k':key, x:bitstring;
	event(fullB(A,k,x)) && event(fullA(B,k',x)) ==> k = k' public_vars keyA, keyB. 

let processA(Kas: key) = 
	in(c1, hb: host);
	new M : nonce;
	new Na : nonce;
	let ea1 = enc(concat1(Na, M, A, hb), Kas) in
	event partA(hb, (M, A, hb));
	out(c2, (M, A, hb, ea1));
	in(c3, (=M, ea2: bitstring));
	let injbot(concat2(=Na, k)) = dec(ea2, Kas) in
	event fullA(hb, k, (M, A, hb));
	if hb = B then
	(
	let keyA:key = k in out(c11, acceptA(hb))
	)
	else
	(
	out(c11, acceptA(hb)); in(cAK, ()); out(cAK, k)
	).

let processB(Kbs: key) =
	in(c4, (M: nonce, ha: host, =B, ea:bitstring));
	new Nb : nonce;
	let e2 = enc(concat1(Nb, M, ha, B), Kbs) in
	event partB2(ha, (M, ha, B));
	out(c5, (M, ha, B, ea, e2));
	in(c6, (=M, ea2: bitstring, eb2: bitstring));
	let injbot(concat2(=Nb, k)) = dec(eb2, Kbs) in
	event partB(ha, (M, ha, B));
	event fullB(ha, k, (M, ha, B));
	if ha = A then
	(
	let keyB:key = k in out(c7, (M, ea2, acceptB(ha)))
	)
	else
	(
	out(c7, (M, ea2, acceptB(ha))); in(cBK, ()); out(cBK, k)
	).

let processK(Kas: key, Kbs: key) =
	in(c8, (Khost: host, Kkey: key));
	if Khost = A then insert keys(A,Kas) else
	if Khost = B then insert keys(B,Kbs) else
	insert keys(Khost,Kkey).

let processS =
	in(c9, (M: nonce, ha: host, hb: host, ea1: bitstring, eb1: bitstring));
	get keys(=ha, kas) in
	get keys(=hb, kbs) in
	let injbot(concat1(Na, =M, =ha, =hb)) = dec(ea1, kas) in
	let injbot(concat1(Nb, =M, =ha, =hb)) = dec(eb1, kbs) in
	new k: key;
	let e1 = enc(concat2(Na, k), kas) in
	let e2 = enc(concat2(Nb, k), kbs) in
	out(c10, (M, e1, e2)).
	
process 
	in(start, ());
	new Kas: key;
	new Kbs: key;
	out(c13, ());
	((! iA <= NA processA(Kas)) |
	 (! iB <= NB processB(Kbs)) | 
	 (! iS <= NS processS) |
	 (! iK <= NK processK(Kas, Kbs)))

(* EXPECTED
RESULT Could not prove event(fullB(A, k, x)) && event(fullA(B, k', x)) ==> (k = k') with public variables keyA, keyB; inj-event(fullA(B, k, x)) ==> inj-event(partB(A, x)) with public variables keyA, keyB; inj-event(fullA(B, k, x)) ==> inj-event(fullB(A, k, x)) with public variables keyA, keyB; inj-event(partB(A, x)) ==> inj-event(partA(B, x)) with public variables keyA, keyB.
1.236s (user 1.220s + system 0.016s), max rss 29972K
END *)

(* EXPECTPV
RESULT secret keyB is true.
RESULT secret keyA is true.
RESULT event(partB(A,x)) ==> event(partA(B,x)) public_vars keyA,keyB is true.
RESULT inj-event(partB(A,x)) ==> inj-event(partA(B,x)) public_vars keyA,keyB is false.
RESULT (query encoded as inj-event(partB(A,x)) ==> inj-event(partA(B,x)))
RESULT (but event(partB(A,x)) ==> event(partA(B,x)) is true.)
RESULT inj-event(fullA(B,k_3,x)) ==> inj-event(fullB(A,k_3,x)) public_vars keyA,keyB is false.
RESULT (query encoded as inj-event(fullA(B,k_3,x)) ==> inj-event(fullB(A,k_3,x)))
RESULT (even event(fullA(B,k_3,x)) ==> event(fullB(A,k_3,x)) is false.)
RESULT inj-event(fullA(B,k_3,x)) ==> inj-event(partB2(A,x)) public_vars keyA,keyB is true.
RESULT inj-event(fullA(B,k_3,x)) ==> inj-event(partB(A,x)) public_vars keyA,keyB is false.
RESULT (query encoded as inj-event(fullA(B,k_3,x)) ==> inj-event(partB(A,x)))
RESULT (even event(fullA(B,k_3,x)) ==> event(partB(A,x)) is false.)
RESULT event(fullB(A,k_3,x)) && event(fullA(B,k',x)) ==> k_3 = k' public_vars keyA,keyB is false.
0.220s (user 0.200s + system 0.020s), max rss 12672K
END *)
