ikev2: add support for AES-GCM cipher in IKE 08/27908/3
authorFilip Tehlar <ftehlar@cisco.com>
Wed, 8 Jul 2020 13:25:34 +0000 (13:25 +0000)
committerBenoît Ganne <bganne@cisco.com>
Wed, 15 Jul 2020 16:12:16 +0000 (16:12 +0000)
Type: feature
Ticket: VPP-1920

Change-Id: I6e30f3594cb30553f3ca5a35e0a4f679325aacec
Signed-off-by: Filip Tehlar <ftehlar@cisco.com>
src/plugins/ikev2/ikev2.c
src/plugins/ikev2/ikev2.h
src/plugins/ikev2/ikev2_cli.c
src/plugins/ikev2/ikev2_crypto.c
src/plugins/ikev2/ikev2_priv.h
src/plugins/ikev2/test/test_ikev2.py

index 08a66cb..c11bd0f 100644 (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);
+  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;
index 26df41d..dd1c646 100644 (file)
 #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* */
index 6c75557..30de10b 100644 (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,
index 5a07bde..b1fdf89 100644 (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;
 }
 
+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;
index b018a64..7e40ed3 100644 (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__ */
 
 
index 918b5ce..77698d6 100644 (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()
-
-    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)