aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFilip Tehlar <ftehlar@cisco.com>2020-07-08 13:25:34 +0000
committerBenoƮt Ganne <bganne@cisco.com>2020-07-15 16:12:16 +0000
commita7b963df2758d7c25de366db1999ca1024e12d30 (patch)
tree5cbf77b9e1248239b193324dc1af17df15724bd1
parent8046fdc10b14fd161ee81d0a25cfa79793ef698b (diff)
ikev2: add support for AES-GCM cipher in IKE
Type: feature Ticket: VPP-1920 Change-Id: I6e30f3594cb30553f3ca5a35e0a4f679325aacec Signed-off-by: Filip Tehlar <ftehlar@cisco.com>
-rw-r--r--src/plugins/ikev2/ikev2.c119
-rw-r--r--src/plugins/ikev2/ikev2.h5
-rw-r--r--src/plugins/ikev2/ikev2_cli.c23
-rw-r--r--src/plugins/ikev2/ikev2_crypto.c123
-rw-r--r--src/plugins/ikev2/ikev2_priv.h21
-rw-r--r--src/plugins/ikev2/test/test_ikev2.py174
6 files changed, 345 insertions, 120 deletions
diff --git a/src/plugins/ikev2/ikev2.c b/src/plugins/ikev2/ikev2.c
index 08a66cbf8fa..c11bd0f9b08 100644
--- a/src/plugins/ikev2/ikev2.c
+++ b/src/plugins/ikev2/ikev2.c
@@ -149,9 +149,8 @@ ikev2_select_proposal (ikev2_sa_proposal_t * proposals,
if (prot_id == IKEV2_PROTOCOL_IKE)
{
mandatory_bitmap = (1 << IKEV2_TRANSFORM_TYPE_ENCR) |
- (1 << IKEV2_TRANSFORM_TYPE_PRF) |
- (1 << IKEV2_TRANSFORM_TYPE_INTEG) | (1 << IKEV2_TRANSFORM_TYPE_DH);
- optional_bitmap = mandatory_bitmap;
+ (1 << IKEV2_TRANSFORM_TYPE_PRF) | (1 << IKEV2_TRANSFORM_TYPE_DH);
+ optional_bitmap = mandatory_bitmap | (1 << IKEV2_TRANSFORM_TYPE_INTEG);
}
else if (prot_id == IKEV2_PROTOCOL_ESP)
{
@@ -459,7 +458,7 @@ ikev2_calc_keys (ikev2_sa_t * sa)
/* calculate SKEYSEED = prf(Ni | Nr, g^ir) */
u8 *skeyseed = 0;
u8 *s = 0;
- u16 integ_key_len = 0;
+ u16 integ_key_len = 0, salt_len = 0;
ikev2_sa_transform_t *tr_encr, *tr_prf, *tr_integ;
tr_encr =
ikev2_sa_get_td_for_type (sa->r_proposals, IKEV2_TRANSFORM_TYPE_ENCR);
@@ -470,6 +469,8 @@ ikev2_calc_keys (ikev2_sa_t * sa)
if (tr_integ)
integ_key_len = tr_integ->key_len;
+ else
+ salt_len = sizeof (u32);
vec_append (s, sa->i_nonce);
vec_append (s, sa->r_nonce);
@@ -487,7 +488,8 @@ ikev2_calc_keys (ikev2_sa_t * sa)
int len = tr_prf->key_trunc + /* SK_d */
integ_key_len * 2 + /* SK_ai, SK_ar */
tr_encr->key_len * 2 + /* SK_ei, SK_er */
- tr_prf->key_len * 2; /* SK_pi, SK_pr */
+ tr_prf->key_len * 2 + /* SK_pi, SK_pr */
+ salt_len * 2;
keymat = ikev2_calc_prfplus (tr_prf, skeyseed, s, len);
vec_free (skeyseed);
@@ -514,14 +516,14 @@ ikev2_calc_keys (ikev2_sa_t * sa)
}
/* SK_ei */
- sa->sk_ei = vec_new (u8, tr_encr->key_len);
- clib_memcpy_fast (sa->sk_ei, keymat + pos, tr_encr->key_len);
- pos += tr_encr->key_len;
+ sa->sk_ei = vec_new (u8, tr_encr->key_len + salt_len);
+ clib_memcpy_fast (sa->sk_ei, keymat + pos, tr_encr->key_len + salt_len);
+ pos += tr_encr->key_len + salt_len;
/* SK_er */
- sa->sk_er = vec_new (u8, tr_encr->key_len);
- clib_memcpy_fast (sa->sk_er, keymat + pos, tr_encr->key_len);
- pos += tr_encr->key_len;
+ sa->sk_er = vec_new (u8, tr_encr->key_len + salt_len);
+ clib_memcpy_fast (sa->sk_er, keymat + pos, tr_encr->key_len + salt_len);
+ pos += tr_encr->key_len + salt_len;
/* SK_pi */
sa->sk_pi = vec_new (u8, tr_prf->key_len);
@@ -833,17 +835,22 @@ ikev2_process_sa_init_resp (vlib_main_t * vm, ikev2_sa_t * sa,
static u8 *
ikev2_decrypt_sk_payload (ikev2_sa_t * sa, ike_header_t * ike, u8 * payload)
{
+ ikev2_main_per_thread_data_t *ptd = ikev2_get_per_thread_data ();
int p = 0;
- u8 last_payload = 0;
+ u8 last_payload = 0, *plaintext = 0;
u8 *hmac = 0;
u32 len = clib_net_to_host_u32 (ike->length);
ike_payload_header_t *ikep = 0;
u32 plen = 0;
ikev2_sa_transform_t *tr_integ;
+ ikev2_sa_transform_t *tr_encr;
tr_integ =
ikev2_sa_get_td_for_type (sa->r_proposals, IKEV2_TRANSFORM_TYPE_INTEG);
+ tr_encr =
+ ikev2_sa_get_td_for_type (sa->r_proposals, IKEV2_TRANSFORM_TYPE_ENCR);
+ int is_aead = tr_encr->encr_type == IKEV2_TRANSFORM_ENCR_TYPE_AES_GCM_16;
- if (!sa->sk_ar || !sa->sk_ai)
+ if ((!sa->sk_ar || !sa->sk_ai) && !is_aead)
return 0;
while (p < len &&
@@ -880,21 +887,45 @@ ikev2_decrypt_sk_payload (ikev2_sa_t * sa, ike_header_t * ike, u8 * payload)
return 0;
}
- hmac =
- ikev2_calc_integr (tr_integ, sa->is_initiator ? sa->sk_ar : sa->sk_ai,
- (u8 *) ike, len - tr_integ->key_trunc);
+ if (is_aead)
+ {
+ if (plen < sizeof (*ikep) + IKEV2_GCM_ICV_SIZE)
+ return 0;
- plen = plen - sizeof (*ikep) - tr_integ->key_trunc;
+ plen -= sizeof (*ikep) + IKEV2_GCM_ICV_SIZE;
+ u8 *aad = (u8 *) ike;
+ u32 aad_len = ikep->payload - aad;
+ u8 *tag = ikep->payload + plen;
- if (memcmp (hmac, &ikep->payload[plen], tr_integ->key_trunc))
+ plaintext = ikev2_decrypt_aead_data (ptd, sa, tr_encr, ikep->payload,
+ plen, aad, aad_len, tag);
+ }
+ else
{
- ikev2_elog_error ("message integrity check failed");
+ if (len < tr_integ->key_trunc)
+ return 0;
+
+ hmac =
+ ikev2_calc_integr (tr_integ, sa->is_initiator ? sa->sk_ar : sa->sk_ai,
+ (u8 *) ike, len - tr_integ->key_trunc);
+
+ if (plen < sizeof (*ikep) + tr_integ->key_trunc)
+ return 0;
+
+ plen = plen - sizeof (*ikep) - tr_integ->key_trunc;
+
+ if (memcmp (hmac, &ikep->payload[plen], tr_integ->key_trunc))
+ {
+ ikev2_elog_error ("message integrity check failed");
+ vec_free (hmac);
+ return 0;
+ }
vec_free (hmac);
- return 0;
+
+ plaintext = ikev2_decrypt_data (ptd, sa, tr_encr, ikep->payload, plen);
}
- vec_free (hmac);
- return ikev2_decrypt_data (sa, ikep->payload, plen);
+ return plaintext;
}
static_always_inline int
@@ -2296,7 +2327,7 @@ ikev2_generate_message (ikev2_sa_t * sa, ike_header_t * ike, void *user,
}
else
{
-
+ ikev2_main_per_thread_data_t *ptd = ikev2_get_per_thread_data ();
ikev2_payload_chain_add_padding (chain, tr_encr->block_size);
/* SK payload */
@@ -2304,24 +2335,40 @@ ikev2_generate_message (ikev2_sa_t * sa, ike_header_t * ike, void *user,
ph = (ike_payload_header_t *) & ike->payload[0];
ph->nextpayload = chain->first_payload_type;
ph->flags = 0;
- int enc_len = ikev2_encrypt_data (sa, chain->data, ph->payload);
- plen += enc_len;
-
- /* add space for hmac */
- plen += tr_integ->key_trunc;
+ int is_aead =
+ tr_encr->encr_type == IKEV2_TRANSFORM_ENCR_TYPE_AES_GCM_16;
+ int iv_len = is_aead ? IKEV2_GCM_IV_SIZE : tr_encr->block_size;
+ plen += vec_len (chain->data) + iv_len;
+
+ /* add space for hmac/tag */
+ if (tr_integ)
+ plen += tr_integ->key_trunc;
+ else
+ plen += IKEV2_GCM_ICV_SIZE;
tlen += plen;
/* payload and total length */
ph->length = clib_host_to_net_u16 (plen);
ike->length = clib_host_to_net_u32 (tlen);
- /* calc integrity data for whole packet except hash itself */
- integ =
- ikev2_calc_integr (tr_integ, sa->is_initiator ? sa->sk_ai : sa->sk_ar,
- (u8 *) ike, tlen - tr_integ->key_trunc);
-
- clib_memcpy_fast (ike->payload + tlen - tr_integ->key_trunc -
- sizeof (*ike), integ, tr_integ->key_trunc);
+ if (is_aead)
+ {
+ ikev2_encrypt_aead_data (ptd, sa, tr_encr, chain->data,
+ ph->payload, (u8 *) ike,
+ sizeof (*ike) + sizeof (*ph),
+ ph->payload + plen - sizeof (*ph) -
+ IKEV2_GCM_ICV_SIZE);
+ }
+ else
+ {
+ ikev2_encrypt_data (ptd, sa, tr_encr, chain->data, ph->payload);
+ integ =
+ ikev2_calc_integr (tr_integ,
+ sa->is_initiator ? sa->sk_ai : sa->sk_ar,
+ (u8 *) ike, tlen - tr_integ->key_trunc);
+ clib_memcpy_fast (ike->payload + tlen - tr_integ->key_trunc -
+ sizeof (*ike), integ, tr_integ->key_trunc);
+ }
/* store whole IKE payload - needed for retransmit */
vec_free (sa->last_res_packet_data);
@@ -2988,7 +3035,7 @@ ikev2_set_initiator_proposals (vlib_main_t * vm, ikev2_sa_t * sa,
return r;
}
- if (is_ike || IKEV2_TRANSFORM_ENCR_TYPE_AES_GCM_16 != ts->crypto_alg)
+ if (IKEV2_TRANSFORM_INTEG_TYPE_NONE != ts->integ_alg)
{
/* Integrity */
error = 1;
diff --git a/src/plugins/ikev2/ikev2.h b/src/plugins/ikev2/ikev2.h
index 26df41d8595..dd1c646e6f2 100644
--- a/src/plugins/ikev2/ikev2.h
+++ b/src/plugins/ikev2/ikev2.h
@@ -25,6 +25,11 @@
#define IKEV2_PORT_NATT 4500
#define IKEV2_KEY_PAD "Key Pad for IKEv2"
+#define IKEV2_GCM_ICV_SIZE 16
+#define IKEV2_GCM_NONCE_SIZE 12
+#define IKEV2_GCM_SALT_SIZE 4
+#define IKEV2_GCM_IV_SIZE (IKEV2_GCM_NONCE_SIZE - IKEV2_GCM_SALT_SIZE)
+
typedef u8 v8;
/* *INDENT-OFF* */
diff --git a/src/plugins/ikev2/ikev2_cli.c b/src/plugins/ikev2/ikev2_cli.c
index 6c75557e41a..30de10b6d95 100644
--- a/src/plugins/ikev2/ikev2_cli.c
+++ b/src/plugins/ikev2/ikev2_cli.c
@@ -88,9 +88,12 @@ show_ikev2_sa_command_fn (vlib_main_t * vm,
vlib_cli_output(vm, " SK_d %U",
format_hex_bytes, sa->sk_d, vec_len(sa->sk_d));
- vlib_cli_output(vm, " SK_a i:%U\n r:%U",
- format_hex_bytes, sa->sk_ai, vec_len(sa->sk_ai),
- format_hex_bytes, sa->sk_ar, vec_len(sa->sk_ar));
+ if (sa->sk_ai)
+ {
+ vlib_cli_output(vm, " SK_a i:%U\n r:%U",
+ format_hex_bytes, sa->sk_ai, vec_len(sa->sk_ai),
+ format_hex_bytes, sa->sk_ar, vec_len(sa->sk_ar));
+ }
vlib_cli_output(vm, " SK_e i:%U\n r:%U",
format_hex_bytes, sa->sk_ei, vec_len(sa->sk_ei),
format_hex_bytes, sa->sk_er, vec_len(sa->sk_er));
@@ -355,6 +358,20 @@ ikev2_profile_add_del_command_fn (vlib_main_t * vm,
else
if (unformat
(line_input,
+ "set %U ike-crypto-alg %U %u ike-dh %U",
+ unformat_token, valid_chars, &name,
+ unformat_ikev2_transform_encr_type, &crypto_alg, &tmp1,
+ unformat_ikev2_transform_dh_type, &dh_type))
+ {
+ r =
+ ikev2_set_profile_ike_transforms (vm, name, crypto_alg,
+ IKEV2_TRANSFORM_INTEG_TYPE_NONE,
+ dh_type, tmp1);
+ goto done;
+ }
+ else
+ if (unformat
+ (line_input,
"set %U esp-crypto-alg %U %u esp-integ-alg %U",
unformat_token, valid_chars, &name,
unformat_ikev2_transform_encr_type, &crypto_alg, &tmp1,
diff --git a/src/plugins/ikev2/ikev2_crypto.c b/src/plugins/ikev2/ikev2_crypto.c
index 5a07bde9b0a..b1fdf890e08 100644
--- a/src/plugins/ikev2/ikev2_crypto.c
+++ b/src/plugins/ikev2/ikev2_crypto.c
@@ -256,10 +256,7 @@ static const char modp_dh_2048_256_generator[] =
v8 *
ikev2_calc_prf (ikev2_sa_transform_t * tr, v8 * key, v8 * data)
{
- ikev2_main_t *km = &ikev2_main;
- u32 thread_index = vlib_get_thread_index ();
- ikev2_main_per_thread_data_t *ptd =
- vec_elt_at_index (km->per_thread_data, thread_index);
+ ikev2_main_per_thread_data_t *ptd = ikev2_get_per_thread_data ();
HMAC_CTX *ctx = ptd->hmac_ctx;
v8 *prf;
unsigned int len = 0;
@@ -318,10 +315,7 @@ ikev2_calc_prfplus (ikev2_sa_transform_t * tr, u8 * key, u8 * seed, int len)
v8 *
ikev2_calc_integr (ikev2_sa_transform_t * tr, v8 * key, u8 * data, int len)
{
- ikev2_main_t *km = &ikev2_main;
- u32 thread_index = vlib_get_thread_index ();
- ikev2_main_per_thread_data_t *ptd =
- vec_elt_at_index (km->per_thread_data, thread_index);
+ ikev2_main_per_thread_data_t *ptd = ikev2_get_per_thread_data ();
HMAC_CTX *ctx = ptd->hmac_ctx;
v8 *r;
unsigned int l;
@@ -348,20 +342,60 @@ ikev2_calc_integr (ikev2_sa_transform_t * tr, v8 * key, u8 * data, int len)
return r;
}
+static_always_inline void
+ikev2_init_gcm_nonce (u8 * nonce, u8 * salt, u8 * iv)
+{
+ clib_memcpy (nonce, salt, IKEV2_GCM_SALT_SIZE);
+ clib_memcpy (nonce + IKEV2_GCM_SALT_SIZE, iv, IKEV2_GCM_IV_SIZE);
+}
+
+u8 *
+ikev2_decrypt_aead_data (ikev2_main_per_thread_data_t * ptd, ikev2_sa_t * sa,
+ ikev2_sa_transform_t * tr_encr, u8 * data,
+ int data_len, u8 * aad, u32 aad_len, u8 * tag)
+{
+ EVP_CIPHER_CTX *ctx = ptd->evp_ctx;
+ int len = 0;
+ u8 *key = sa->is_initiator ? sa->sk_er : sa->sk_ei;
+ u8 nonce[IKEV2_GCM_NONCE_SIZE];
+
+ if (data_len <= IKEV2_GCM_IV_SIZE)
+ /* runt data */
+ return 0;
+
+ /* extract salt from the end of the key */
+ u8 *salt = key + vec_len (key) - IKEV2_GCM_SALT_SIZE;
+ ikev2_init_gcm_nonce (nonce, salt, data);
+
+ data += IKEV2_GCM_IV_SIZE;
+ data_len -= IKEV2_GCM_IV_SIZE;
+ v8 *r = vec_new (u8, data_len);
+
+ EVP_DecryptInit_ex (ctx, tr_encr->cipher, 0, 0, 0);
+ EVP_CIPHER_CTX_ctrl (ctx, EVP_CTRL_GCM_SET_IVLEN, 12, 0);
+ EVP_DecryptInit_ex (ctx, 0, 0, key, nonce);
+ EVP_DecryptUpdate (ctx, 0, &len, aad, aad_len);
+ EVP_DecryptUpdate (ctx, r, &len, data, data_len);
+ EVP_CIPHER_CTX_ctrl (ctx, EVP_CTRL_GCM_SET_TAG, IKEV2_GCM_ICV_SIZE, tag);
+
+ if (EVP_DecryptFinal_ex (ctx, r + len, &len) > 0)
+ {
+ /* remove padding */
+ _vec_len (r) -= r[vec_len (r) - 1] + 1;
+ return r;
+ }
+
+ vec_free (r);
+ return 0;
+}
+
v8 *
-ikev2_decrypt_data (ikev2_sa_t * sa, u8 * data, int len)
+ikev2_decrypt_data (ikev2_main_per_thread_data_t * ptd, ikev2_sa_t * sa,
+ ikev2_sa_transform_t * tr_encr, u8 * data, int len)
{
- ikev2_main_t *km = &ikev2_main;
- u32 thread_index = vlib_get_thread_index ();
- ikev2_main_per_thread_data_t *ptd =
- vec_elt_at_index (km->per_thread_data, thread_index);
EVP_CIPHER_CTX *ctx = ptd->evp_ctx;
int out_len = 0, block_size;
- ikev2_sa_transform_t *tr_encr;
u8 *key = sa->is_initiator ? sa->sk_er : sa->sk_ei;
-
- tr_encr =
- ikev2_sa_get_td_for_type (sa->r_proposals, IKEV2_TRANSFORM_TYPE_ENCR);
block_size = tr_encr->block_size;
/* check if data is multiplier of cipher block size */
@@ -382,28 +416,55 @@ ikev2_decrypt_data (ikev2_sa_t * sa, u8 * data, int len)
}
int
-ikev2_encrypt_data (ikev2_sa_t * sa, v8 * src, u8 * dst)
+ikev2_encrypt_aead_data (ikev2_main_per_thread_data_t * ptd, ikev2_sa_t * sa,
+ ikev2_sa_transform_t * tr_encr,
+ v8 * src, u8 * dst, u8 * aad, u32 aad_len, u8 * tag)
{
- ikev2_main_t *km = &ikev2_main;
- u32 thread_index = vlib_get_thread_index ();
- ikev2_main_per_thread_data_t *ptd =
- vec_elt_at_index (km->per_thread_data, thread_index);
EVP_CIPHER_CTX *ctx = ptd->evp_ctx;
- int out_len;
- int bs;
- ikev2_sa_transform_t *tr_encr;
+ int out_len = 0, len = 0;
+ u8 nonce[IKEV2_GCM_NONCE_SIZE];
u8 *key = sa->is_initiator ? sa->sk_ei : sa->sk_er;
- tr_encr =
- ikev2_sa_get_td_for_type (sa->r_proposals, IKEV2_TRANSFORM_TYPE_ENCR);
- bs = tr_encr->block_size;
+ /* generate IV; its length must be 8 octets for aes-gcm (rfc5282) */
+ RAND_bytes (dst, IKEV2_GCM_IV_SIZE);
+ ikev2_init_gcm_nonce (nonce, key + vec_len (key) - IKEV2_GCM_SALT_SIZE,
+ dst);
+ dst += IKEV2_GCM_IV_SIZE;
+
+ EVP_EncryptInit_ex (ctx, tr_encr->cipher, 0, 0, 0);
+ EVP_CIPHER_CTX_ctrl (ctx, EVP_CTRL_GCM_SET_IVLEN, 12, NULL);
+ EVP_EncryptInit_ex (ctx, 0, 0, key, nonce);
+ EVP_EncryptUpdate (ctx, NULL, &out_len, aad, aad_len);
+ EVP_EncryptUpdate (ctx, dst, &out_len, src, vec_len (src));
+ EVP_EncryptFinal_ex (ctx, dst + out_len, &len);
+ EVP_CIPHER_CTX_ctrl (ctx, EVP_CTRL_GCM_GET_TAG, 16, tag);
+ out_len += len;
+ ASSERT (vec_len (src) == out_len);
+
+ return out_len + IKEV2_GCM_IV_SIZE;
+}
+
+int
+ikev2_encrypt_data (ikev2_main_per_thread_data_t * ptd, ikev2_sa_t * sa,
+ ikev2_sa_transform_t * tr_encr, v8 * src, u8 * dst)
+{
+ EVP_CIPHER_CTX *ctx = ptd->evp_ctx;
+ int out_len = 0, len = 0;
+ int bs = tr_encr->block_size;
+ u8 *key = sa->is_initiator ? sa->sk_ei : sa->sk_er;
/* generate IV */
- RAND_bytes (dst, bs);
+ u8 *iv = dst;
+ RAND_bytes (iv, bs);
+ dst += bs;
- EVP_EncryptInit_ex (ctx, tr_encr->cipher, NULL, key, dst /* dst */ );
- EVP_EncryptUpdate (ctx, dst + bs, &out_len, src, vec_len (src));
+ EVP_EncryptInit_ex (ctx, tr_encr->cipher, NULL, key, iv);
+ /* disable padding as pad data were added before */
+ EVP_CIPHER_CTX_set_padding (ctx, 0);
+ EVP_EncryptUpdate (ctx, dst, &out_len, src, vec_len (src));
+ EVP_EncryptFinal_ex (ctx, dst + out_len, &len);
+ out_len += len;
ASSERT (vec_len (src) == out_len);
return out_len + bs;
diff --git a/src/plugins/ikev2/ikev2_priv.h b/src/plugins/ikev2/ikev2_priv.h
index b018a6433e9..7e40ed34a5e 100644
--- a/src/plugins/ikev2/ikev2_priv.h
+++ b/src/plugins/ikev2/ikev2_priv.h
@@ -521,8 +521,18 @@ u8 *ikev2_calc_prfplus (ikev2_sa_transform_t * tr, u8 * key, u8 * seed,
int len);
v8 *ikev2_calc_integr (ikev2_sa_transform_t * tr, v8 * key, u8 * data,
int len);
-v8 *ikev2_decrypt_data (ikev2_sa_t * sa, u8 * data, int len);
-int ikev2_encrypt_data (ikev2_sa_t * sa, v8 * src, u8 * dst);
+v8 *ikev2_decrypt_data (ikev2_main_per_thread_data_t * ptd, ikev2_sa_t * sa,
+ ikev2_sa_transform_t * tr_encr, u8 * data, int len);
+int ikev2_encrypt_data (ikev2_main_per_thread_data_t * ptd, ikev2_sa_t * sa,
+ ikev2_sa_transform_t * tr_encr, v8 * src, u8 * dst);
+int ikev2_encrypt_aead_data (ikev2_main_per_thread_data_t * ptd,
+ ikev2_sa_t * sa, ikev2_sa_transform_t * tr_encr,
+ v8 * src, u8 * dst, u8 * aad,
+ u32 aad_len, u8 * tag);
+u8 *ikev2_decrypt_aead_data (ikev2_main_per_thread_data_t * ptd,
+ ikev2_sa_t * sa, ikev2_sa_transform_t * tr_encr,
+ u8 * data, int data_len, u8 * aad, u32 aad_len,
+ u8 * tag);
void ikev2_generate_dh (ikev2_sa_t * sa, ikev2_sa_transform_t * t);
void ikev2_complete_dh (ikev2_sa_t * sa, ikev2_sa_transform_t * t);
int ikev2_verify_sign (EVP_PKEY * pkey, u8 * sigbuf, u8 * data);
@@ -567,6 +577,13 @@ ikev2_ts_t *ikev2_parse_ts_payload (ike_payload_header_t * ikep);
ikev2_delete_t *ikev2_parse_delete_payload (ike_payload_header_t * ikep);
ikev2_notify_t *ikev2_parse_notify_payload (ike_payload_header_t * ikep);
int ikev2_set_log_level (ikev2_log_level_t log_level);
+
+static_always_inline ikev2_main_per_thread_data_t *
+ikev2_get_per_thread_data ()
+{
+ u32 thread_index = vlib_get_thread_index ();
+ return vec_elt_at_index (ikev2_main.per_thread_data, thread_index);
+}
#endif /* __included_ikev2_priv_h__ */
diff --git a/src/plugins/ikev2/test/test_ikev2.py b/src/plugins/ikev2/test/test_ikev2.py
index 918b5cef624..77698d6a5ed 100644
--- a/src/plugins/ikev2/test/test_ikev2.py
+++ b/src/plugins/ikev2/test/test_ikev2.py
@@ -19,6 +19,9 @@ from vpp_papi import VppEnum
KEY_PAD = b"Key Pad for IKEv2"
+SALT_SIZE = 4
+GCM_ICV_SIZE = 16
+GCM_IV_SIZE = 8
# defined in rfc3526
@@ -65,19 +68,47 @@ class CryptoAlgo(object):
if self.cipher is not None:
self.bs = self.cipher.block_size // 8
- def encrypt(self, data, key):
- iv = os.urandom(self.bs)
- encryptor = Cipher(self.cipher(key), self.mode(iv),
- default_backend()).encryptor()
- return iv + encryptor.update(data) + encryptor.finalize()
-
- def decrypt(self, data, key, icv=None):
- iv = data[:self.bs]
- ct = data[self.bs:]
- decryptor = Cipher(algorithms.AES(key),
- modes.CBC(iv),
- default_backend()).decryptor()
- return decryptor.update(ct) + decryptor.finalize()
+ if self.name == 'AES-GCM-16ICV':
+ self.iv_len = GCM_IV_SIZE
+ else:
+ self.iv_len = self.bs
+
+ def encrypt(self, data, key, aad=None):
+ iv = os.urandom(self.iv_len)
+ if aad is None:
+ encryptor = Cipher(self.cipher(key), self.mode(iv),
+ default_backend()).encryptor()
+ return iv + encryptor.update(data) + encryptor.finalize()
+ else:
+ salt = key[-SALT_SIZE:]
+ nonce = salt + iv
+ encryptor = Cipher(self.cipher(key[:-SALT_SIZE]), self.mode(nonce),
+ default_backend()).encryptor()
+ encryptor.authenticate_additional_data(aad)
+ data = encryptor.update(data) + encryptor.finalize()
+ data += encryptor.tag[:GCM_ICV_SIZE]
+ return iv + data
+
+ def decrypt(self, data, key, aad=None, icv=None):
+ if aad is None:
+ iv = data[:self.iv_len]
+ ct = data[self.iv_len:]
+ decryptor = Cipher(algorithms.AES(key),
+ self.mode(iv),
+ default_backend()).decryptor()
+ return decryptor.update(ct) + decryptor.finalize()
+ else:
+ salt = key[-SALT_SIZE:]
+ nonce = salt + data[:GCM_IV_SIZE]
+ ct = data[GCM_IV_SIZE:]
+ key = key[:-SALT_SIZE]
+ decryptor = Cipher(algorithms.AES(key),
+ self.mode(nonce, icv, len(icv)),
+ default_backend()).decryptor()
+ decryptor.authenticate_additional_data(aad)
+ pt = decryptor.update(ct) + decryptor.finalize()
+ pad_len = pt[-1] + 1
+ return pt[:-pad_len]
def pad(self, data):
pad_len = (len(data) // self.bs + 1) * self.bs - len(data)
@@ -241,7 +272,7 @@ class IKEv2SA(object):
return r
def calc_prf(self, prf, key, data):
- h = self.ike_integ_alg.mac(key, prf, backend=default_backend())
+ h = self.ike_prf_alg.mac(key, prf, backend=default_backend())
h.update(data)
return h.finalize()
@@ -258,10 +289,16 @@ class IKEv2SA(object):
encr_key_len = self.ike_crypto_key_len
tr_prf_key_len = self.ike_prf_alg.key_len
integ_key_len = self.ike_integ_alg.key_len
+ if integ_key_len == 0:
+ salt_size = 4
+ else:
+ salt_size = 0
+
l = (prf_key_trunc +
integ_key_len * 2 +
encr_key_len * 2 +
- tr_prf_key_len * 2)
+ tr_prf_key_len * 2 +
+ salt_size * 2)
keymat = self.calc_prfplus(prf, self.skeyseed, s, l)
pos = 0
@@ -273,10 +310,10 @@ class IKEv2SA(object):
self.sk_ar = keymat[pos:pos+integ_key_len]
pos += integ_key_len
- self.sk_ei = keymat[pos:pos+encr_key_len]
- pos += encr_key_len
- self.sk_er = keymat[pos:pos+encr_key_len]
- pos += encr_key_len
+ self.sk_ei = keymat[pos:pos+encr_key_len + salt_size]
+ pos += encr_key_len + salt_size
+ self.sk_er = keymat[pos:pos+encr_key_len + salt_size]
+ pos += encr_key_len + salt_size
self.sk_pi = keymat[pos:pos+tr_prf_key_len]
pos += tr_prf_key_len
@@ -303,9 +340,9 @@ class IKEv2SA(object):
else:
raise TypeError('unknown auth method type!')
- def encrypt(self, data):
+ def encrypt(self, data, aad=None):
data = self.ike_crypto_alg.pad(data)
- return self.ike_crypto_alg.encrypt(data, self.my_cryptokey)
+ return self.ike_crypto_alg.encrypt(data, self.my_cryptokey, aad)
@property
def peer_authkey(self):
@@ -355,17 +392,23 @@ class IKEv2SA(object):
h.update(data)
return h.finalize()
- def decrypt(self, data):
- return self.ike_crypto_alg.decrypt(data, self.peer_cryptokey)
+ def decrypt(self, data, aad=None, icv=None):
+ return self.ike_crypto_alg.decrypt(data, self.peer_cryptokey, aad, icv)
def hmac_and_decrypt(self, ike):
ep = ike[ikev2.IKEv2_payload_Encrypted]
- self.verify_hmac(raw(ike))
- integ_trunc = self.ike_integ_alg.trunc_len
+ if self.ike_crypto == 'AES-GCM-16ICV':
+ aad_len = len(ikev2.IKEv2_payload_Encrypted()) + len(ikev2.IKEv2())
+ ct = ep.load[:-GCM_ICV_SIZE]
+ tag = ep.load[-GCM_ICV_SIZE:]
+ return self.decrypt(ct, raw(ike)[:aad_len], tag)
+ else:
+ self.verify_hmac(raw(ike))
+ integ_trunc = self.ike_integ_alg.trunc_len
- # remove ICV and decrypt payload
- ct = ep.load[:-integ_trunc]
- return self.decrypt(ct)
+ # remove ICV and decrypt payload
+ ct = ep.load[:-integ_trunc]
+ return self.decrypt(ct)
def generate_ts(self):
c = self.child_sas[0]
@@ -388,7 +431,7 @@ class IKEv2SA(object):
if integ not in AUTH_ALGOS:
raise TypeError('unsupported auth algo %r' % integ)
- self.ike_integ = integ
+ self.ike_integ = None if integ == 'NULL' else integ
self.ike_integ_alg = AUTH_ALGOS[integ]
if prf not in PRF_ALGOS:
@@ -411,7 +454,7 @@ class IKEv2SA(object):
self.esp_integ_alg = AUTH_ALGOS[integ]
def crypto_attr(self, key_len):
- if self.ike_crypto in ['AES-CBC', 'AES-GCM']:
+ if self.ike_crypto in ['AES-CBC', 'AES-GCM-16ICV']:
return (0x800e << 16 | key_len << 3, 12)
else:
raise Exception('unsupported attribute type')
@@ -542,24 +585,47 @@ class TemplateResponder(VppTestCase):
number_of_TSs=len(tsr),
traffic_selector=tsr) /
ikev2.IKEv2_payload_Notify(type='INITIAL_CONTACT'))
- encr = self.sa.encrypt(raw(plain))
-
- trunc_len = self.sa.ike_integ_alg.trunc_len
- plen = len(encr) + len(ikev2.IKEv2_payload_Encrypted()) + trunc_len
- tlen = plen + len(ikev2.IKEv2())
-
- sk_p = ikev2.IKEv2_payload_Encrypted(next_payload='IDi',
- length=plen, load=encr)
- sa_auth = (ikev2.IKEv2(init_SPI=self.sa.ispi, resp_SPI=self.sa.rspi,
- length=tlen, flags='Initiator', exch_type='IKE_AUTH', id=1))
- sa_auth /= sk_p
-
- integ_data = raw(sa_auth)
- hmac_data = self.sa.compute_hmac(self.sa.ike_integ_alg.mod(),
- self.sa.my_authkey, integ_data)
- sa_auth = sa_auth / Raw(hmac_data[:trunc_len])
- assert(len(sa_auth) == tlen)
+ if self.sa.ike_crypto == 'AES-GCM-16ICV':
+ data = self.sa.ike_crypto_alg.pad(raw(plain))
+ plen = len(data) + GCM_IV_SIZE + GCM_ICV_SIZE +\
+ len(ikev2.IKEv2_payload_Encrypted())
+ tlen = plen + len(ikev2.IKEv2())
+
+ # prepare aad data
+ sk_p = ikev2.IKEv2_payload_Encrypted(next_payload='IDi',
+ length=plen)
+ sa_auth = (ikev2.IKEv2(init_SPI=self.sa.ispi,
+ resp_SPI=self.sa.rspi, id=1,
+ length=tlen, flags='Initiator', exch_type='IKE_AUTH'))
+ sa_auth /= sk_p
+
+ encr = self.sa.encrypt(raw(plain), raw(sa_auth))
+ sk_p = ikev2.IKEv2_payload_Encrypted(next_payload='IDi',
+ length=plen, load=encr)
+ sa_auth = (ikev2.IKEv2(init_SPI=self.sa.ispi,
+ resp_SPI=self.sa.rspi, id=1,
+ length=tlen, flags='Initiator', exch_type='IKE_AUTH'))
+ sa_auth /= sk_p
+ else:
+ encr = self.sa.encrypt(raw(plain))
+ trunc_len = self.sa.ike_integ_alg.trunc_len
+ plen = len(encr) + len(ikev2.IKEv2_payload_Encrypted()) + trunc_len
+ tlen = plen + len(ikev2.IKEv2())
+
+ sk_p = ikev2.IKEv2_payload_Encrypted(next_payload='IDi',
+ length=plen, load=encr)
+ sa_auth = (ikev2.IKEv2(init_SPI=self.sa.ispi,
+ resp_SPI=self.sa.rspi, id=1,
+ length=tlen, flags='Initiator', exch_type='IKE_AUTH'))
+ sa_auth /= sk_p
+
+ integ_data = raw(sa_auth)
+ hmac_data = self.sa.compute_hmac(self.sa.ike_integ_alg.mod(),
+ self.sa.my_authkey, integ_data)
+ sa_auth = sa_auth / Raw(hmac_data[:trunc_len])
+
+ assert(len(sa_auth) == tlen)
packet = self.create_ike_msg(self.pg0, sa_auth, self.sa.sport,
self.sa.dport, self.sa.natt)
self.pg0.add_stream(packet)
@@ -590,8 +656,9 @@ class TemplateResponder(VppTestCase):
sa = ih[ikev2.IKEv2_payload_SA]
self.sa.r_nonce = ih[ikev2.IKEv2_payload_Nonce].load
self.sa.r_dh_data = ih[ikev2.IKEv2_payload_KE].load
- except AttributeError as e:
+ except IndexError as e:
self.logger.error("unexpected reply: SA/Nonce/KE payload found!")
+ self.logger.error(ih.show())
raise
self.sa.complete_dh_data()
self.sa.calc_keys()
@@ -783,5 +850,16 @@ class TestAES_CBC_128_SHA256_128_MODP3072_ESP_AES_GCM_16\
'ike-dh': '3072MODPgr'})
+class Test_IKE_AES_GCM_16_256(TemplateResponder, Ikev2Params):
+ """
+ IKE:AES_GCM_16_256
+ """
+ def config_tc(self):
+ self.config_params({
+ 'ike-crypto': ('AES-GCM-16ICV', 32),
+ 'ike-integ': 'NULL',
+ 'ike-dh': '2048MODPgr'})
+
+
if __name__ == '__main__':
unittest.main(testRunner=VppTestRunner)