I have the following simplified public-key Needham-Schroeder protocol:
A → B: {Na, A} Kb
B → A: {Na, Nb} Ka
A → B: {Nb} Kb
where Na
, Nb
are the nonces of A
, B
, and Ka
, Kb
are the public keys of A
, B
respectively.
Messages encrypted by a party’s public key can only be decrypted by the party.
At Step (1),
A
initiates the protocol by sending a nonce and its identity (encrypted byB
’s public key) toB
. Using its private key,B
deciphers the message and getsA
’s identity.At Step (2),
B
sendsA
’s and its nonces (encrypted byA
’s public key) back toA
. Using its private key,A
decodes the message and checks its nonce is returned.At Step (3),
A
returnsB
’s nonce (encrypted byB
’s public key) back toB
.
Here is the main-in-the-middle attack to the above simplified protocol:
- (1A)
A → E: {Na, A} Ke
(A
wants to talk toE
) - (1B)
E → B: {Na, A} Kb
(E
wants to convinceB
that it isA
) - (2B)
B → E: {Na, Nb} Ka
(B
returns nonces encrypted byKa
) - (2A)
E → A: {Na, Nb} Ka
(E
forwards the encrypted message toA
) - (3A)
A → E: {Nb} Ke
(A
confirms it is talking toE
) - (3B)
E → B: {Nb} Kb
(E
returnsB
’s nonce back)
I hope that when the attack was found, a fix was proposed to prevent the attack (B
sends its identity along with the nonces back to A
):
A → B: {Na, A} Kb
B → A: {Na, Nb, B} Ka
(B
sends its identity along with the nonces back toA
)A → B: {Nb} Kb
The questions are:
- How can I write an LTL formula and a NuSMV module
eve
to model the attacker and witness the man-in-the middle attack? - How to prevents the attack?
The process of alice(A):
MODULE alice (in0, in1, inkey, out0, out1, outkey)
VAR
st : { request, wait, attack, finish };
nonce : { NONE, Na, Nb, Ne };
ASSIGN
init (st) := request;
next (st) := case
st = request : wait;
st = wait & in0 = Na & inkey = Ka : attack;
st = attack : finish;
TRUE : st;
esac;
init (nonce) := NONE;
next (nonce) := case
st = wait & in0 = Na & inkey = Ka : in1;
TRUE : nonce;
esac;
init (out0) := NONE;
next (out0) := case
st = request : Na;
st = attack : nonce;
TRUE : out0;
esac;
init (out1) := NOONE;
next (out1) := case
st = request : Ia;
st = attack : NOONE;
TRUE : out1;
esac;
init (outkey) := NOKEY;
next (outkey) := case
st = request : { Kb };
TRUE : outkey;
esac;
FAIRNESS running;
The process of bob(B):
MODULE bob (in0, in1, inkey, out0, out1, outkey)
VAR
st : { wait, receive, confirm, done };
nonce : { NONE, Na, Nb, Ne };
other : { NOONE, Ia, Ib, Ie };
ASSIGN
init (st) := wait;
next (st) := case
st = wait & in0 = Na & in1 = Ia & inkey = Kb : receive;
st = wait & in0 = Ne & in1 = Ie & inkey = Kb : receive;
st = receive : confirm;
st = confirm & in0 = Nb & in1 = NOONE & inkey = Kb : done;
TRUE : st;
esac;
init (nonce) := NONE;
next (nonce) := case
st = wait & in0 = Na & in1 = Ia & inkey = Kb : in0;
st = wait & in0 = Ne & in1 = Ie & inkey = Kb : in0;
TRUE : nonce;
esac;
init (other) := NOONE;
next (other) := case
st = wait & in0 = Na & in1 = Ia & inkey = Kb : in1;
st = wait & in0 = Ne & in1 = Ie & inkey = Kb : in1;
TRUE : other;
esac;
init (out0) := NONE;
next (out0) := case
st = confirm : nonce;
TRUE : out0;
esac;
init (out1) := NONE;
next (out1) := case
st = confirm : Nb;
TRUE : out1;
esac;
init (outkey) := NOKEY;
next (outkey) := case
st = confirm & other = Ia : Ka;
st = confirm & other = Ie : Ke;
TRUE : outkey;
esac;
FAIRNESS running;
The process of main:
MODULE main
VAR
a_in0 : { NONE, Na, Nb, Ne };
a_in1 : { NONE, Na, Nb, Ne };
a_out0 : { NONE, Na, Nb, Ne };
a_out1 : { NOONE, Ia, Ib, Ie };
a_inkey : { NOKEY, Ka, Kb, Ke };
a_outkey : { NOKEY, Ka, Kb, Ke };
a : process alice (a_in0, a_in1, a_inkey, a_out0, a_out1, a_outkey);
b : process bob (a_out0, a_out1, a_outkey, a_in0, a_in1, a_inkey);
FAIRNESS running;
LTLSPEC F (a.st = finish & b.st = done)
Thanks a lot!
(note: modeling and verifying the system you have in mind with some other tool (e.g.
Spin
or theSTIATE Toolkit
) would be much more simple.)Alice and Bob.
Here we model the type of
user
that behaves in a honest, transparent manner and that in your use-case can be instantiated as eitherAlice
orBob
.As a simplification, i hard-coded the fact that if the
user
isAlice
then it will initiate the protocol by trying to contact the other entity.The inputs
my_nonce
,my_id
andmy_key
define auser
's identity, whereasother_key
andother_id
represent the publicly available information about the otheruser
we want to get in touch with. Inputsin_1
,in_2
andin_k
are just like in your code example, whereasin_3
is reserved for exchanging the third value used in the patched version of the protocol.A
user
can be in one of five states:IDLE
: initial state,Alice
will initiate the protocol whereasBob
waits for some request.WAIT_RESPONSE
: whenAlice
waits to a response to herrequest
WAIT_CONFIRMATION
: whenBob
waits to a confirmation to hisresponse
OK
: whenAlice
andBob
handshake has been successfulERROR
: when something goes wrong in the handshake (e.g. unexpected inputs)A user can perform one of the following actions:
SEND_REQUEST
:{Na, IA} Kb
SEND_RESPONSE
:{Na, Nb} Ka
SEND_CONFIRMATION
:{Nb} Kb
As a simplification, similarly to your own model, I made output values persistent along several transitions instead of putting them back immediately to
NONE
. In this way, I don't have to add extra variables to keep track of input values before they are resetted.We add a very simple
main
module and a simpleCTL
property to check thatAlice
andBob
behave in the expected way and are able to complete the handshake in normal circumstances:The output is the following one:
Alice, Bob and Eve.
In order to model our attack scenario, we first need to model the
attacker
. This is very similar toAlice
andBob
, only that it has doubleinputs
andoutputs
so that it can communicate with bothAlice
andBob
at the same time.Its design is very similar to that of
Alice
andBob
, so I won't spend many words on it. As a simplification, i removed any error checking on theattacker
, since it does not actually have any meaningful reason to fail in the use-case scenario being considered. Not doing so would complicate the code for no good reason.Again, we add a very simple
main
module and a simpleCTL
property to check thatEve
is able to successfully attackAlice
andBob
.. at the end of it,Alice
thinks to be talking toEve
(as it is) andBob
thinks to be talking withAlice
when it's truly talking withEve
.The output follows:
Patched Alice, Bob and Eve.
Luckily, I already sneaked the patched version of
Alice
andBob
in the code that I have already shown. So, all that it remains to do is to check that the patch meets the desired behavior by writing a newmain
that combinesAlice
,Bob
andEve
together:The output follows:
The first property confirms that the attack fails and the handshake is not completed for neither
Alice
norBob
becauseEve
does not fulfil it. The second property shows how the attack is attempted and how it fails in practice.