Skip to content

Commit 68ad625

Browse files
Filip Tehlarbganne
authored andcommitted
ikev2: fix reply during rekey
Type: fix Change-Id: If87f4b8ae92508215fe91178958fe2ddb91e5a35 Signed-off-by: Filip Tehlar <ftehlar@cisco.com>
1 parent 761f8f0 commit 68ad625

File tree

2 files changed

+192
-44
lines changed

2 files changed

+192
-44
lines changed

src/plugins/ikev2/ikev2.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3155,7 +3155,7 @@ ikev2_node_internal (vlib_main_t * vm,
31553155
ikev2_create_tunnel_interface (vm, sa0, child, p[0],
31563156
child - sa0->childs, 1);
31573157
}
3158-
if (sa0->is_initiator)
3158+
if (ike_hdr_is_response (ike0))
31593159
{
31603160
vec_free (sa0->rekey);
31613161
}

src/plugins/ikev2/test/test_ikev2.py

Lines changed: 191 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -151,10 +151,28 @@ def __init__(self, name, mac, mod, key_len, trunc_len=None):
151151
hashes.SHA256, 32),
152152
}
153153

154+
CRYPTO_IDS = {
155+
12: 'AES-CBC',
156+
20: 'AES-GCM-16ICV',
157+
}
158+
159+
INTEG_IDS = {
160+
2: 'HMAC-SHA1-96',
161+
12: 'SHA2-256-128',
162+
13: 'SHA2-384-192',
163+
14: 'SHA2-512-256',
164+
}
165+
154166

155167
class IKEv2ChildSA(object):
156-
def __init__(self, local_ts, remote_ts, spi=None):
157-
self.spi = spi or os.urandom(4)
168+
def __init__(self, local_ts, remote_ts, is_initiator):
169+
spi = os.urandom(4)
170+
if is_initiator:
171+
self.ispi = spi
172+
self.rspi = None
173+
else:
174+
self.rspi = spi
175+
self.ispi = None
158176
self.local_ts = local_ts
159177
self.remote_ts = remote_ts
160178

@@ -193,7 +211,8 @@ def __init__(self, test, is_initiator=True, i_id=None, r_id=None,
193211
self.rspi = spi
194212
self.ispi = 8 * b'\x00'
195213
self.r_nonce = nonce
196-
self.child_sas = [IKEv2ChildSA(local_ts, remote_ts)]
214+
self.child_sas = [IKEv2ChildSA(local_ts, remote_ts,
215+
self.is_initiator)]
197216

198217
def new_msg_id(self):
199218
self.msg_id += 1
@@ -559,9 +578,11 @@ def setUp(self):
559578
self.config_tc()
560579
self.p.add_vpp_config()
561580
self.assertIsNotNone(self.p.query_vpp_config())
562-
self.sa.generate_dh_data()
581+
if self.sa.is_initiator:
582+
self.sa.generate_dh_data()
563583
self.vapi.cli('ikev2 set logging level 4')
564584
self.vapi.cli('event-lo clear')
585+
self.vapi.cli('ikev2 dpd disable')
565586

566587
def create_packet(self, src_if, msg, sport=500, dport=500, natt=False,
567588
use_ip6=False):
@@ -641,16 +662,30 @@ def encrypt_ike_msg(self, header, plain, first_payload):
641662
assert(len(res) == tlen)
642663
return res
643664

644-
def verify_ipsec_sas(self):
665+
def verify_ipsec_sas(self, is_rekey=False):
645666
sas = self.vapi.ipsec_sa_dump()
646-
self.assertEqual(len(sas), 2)
667+
if is_rekey:
668+
# after rekey there is a short period of time in which old
669+
# inbound SA is still present
670+
sa_count = 3
671+
else:
672+
sa_count = 2
673+
self.assertEqual(len(sas), sa_count)
647674
e = VppEnum.vl_api_ipsec_sad_flags_t
648675
if self.sa.is_initiator:
649-
sa0 = sas[0].entry
650-
sa1 = sas[1].entry
676+
if is_rekey:
677+
sa0 = sas[0].entry
678+
sa1 = sas[2].entry
679+
else:
680+
sa0 = sas[0].entry
681+
sa1 = sas[1].entry
651682
else:
652-
sa1 = sas[0].entry
653-
sa0 = sas[1].entry
683+
if is_rekey:
684+
sa0 = sas[2].entry
685+
sa1 = sas[0].entry
686+
else:
687+
sa1 = sas[0].entry
688+
sa0 = sas[1].entry
654689

655690
c = self.sa.child_sas[0]
656691

@@ -738,6 +773,8 @@ def verify_ike_sas(self):
738773
self.verify_keymat(csa.keys, c, 'sk_ar')
739774
self.verify_keymat(csa.keys, c, 'sk_ei')
740775
self.verify_keymat(csa.keys, c, 'sk_er')
776+
self.assertEqual(csa.i_spi.to_bytes(4, 'big'), c.ispi)
777+
self.assertEqual(csa.r_spi.to_bytes(4, 'big'), c.rspi)
741778

742779
tsi, tsr = self.sa.generate_ts(self.p.ts_is_ip4)
743780
tsi = tsi[0]
@@ -888,9 +925,23 @@ def verify_sa_init_request(self, packet):
888925
self.p.ike_transforms['dh_group'])
889926

