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:
Filip Tehlar
2020-07-08 13:25:34 +00:00
committed by Benoît Ganne
parent 8046fdc10b
commit a7b963df27
6 changed files with 345 additions and 120 deletions

View File

@ -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;

View File

@ -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* */

View File

@ -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,

View File

@ -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;

View File

@ -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__ */

View File

@ -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)