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>
This commit is contained in:

committed by
Benoît Ganne

parent
8046fdc10b
commit
a7b963df27
@ -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);
|
||||
|
||||
plen = plen - sizeof (*ikep) - tr_integ->key_trunc;
|
||||
|
||||
if (memcmp (hmac, &ikep->payload[plen], tr_integ->key_trunc))
|
||||
if (is_aead)
|
||||
{
|
||||
ikev2_elog_error ("message integrity check failed");
|
||||
vec_free (hmac);
|
||||
return 0;
|
||||
}
|
||||
vec_free (hmac);
|
||||
if (plen < sizeof (*ikep) + IKEV2_GCM_ICV_SIZE)
|
||||
return 0;
|
||||
|
||||
return ikev2_decrypt_data (sa, ikep->payload, plen);
|
||||
plen -= sizeof (*ikep) + IKEV2_GCM_ICV_SIZE;
|
||||
u8 *aad = (u8 *) ike;
|
||||
u32 aad_len = ikep->payload - aad;
|
||||
u8 *tag = ikep->payload + plen;
|
||||
|
||||
plaintext = ikev2_decrypt_aead_data (ptd, sa, tr_encr, ikep->payload,
|
||||
plen, aad, aad_len, tag);
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
|
||||
plaintext = ikev2_decrypt_data (ptd, sa, tr_encr, 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;
|
||||
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 */
|
||||
plen += tr_integ->key_trunc;
|
||||
/* 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;
|
||||
|
@ -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* */
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
v8 *
|
||||
ikev2_decrypt_data (ikev2_sa_t * sa, u8 * data, int len)
|
||||
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_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;
|
||||
|
@ -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__ */
|
||||
|
||||
|
||||
|
@ -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()
|
||||
if self.name == 'AES-GCM-16ICV':
|
||||
self.iv_len = GCM_IV_SIZE
|
||||
else:
|
||||
self.iv_len = self.bs
|
||||
|
||||
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()
|
||||
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())
|
||||
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())
|
||||
|
||||
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
|
||||
# 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])
|
||||
|
||||
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)
|
||||
|
Reference in New Issue
Block a user