890927
self.verify_nat_detection(packet)
928+
self.sa.set_ike_props(
929+
crypto='AES-GCM-16ICV', crypto_key_len=32,
930+
integ='NULL', prf='PRF_HMAC_SHA2_256', dh='3072MODPgr')
931+
self.sa.set_esp_props(crypto='AES-CBC', crypto_key_len=32,
932+
integ='SHA2-256-128')
933+
self.sa.generate_dh_data()
891934
self.sa.complete_dh_data()
892935
self.sa.calc_keys()
893936

937+
def update_esp_transforms(self, trans, sa):
938+
while trans:
939+
if trans.transform_type == 1: # ecryption
940+
sa.esp_crypto = CRYPTO_IDS[trans.transform_id]
941+
elif trans.transform_type == 3: # integrity
942+
sa.esp_integ = INTEG_IDS[trans.transform_id]
943+
trans = trans.payload
944+
894945
def verify_sa_auth_req(self, packet):
895946
ih = self.get_ike_header(packet)
896947
self.assertEqual(ih.resp_SPI, self.sa.rspi)
@@ -908,6 +959,11 @@ def verify_sa_auth_req(self, packet):
908959
idr = ikev2.IKEv2_payload_IDr(idi.payload)
909960
self.assertEqual(idi.load, self.sa.i_id)
910961
self.assertEqual(idr.load, self.sa.r_id)
962+
prop = idi[ikev2.IKEv2_payload_Proposal]
963+
c = self.sa.child_sas[0]
964+
c.ispi = prop.SPI
965+
self.update_esp_transforms(
966+
prop[ikev2.IKEv2_payload_Transform], self.sa)
911967

912968
def send_init_response(self):
913969
tr_attr = self.sa.ike_crypto_attr()
@@ -961,8 +1017,9 @@ def send_auth_response(self):
9611017
transform_type='Extended Sequence Number',
9621018
transform_id='ESN'))
9631019

1020+
c = self.sa.child_sas[0]
9641021
props = (ikev2.IKEv2_payload_Proposal(proposal=1, proto='ESP',
965-
SPIsize=4, SPI=os.urandom(4), trans_nb=4, trans=trans))
1022+
SPIsize=4, SPI=c.rspi, trans_nb=4, trans=trans))
9661023

