(* Denning-Sacco key distribution protocol. (original, flawed version) *)

(*
Secrecy of keyA is wrong here, because the adversary can register the
public signature key of A with another identity C. Then
A sends (A, pkA, certA, B, pkB, certB, { {k}skA }pkB)
the adversary sends to B (C, pkA, certC, B, pkB, certB, { {k}skA}pkB)
which is ok, because pkA is also the public key of C, so
B publishes k since he thinks it is a key with C.

query k:shkey, x:bitstring;
	event fullB(A,k,x) ==> fullA(B,k,x).
is wrong due to the standard attack against Denning-Sacco.

query k:shkey, k':shkey, x:bitstring;
	event fullB(A,k,x) && fullA(B,k',x) ==> k = k'.
is true.
*)

(* In order to determine the proof, use interactive mode:
   set interactiveMode = true.
The proof is as follows: *)

proof {
  success; (* for fullB(A,k,x) && fullA(B,k',x) ==> k = k'. *)
  crypto ind_cca2(enc) rkB;
  crypto uf_cma(sign) rkS;
  crypto uf_cma(sign) rkA;
  success;
  auto
}

param NA, NB, NS, NSS, NK, NSK.

type nonce [large,fixed].
type host [bounded].
type pkey [bounded].
type skey [bounded].
type keyseed [large,fixed].
type spkey [bounded].
type sskey [bounded].
type skeyseed [large,fixed].
type signature [bounded].
type blocksize [fixed].
type blocksizebot [bounded].
type sblocksize [bounded].
type shkey [fixed]. (* distributed key *)

fun concat1(shkey, signature):blocksize [data].
fun concat3(host, pkey):sblocksize [data].
fun concat4(host, spkey):sblocksize [data].
fun pad(shkey):sblocksize [data].

equation forall y:host,z:pkey,y2:shkey;
	concat3(y,z) <> pad(y2).
equation forall y:host,z:pkey,y2:host,z2:spkey;
	concat3(y,z) <> concat4(y2,z2).
equation forall y:host,z:spkey,y2:shkey;
        concat4(y,z) <> pad(y2).

(* Public-key encryption (CCA2) *)

proba Penc.
proba Penccoll.

expand IND_CCA2_public_key_enc(keyseed, pkey, skey, blocksize, bitstring, skgen, pkgen, enc, dec, injbot, Z, Penc, Penccoll).

const Zblocksize: blocksize.
equation forall x: blocksize; Z(x) = Zblocksize.

(* Signatures *)

proba Psign.
proba Psigncoll.

expand UF_CMA_proba_signature(skeyseed, spkey, sskey, sblocksize, signature, sskgen, spkgen, sign, check, Psign, Psigncoll).

(* Tables *)

table enc_key_tbl(host, pkey).
table sign_key_tbl(host, spkey).

(* Queries *)

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

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

event fullA(host, shkey, bitstring).
event fullB(host, shkey, bitstring).

query secret keyA.
query x: bitstring, k: shkey;
  inj-event(fullB(A, k, x)) ==> inj-event(fullA(B, k, x)) public_vars keyA. (* WRONG: replay attack *)
query x: bitstring, k: shkey;
  event(fullB(A, k, x)) ==> event(fullA(B, k, x)) public_vars keyA.
query k': shkey, x: bitstring, k: shkey;
  event(fullB(A, k, x)) && event(fullA(B, k', x)) ==> (k = k') public_vars keyA.
  
let processA(pkA: spkey, skA: sskey, pkS: spkey) =
	in(c1, (=A, =pkA, cert1:signature, hostX: host, pkX:pkey, cert2:signature));
	if check(concat4(A, pkA), pkS, cert1) then
	if check(concat3(hostX, pkX), pkS, cert2) then
	new k:shkey;
	let m1enc = enc(concat1(k, sign(pad(k), skA)), pkX) in
	let m1 = (A, pkA, cert1, hostX, pkX, cert2, m1enc) in
	(* Last message and OK *)
	event fullA(hostX, k, (A, pkA, hostX, pkX, m1enc));
	if hostX = B then
		(let keyA:shkey = k in out(c2, (m1, acceptA(hostX))))
	else
		(out(c2, (m1, acceptA(hostX))); in(cAK, ()); out(cAK, k)).

let processB(pkB: pkey, skB: skey, pkS: spkey) =
	in(c3, (hostY:host, pkY:spkey, cert1:signature, =B, =pkB, cert2:signature, m:bitstring));
	if check(concat4(hostY, pkY), pkS, cert1) then
	if check(concat3(B, pkB), pkS, cert2) then
        let injbot(concat1(k, s)) = dec(m,skB) in
	if check(pad(k), pkY, s) then
	(* OK *)
	event fullB(hostY, k, (hostY, pkY, B, pkB, m));
        if hostY = A then
		(out(c9, acceptB(hostY)))
	else
		(out(c9, acceptB(hostY)); in(cBK, ()); out(cBK, k)).

let processK(pkB: pkey) =
	in(c4, (Khost: host, Kkey: pkey));
	if Khost = B then
	   insert enc_key_tbl(B, pkB)
	else
	   insert enc_key_tbl(Khost, Kkey).

let processS(skS: sskey) =
	in(c5, (h1: host, h2: host));
	get enc_key_tbl(=h2, kh2) in
	out(c6, (h2, kh2, sign(concat3(h2, kh2), skS))).

let processSK(pkA: spkey) =
	in(c10, (SKhost: host, SKkey: spkey));
	if SKhost = A then
	   insert sign_key_tbl(A, pkA)
	else
	   insert sign_key_tbl(SKhost, SKkey).

let processSS(skS: sskey) =
	in(c11, (h1: host, h2: host));
	get sign_key_tbl(=h2, kh2) in
	out(c12, (h2, kh2, sign(concat4(h2, kh2), skS))).

process
	in(start, ());
	new rkA: skeyseed;
	let pkA = spkgen(rkA) in
	let skA = sskgen(rkA) in
	new rkB: keyseed;
	let pkB = pkgen(rkB) in
	let skB = skgen(rkB) in
	new rkS: skeyseed;
	let pkS = spkgen(rkS) in
	let skS = sskgen(rkS) in
	out(c7, (pkA, pkB, pkS));
	((! i <= NA processA(pkA, skA, pkS)) |
	 (! i <= NB processB(pkB, skB, pkS)) |
	 (! i <= NS processS(skS)) |
	 (! i <= NSS processSS(skS)) |
	 (! i <= NK processK(pkB)) |
	 (! i <= NSK processSK(pkA)))


(* EXPECTED
RESULT Could not prove event(fullB(A, k, x)) ==> event(fullA(B, k, x)) with public variables keyA; inj-event(fullB(A, k, x)) ==> inj-event(fullA(B, k, x)) with public variables keyA; secrecy of keyA.
0.796s (user 0.788s + system 0.008s), max rss 28308K
END *)

(* EXPECTPV
RESULT secret keyA is false.
RESULT inj-event(fullB(A,k_2,x)) ==> inj-event(fullA(B,k_2,x)) public_vars keyA is false.
RESULT (query encoded as inj-event(fullB(A,k_2,x)) ==> inj-event(fullA(B,k_2,x)))
RESULT (even event(fullB(A,k_2,x)) ==> event(fullA(B,k_2,x)) is false.)
RESULT event(fullB(A,k_2,x)) ==> event(fullA(B,k_2,x)) public_vars keyA is false.
RESULT event(fullB(A,k_2,x)) && event(fullA(B,k',x)) ==> k_2 = k' public_vars keyA is true.
0.244s (user 0.228s + system 0.016s), max rss 14324K
END *)
