diff options
-rw-r--r-- | src/plugins/ikev2/ikev2.c | 234 | ||||
-rw-r--r-- | src/plugins/ikev2/ikev2_payload.c | 30 | ||||
-rw-r--r-- | src/plugins/ikev2/ikev2_priv.h | 25 | ||||
-rw-r--r-- | test/test_ikev2.py | 176 |
4 files changed, 417 insertions, 48 deletions
diff --git a/src/plugins/ikev2/ikev2.c b/src/plugins/ikev2/ikev2.c index ad36068c34d..4ee2f60b085 100644 --- a/src/plugins/ikev2/ikev2.c +++ b/src/plugins/ikev2/ikev2.c @@ -211,6 +211,8 @@ ikev2_select_proposal (ikev2_sa_proposal_t * proposals, rv->proposal_num = proposal->proposal_num; rv->protocol_id = proposal->protocol_id; RAND_bytes ((u8 *) & rv->spi, sizeof (rv->spi)); + if (rv->protocol_id != IKEV2_PROTOCOL_IKE) + rv->spi &= 0xffffffff; goto done; } else @@ -480,11 +482,10 @@ ikev2_complete_sa_data (ikev2_sa_t * sa, ikev2_sa_t * sai) } static void -ikev2_calc_keys (ikev2_sa_t * sa) +ikev2_calc_keys_internal (ikev2_sa_t *sa, u8 *skeyseed) { u8 *tmp; /* calculate SKEYSEED = prf(Ni | Nr, g^ir) */ - u8 *skeyseed = 0; u8 *s = 0; u16 integ_key_len = 0, salt_len = 0; ikev2_sa_transform_t *tr_encr, *tr_prf, *tr_integ; @@ -502,7 +503,6 @@ ikev2_calc_keys (ikev2_sa_t * sa) vec_append (s, sa->i_nonce); vec_append (s, sa->r_nonce); - skeyseed = ikev2_calc_prf (tr_prf, s, sa->dh_shared_key); /* Calculate S = Ni | Nr | SPIi | SPIr */ u64 *spi; @@ -520,7 +520,6 @@ ikev2_calc_keys (ikev2_sa_t * sa) salt_len * 2; keymat = ikev2_calc_prfplus (tr_prf, skeyseed, s, len); - vec_free (skeyseed); vec_free (s); int pos = 0; @@ -568,6 +567,41 @@ ikev2_calc_keys (ikev2_sa_t * sa) } static void +ikev2_calc_keys_rekey (ikev2_sa_t *sa_new, ikev2_sa_t *sa_old) +{ + u8 *s = 0, *skeyseed = 0; + ikev2_sa_transform_t *tr_prf = + ikev2_sa_get_td_for_type (sa_old->r_proposals, IKEV2_TRANSFORM_TYPE_PRF); + + vec_append (s, sa_new->dh_shared_key); + vec_append (s, sa_new->i_nonce); + vec_append (s, sa_new->r_nonce); + skeyseed = ikev2_calc_prf (tr_prf, sa_old->sk_d, s); + + ikev2_calc_keys_internal (sa_new, skeyseed); + + vec_free (skeyseed); + vec_free (s); +} + +static void +ikev2_calc_keys (ikev2_sa_t *sa) +{ + u8 *s = 0, *skeyseed = 0; + ikev2_sa_transform_t *tr_prf = + ikev2_sa_get_td_for_type (sa->r_proposals, IKEV2_TRANSFORM_TYPE_PRF); + + vec_append (s, sa->i_nonce); + vec_append (s, sa->r_nonce); + skeyseed = ikev2_calc_prf (tr_prf, s, sa->dh_shared_key); + + ikev2_calc_keys_internal (sa, skeyseed); + + vec_free (skeyseed); + vec_free (s); +} + +static void ikev2_calc_child_keys (ikev2_sa_t *sa, ikev2_child_sa_t *child, u8 kex) { u8 *s = 0; @@ -1410,6 +1444,110 @@ ikev2_process_create_child_sa_rekey (ikev2_sa_t *sa, ikev2_sa_t *sar, return 1; } +static void +ikev2_complete_sa_rekey (ikev2_sa_t *sa_new, ikev2_sa_t *sa_old, + ikev2_sa_rekey_t *sa_rekey) +{ + sa_new->del = 0; + sa_new->rekey = 0; + sa_new->new_child = 0; + sa_new->sa_rekey = 0; + sa_new->last_sa_init_req_packet_data = 0; + sa_new->last_sa_init_res_packet_data = 0; + sa_new->last_msg_id = ~0; + sa_new->last_res_packet_data = 0; + sa_new->last_init_msg_id = 0; + clib_memset (&sa_new->stats, 0, sizeof (sa_new->stats)); + + sa_new->ispi = sa_rekey->ispi; + sa_new->rspi = sa_rekey->rspi; + sa_new->i_nonce = sa_rekey->i_nonce; + sa_new->r_nonce = sa_rekey->r_nonce; + sa_new->dh_group = sa_rekey->dh_group; + sa_new->dh_shared_key = sa_rekey->dh_shared_key; + sa_new->dh_private_key = sa_rekey->dh_private_key; + sa_new->i_dh_data = sa_rekey->i_dh_data; + sa_new->r_dh_data = sa_rekey->r_dh_data; + sa_new->i_proposals = sa_rekey->i_proposals; + sa_new->r_proposals = sa_rekey->r_proposals; + + sa_new->sk_d = 0; + sa_new->sk_ai = 0; + sa_new->sk_ar = 0; + sa_new->sk_ei = 0; + sa_new->sk_er = 0; + sa_new->sk_pi = 0; + sa_new->sk_pr = 0; + ikev2_calc_keys_rekey (sa_new, sa_old); + + sa_new->i_auth.data = vec_dup (sa_old->i_auth.data); + sa_new->i_auth.key = sa_old->i_auth.key; + if (sa_new->i_auth.key) + EVP_PKEY_up_ref (sa_new->i_auth.key); + + sa_new->r_auth.data = vec_dup (sa_old->r_auth.data); + sa_new->r_auth.key = sa_old->r_auth.key; + if (sa_new->r_auth.key) + EVP_PKEY_up_ref (sa_new->r_auth.key); + + sa_new->i_id.data = vec_dup (sa_old->i_id.data); + sa_new->r_id.data = vec_dup (sa_old->r_id.data); + + sa_old->is_tun_itf_set = 0; + sa_old->tun_itf = ~0; + sa_old->old_id_expiration = 0; + sa_old->current_remote_id_mask = 0; + sa_old->old_remote_id = 0; + sa_old->old_remote_id_present = 0; + sa_old->childs = 0; + sa_old->sw_if_index = ~0; +} + +static void +ikev2_process_sa_rekey (ikev2_sa_t *sa_new, ikev2_sa_t *sa_old, + ikev2_sa_rekey_t *sa_rekey) +{ + ikev2_sa_transform_t *tr; + + if (ikev2_generate_sa_init_data (sa_new) != IKEV2_GENERATE_SA_INIT_OK) + { + sa_rekey->notify_type = IKEV2_NOTIFY_MSG_INVALID_KE_PAYLOAD; + return; + } + + sa_new->r_proposals = + ikev2_select_proposal (sa_new->i_proposals, IKEV2_PROTOCOL_IKE); + + tr = ikev2_sa_get_td_for_type (sa_new->r_proposals, IKEV2_TRANSFORM_TYPE_DH); + if (!tr || tr->dh_type != sa_new->dh_group) + { + sa_rekey->notify_type = IKEV2_NOTIFY_MSG_INVALID_KE_PAYLOAD; + return; + } + + sa_rekey->notify_type = 0; + sa_rekey->ispi = sa_new->i_proposals[0].spi; + sa_rekey->rspi = sa_new->r_proposals[0].spi; + sa_rekey->i_nonce = sa_new->i_nonce; + sa_rekey->r_nonce = sa_new->r_nonce; + sa_rekey->dh_group = sa_new->dh_group; + sa_rekey->dh_shared_key = sa_new->dh_shared_key; + sa_rekey->dh_private_key = sa_new->dh_private_key; + sa_rekey->i_dh_data = sa_new->i_dh_data; + sa_rekey->r_dh_data = sa_new->r_dh_data; + sa_rekey->i_proposals = sa_new->i_proposals; + sa_rekey->r_proposals = sa_new->r_proposals; + + sa_new->i_nonce = 0; + sa_new->r_nonce = 0; + sa_new->dh_shared_key = 0; + sa_new->dh_private_key = 0; + sa_new->i_dh_data = 0; + sa_new->r_dh_data = 0; + sa_new->i_proposals = 0; + sa_new->r_proposals = 0; +} + static int ikev2_process_create_child_sa_req (vlib_main_t * vm, ikev2_sa_t * sa, ike_header_t * ike, @@ -1515,7 +1653,9 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm, p += plen; } - if (!proposal || proposal->protocol_id != IKEV2_PROTOCOL_ESP || !nonce) + if (!proposal || !nonce || + (proposal->protocol_id != IKEV2_PROTOCOL_ESP && + proposal->protocol_id != IKEV2_PROTOCOL_IKE)) goto cleanup_and_exit; if (sa->is_initiator) @@ -1571,6 +1711,16 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm, vec_free (tsi); } } + else if (proposal[0].protocol_id == IKEV2_PROTOCOL_IKE) + { + ikev2_sa_rekey_t *sa_rekey; + if (tsi || tsr) + goto cleanup_and_exit; + sar.i_proposals = proposal; + vec_add (sar.i_nonce, nonce, nonce_len); + vec_add2 (sa->sa_rekey, sa_rekey, 1); + ikev2_process_sa_rekey (&sar, sa, sa_rekey); + } else { /* create new child SA */ @@ -2414,19 +2564,26 @@ ikev2_add_invalid_ke_payload (ikev2_sa_t *sa, ikev2_payload_chain_t *chain) } static void +ikev2_add_notify_payload (ikev2_sa_t *sa, ikev2_payload_chain_t *chain, + u16 notify_type) +{ + if (notify_type == IKEV2_NOTIFY_MSG_INVALID_KE_PAYLOAD) + ikev2_add_invalid_ke_payload (sa, chain); + else + ikev2_payload_add_notify (chain, notify_type, 0); +} + +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); + ikev2_add_notify_payload (sa, chain, rekey->notify_type); return; } - ikev2_payload_add_sa (chain, rekey->r_proposal); + ikev2_payload_add_sa (chain, rekey->r_proposal, 0); ikev2_payload_add_nonce (chain, sa->r_nonce); if (rekey->kex) ikev2_payload_add_ke (chain, sa->dh_group, sa->r_dh_data); @@ -2482,7 +2639,7 @@ ikev2_generate_message (vlib_buffer_t *b, ikev2_sa_t *sa, ike_header_t *ike, ASSERT (udp); ike->rspi = clib_host_to_net_u64 (sa->rspi); - ikev2_payload_add_sa (chain, sa->r_proposals); + ikev2_payload_add_sa (chain, sa->r_proposals, 0); ikev2_payload_add_ke (chain, sa->dh_group, sa->r_dh_data); ikev2_payload_add_nonce (chain, sa->r_nonce); @@ -2510,7 +2667,7 @@ ikev2_generate_message (vlib_buffer_t *b, ikev2_sa_t *sa, ike_header_t *ike, { ikev2_payload_add_id (chain, &sa->r_id, IKEV2_PAYLOAD_IDR); ikev2_payload_add_auth (chain, &sa->r_auth); - ikev2_payload_add_sa (chain, sa->childs[0].r_proposals); + ikev2_payload_add_sa (chain, sa->childs[0].r_proposals, 0); ikev2_payload_add_ts (chain, sa->childs[0].tsi, IKEV2_PAYLOAD_TSI); ikev2_payload_add_ts (chain, sa->childs[0].tsr, IKEV2_PAYLOAD_TSR); } @@ -2555,7 +2712,7 @@ ikev2_generate_message (vlib_buffer_t *b, ikev2_sa_t *sa, ike_header_t *ike, if (sa->r_id.type != 0) ikev2_payload_add_id (chain, &sa->r_id, IKEV2_PAYLOAD_IDR); ikev2_payload_add_auth (chain, &sa->i_auth); - ikev2_payload_add_sa (chain, sa->childs[0].i_proposals); + ikev2_payload_add_sa (chain, sa->childs[0].i_proposals, 0); ikev2_payload_add_ts (chain, sa->childs[0].tsi, IKEV2_PAYLOAD_TSI); ikev2_payload_add_ts (chain, sa->childs[0].tsr, IKEV2_PAYLOAD_TSR); ikev2_payload_add_notify (chain, IKEV2_NOTIFY_MSG_INITIAL_CONTACT, @@ -2632,7 +2789,7 @@ ikev2_generate_message (vlib_buffer_t *b, ikev2_sa_t *sa, ike_header_t *ike, notify.spi = sa->childs[0].i_proposals->spi; *(u32 *) data = clib_host_to_net_u32 (notify.spi); - ikev2_payload_add_sa (chain, proposals); + ikev2_payload_add_sa (chain, proposals, 0); ikev2_payload_add_nonce (chain, sa->i_nonce); ikev2_payload_add_ts (chain, sa->childs[0].tsi, IKEV2_PAYLOAD_TSI); ikev2_payload_add_ts (chain, sa->childs[0].tsr, IKEV2_PAYLOAD_TSR); @@ -2651,6 +2808,19 @@ ikev2_generate_message (vlib_buffer_t *b, ikev2_sa_t *sa, ike_header_t *ike, ikev2_add_create_child_resp (sa, &sa->new_child[0], chain); vec_del1 (sa->new_child, 0); } + else if (vec_len (sa->sa_rekey) > 0) + { + if (sa->sa_rekey[0].notify_type) + ikev2_add_notify_payload (sa, chain, sa->sa_rekey[0].notify_type); + else + { + ikev2_payload_add_sa (chain, sa->sa_rekey[0].r_proposals, 1); + ikev2_payload_add_nonce (chain, sa->sa_rekey[0].r_nonce); + ikev2_payload_add_ke (chain, sa->sa_rekey[0].dh_group, + sa->sa_rekey[0].r_dh_data); + } + vec_del1 (sa->sa_rekey, 0); + } else if (sa->unsupported_cp) { u8 *data = vec_new (u8, 1); @@ -3456,6 +3626,38 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node, vm, node->node_index, IKEV2_ERROR_NO_BUFF_SPACE, 1); } } + else if (vec_len (sa0->sa_rekey) > 0) + { + if (!sa0->sa_rekey[0].notify_type) + { + ikev2_sa_t *sar, *tmp = 0; + pool_get (ptd->sas, tmp); + sa0 = pool_elt_at_index (ptd->sas, p[0]); + /* swap old/new SAs to keep index and inherit IPsec SA */ + clib_memcpy_fast (tmp, sa0, sizeof (*tmp)); + sar = sa0; + sa0 = tmp; + hash_set (ptd->sa_by_rspi, sa0->rspi, sa0 - ptd->sas); + p = hash_get (ptd->sa_by_rspi, sa0->rspi); + ikev2_complete_sa_rekey (sar, sa0, &sa0->sa_rekey[0]); + hash_set (ptd->sa_by_rspi, sar->rspi, sar - ptd->sas); + } + if (ike_hdr_is_response (ike0)) + { + vec_free (sa0->sa_rekey); + } + else + { + stats->n_rekey_req++; + sa0->stats.n_rekey_req++; + ike0->flags = IKEV2_HDR_FLAG_RESPONSE; + slen = + ikev2_generate_message (b0, sa0, ike0, 0, udp0, stats); + if (~0 == slen) + vlib_node_increment_counter ( + vm, node->node_index, IKEV2_ERROR_NO_BUFF_SPACE, 1); + } + } } } else @@ -4526,7 +4728,7 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name) proposals[0].protocol_id = IKEV2_PROTOCOL_IKE; /* Add and then cleanup proposal data */ - ikev2_payload_add_sa (chain, proposals); + ikev2_payload_add_sa (chain, proposals, 0); ikev2_sa_free_proposal_vector (&proposals); sa.is_initiator = 1; @@ -4560,6 +4762,7 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name) sa.childs[0].i_proposals[0].protocol_id = IKEV2_PROTOCOL_ESP; RAND_bytes ((u8 *) & sa.childs[0].i_proposals[0].spi, sizeof (sa.childs[0].i_proposals[0].spi)); + sa.childs[0].i_proposals[0].spi &= 0xffffffff; /* Add NAT detection notification messages (mandatory) */ u8 *nat_detection_sha1 = ikev2_compute_nat_sha1 ( @@ -4807,6 +5010,7 @@ ikev2_rekey_child_sa_internal (vlib_main_t * vm, ikev2_sa_t * sa, /*need new ispi */ RAND_bytes ((u8 *) & proposals[0].spi, sizeof (proposals[0].spi)); + proposals[0].spi &= 0xffffffff; rekey->spi = proposals[0].spi; rekey->ispi = csa->i_proposals->spi; len = ikev2_generate_message (b0, sa, ike0, proposals, 0, 0); diff --git a/src/plugins/ikev2/ikev2_payload.c b/src/plugins/ikev2/ikev2_payload.c index 294864d8c43..689c502d4a3 100644 --- a/src/plugins/ikev2/ikev2_payload.c +++ b/src/plugins/ikev2/ikev2_payload.c @@ -167,8 +167,8 @@ ikev2_payload_add_notify_2 (ikev2_payload_chain_t * c, u16 msg_type, } void -ikev2_payload_add_sa (ikev2_payload_chain_t * c, - ikev2_sa_proposal_t * proposals) +ikev2_payload_add_sa (ikev2_payload_chain_t *c, ikev2_sa_proposal_t *proposals, + u8 force_spi) { ike_payload_header_t *ph; ike_sa_proposal_data_t *prop; @@ -184,7 +184,13 @@ ikev2_payload_add_sa (ikev2_payload_chain_t * c, vec_foreach (p, proposals) { - int spi_size = (p->protocol_id == IKEV2_PROTOCOL_ESP) ? 4 : 0; + int spi_size = 0; + + if (p->protocol_id == IKEV2_PROTOCOL_ESP) + spi_size = 4; + else if (force_spi && p->protocol_id == IKEV2_PROTOCOL_IKE) + spi_size = 8; + pr_data = vec_new (u8, sizeof (ike_sa_proposal_data_t) + spi_size); prop = (ike_sa_proposal_data_t *) pr_data; prop->last_or_more = proposals - p + 1 < vec_len (proposals) ? 2 : 0; @@ -193,8 +199,13 @@ ikev2_payload_add_sa (ikev2_payload_chain_t * c, prop->spi_size = spi_size; prop->num_transforms = vec_len (p->transforms); - if (spi_size) + if (spi_size == 4) prop->spi[0] = clib_host_to_net_u32 (p->spi); + else if (spi_size == 8) + { + u64 s = clib_host_to_net_u64 (p->spi); + clib_memcpy_fast (prop->spi, &s, sizeof (s)); + } vec_foreach (t, p->transforms) { @@ -384,8 +395,9 @@ ikev2_parse_sa_payload (ike_payload_header_t * ikep, u32 rlen) sap = (ike_sa_proposal_data_t *) & ikep->payload[proposal_ptr]; int i, transform_ptr; - /* IKE proposal should not have SPI */ - if (sap->protocol_id == IKEV2_PROTOCOL_IKE && sap->spi_size != 0) + /* IKE proposal should have 8 bytes or no SPI */ + if (sap->protocol_id == IKEV2_PROTOCOL_IKE && sap->spi_size != 0 && + sap->spi_size != 8) goto data_corrupted; /* IKE proposal should not have SPI */ @@ -404,6 +416,12 @@ ikev2_parse_sa_payload (ike_payload_header_t * ikep, u32 rlen) { proposal->spi = clib_net_to_host_u32 (sap->spi[0]); } + else if (sap->spi_size == 8) + { + u64 s; + clib_memcpy_fast (&s, &sap->spi[0], sizeof (s)); + proposal->spi = clib_net_to_host_u64 (s); + } for (i = 0; i < sap->num_transforms; i++) { diff --git a/src/plugins/ikev2/ikev2_priv.h b/src/plugins/ikev2/ikev2_priv.h index dca2fe80c57..a11538f92c7 100644 --- a/src/plugins/ikev2/ikev2_priv.h +++ b/src/plugins/ikev2/ikev2_priv.h @@ -243,7 +243,7 @@ typedef struct { u8 proposal_num; ikev2_protocol_id_t protocol_id:8; - u32 spi; + u64 spi; ikev2_sa_transform_t *transforms; } ikev2_sa_proposal_t; @@ -330,6 +330,22 @@ typedef struct typedef struct { + u16 notify_type; + u16 dh_group; + u64 ispi; + u64 rspi; + u8 *i_nonce; + u8 *r_nonce; + u8 *dh_shared_key; + u8 *dh_private_key; + u8 *i_dh_data; + u8 *r_dh_data; + ikev2_sa_proposal_t *i_proposals; + ikev2_sa_proposal_t *r_proposals; +} ikev2_sa_rekey_t; + +typedef struct +{ u16 msg_type; u8 protocol_id; u32 spi; @@ -432,6 +448,9 @@ typedef struct ikev2_rekey_t *new_child; + /* pending sa rekeyings */ + ikev2_sa_rekey_t *sa_rekey; + /* packet data */ u8 *last_sa_init_req_packet_data; u8 *last_sa_init_res_packet_data; @@ -601,8 +620,8 @@ void ikev2_payload_add_notify (ikev2_payload_chain_t * c, u16 msg_type, u8 * data); void ikev2_payload_add_notify_2 (ikev2_payload_chain_t * c, u16 msg_type, u8 * data, ikev2_notify_t * notify); -void ikev2_payload_add_sa (ikev2_payload_chain_t * c, - ikev2_sa_proposal_t * proposals); +void ikev2_payload_add_sa (ikev2_payload_chain_t *c, + ikev2_sa_proposal_t *proposals, u8 force_spi); void ikev2_payload_add_ke (ikev2_payload_chain_t * c, u16 dh_group, u8 * dh_data); void ikev2_payload_add_nonce (ikev2_payload_chain_t * c, u8 * nonce); diff --git a/test/test_ikev2.py b/test/test_ikev2.py index 30ee2b98110..d2b4c691f85 100644 --- a/test/test_ikev2.py +++ b/test/test_ikev2.py @@ -361,14 +361,21 @@ class IKEv2SA(object): h.update(data) return h.finalize() - def calc_keys(self): + def calc_keys(self, sk_d=None): prf = self.ike_prf_alg.mod() - # SKEYSEED = prf(Ni | Nr, g^ir) - s = self.i_nonce + self.r_nonce - self.skeyseed = self.calc_prf(prf, s, self.dh_shared_secret) + if sk_d is None: + # SKEYSEED = prf(Ni | Nr, g^ir) + self.skeyseed = self.calc_prf( + prf, self.i_nonce + self.r_nonce, self.dh_shared_secret + ) + else: + # SKEYSEED = prf(SK_d (old), g^ir (new) | Ni | Nr) + self.skeyseed = self.calc_prf( + prf, sk_d, self.dh_shared_secret + self.i_nonce + self.r_nonce + ) # calculate S = Ni | Nr | SPIi SPIr - s = s + self.ispi + self.rspi + s = self.i_nonce + self.r_nonce + self.ispi + self.rspi prf_key_trunc = self.ike_prf_alg.trunc_len encr_key_len = self.ike_crypto_key_len @@ -585,6 +592,45 @@ class IKEv2SA(object): digest.update(data) return digest.finalize() + def clone(self, test, **kwargs): + if "spi" not in kwargs: + kwargs["spi"] = self.ispi if self.is_initiator else self.rspi + if "nonce" not in kwargs: + kwargs["nonce"] = self.i_nonce if self.is_initiator else self.r_nonce + if self.child_sas: + if "local_ts" not in kwargs: + kwargs["local_ts"] = self.child_sas[0].local_ts + if "remote_ts" not in kwargs: + kwargs["remote_ts"] = self.child_sas[0].remote_ts + sa = type(self)( + test, + is_initiator=self.is_initiator, + i_id=self.i_id, + r_id=self.r_id, + id_type=self.id_type, + auth_data=self.auth_data, + auth_method=self.auth_method, + priv_key=self.priv_key, + i_natt=self.i_natt, + r_natt=self.r_natt, + udp_encap=self.udp_encap, + **kwargs, + ) + if sa.is_initiator: + sa.set_ike_props( + crypto=self.ike_crypto, + crypto_key_len=self.ike_crypto_key_len, + integ=self.ike_integ, + prf=self.ike_prf, + dh=self.ike_dh, + ) + sa.set_esp_props( + crypto=self.esp_crypto, + crypto_key_len=self.esp_crypto_key_len, + integ=self.esp_integ, + ) + return sa + @unittest.skipIf("ikev2" in config.excluded_plugins, "Exclude IKEv2 plugin tests") class IkePeer(VppTestCase): @@ -650,6 +696,20 @@ class IkePeer(VppTestCase): self.pg0, ike_msg, self.sa.sport, self.sa.dport, self.sa.natt, self.ip6 ) + def create_sa_rekey_request(self, **kwargs): + sa = self.generate_sa_init_payload(**kwargs) + header = ikev2.IKEv2( + init_SPI=self.sa.ispi, + resp_SPI=self.sa.rspi, + id=self.sa.new_msg_id(), + flags="Initiator", + exch_type="CREATE_CHILD_SA", + ) + ike_msg = self.encrypt_ike_msg(header, sa, "SA") + return self.create_packet( + self.pg0, ike_msg, self.sa.sport, self.sa.dport, self.sa.natt, self.ip6 + ) + def create_empty_request(self): header = ikev2.IKEv2( init_SPI=self.sa.ispi, @@ -830,10 +890,15 @@ class IkePeer(VppTestCase): self.assertEqual(api_id.data_len, exp_id.data_len) self.assertEqual(bytes(api_id.data, "ascii"), exp_id.type) - def verify_ike_sas(self): + def verify_ike_sas(self, is_rekey=False): r = self.vapi.ikev2_sa_dump() - self.assertEqual(len(r), 1) - sa = r[0].sa + if is_rekey: + sa_count = 2 + sa = r[1].sa + else: + sa_count = 1 + sa = r[0].sa + self.assertEqual(len(r), sa_count) self.assertEqual(self.sa.ispi, (sa.ispi).to_bytes(8, "big")) self.assertEqual(self.sa.rspi, (sa.rspi).to_bytes(8, "big")) if self.ip6: @@ -865,7 +930,16 @@ class IkePeer(VppTestCase): self.assertEqual(bytes(sa.i_id.data, "ascii"), self.sa.i_id) self.assertEqual(bytes(sa.r_id.data, "ascii"), self.idr) + n = self.vapi.ikev2_nonce_get(is_initiator=True, sa_index=sa.sa_index) + self.verify_nonce(n, self.sa.i_nonce) + n = self.vapi.ikev2_nonce_get(is_initiator=False, sa_index=sa.sa_index) + self.verify_nonce(n, self.sa.r_nonce) + r = self.vapi.ikev2_child_sa_dump(sa_index=sa.sa_index) + if is_rekey: + self.assertEqual(len(r), 0) + return + self.assertEqual(len(r), 1) csa = r[0].child_sa self.assertEqual(csa.sa_index, sa.sa_index) @@ -894,11 +968,6 @@ class IkePeer(VppTestCase): self.assertEqual(len(r), 1) self.verify_ts(r[0].ts, tsr[0], False) - n = self.vapi.ikev2_nonce_get(is_initiator=True, sa_index=sa.sa_index) - self.verify_nonce(n, self.sa.i_nonce) - n = self.vapi.ikev2_nonce_get(is_initiator=False, sa_index=sa.sa_index) - self.verify_nonce(n, self.sa.r_nonce) - def verify_nonce(self, api_nonce, nonce): self.assertEqual(api_nonce.data_len, len(nonce)) self.assertEqual(api_nonce.nonce, nonce) @@ -1281,7 +1350,9 @@ class TemplateResponder(IkePeer): capture = self.pg0.get_capture(1) self.verify_del_sa(capture[0]) - def send_sa_init_req(self): + def generate_sa_init_payload( + self, spi=None, dh_pub_key=None, nonce=None, next_payload=None + ): tr_attr = self.sa.ike_crypto_attr() trans = ( ikev2.IKEv2_payload_Transform( @@ -1301,23 +1372,36 @@ class TemplateResponder(IkePeer): ) ) + if spi is None: + pargs = {} + else: + pargs = {"SPI": spi, "SPIsize": len(spi)} props = ikev2.IKEv2_payload_Proposal( - proposal=1, proto="IKEv2", trans_nb=4, trans=trans + proposal=1, + proto="IKEv2", + trans_nb=4, + trans=trans, + **pargs, ) - next_payload = None if self.ip6 else "Notify" - - self.sa.init_req_packet = ( - ikev2.IKEv2( - init_SPI=self.sa.ispi, flags="Initiator", exch_type="IKE_SA_INIT" - ) - / ikev2.IKEv2_payload_SA(next_payload="KE", prop=props) + return ( + ikev2.IKEv2_payload_SA(next_payload="KE", prop=props) / ikev2.IKEv2_payload_KE( - next_payload="Nonce", group=self.sa.ike_dh, load=self.sa.my_dh_pub_key + next_payload="Nonce", + group=self.sa.ike_dh, + load=self.sa.my_dh_pub_key if dh_pub_key is None else dh_pub_key, + ) + / ikev2.IKEv2_payload_Nonce( + next_payload=next_payload, + load=self.sa.i_nonce if nonce is None else nonce, ) - / ikev2.IKEv2_payload_Nonce(next_payload=next_payload, load=self.sa.i_nonce) ) + def send_sa_init_req(self): + self.sa.init_req_packet = ikev2.IKEv2( + init_SPI=self.sa.ispi, flags="Initiator", exch_type="IKE_SA_INIT" + ) / self.generate_sa_init_payload(next_payload=None if self.ip6 else "Notify") + if not self.ip6: if self.sa.i_natt: src_address = b"\x0a\x0a\x0a\x01" @@ -2186,6 +2270,50 @@ class TestResponderRekeyRepeatKEX(TestResponderRekeyRepeat): WITH_KEX = True +@tag_fixme_vpp_workers +class TestResponderRekeySA(TestResponderPsk): + """test ikev2 responder - rekey IKE SA""" + + def send_rekey_from_initiator(self, newsa): + packet = self.create_sa_rekey_request( + spi=newsa.ispi, + dh_pub_key=newsa.my_dh_pub_key, + nonce=newsa.i_nonce, + ) + self.pg0.add_stream(packet) + self.pg0.enable_capture() + self.pg_start() + capture = self.pg0.get_capture(1) + return capture + + def process_rekey_response(self, newsa, capture): + ih = self.get_ike_header(capture[0]) + plain = self.sa.hmac_and_decrypt(ih) + sa = ikev2.IKEv2_payload_SA(plain) + prop = sa[ikev2.IKEv2_payload_Proposal] + newsa.rspi = prop.SPI + newsa.r_nonce = sa[ikev2.IKEv2_payload_Nonce].load + newsa.r_dh_data = sa[ikev2.IKEv2_payload_KE].load + newsa.complete_dh_data() + newsa.calc_keys(sk_d=self.sa.sk_d) + newsa.child_sas = self.sa.child_sas + self.sa.child_sas = [] + + def test_responder(self): + super(TestResponderRekeySA, self).test_responder() + newsa = self.sa.clone(self, spi=os.urandom(8)) + newsa.generate_dh_data() + capture = self.send_rekey_from_initiator(newsa) + self.process_rekey_response(newsa, capture) + self.verify_ike_sas(is_rekey=True) + self.assert_counter(1, "rekey_req", "ip4") + r = self.vapi.ikev2_sa_dump() + self.assertEqual(r[1].sa.stats.n_rekey_req, 1) + self.initiate_del_sa_from_initiator() + self.sa = newsa + self.verify_ike_sas() + + @tag_fixme_ubuntu2204 @tag_fixme_debian11 class TestResponderVrf(TestResponderPsk, Ikev2Params): |