9671024
tsi, tsr = self.sa.generate_ts(self.p.ts_is_ip4)
9681025
plain = (ikev2.IKEv2_payload_IDi(next_payload='IDr',
@@ -1103,8 +1160,9 @@ def send_sa_init_req(self, behind_nat=False):
11031160
capture = self.pg0.get_capture(1)
11041161
self.verify_sa_init(capture[0])
11051162

1106-
def send_sa_auth(self):
1163+
def generate_auth_payload(self, last_payload=None, is_rekey=False):
11071164
tr_attr = self.sa.esp_crypto_attr()
1165+
last_payload = last_payload or 'Notify'
11081166
trans = (ikev2.IKEv2_payload_Transform(transform_type='Encryption',
11091167
transform_id=self.sa.esp_crypto, length=tr_attr[1],
11101168
key_length=tr_attr[0]) /
@@ -1117,32 +1175,45 @@ def send_sa_auth(self):
11171175
transform_type='Extended Sequence Number',
11181176
transform_id='ESN'))
11191177

1178+
c = self.sa.child_sas[0]
11201179
props = (ikev2.IKEv2_payload_Proposal(proposal=1, proto='ESP',
1121-
SPIsize=4, SPI=os.urandom(4), trans_nb=4, trans=trans))
1180+
SPIsize=4, SPI=c.ispi, trans_nb=4, trans=trans))
11221181

11231182
tsi, tsr = self.sa.generate_ts(self.p.ts_is_ip4)
1124-
plain = (ikev2.IKEv2_payload_IDi(next_payload='IDr',
1125-
IDtype=self.sa.id_type, load=self.sa.i_id) /
1126-
ikev2.IKEv2_payload_IDr(next_payload='AUTH',
1127-
IDtype=self.sa.id_type, load=self.sa.r_id) /
1128-
ikev2.IKEv2_payload_AUTH(next_payload='SA',
1183+
plain = (ikev2.IKEv2_payload_AUTH(next_payload='SA',
11291184
auth_type=AuthMethod.value(self.sa.auth_method),
11301185
load=self.sa.auth_data) /
11311186
ikev2.IKEv2_payload_SA(next_payload='TSi', prop=props) /
11321187
ikev2.IKEv2_payload_TSi(next_payload='TSr',
1133-
number_of_TSs=len(tsi),
1134-
traffic_selector=tsi) /
1135-
ikev2.IKEv2_payload_TSr(next_payload='Notify',
1136-
number_of_TSs=len(tsr),
1137-
traffic_selector=tsr) /
1138-
ikev2.IKEv2_payload_Notify(type='INITIAL_CONTACT'))
1188+
number_of_TSs=len(tsi), traffic_selector=tsi) /
1189+
ikev2.IKEv2_payload_TSr(next_payload=last_payload,
1190+
number_of_TSs=len(tsr), traffic_selector=tsr))
1191+
1192+
if is_rekey:
1193+
first_payload = 'Nonce'
1194+
plain = (ikev2.IKEv2_payload_Nonce(load=self.sa.i_nonce,
1195+
next_payload='SA') / plain /
1196+
ikev2.IKEv2_payload_Notify(type='REKEY_SA',
1197+
proto='ESP', SPI=c.ispi))
1198+
else:
1199+
first_payload = 'IDi'
1200+
ids = (ikev2.IKEv2_payload_IDi(next_payload='IDr',
1201+
IDtype=self.sa.id_type, load=self.sa.i_id) /
1202+
ikev2.IKEv2_payload_IDr(next_payload='AUTH',
1203+
IDtype=self.sa.id_type, load=self.sa.r_id))
1204+
plain = ids / plain
1205+
return plain, first_payload
11391206

1207+
def send_sa_auth(self):
1208+
plain, first_payload = self.generate_auth_payload(
1209+
last_payload='Notify')
1210+
plain = plain / ikev2.IKEv2_payload_Notify(type='INITIAL_CONTACT')
11401211
header = ikev2.IKEv2(
11411212
init_SPI=self.sa.ispi,
11421213
resp_SPI=self.sa.rspi, id=self.sa.new_msg_id(),
11431214
flags='Initiator', exch_type='IKE_AUTH')
11441215

