(*

Woo and Lam public-key auth protocol (1992, computer)

A -> B: A
B -> A: N
A -> B: { N }_skA
B -> S: A
S -> B: A, { A, pkA }_skS

Attack
Full agreement is true, but there is an attack !
(Indeed, the messages never mention B.)

Session identifier = (A, N)
Authentification in one direction only.
The adversary can forge a signature for a message that has already
been signed, so the signature {N}_skA is not authenticated.

*)

param NA, NB, NS, NK.

type nonce [large,fixed].
type host [bounded].
type skey [bounded].
type pkey [bounded].
type keyseed [large,fixed].
type signature [bounded].
type blocksize [fixed].
type blocksizebot [bounded].

fun pad(nonce):blocksize [data].
fun concat2(host, pkey):blocksize [data].

equation forall z:nonce, x2:host, y2:pkey;
	pad(z) <> concat2(x2,y2).

(* Signatures *)

proba Psign.
proba Psigncoll.

expand UF_CMA_proba_signature(keyseed, pkey, skey, blocksize, signature, sskgen, spkgen, sign, check, Psign, Psigncoll).

(* Queries *)

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

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

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

query y:bitstring;
	inj-event(fullB(A, y)) ==> inj-event(fullA(B,y)).

let processA(skA: skey) =
	in(c1, hostB0:host);
	out(c2, A);
	in(c3, n:nonce);
	let m3 = sign(pad(n), skA) in
	event fullA(hostB0, (A, n));
	out(c4, (m3, acceptA(hostB0))).

let processB(pkS: pkey) =
	in(c5, hostA0:host);
	new n:nonce;
	out(c6, n);
	in(c7, m:signature);
	out(c8, hostA0);
	in(c9, (=hostA0, pkA2:pkey, m4:signature));
	if check(concat2(hostA0, pkA2), pkS, m4) then
	if check(pad(n), pkA2, m) then
	(* OK *)
	event fullB(hostA0, (hostA0, n));
        out(c14, acceptB(hostA0)).


table keys(host, pkey).

let processS(skS: skey) =
	in(c10, hostA:host);
	get keys(=hostA, pka) in
	out(c11, (hostA, pka, sign(concat2(hostA, pka), skS))).

let processK(pkA: pkey, pkB: pkey) =
	in(c12, (Khost: host, Kkey: pkey));
	if Khost = A then insert keys(A, pkA) else
	if Khost = B then insert keys(B, pkB) else
	insert keys(Khost, Kkey).

process 
	in(start, ());
	new rkA: keyseed;
	let pkA = spkgen(rkA) in
	let skA = sskgen(rkA) in
	new rkB: keyseed;
	let pkB = spkgen(rkB) in
	let skB = sskgen(rkB) in
	new rkS: keyseed;
	let pkS = spkgen(rkS) in
	let skS = sskgen(rkS) in
	out(c13, (pkA, pkB, pkS));
	((! iA <= NA processA(skA)) |
	 (! iB <= NB processB(pkS)) | 
	 (! iS <= NS processS(skS)) |
	 (! iK <= NK processK(pkA,pkB)))


(* EXPECTED
RESULT Could not prove inj-event(fullB(A, y)) ==> inj-event(fullA(B, y)).
0.180s (user 0.172s + system 0.008s), max rss 19012K
END *)

(* EXPECTPV
RESULT inj-event(fullB(A,y)) ==> inj-event(fullA(B,y)) is false.
RESULT (even event(fullB(A,y)) ==> event(fullA(B,y)) is false.)
0.024s (user 0.016s + system 0.008s), max rss 9964K
END *)
