From a7b963df2758d7c25de366db1999ca1024e12d30 Mon Sep 17 00:00:00 2001 From: Filip Tehlar Date: Wed, 8 Jul 2020 13:25:34 +0000 Subject: ikev2: add support for AES-GCM cipher in IKE Type: feature Ticket: VPP-1920 Change-Id: I6e30f3594cb30553f3ca5a35e0a4f679325aacec Signed-off-by: Filip Tehlar --- src/plugins/ikev2/ikev2.c | 119 ++++++++++++++++-------- src/plugins/ikev2/ikev2.h | 5 + src/plugins/ikev2/ikev2_cli.c | 23 ++++- src/plugins/ikev2/ikev2_crypto.c | 123 ++++++++++++++++++------- src/plugins/ikev2/ikev2_priv.h | 21 ++++- src/plugins/ikev2/test/test_ikev2.py | 174 +++++++++++++++++++++++++---------- 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)); @@ -352,6 +355,20 @@ ikev2_profile_add_del_command_fn (vlib_main_t * vm, dh_type, tmp1); goto done; } + 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, 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) -- cgit 1.2.3-korg