1145-
ike_msg = self.encrypt_ike_msg(header, plain, 'IDi')
1216+
ike_msg = self.encrypt_ike_msg(header, plain, first_payload)
11461217
packet = self.create_packet(self.pg0, ike_msg, self.sa.sport,
11471218
self.sa.dport, self.sa.natt, self.ip6)
11481219
self.pg0.add_stream(packet)
@@ -1178,6 +1249,10 @@ def verify_sa_auth_resp(self, packet):
11781249
self.verify_udp(udp)
11791250
self.assertEqual(ike.id, self.sa.msg_id)
11801251
plain = self.sa.hmac_and_decrypt(ike)
1252+
idr = ikev2.IKEv2_payload_IDr(plain)
1253+
prop = idr[ikev2.IKEv2_payload_Proposal]
1254+
self.assertEqual(prop.SPIsize, 4)
1255+
self.sa.child_sas[0].rspi = prop.SPI
11811256
self.sa.calc_child_keys()
11821257

11831258
def test_responder(self):
@@ -1264,24 +1339,25 @@ def config_params(self, params={}):
12641339
auth_data=auth_data,
12651340
local_ts=self.p.remote_ts, remote_ts=self.p.local_ts)
12661341

1267-
ike_crypto = ('AES-CBC', 32) if 'ike-crypto' not in params else\
1268-
params['ike-crypto']
1269-
ike_integ = 'HMAC-SHA1-96' if 'ike-integ' not in params else\
1270-
params['ike-integ']
1271-
ike_dh = '2048MODPgr' if 'ike-dh' not in params else\
1272-
params['ike-dh']
1273-
1274-
esp_crypto = ('AES-CBC', 32) if 'esp-crypto' not in params else\
1275-
params['esp-crypto']
1276-
esp_integ = 'HMAC-SHA1-96' if 'esp-integ' not in params else\
1277-
params['esp-integ']
1278-
1279-
self.sa.set_ike_props(
1280-
crypto=ike_crypto[0], crypto_key_len=ike_crypto[1],
1281-
integ=ike_integ, prf='PRF_HMAC_SHA2_256', dh=ike_dh)
1282-
self.sa.set_esp_props(
1283-
crypto=esp_crypto[0], crypto_key_len=esp_crypto[1],
1284-
integ=esp_integ)
1342+
if is_init:
1343+
ike_crypto = ('AES-CBC', 32) if 'ike-crypto' not in params else\
1344+
params['ike-crypto']
1345+
ike_integ = 'HMAC-SHA1-96' if 'ike-integ' not in params else\
1346+
params['ike-integ']
1347+
ike_dh = '2048MODPgr' if 'ike-dh' not in params else\
1348+
params['ike-dh']
1349+
1350+
esp_crypto = ('AES-CBC', 32) if 'esp-crypto' not in params else\
1351+
params['esp-crypto']
1352+
esp_integ = 'HMAC-SHA1-96' if 'esp-integ' not in params else\
1353+
params['esp-integ']
1354+
1355+
self.sa.set_ike_props(
1356+
crypto=ike_crypto[0], crypto_key_len=ike_crypto[1],
1357+
integ=ike_integ, prf='PRF_HMAC_SHA2_256', dh=ike_dh)
1358+
self.sa.set_esp_props(
1359+
crypto=esp_crypto[0], crypto_key_len=esp_crypto[1],
1360+
integ=esp_integ)
12851361

12861362

12871363
class TestApi(VppTestCase):
@@ -1491,6 +1567,44 @@ def config_tc(self):
14911567
'integ_alg': 12}})
14921568

14931569

