%
needhamschroeder: CONTEXT =
BEGIN
ids: TYPE = {a, b, e, X};
participants: TYPE = {x: ids | x /= X};
intruder(x: participants): BOOLEAN = x=e;
intruders: TYPE = {x: participants | intruder(x)};
principals: TYPE = {x: participants | NOT intruder(x)};
nonces: TYPE = ids;
nonce(a: participants): nonces = a;
dmsg: TYPE = [ids, nonces, nonces];
arb: dmsg = (X,X,X);
emsg: TYPE = DATATYPE
enc(key: ids, payload: dmsg)
END;
msg: TYPE = [# src: participants, dest: participants, em: emsg #];
net: CONTEXT = network{msg;};
dec(k: participants, m:emsg): dmsg =
IF key(m)=k THEN payload(m) ELSE arb ENDIF;
states: TYPE = {sleeping, waiting, tentative, engaged, responding};
principal[i: principals]: MODULE =
BEGIN
INPUT nstate: net!bufferstate, imsg: msg
GLOBAL act: net!action, omsg: msg
LOCAL pc: states, responder: participants
INITIALIZATION
pc = sleeping;
responder = i;
TRANSITION
[
([] (j: participants): i /= j AND
pc = sleeping AND nstate = net!empty -->
pc' = waiting;
responder' = j;
omsg' = (# src := i, dest := j, em := enc(j, (i, nonce(i), X)) #);
act' = net!write;
)
[]
([] (j: participants): i /= j AND
pc = sleeping AND nstate = net!full
AND imsg.src = j AND imsg.dest = i AND dec(i, imsg.em).1=j -->
responder' = j;
pc' = tentative;
act' = net!overwrite;
omsg' = (# src := i, dest := j,
% following line is the bug
em := enc(j, (X, dec(i,imsg.em).2, nonce(i))) #);
% To correct, replace by the following and add a check to the next rule
% em := enc(j, (i, dec(i,imsg.em).2, nonce(i))) #);
)
[]
pc = waiting AND nstate = net!full
AND imsg.src = responder AND imsg.dest = i
% Add this check to correct the bug
% AND dec(i,imsg.em).1 = responder
AND dec(i,imsg.em).2 = nonce(i) -->
pc' = engaged;
act' = net!overwrite;
omsg' = (# src := i, dest := responder,
em := enc(responder, (X, dec(i,imsg.em).3, X)) #);
[]
pc = tentative AND nstate = net!full
AND imsg.src = responder AND imsg.dest = i
AND dec(i,imsg.em).3 = nonce(i) -->
pc' = responding;
act' = net!read;
[]
ELSE -->
]
END;
intruder[x:intruders]: MODULE =
BEGIN
GLOBAL act: net!action, omsg: msg
INPUT nstate: net!bufferstate, imsg: msg
LOCAL nmem, n1, n2: nonces, mmem: emsg
INITIALIZATION
nmem = nonce(e);
% n1 = nonce(e); n2 = nonce(e);
mmem = enc(X,(X,X,X));
TRANSITION
[
nstate = net!full AND imsg.dest = x -->
% this is too weak, but adequate to find the bug--see the discussion in the report.
nmem' IN {dec(x,imsg.em).2, nmem};
% this is the stronger version
% nmem' IN {dec(x,imsg.em).2, dec(x,imsg.em).3, dec(x,imsg.em).2, nmem};
act' IN {net!read, net!copy};
[]
nstate = net!full AND imsg.dest /= x -->
mmem' IN {imsg.em, mmem};
act' IN {net!read, net!copy};
[]
([] (i: participants, j: principals): TRUE -->
act' = IF nstate = net!empty THEN net!write ELSE net!overwrite ENDIF;
omsg' = (# src := i, dest := j, em := mmem#);
)
[]
([] (i: participants, j: principals): TRUE -->
act' = IF nstate = net!empty THEN net!write ELSE net!overwrite ENDIF;
n1' IN {nmem, nonce(x)};
n2' IN {nmem, nonce(x)};
omsg' = (# src := i, dest := j, em := enc(j, (i, n1', n2')) #);
)
[]
ELSE -->
]
END;
system: MODULE = (([] (id: principals): principal[id]) [] intruder[e])
|| (RENAME buffer TO imsg, inms TO omsg IN net!network);
prop: THEOREM system |- G((FORALL (x,y: principals):
(pc[x]=responding AND responder[x]=y) =>
((pc[y]=waiting OR pc[y]=engaged) AND responder[y]=x)));
test: THEOREM system |- G(NOT (pc[a]=tentative AND pc[b]=tentative));
test2: THEOREM system |- G(NOT (pc[a]=responding AND responder[a]=e));
END