aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAtzm Watanabe <atzmism@gmail.com>2022-08-12 14:29:31 +0900
committerBeno�t Ganne <bganne@cisco.com>2022-08-18 08:03:13 +0000
commitc65921f7744a0da09ede876b6588628e3a188529 (patch)
treefb004fb74260e71a18d245d691c78cc81bb37889
parent138f65c5b7796c6401cde6ff92edc85f02c367a4 (diff)
ikev2: accept key exchange on CREATE_CHILD_SA
In RFC 7296, CREATE_CHILD_SA Exchange may contain the KE payload to enable stronger guarantees of forward secrecy. When the KEi payload is included in the CREATE_CHILD_SA request, responder should reply with the KEr payload and complete the key exchange, in accordance with the RFC. Type: improvement Signed-off-by: Atzm Watanabe <atzmism@gmail.com> Change-Id: I13cf6cf24359c11c3366757e585195bb7e999638
-rw-r--r--src/plugins/ikev2/ikev2.c188
-rw-r--r--src/plugins/ikev2/ikev2_priv.h1
-rw-r--r--test/test_ikev2.py62
3 files changed, 179 insertions, 72 deletions
diff --git a/src/plugins/ikev2/ikev2.c b/src/plugins/ikev2/ikev2.c
index 5c1aa58814e..c1b7efdc98e 100644
--- a/src/plugins/ikev2/ikev2.c
+++ b/src/plugins/ikev2/ikev2.c
@@ -568,7 +568,7 @@ ikev2_calc_keys (ikev2_sa_t * sa)
}
static void
-ikev2_calc_child_keys (ikev2_sa_t * sa, ikev2_child_sa_t * child)
+ikev2_calc_child_keys (ikev2_sa_t *sa, ikev2_child_sa_t *child, u8 kex)
{
u8 *s = 0;
u16 integ_key_len = 0;
@@ -587,6 +587,8 @@ ikev2_calc_child_keys (ikev2_sa_t * sa, ikev2_child_sa_t * child)
else
salt_len = sizeof (u32);
+ if (kex)
+ vec_append (s, sa->dh_shared_key);
vec_append (s, sa->i_nonce);
vec_append (s, sa->r_nonce);
/* calculate PRFplus */
@@ -1360,6 +1362,55 @@ ikev2_process_informational_req (vlib_main_t * vm,
}
static int
+ikev2_process_create_child_sa_rekey (ikev2_sa_t *sa, ikev2_sa_t *sar,
+ ikev2_rekey_t *rekey,
+ ikev2_sa_proposal_t *proposal,
+ ikev2_ts_t *tsi, ikev2_ts_t *tsr,
+ const u8 *nonce, int nonce_len)
+{
+ ikev2_sa_transform_t *tr;
+
+ rekey->i_proposal = proposal;
+ rekey->r_proposal = ikev2_select_proposal (proposal, IKEV2_PROTOCOL_ESP);
+
+ if (sar->dh_group)
+ {
+ tr =
+ ikev2_sa_get_td_for_type (rekey->r_proposal, IKEV2_TRANSFORM_TYPE_DH);
+
+ if (!tr || tr->dh_type != sar->dh_group)
+ {
+ rekey->notify_type = IKEV2_NOTIFY_MSG_INVALID_KE_PAYLOAD;
+ ikev2_sa_free_proposal_vector (&rekey->r_proposal);
+ return 0;
+ }
+
+ vec_free (sa->dh_shared_key);
+ vec_free (sa->dh_private_key);
+ vec_free (sa->i_dh_data);
+ vec_free (sa->r_dh_data);
+
+ sa->dh_group = sar->dh_group;
+ sa->i_dh_data = sar->i_dh_data;
+ sar->i_dh_data = 0;
+
+ ikev2_generate_dh (sa, tr);
+ rekey->kex = 1;
+ }
+
+ vec_reset_length (sa->i_nonce);
+ vec_add (sa->i_nonce, nonce, nonce_len);
+
+ vec_validate (sa->r_nonce, nonce_len - 1);
+ RAND_bytes ((u8 *) sa->r_nonce, nonce_len);
+
+ rekey->tsi = tsi;
+ rekey->tsr = tsr;
+
+ return 1;
+}
+
+static int
ikev2_process_create_child_sa_req (vlib_main_t * vm,
ikev2_sa_t * sa, ike_header_t * ike,
u32 len)
@@ -1378,6 +1429,9 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm,
u16 plen;
const u8 *nonce = 0;
int nonce_len = 0;
+ ikev2_sa_t sar;
+
+ clib_memset (&sar, 0, sizeof (sar));
if (sa->is_initiator)
src = ip_addr_v4 (&sa->raddr).as_u32;
@@ -1407,6 +1461,12 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm,
{
proposal = ikev2_parse_sa_payload (ikep, current_length);
}
+ else if (payload == IKEV2_PAYLOAD_KE)
+ {
+ if (!ikev2_parse_ke_payload (ikep, current_length, &sar,
+ &sar.i_dh_data))
+ goto cleanup_and_exit;
+ }
else if (payload == IKEV2_PAYLOAD_NOTIFY)
{
ikev2_notify_t *n0;
@@ -1493,6 +1553,7 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm,
}
vec_add2 (sa->rekey, rekey, 1);
rekey->notify_type = 0;
+ rekey->kex = 0;
rekey->protocol_id = n->protocol_id;
rekey->spi = n->spi;
if (sa->old_remote_id_present)
@@ -1502,17 +1563,12 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm,
vec_free (tsr);
vec_free (tsi);
}
- else
+ else if (!ikev2_process_create_child_sa_rekey (
+ sa, &sar, rekey, proposal, tsi, tsr, nonce, nonce_len))
{
- rekey->i_proposal = proposal;
- rekey->r_proposal =
- ikev2_select_proposal (proposal, IKEV2_PROTOCOL_ESP);
- /* update Ni */
- vec_reset_length (sa->i_nonce);
- vec_add (sa->i_nonce, nonce, nonce_len);
- /* generate new Nr */
- vec_validate (sa->r_nonce, nonce_len - 1);
- RAND_bytes ((u8 *) sa->r_nonce, nonce_len);
+ vec_free (proposal);
+ vec_free (tsr);
+ vec_free (tsi);
}
}
else
@@ -1520,20 +1576,18 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm,
/* create new child SA */
vec_add2 (sa->new_child, rekey, 1);
rekey->notify_type = 0;
- rekey->i_proposal = proposal;
- rekey->r_proposal =
- ikev2_select_proposal (proposal, IKEV2_PROTOCOL_ESP);
- /* update Ni */
- vec_reset_length (sa->i_nonce);
- vec_add (sa->i_nonce, nonce, nonce_len);
- /* generate new Nr */
- vec_validate (sa->r_nonce, nonce_len - 1);
- RAND_bytes ((u8 *) sa->r_nonce, nonce_len);
+ rekey->kex = 0;
+ if (!ikev2_process_create_child_sa_rekey (
+ sa, &sar, rekey, proposal, tsi, tsr, nonce, nonce_len))
+ {
+ vec_free (proposal);
+ vec_free (tsr);
+ vec_free (tsi);
+ }
}
- rekey->tsi = tsi;
- rekey->tsr = tsr;
}
vec_free (n);
+ ikev2_sa_free_all_vec (&sar);
return 1;
cleanup_and_exit:
@@ -1541,6 +1595,7 @@ cleanup_and_exit:
vec_free (proposal);
vec_free (tsr);
vec_free (tsi);
+ ikev2_sa_free_all_vec (&sar);
return 0;
}
@@ -2013,10 +2068,9 @@ err0:
}
static int
-ikev2_create_tunnel_interface (vlib_main_t * vm,
- ikev2_sa_t * sa,
- ikev2_child_sa_t * child, u32 sa_index,
- u32 child_index, u8 is_rekey)
+ikev2_create_tunnel_interface (vlib_main_t *vm, ikev2_sa_t *sa,
+ ikev2_child_sa_t *child, u32 sa_index,
+ u32 child_index, u8 is_rekey, u8 kex)
{
u32 thread_index = vlib_get_thread_index ();
ikev2_main_t *km = &ikev2_main;
@@ -2159,7 +2213,7 @@ ikev2_create_tunnel_interface (vlib_main_t * vm,
}
a.integ_type = integ_type;
- ikev2_calc_child_keys (sa, child);
+ ikev2_calc_child_keys (sa, child, kex);
if (sa->is_initiator)
{
@@ -2346,6 +2400,40 @@ ikev2_delete_tunnel_interface (vnet_main_t * vnm, ikev2_sa_t * sa,
return 0;
}
+static void
+ikev2_add_invalid_ke_payload (ikev2_sa_t *sa, ikev2_payload_chain_t *chain)
+{
+ u8 *data = vec_new (u8, 2);
+ ikev2_sa_transform_t *tr_dh =
+ ikev2_sa_get_td_for_type (sa->r_proposals, IKEV2_TRANSFORM_TYPE_DH);
+ ASSERT (tr_dh && tr_dh->dh_type);
+ data[0] = (tr_dh->dh_type >> 8) & 0xff;
+ data[1] = (tr_dh->dh_type) & 0xff;
+ ikev2_payload_add_notify (chain, IKEV2_NOTIFY_MSG_INVALID_KE_PAYLOAD, data);
+ vec_free (data);
+}
+
+static void
+ikev2_add_create_child_resp (ikev2_sa_t *sa, ikev2_rekey_t *rekey,
+ ikev2_payload_chain_t *chain)
+{
+ if (rekey->notify_type)
+ {
+ if (rekey->notify_type == IKEV2_NOTIFY_MSG_INVALID_KE_PAYLOAD)
+ ikev2_add_invalid_ke_payload (sa, chain);
+ else
+ ikev2_payload_add_notify (chain, rekey->notify_type, 0);
+ return;
+ }
+
+ ikev2_payload_add_sa (chain, rekey->r_proposal);
+ ikev2_payload_add_nonce (chain, sa->r_nonce);
+ if (rekey->kex)
+ ikev2_payload_add_ke (chain, sa->dh_group, sa->r_dh_data);
+ ikev2_payload_add_ts (chain, rekey->tsi, IKEV2_PAYLOAD_TSI);
+ ikev2_payload_add_ts (chain, rekey->tsr, IKEV2_PAYLOAD_TSR);
+}
+
static u32
ikev2_generate_message (vlib_buffer_t *b, ikev2_sa_t *sa, ike_header_t *ike,
void *user, udp_header_t *udp, ikev2_stats_t *stats)
@@ -2376,20 +2464,7 @@ ikev2_generate_message (vlib_buffer_t *b, ikev2_sa_t *sa, ike_header_t *ike,
}
else if (sa->dh_group == IKEV2_TRANSFORM_DH_TYPE_NONE)
{
- u8 *data = vec_new (u8, 2);
- ikev2_sa_transform_t *tr_dh;
- tr_dh =
- ikev2_sa_get_td_for_type (sa->r_proposals,
- IKEV2_TRANSFORM_TYPE_DH);
- ASSERT (tr_dh && tr_dh->dh_type);
-
- data[0] = (tr_dh->dh_type >> 8) & 0xff;
- data[1] = (tr_dh->dh_type) & 0xff;
-
- ikev2_payload_add_notify (chain,
- IKEV2_NOTIFY_MSG_INVALID_KE_PAYLOAD,
- data);
- vec_free (data);
+ ikev2_add_invalid_ke_payload (sa, chain);
ikev2_set_state (sa, IKEV2_STATE_NOTIFY_AND_DELETE);
}
else if (sa->state == IKEV2_STATE_NOTIFY_AND_DELETE)
@@ -2568,27 +2643,12 @@ ikev2_generate_message (vlib_buffer_t *b, ikev2_sa_t *sa, ike_header_t *ike,
}
else if (vec_len (sa->rekey) > 0)
{
- if (sa->rekey[0].notify_type)
- ikev2_payload_add_notify (chain, sa->rekey[0].notify_type, 0);
- else
- {
- ikev2_payload_add_sa (chain, sa->rekey[0].r_proposal);
- ikev2_payload_add_nonce (chain, sa->r_nonce);
- ikev2_payload_add_ts (chain, sa->rekey[0].tsi,
- IKEV2_PAYLOAD_TSI);
- ikev2_payload_add_ts (chain, sa->rekey[0].tsr,
- IKEV2_PAYLOAD_TSR);
- }
+ ikev2_add_create_child_resp (sa, &sa->rekey[0], chain);
vec_del1 (sa->rekey, 0);
}
else if (vec_len (sa->new_child) > 0)
{
- ikev2_payload_add_sa (chain, sa->new_child[0].r_proposal);
- ikev2_payload_add_nonce (chain, sa->r_nonce);
- ikev2_payload_add_ts (chain, sa->new_child[0].tsi,
- IKEV2_PAYLOAD_TSI);
- ikev2_payload_add_ts (chain, sa->new_child[0].tsr,
- IKEV2_PAYLOAD_TSR);
+ ikev2_add_create_child_resp (sa, &sa->new_child[0], chain);
vec_del1 (sa->new_child, 0);
}
else if (sa->unsupported_cp)
@@ -3211,9 +3271,8 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node,
ikev2_initial_contact_cleanup (ptd, sa0);
ikev2_sa_match_ts (sa0);
if (sa0->state != IKEV2_STATE_TS_UNACCEPTABLE)
- ikev2_create_tunnel_interface (vm, sa0,
- &sa0->childs[0],
- p[0], 0, 0);
+ ikev2_create_tunnel_interface (vm, sa0, &sa0->childs[0],
+ p[0], 0, 0, 0);
}
if (sa0->is_initiator)
@@ -3353,7 +3412,8 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node,
child->tsi = sa0->rekey[0].tsi;
child->tsr = sa0->rekey[0].tsr;
ikev2_create_tunnel_interface (vm, sa0, child, p[0],
- child - sa0->childs, 1);
+ child - sa0->childs, 1,
+ sa0->rekey[0].kex);
}
if (ike_hdr_is_response (ike0))
{
@@ -3382,7 +3442,8 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node,
c->tsi = sa0->new_child[0].tsi;
c->tsr = sa0->new_child[0].tsr;
ikev2_create_tunnel_interface (vm, sa0, c, p[0],
- c - sa0->childs, 0);
+ c - sa0->childs, 0,
+ sa0->new_child[0].kex);
if (ike_hdr_is_request (ike0))
{
ike0->flags = IKEV2_HDR_FLAG_RESPONSE;
@@ -4736,6 +4797,7 @@ ikev2_rekey_child_sa_internal (vlib_main_t * vm, ikev2_sa_t * sa,
ikev2_rekey_t *rekey;
vec_reset_length (sa->rekey);
vec_add2 (sa->rekey, rekey, 1);
+ rekey->kex = 0;
ikev2_sa_proposal_t *proposals = vec_dup (csa->i_proposals);
/*need new ispi */
diff --git a/src/plugins/ikev2/ikev2_priv.h b/src/plugins/ikev2/ikev2_priv.h
index 379b68dbdfc..4ce147890d5 100644
--- a/src/plugins/ikev2/ikev2_priv.h
+++ b/src/plugins/ikev2/ikev2_priv.h
@@ -313,6 +313,7 @@ typedef struct
typedef struct
{
u16 notify_type;
+ u8 kex;
u8 protocol_id;
u32 spi;
u32 ispi;
diff --git a/test/test_ikev2.py b/test/test_ikev2.py
index 3c588719581..ffddc79dbcf 100644
--- a/test/test_ikev2.py
+++ b/test/test_ikev2.py
@@ -296,9 +296,11 @@ class IKEv2SA(object):
def complete_dh_data(self):
self.dh_shared_secret = self.compute_secret()
- def calc_child_keys(self):
+ def calc_child_keys(self, kex=False):
prf = self.ike_prf_alg.mod()
s = self.i_nonce + self.r_nonce
+ if kex:
+ s = self.dh_shared_secret + s
c = self.child_sas[0]
encr_key_len = self.esp_crypto_key_len
@@ -625,8 +627,8 @@ class IkePeer(VppTestCase):
node_name = "/err/ikev2-%s/" % version + name
self.assertEqual(count, self.statistics.get_err_counter(node_name))
- def create_rekey_request(self):
- sa, first_payload = self.generate_auth_payload(is_rekey=True)
+ def create_rekey_request(self, kex=False):
+ sa, first_payload = self.generate_auth_payload(is_rekey=True, kex=kex)
header = ikev2.IKEv2(
init_SPI=self.sa.ispi,
resp_SPI=self.sa.rspi,
@@ -1345,9 +1347,10 @@ class TemplateResponder(IkePeer):
capture = self.pg0.get_capture(1)
self.verify_sa_init(capture[0])
- def generate_auth_payload(self, last_payload=None, is_rekey=False):
+ def generate_auth_payload(self, last_payload=None, is_rekey=False, kex=False):
tr_attr = self.sa.esp_crypto_attr()
last_payload = last_payload or "Notify"
+ trans_nb = 4
trans = (
ikev2.IKEv2_payload_Transform(
transform_type="Encryption",
@@ -1366,9 +1369,20 @@ class TemplateResponder(IkePeer):
)
)
+ if kex:
+ trans_nb += 1
+ trans /= ikev2.IKEv2_payload_Transform(
+ transform_type="GroupDesc", transform_id=self.sa.ike_dh
+ )
+
c = self.sa.child_sas[0]
props = ikev2.IKEv2_payload_Proposal(
- proposal=1, proto="ESP", SPIsize=4, SPI=c.ispi, trans_nb=4, trans=trans
+ proposal=1,
+ proto="ESP",
+ SPIsize=4,
+ SPI=c.ispi,
+ trans_nb=trans_nb,
+ trans=trans,
)
tsi, tsr = self.sa.generate_ts(self.p.ts_is_ip4)
@@ -1389,8 +1403,18 @@ class TemplateResponder(IkePeer):
if is_rekey:
first_payload = "Nonce"
+ if kex:
+ head = ikev2.IKEv2_payload_Nonce(
+ load=self.sa.i_nonce, next_payload="KE"
+ ) / ikev2.IKEv2_payload_KE(
+ group=self.sa.ike_dh, load=self.sa.my_dh_pub_key, next_payload="SA"
+ )
+ else:
+ head = ikev2.IKEv2_payload_Nonce(
+ load=self.sa.i_nonce, next_payload="SA"
+ )
plain = (
- ikev2.IKEv2_payload_Nonce(load=self.sa.i_nonce, next_payload="SA")
+ head
/ plain
/ ikev2.IKEv2_payload_Notify(
type="REKEY_SA",
@@ -2079,8 +2103,12 @@ class TestResponderDpd(TestResponderPsk):
class TestResponderRekey(TestResponderPsk):
"""test ikev2 responder - rekey"""
+ WITH_KEX = False
+
def send_rekey_from_initiator(self):
- packet = self.create_rekey_request()
+ if self.WITH_KEX:
+ self.sa.generate_dh_data()
+ packet = self.create_rekey_request(kex=self.WITH_KEX)
self.pg0.add_stream(packet)
self.pg0.enable_capture()
self.pg_start()
@@ -2095,11 +2123,14 @@ class TestResponderRekey(TestResponderPsk):
self.sa.r_nonce = sa[ikev2.IKEv2_payload_Nonce].load
# update new responder SPI
self.sa.child_sas[0].rspi = prop.SPI
+ if self.WITH_KEX:
+ self.sa.r_dh_data = sa[ikev2.IKEv2_payload_KE].load
+ self.sa.complete_dh_data()
+ self.sa.calc_child_keys(kex=self.WITH_KEX)
def test_responder(self):
super(TestResponderRekey, self).test_responder()
self.process_rekey_response(self.send_rekey_from_initiator())
- self.sa.calc_child_keys()
self.verify_ike_sas()
self.verify_ipsec_sas(is_rekey=True)
self.assert_counter(1, "rekey_req", "ip4")
@@ -2128,11 +2159,24 @@ class TestResponderRekeyRepeat(TestResponderRekey):
else:
self.fail("old IPsec SA not expired")
self.process_rekey_response(self.send_rekey_from_initiator())
- self.sa.calc_child_keys()
self.verify_ike_sas()
self.verify_ipsec_sas(sa_count=3)
+@tag_fixme_vpp_workers
+class TestResponderRekeyKEX(TestResponderRekey):
+ """test ikev2 responder - rekey with key exchange"""
+
+ WITH_KEX = True
+
+
+@tag_fixme_vpp_workers
+class TestResponderRekeyRepeatKEX(TestResponderRekeyRepeat):
+ """test ikev2 responder - rekey repeat with key exchange"""
+
+ WITH_KEX = True
+
+
class TestResponderVrf(TestResponderPsk, Ikev2Params):
"""test ikev2 responder - non-default table id"""