1570+
class TestInitiatorRekey(TestInitiatorPsk):
1571+
""" test ikev2 initiator - rekey """
1572+
1573+
def rekey_from_initiator(self):
1574+
ispi = int.from_bytes(self.sa.child_sas[0].ispi, 'little')
1575+
self.pg0.enable_capture()
1576+
self.pg_start()
1577+
self.vapi.ikev2_initiate_rekey_child_sa(ispi=ispi)
1578+
capture = self.pg0.get_capture(1)
1579+
ih = self.get_ike_header(capture[0])
1580+
self.assertEqual(ih.exch_type, 36) # CHILD_SA
1581+
self.assertNotIn('Response', ih.flags)
1582+
self.assertIn('Initiator', ih.flags)
1583+
plain = self.sa.hmac_and_decrypt(ih)
1584+
sa = ikev2.IKEv2_payload_SA(plain)
1585+
prop = sa[ikev2.IKEv2_payload_Proposal]
1586+
nonce = sa[ikev2.IKEv2_payload_Nonce]
1587+
self.sa.i_nonce = sa[ikev2.IKEv2_payload_Nonce].load
1588+
self.sa.r_nonce = self.sa.i_nonce
1589+
# update new responder SPI
1590+
self.sa.child_sas[0].ispi = prop.SPI
1591+
self.sa.child_sas[0].rspi = prop.SPI
1592+
self.sa.calc_child_keys()
1593+
header = ikev2.IKEv2(init_SPI=self.sa.ispi, resp_SPI=self.sa.rspi,
1594+
flags='Response', exch_type=36,
1595+
id=ih.id, next_payload='Encrypted')
1596+
resp = self.encrypt_ike_msg(header, sa, 'SA')
1597+
packet = self.create_packet(self.pg0, resp, self.sa.sport,
1598+
self.sa.dport, self.sa.natt, self.ip6)
1599+
self.send_and_assert_no_replies(self.pg0, packet)
1600+
1601+
def test_initiator(self):
1602+
super(TestInitiatorRekey, self).test_initiator()
1603+
self.rekey_from_initiator()
1604+
self.verify_ike_sas()
1605+
self.verify_ipsec_sas(is_rekey=True)
1606+
1607+
14941608
class TestInitiatorDelSAFromResponder(TemplateInitiator, Ikev2Params):
14951609
""" test ikev2 initiator - delete IKE SA from responder """
14961610

@@ -1529,6 +1643,40 @@ def config_tc(self):
15291643
self.config_params()
15301644

15311645

1646+
class TestResponderRekey(TestResponderPsk):
1647+
""" test ikev2 responder - rekey """
1648+
1649+
def rekey_from_initiator(self):
1650+
sa, first_payload = self.generate_auth_payload(is_rekey=True)
1651+
header = ikev2.IKEv2(
1652+
init_SPI=self.sa.ispi,
1653+
resp_SPI=self.sa.rspi, id=self.sa.new_msg_id(),
1654+
flags='Initiator', exch_type='CREATE_CHILD_SA')
1655+
1656+
ike_msg = self.encrypt_ike_msg(header, sa, first_payload)
1657+
packet = self.create_packet(self.pg0, ike_msg, self.sa.sport,
1658+
self.sa.dport, self.sa.natt, self.ip6)
1659+
self.pg0.add_stream(packet)
1660+
self.pg0.enable_capture()
1661+
self.pg_start()
1662+
capture = self.pg0.get_capture(1)
1663+
ih = self.get_ike_header(capture[0])
1664+
plain = self.sa.hmac_and_decrypt(ih)
1665+
sa = ikev2.IKEv2_payload_SA(plain)
1666+
prop = sa[ikev2.IKEv2_payload_Proposal]
1667+
nonce = sa[ikev2.IKEv2_payload_Nonce]
1668+
self.sa.r_nonce = sa[ikev2.IKEv2_payload_Nonce].load
1669+
# update new responder SPI
1670+
self.sa.child_sas[0].rspi = prop.SPI
1671+
1672+
def test_responder(self):
1673+
super(TestResponderRekey, self).test_responder()
1674+
self.rekey_from_initiator()
1675+
self.sa.calc_child_keys()
1676+
self.verify_ike_sas()
1677+
self.verify_ipsec_sas(is_rekey=True)
1678+
1679+
15321680
class TestResponderRsaSign(TemplateResponder, Ikev2Params):
15331681
""" test ikev2 responder - cert based auth """
15341682
def config_tc(self):

0 commit comments

Comments
 (0)