iked(8): AES_GCM ciphers for IKE

classic Classic list List threaded Threaded
10 messages Options
Reply | Threaded
Open this post in threaded view
|

iked(8): AES_GCM ciphers for IKE

Tobias Heider-2
Hi,

currently iked(8) supports AES-GCM only for ESP.
The diff below adds the ENCR_AES_GCM_16 and ENCR_AES_GCM_12 variants for IKE.
(for more information see [1] and [2]).
Both variants support the 128, 196, and 256 bit key lengths.

The new new ciphers can be configured with:
- aes-128-gcm, aes-196-gcm and aes-256-gcm for ENCR_AES_GCM_16
- aes-128-gcm-12, aes-196-gcm-12 and aes-256-gcm-12 for ENCR_AES_GCM_12

It would be nice if we could get some interop testing with different IKEv2
implementations.  I have so far successfully tested strongswan <-> iked and
of course iked <-> iked.

Feedback welcome ;)

[1] https://tools.ietf.org/html/rfc5282
[2] https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml#ikev2-parameters-5

Index: crypto.c
===================================================================
RCS file: /cvs/src/sbin/iked/crypto.c,v
retrieving revision 1.26
diff -u -p -r1.26 crypto.c
--- crypto.c 22 Apr 2020 17:26:54 -0000 1.26
+++ crypto.c 14 May 2020 20:00:28 -0000
@@ -92,7 +92,7 @@ hash_new(uint8_t type, uint16_t id)
  struct iked_hash *hash;
  const EVP_MD *md = NULL;
  HMAC_CTX *ctx = NULL;
- int length = 0, fixedkey = 0, trunc = 0;
+ int length = 0, fixedkey = 0, trunc = 0, isaead = 0;
 
  switch (type) {
  case IKEV2_XFORMTYPE_PRF:
@@ -156,6 +156,14 @@ hash_new(uint8_t type, uint16_t id)
  length = SHA512_DIGEST_LENGTH;
  trunc = 32;
  break;
+ case IKEV2_XFORMAUTH_AES_GCM_12:
+ length = 12;
+ isaead = 1;
+ break;
+ case IKEV2_XFORMAUTH_AES_GCM_16:
+ length = 16;
+ isaead = 1;
+ break;
  case IKEV2_XFORMAUTH_NONE:
  case IKEV2_XFORMAUTH_DES_MAC:
  case IKEV2_XFORMAUTH_KPDK_MD5:
@@ -177,7 +185,7 @@ hash_new(uint8_t type, uint16_t id)
     print_map(id, ikev2_xformtype_map));
  break;
  }
- if (md == NULL)
+ if (!isaead && md == NULL)
  return (NULL);
 
  if ((hash = calloc(1, sizeof(*hash))) == NULL) {
@@ -192,6 +200,10 @@ hash_new(uint8_t type, uint16_t id)
  hash->hash_trunc = trunc;
  hash->hash_length = length;
  hash->hash_fixedkey = fixedkey;
+ hash->hash_isaead = isaead;
+
+ if (isaead)
+ return (hash);
 
  if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
  log_debug("%s: alloc hash ctx", __func__);
@@ -276,6 +288,7 @@ cipher_new(uint8_t type, uint16_t id, ui
  const EVP_CIPHER *cipher = NULL;
  EVP_CIPHER_CTX *ctx = NULL;
  int length = 0, fixedkey = 0, ivlength = 0;
+ int saltlength = 0, authid = 0;
 
  switch (type) {
  case IKEV2_XFORMTYPE_ENCR:
@@ -309,6 +322,39 @@ cipher_new(uint8_t type, uint16_t id, ui
  ivlength = EVP_CIPHER_iv_length(cipher);
  fixedkey = EVP_CIPHER_key_length(cipher);
  break;
+ case IKEV2_XFORMENCR_AES_GCM_16:
+ case IKEV2_XFORMENCR_AES_GCM_12:
+ switch (id_length) {
+ case 128:
+ cipher = EVP_aes_128_gcm();
+ break;
+ case 192:
+ cipher = EVP_aes_192_gcm();
+ break;
+ case 256:
+ cipher = EVP_aes_256_gcm();
+ break;
+ default:
+ log_debug("%s: invalid key length %d"
+    " for cipher %s", __func__, id_length,
+    print_map(id, ikev2_xformencr_map));
+ break;
+ }
+ if (cipher == NULL)
+ break;
+ switch(id) {
+ case IKEV2_XFORMENCR_AES_GCM_16:
+ authid = IKEV2_XFORMAUTH_AES_GCM_16;
+ break;
+ case IKEV2_XFORMENCR_AES_GCM_12:
+ authid = IKEV2_XFORMAUTH_AES_GCM_12;
+ break;
+ }
+ length = EVP_CIPHER_block_size(cipher);
+ ivlength = 8;
+ saltlength = 4;
+ fixedkey = EVP_CIPHER_key_length(cipher) + saltlength;
+ break;
  case IKEV2_XFORMENCR_DES_IV64:
  case IKEV2_XFORMENCR_DES:
  case IKEV2_XFORMENCR_RC5:
@@ -346,6 +392,8 @@ cipher_new(uint8_t type, uint16_t id, ui
  encr->encr_length = length;
  encr->encr_fixedkey = fixedkey;
  encr->encr_ivlength = ivlength ? ivlength : length;
+ encr->encr_saltlength = saltlength;
+ encr->encr_authid = authid;
 
  if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
  log_debug("%s: alloc cipher ctx", __func__);
@@ -392,6 +440,20 @@ cipher_setiv(struct iked_cipher *encr, v
  return (encr->encr_iv);
 }
 
+int
+cipher_settag(struct iked_cipher *encr, uint8_t *data, size_t len)
+{
+ return (EVP_CIPHER_CTX_ctrl(encr->encr_ctx,
+    EVP_CTRL_GCM_SET_TAG, len, data) != 1);
+}
+
+int
+cipher_gettag(struct iked_cipher *encr, uint8_t *data, size_t len)
+{
+ return (EVP_CIPHER_CTX_ctrl(encr->encr_ctx,
+    EVP_CTRL_GCM_GET_TAG, len, data) != 1);
+}
+
 void
 cipher_free(struct iked_cipher *encr)
 {
@@ -676,6 +738,20 @@ dsa_setkey(struct iked_dsa *dsa, void *k
  ibuf_release(dsa->dsa_keydata);
  dsa->dsa_keydata = NULL;
  return (NULL);
+}
+
+void
+cipher_aad(struct iked_cipher *encr, void *in, size_t inlen,
+    size_t *outlen)
+{
+ int olen = 0;
+
+ if (EVP_CipherUpdate(encr->encr_ctx, NULL, &olen, in, inlen) != 1) {
+ ca_sslerror(__func__);
+ *outlen = 0;
+ return;
+ }
+ *outlen = (size_t)olen;
 }
 
 int
Index: iked.conf.5
===================================================================
RCS file: /cvs/src/sbin/iked/iked.conf.5,v
retrieving revision 1.68
diff -u -p -r1.68 iked.conf.5
--- iked.conf.5 1 May 2020 17:44:02 -0000 1.68
+++ iked.conf.5 14 May 2020 20:00:28 -0000
@@ -861,12 +861,15 @@ keyword:
 .It Li aes-128 Ta "128 bits" Ta ""
 .It Li aes-192 Ta "192 bits" Ta ""
 .It Li aes-256 Ta "256 bits" Ta ""
+.It Li aes-128-gcm Ta "160 bits" Ta ""
+.It Li aes-192-gcm Ta "224 bits" Ta ""
+.It Li aes-256-gcm Ta "288 bits" Ta ""
+.It Li aes-128-gcm-12 Ta "160 bits" Ta "[IKE only]"
+.It Li aes-192-gcm-12 Ta "224 bits" Ta "[IKE only]"
+.It Li aes-256-gcm-12 Ta "288 bits" Ta "[IKE only]"
 .It Li aes-128-ctr Ta "160 bits" Ta "[ESP only]"
 .It Li aes-192-ctr Ta "224 bits" Ta "[ESP only]"
 .It Li aes-256-ctr Ta "288 bits" Ta "[ESP only]"
-.It Li aes-128-gcm Ta "160 bits" Ta "[ESP only]"
-.It Li aes-192-gcm Ta "224 bits" Ta "[ESP only]"
-.It Li aes-256-gcm Ta "288 bits" Ta "[ESP only]"
 .It Li blowfish Ta "160 bits" Ta "[ESP only]"
 .It Li cast Ta "128 bits" Ta "[ESP only]"
 .It Li chacha20-poly1305 Ta "288 bits" Ta "[ESP only]"
Index: iked.h
===================================================================
RCS file: /cvs/src/sbin/iked/iked.h,v
retrieving revision 1.149
diff -u -p -r1.149 iked.h
--- iked.h 13 May 2020 18:28:51 -0000 1.149
+++ iked.h 14 May 2020 20:00:29 -0000
@@ -309,6 +309,7 @@ struct iked_hash {
  size_t hash_length; /* Output length */
  size_t hash_trunc; /* Truncate the output length */
  struct iked_hash *hash_prf; /* PRF pointer */
+ int hash_isaead;
 };
 
 struct iked_cipher {
@@ -321,6 +322,8 @@ struct iked_cipher {
  struct ibuf *encr_iv; /* Initialization Vector */
  size_t encr_ivlength; /* IV length */
  size_t encr_length; /* Block length */
+ size_t encr_saltlength; /* IV salt length */
+ uint16_t encr_authid; /* ID of associated authentication */
 };
 
 struct iked_dsa {
@@ -933,7 +936,8 @@ int ikev2_msg_send(struct iked *, struc
 int ikev2_msg_send_encrypt(struct iked *, struct iked_sa *,
     struct ibuf **, uint8_t, uint8_t, int);
 struct ibuf
- *ikev2_msg_encrypt(struct iked *, struct iked_sa *, struct ibuf *);
+ *ikev2_msg_encrypt(struct iked *, struct iked_sa *, struct ibuf *,
+    struct ibuf *);
 struct ibuf *
  ikev2_msg_decrypt(struct iked *, struct iked_sa *,
     struct ibuf *, struct ibuf *);
@@ -1129,5 +1133,6 @@ void print_policy(struct iked_policy *)
 size_t keylength_xf(unsigned int, unsigned int, unsigned int);
 size_t noncelength_xf(unsigned int, unsigned int);
 int cmdline_symset(char *);
+int encxf_noauth(unsigned int);
 
 #endif /* IKED_H */
Index: ikev2.c
===================================================================
RCS file: /cvs/src/sbin/iked/ikev2.c,v
retrieving revision 1.225
diff -u -p -r1.225 ikev2.c
--- ikev2.c 11 May 2020 20:11:35 -0000 1.225
+++ ikev2.c 14 May 2020 20:00:31 -0000
@@ -4473,7 +4473,7 @@ ikev2_send_informational(struct iked *en
  goto done;
 
  /* Encrypt message and add as an E payload */
- if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) {
+ if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) {
  log_debug("%s: encryption failed", __func__);
  goto done;
  }
@@ -4666,6 +4666,16 @@ ikev2_sa_initiator(struct iked *env, str
  }
  }
 
+ /* For AEAD ciphers integrity is implicit */
+ if (sa->sa_encr->encr_authid && sa->sa_integr == NULL) {
+ if ((sa->sa_integr = hash_new(IKEV2_XFORMTYPE_INTEGR,
+    sa->sa_encr->encr_authid)) == NULL) {
+ log_info("%s: failed to get AEAD integr",
+    SPI_SA(sa, __func__));
+ return (-1);
+ }
+ }
+
  if (sa->sa_prf == NULL) {
  if ((xform = config_findtransform(&sa->sa_proposals,
     IKEV2_XFORMTYPE_PRF, 0)) == NULL) {
@@ -4840,6 +4850,16 @@ ikev2_sa_responder(struct iked *env, str
  }
  }
 
+ /* For AEAD ciphers integrity is implicit */
+ if (sa->sa_encr->encr_authid && sa->sa_integr == NULL) {
+ if ((sa->sa_integr = hash_new(IKEV2_XFORMTYPE_INTEGR,
+    sa->sa_encr->encr_authid)) == NULL) {
+ log_info("%s: failed to get AEAD integr",
+    SPI_SA(sa, __func__));
+ return (-1);
+ }
+ }
+
  if (sa->sa_prf == NULL) {
  if ((xform = config_findtransform(&sa->sa_proposals,
     IKEV2_XFORMTYPE_PRF, 0)) == NULL) {
@@ -4883,6 +4903,7 @@ ikev2_sa_keys(struct iked *env, struct i
  size_t nonceminlen, ilen, rlen, tmplen;
  uint64_t ispi, rspi;
  int ret = -1;
+ int isaead = 0;
 
  ninr = dhsecret = skeyseed = s = t = NULL;
 
@@ -4895,6 +4916,9 @@ ikev2_sa_keys(struct iked *env, struct i
  return (-1);
  }
 
+ /* For AEADs no auth keys are required (see RFC 5282) */
+ isaead = !!integr->hash_isaead;
+
  if (prf->hash_fixedkey)
  nonceminlen = prf->hash_fixedkey;
  else
@@ -5026,13 +5050,13 @@ ikev2_sa_keys(struct iked *env, struct i
  * Get the size of the key material we need and the number
  * of rounds we need to run the prf+ function.
  */
- ilen = hash_length(prf) + /* SK_d */
-    hash_keylength(integr) + /* SK_ai */
-    hash_keylength(integr) + /* SK_ar */
-    cipher_keylength(encr) + /* SK_ei */
-    cipher_keylength(encr) + /* SK_er */
-    hash_keylength(prf) + /* SK_pi */
-    hash_keylength(prf); /* SK_pr */
+ ilen = hash_length(prf) + /* SK_d */
+    (isaead ? 0 : hash_keylength(integr)) + /* SK_ai */
+    (isaead ? 0 : hash_keylength(integr)) + /* SK_ar */
+    cipher_keylength(encr) + /* SK_ei */
+    cipher_keylength(encr) + /* SK_er */
+    hash_keylength(prf) + /* SK_pi */
+    hash_keylength(prf); /* SK_pr */
 
  if ((t = ikev2_prfplus(prf, skeyseed, s, ilen)) == NULL) {
  log_info("%s: failed to get IKE SA key material",
@@ -5042,8 +5066,10 @@ ikev2_sa_keys(struct iked *env, struct i
 
  /* ibuf_get() returns a new buffer from the next read offset */
  if ((sa->sa_key_d = ibuf_get(t, hash_length(prf))) == NULL ||
-    (sa->sa_key_iauth = ibuf_get(t, hash_keylength(integr))) == NULL ||
-    (sa->sa_key_rauth = ibuf_get(t, hash_keylength(integr))) == NULL ||
+    (!isaead &&
+    (sa->sa_key_iauth = ibuf_get(t, hash_keylength(integr))) == NULL) ||
+    (!isaead &&
+    (sa->sa_key_rauth = ibuf_get(t, hash_keylength(integr))) == NULL) ||
     (sa->sa_key_iencr = ibuf_get(t, cipher_keylength(encr))) == NULL ||
     (sa->sa_key_rencr = ibuf_get(t, cipher_keylength(encr))) == NULL ||
     (sa->sa_key_iprf = ibuf_get(t, hash_length(prf))) == NULL ||
@@ -5055,12 +5081,16 @@ ikev2_sa_keys(struct iked *env, struct i
  log_debug("%s: SK_d with %zu bytes", __func__,
     ibuf_length(sa->sa_key_d));
  print_hex(sa->sa_key_d->buf, 0, ibuf_length(sa->sa_key_d));
- log_debug("%s: SK_ai with %zu bytes", __func__,
-    ibuf_length(sa->sa_key_iauth));
- print_hex(sa->sa_key_iauth->buf, 0, ibuf_length(sa->sa_key_iauth));
- log_debug("%s: SK_ar with %zu bytes", __func__,
-    ibuf_length(sa->sa_key_rauth));
- print_hex(sa->sa_key_rauth->buf, 0, ibuf_length(sa->sa_key_rauth));
+ if (!isaead) {
+ log_debug("%s: SK_ai with %zu bytes", __func__,
+    ibuf_length(sa->sa_key_iauth));
+ print_hex(sa->sa_key_iauth->buf, 0,
+    ibuf_length(sa->sa_key_iauth));
+ log_debug("%s: SK_ar with %zu bytes", __func__,
+    ibuf_length(sa->sa_key_rauth));
+ print_hex(sa->sa_key_rauth->buf, 0,
+    ibuf_length(sa->sa_key_rauth));
+ }
  log_debug("%s: SK_ei with %zu bytes", __func__,
     ibuf_length(sa->sa_key_iencr));
  print_hex(sa->sa_key_iencr->buf, 0, ibuf_length(sa->sa_key_iencr));
Index: ikev2.h
===================================================================
RCS file: /cvs/src/sbin/iked/ikev2.h,v
retrieving revision 1.32
diff -u -p -r1.32 ikev2.h
--- ikev2.h 28 Apr 2020 15:18:52 -0000 1.32
+++ ikev2.h 14 May 2020 20:00:31 -0000
@@ -224,6 +224,9 @@ extern struct iked_constmap ikev2_xformp
 #define IKEV2_XFORMAUTH_HMAC_SHA2_256_128 12 /* RFC4868 */
 #define IKEV2_XFORMAUTH_HMAC_SHA2_384_192 13 /* RFC4868 */
 #define IKEV2_XFORMAUTH_HMAC_SHA2_512_256 14 /* RFC4868 */
+#define IKEV2_XFORMAUTH_AES_GCM_8 2018 /* private */
+#define IKEV2_XFORMAUTH_AES_GCM_12 2019 /* private */
+#define IKEV2_XFORMAUTH_AES_GCM_16 2020 /* private */
 
 extern struct iked_constmap ikev2_xformauth_map[];
 
Index: ikev2_msg.c
===================================================================
RCS file: /cvs/src/sbin/iked/ikev2_msg.c,v
retrieving revision 1.66
diff -u -p -r1.66 ikev2_msg.c
--- ikev2_msg.c 24 Apr 2020 21:15:05 -0000 1.66
+++ ikev2_msg.c 14 May 2020 20:00:31 -0000
@@ -51,6 +51,8 @@ void ikev2_msg_retransmit_timeout(struc
 int ikev2_check_frag_oversize(struct iked_sa *sa, struct ibuf *buf);
 int ikev2_send_encrypted_fragments(struct iked *env, struct iked_sa *sa,
     struct ibuf *in,uint8_t exchange, uint8_t firstpayload, int response);
+int ikev2_msg_encrypt_prepare(struct iked_sa *, struct ikev2_payload *,
+    struct ibuf*, struct ibuf *, struct ike_header *, uint8_t, int);
 
 void
 ikev2_msg_cb(int fd, short event, void *arg)
@@ -329,8 +331,45 @@ ikev2_msg_id(struct iked *env, struct ik
  return (id);
 }
 
+/*
+ * Calculate the final sizes of the IKEv2 header and the encrypted payload
+ * header.  This must be done before encryption to make sure the correct
+ * headers are authenticated.
+ */
+int
+ikev2_msg_encrypt_prepare(struct iked_sa *sa, struct ikev2_payload *pld,
+    struct ibuf *buf, struct ibuf *e, struct ike_header *hdr,
+    uint8_t firstpayload, int fragmentation)
+{
+ size_t len, ivlen, encrlen, integrlen, blocklen, pldlen, outlen;
+
+ if (sa == NULL ||
+    sa->sa_encr == NULL ||
+    sa->sa_integr == NULL) {
+ log_debug("%s: invalid SA", __func__);
+ return (-1);
+ }
+
+ len = ibuf_size(e);
+ blocklen = cipher_length(sa->sa_encr);
+ integrlen = hash_length(sa->sa_integr);
+ ivlen = cipher_ivlength(sa->sa_encr);
+ encrlen = roundup(len + 1, blocklen);
+ outlen = cipher_outlength(sa->sa_encr, encrlen);
+ pldlen = ivlen + outlen + integrlen +
+    (fragmentation ? sizeof(struct ikev2_frag_payload) : 0);
+
+ if (ikev2_next_payload(pld, pldlen, firstpayload) == -1)
+ return (-1);
+ if (ikev2_set_header(hdr, ibuf_size(buf) + pldlen - sizeof(*hdr)) == -1)
+ return (-1);
+
+ return (0);
+}
+
 struct ibuf *
-ikev2_msg_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf *src)
+ikev2_msg_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf *src,
+    struct ibuf *aad)
 {
  size_t len, ivlen, encrlen, integrlen, blocklen,
     outlen;
@@ -423,18 +462,13 @@ ikev2_msg_integr(struct iked *env, struc
  print_hex(ibuf_data(src), 0, ibuf_size(src));
 
  if (sa == NULL ||
+    sa->sa_encr == NULL ||
     sa->sa_integr == NULL) {
  log_debug("%s: invalid SA", __func__);
  return (-1);
  }
 
- if (sa->sa_hdr.sh_initiator)
- integr = sa->sa_key_iauth;
- else
- integr = sa->sa_key_rauth;
-
  integrlen = hash_length(sa->sa_integr);
-
  log_debug("%s: integrity checksum length %zu", __func__,
     integrlen);
 
@@ -444,21 +478,33 @@ ikev2_msg_integr(struct iked *env, struc
  if ((tmp = ibuf_new(NULL, hash_keylength(sa->sa_integr))) == NULL)
  goto done;
 
- hash_setkey(sa->sa_integr, ibuf_data(integr), ibuf_size(integr));
- hash_init(sa->sa_integr);
- hash_update(sa->sa_integr, ibuf_data(src),
-    ibuf_size(src) - integrlen);
- hash_final(sa->sa_integr, ibuf_data(tmp), &tmplen);
+ if (!sa->sa_integr->hash_isaead) {
+ if (sa->sa_hdr.sh_initiator)
+ integr = sa->sa_key_iauth;
+ else
+ integr = sa->sa_key_rauth;
+
+ hash_setkey(sa->sa_integr, ibuf_data(integr),
+    ibuf_size(integr));
+ hash_init(sa->sa_integr);
+ hash_update(sa->sa_integr, ibuf_data(src),
+    ibuf_size(src) - integrlen);
+ hash_final(sa->sa_integr, ibuf_data(tmp), &tmplen);
 
- if (tmplen != integrlen) {
- log_debug("%s: hash failure", __func__);
- goto done;
+ if (tmplen != integrlen) {
+ log_debug("%s: hash failure", __func__);
+ goto done;
+ }
+ } else {
+ /* Append AEAD tag */
+ if (cipher_gettag(sa->sa_encr, ibuf_data(tmp), ibuf_size(tmp)))
+ goto done;
  }
 
  if ((ptr = ibuf_seek(src,
     ibuf_size(src) - integrlen, integrlen)) == NULL)
  goto done;
- memcpy(ptr, ibuf_data(tmp), tmplen);
+ memcpy(ptr, ibuf_data(tmp), integrlen);
 
  print_hex(ibuf_data(tmp), 0, ibuf_size(tmp));
 
@@ -475,7 +521,7 @@ ikev2_msg_decrypt(struct iked *env, stru
 {
  ssize_t ivlen, encrlen, integrlen, blocklen,
     outlen, tmplen;
- uint8_t pad = 0, *ptr;
+ uint8_t pad = 0, *ptr, *integrdata;
  struct ibuf *integr, *encr, *tmp = NULL, *out = NULL;
  off_t ivoff, encroff, integroff;
 
@@ -518,25 +564,30 @@ ikev2_msg_decrypt(struct iked *env, stru
  /*
  * Validate packet checksum
  */
- if ((tmp = ibuf_new(NULL, ibuf_length(integr))) == NULL)
- goto done;
+ if (!sa->sa_integr->hash_isaead) {
+ if ((tmp = ibuf_new(NULL, ibuf_length(integr))) == NULL)
+ goto done;
 
- hash_setkey(sa->sa_integr, integr->buf, ibuf_length(integr));
- hash_init(sa->sa_integr);
- hash_update(sa->sa_integr, ibuf_data(msg),
-    ibuf_size(msg) - integrlen);
- hash_final(sa->sa_integr, tmp->buf, &tmplen);
+ hash_setkey(sa->sa_integr, integr->buf, ibuf_length(integr));
+ hash_init(sa->sa_integr);
+ hash_update(sa->sa_integr, ibuf_data(msg),
+    ibuf_size(msg) - integrlen);
+ hash_final(sa->sa_integr, tmp->buf, &tmplen);
 
- if (memcmp(tmp->buf, ibuf_data(src) + integroff, integrlen) != 0) {
- log_debug("%s: integrity check failed", __func__);
- goto done;
- }
+ integrdata = ibuf_seek(src, integroff, integrlen);
+ if (integrdata == NULL)
+ goto done;
+ if (memcmp(tmp->buf, integrdata, integrlen) != 0) {
+ log_debug("%s: integrity check failed", __func__);
+ goto done;
+ }
 
- log_debug("%s: integrity check succeeded", __func__);
- print_hex(tmp->buf, 0, tmplen);
+ log_debug("%s: integrity check succeeded", __func__);
+ print_hex(tmp->buf, 0, tmplen);
 
- ibuf_release(tmp);
- tmp = NULL;
+ ibuf_release(tmp);
+ tmp = NULL;
+ }
 
  /*
  * Decrypt the payload and strip any padding
@@ -550,10 +601,31 @@ ikev2_msg_decrypt(struct iked *env, stru
  cipher_setiv(sa->sa_encr, ibuf_data(src) + ivoff, ivlen);
  cipher_init_decrypt(sa->sa_encr);
 
+ /* Set AEAD tag */
+ if (sa->sa_integr->hash_isaead) {
+ integrdata = ibuf_seek(src, integroff, integrlen);
+ if (integrdata == NULL)
+ goto done;
+ if (cipher_settag(sa->sa_encr, integrdata, integrlen)) {
+ log_info("%s: failed to set tag.", __func__);
+ goto done;
+ }
+ }
+
  if ((out = ibuf_new(NULL, cipher_outlength(sa->sa_encr,
     encrlen))) == NULL)
  goto done;
 
+ /*
+ * Add additional authenticated data for AEAD ciphers
+ */
+ if (sa->sa_integr->hash_isaead) {
+ log_debug("%s: AAD length %zu", __func__, ibuf_length(msg) - ibuf_length(src));
+ print_hex(ibuf_data(msg), 0, ibuf_length(msg) - ibuf_length(src));
+ cipher_aad(sa->sa_encr, ibuf_data(msg),
+    ibuf_length(msg) - ibuf_length(src), &outlen);
+ }
+
  if ((outlen = ibuf_length(out)) != 0) {
  cipher_update(sa->sa_encr, ibuf_data(src) + encroff, encrlen,
     ibuf_data(out), &outlen);
@@ -562,6 +634,11 @@ ikev2_msg_decrypt(struct iked *env, stru
  pad = *ptr;
  }
 
+ if (cipher_final(sa->sa_encr) == -1) {
+ log_info("%s: decryption failed.", __func__);
+ goto done;
+ }
+
  log_debug("%s: decrypted payload length %zd/%zd padding %d",
     __func__, outlen, encrlen, pad);
  print_hex(ibuf_data(out), 0, ibuf_size(out));
@@ -586,6 +663,13 @@ ikev2_check_frag_oversize(struct iked_sa
  size_t max;
  size_t ivlen, integrlen, blocklen;
 
+ if (sa == NULL ||
+    sa->sa_encr == NULL ||
+    sa->sa_integr == NULL) {
+ log_debug("%s: invalid SA", __func__);
+ return (-1);
+ }
+
  sa_fam = ((struct sockaddr *)&sa->sa_local.addr)->sa_family;
 
  max = sa_fam == AF_INET ? IKEV2_MAXLEN_IPV4_FRAG
@@ -630,18 +714,16 @@ ikev2_msg_send_encrypt(struct iked *env,
  if ((pld = ikev2_add_payload(buf)) == NULL)
  goto done;
 
+ if (ikev2_msg_encrypt_prepare(sa, pld, buf, e, hdr, firstpayload, 0) == -1)
+ goto done;
+
  /* Encrypt message and add as an E payload */
- if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) {
+ if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) {
  log_debug("%s: encryption failed", __func__);
  goto done;
  }
  if (ibuf_cat(buf, e) != 0)
  goto done;
- if (ikev2_next_payload(pld, ibuf_size(e), firstpayload) == -1)
- goto done;
-
- if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1)
- goto done;
 
  /* Add integrity checksum (HMAC) */
  if (ikev2_msg_integr(env, sa, buf) != 0) {
@@ -682,6 +764,13 @@ ikev2_send_encrypted_fragments(struct ik
  uint32_t msgid;
  int ret = -1;
 
+ if (sa == NULL ||
+    sa->sa_encr == NULL ||
+    sa->sa_integr == NULL) {
+ log_debug("%s: invalid SA", __func__);
+ goto done;
+ }
+
  sa_fam = ((struct sockaddr *)&sa->sa_local.addr)->sa_family;
 
  left = ibuf_length(in);
@@ -731,18 +820,16 @@ ikev2_send_encrypted_fragments(struct ik
  if ((e = ibuf_new(data, MIN(left, max_len))) == NULL) {
  goto done;
  }
- if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) {
+
+ if (ikev2_msg_encrypt_prepare(sa, pld, buf, e, hdr,
+    firstpayload, 1) == -1)
+ goto done;
+
+ if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) {
  log_debug("%s: encryption failed", __func__);
  goto done;
  }
  if (ibuf_cat(buf, e) != 0)
- goto done;
-
- if (ikev2_next_payload(pld, ibuf_size(e) + sizeof(*frag),
-    firstpayload) == -1)
- goto done;
-
- if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1)
  goto done;
 
  /* Add integrity checksum (HMAC) */
Index: parse.y
===================================================================
RCS file: /cvs/src/sbin/iked/parse.y,v
retrieving revision 1.99
diff -u -p -r1.99 parse.y
--- parse.y 30 Apr 2020 21:11:13 -0000 1.99
+++ parse.y 14 May 2020 20:00:32 -0000
@@ -198,6 +198,12 @@ const struct ipsec_xf ikeencxfs[] = {
  { "aes-128", IKEV2_XFORMENCR_AES_CBC, 16, 16 },
  { "aes-192", IKEV2_XFORMENCR_AES_CBC, 24, 24 },
  { "aes-256", IKEV2_XFORMENCR_AES_CBC, 32, 32 },
+ { "aes-128-gcm-12", IKEV2_XFORMENCR_AES_GCM_12, 16, 16, 4, 1 },
+ { "aes-192-gcm-12", IKEV2_XFORMENCR_AES_GCM_12, 24, 24, 4, 1 },
+ { "aes-256-gcm-12", IKEV2_XFORMENCR_AES_GCM_12, 32, 32, 4, 1 },
+ { "aes-128-gcm", IKEV2_XFORMENCR_AES_GCM_16, 16, 16, 4, 1 },
+ { "aes-192-gcm", IKEV2_XFORMENCR_AES_GCM_16, 24, 24, 4, 1 },
+ { "aes-256-gcm", IKEV2_XFORMENCR_AES_GCM_16, 32, 32, 4, 1 },
  { NULL }
 };
 
@@ -2417,6 +2423,17 @@ print_xf(unsigned int id, unsigned int l
  return ("unknown");
 }
 
+int
+encxf_noauth(unsigned int id)
+{
+ int i;
+
+ for (i = 0; ikeencxfs[i].name != NULL; i++)
+ if (ikeencxfs[i].id == id)
+ return ikeencxfs[i].noauth;
+ return (0);
+}
+
 size_t
 keylength_xf(unsigned int saproto, unsigned int type, unsigned int id)
 {
@@ -2852,21 +2869,37 @@ create_ike(char *name, int af, uint8_t i
  pol.pol_nproposals++;
  } else {
  for (i = 0; i < ike_sa->nxfs; i++) {
+ noauth = 0;
+ for (j = 0; j < ike_sa->xfs[i]->nencxf; j++) {
+ if (ike_sa->xfs[i]->encxf[j]->noauth)
+ noauth++;
+ }
  if (ike_sa->xfs[i]->nesnxf) {
  yyerror("cannot use ESN with ikesa.");
  goto done;
  }
+ if (noauth && noauth != ike_sa->xfs[i]->nencxf) {
+ yyerror("cannot mix encryption transforms with "
+    "implicit and non-implicit authentication");
+ goto done;
+ }
+ if (noauth && ike_sa->xfs[i]->nauthxf) {
+ yyerror("authentication is implicit for given "
+    "encryption transforms");
+ goto done;
+ }
 
  if ((p = calloc(1, sizeof(*p))) == NULL)
  err(1, "%s", __func__);
 
  xf = NULL;
  xfi = 0;
- copy_transforms(IKEV2_XFORMTYPE_INTEGR,
-    ike_sa->xfs[i]->authxf,
-    ike_sa->xfs[i]->nauthxf, &xf, &xfi,
-    ikev2_default_ike_transforms,
-    ikev2_default_nike_transforms);
+ if (!ike_sa->xfs[i]->nencxf || !noauth)
+ copy_transforms(IKEV2_XFORMTYPE_INTEGR,
+    ike_sa->xfs[i]->authxf,
+    ike_sa->xfs[i]->nauthxf, &xf, &xfi,
+    ikev2_default_ike_transforms,
+    ikev2_default_nike_transforms);
  copy_transforms(IKEV2_XFORMTYPE_ENCR,
     ike_sa->xfs[i]->encxf,
     ike_sa->xfs[i]->nencxf, &xf, &xfi,
Index: policy.c
===================================================================
RCS file: /cvs/src/sbin/iked/policy.c,v
retrieving revision 1.62
diff -u -p -r1.62 policy.c
--- policy.c 13 May 2020 23:03:20 -0000 1.62
+++ policy.c 14 May 2020 20:00:32 -0000
@@ -774,7 +774,7 @@ proposals_match(struct iked_proposal *lo
     struct iked_transform **xforms, int rekey)
 {
  struct iked_transform *tpeer, *tlocal;
- unsigned int i, j, type, score, requiredh = 0;
+ unsigned int i, j, type, score, requiredh = 0, noauth = 0;
  uint8_t protoid = peer->prop_protoid;
  uint8_t peerxfs[IKEV2_XFORMTYPE_MAX];
 
@@ -782,8 +782,18 @@ proposals_match(struct iked_proposal *lo
 
  for (i = 0; i < peer->prop_nxforms; i++) {
  tpeer = peer->prop_xforms + i;
+ /* If any of the ENC transforms is an AEAD, ignore auth */
+ if (tpeer->xform_type == IKEV2_XFORMTYPE_ENCR &&
+    encxf_noauth(tpeer->xform_id))
+ noauth = 1;
+ }
+
+ for (i = 0; i < peer->prop_nxforms; i++) {
+ tpeer = peer->prop_xforms + i;
  if (tpeer->xform_type > IKEV2_XFORMTYPE_MAX)
  continue;
+ if (noauth && tpeer->xform_type == IKEV2_XFORMTYPE_INTEGR)
+ return (0);
 
  /*
  * Record all transform types from the peer's proposal,
@@ -832,7 +842,8 @@ proposals_match(struct iked_proposal *lo
  for (i = score = 0; i < IKEV2_XFORMTYPE_MAX; i++) {
  if (protoid == IKEV2_SAPROTO_IKE && xforms[i] == NULL &&
     (i == IKEV2_XFORMTYPE_ENCR || i == IKEV2_XFORMTYPE_PRF ||
-     i == IKEV2_XFORMTYPE_INTEGR || i == IKEV2_XFORMTYPE_DH)) {
+    (!noauth && i == IKEV2_XFORMTYPE_INTEGR) ||
+    i == IKEV2_XFORMTYPE_DH)) {
  score = 0;
  break;
  } else if (protoid == IKEV2_SAPROTO_AH && xforms[i] == NULL &&

Reply | Threaded
Open this post in threaded view
|

Re: iked(8): AES_GCM ciphers for IKE

Tobias Heider-2
On Thu, May 14, 2020 at 10:07:30PM +0200, Tobias Heider wrote:

> Hi,
>
> currently iked(8) supports AES-GCM only for ESP.
> The diff below adds the ENCR_AES_GCM_16 and ENCR_AES_GCM_12 variants for IKE.
> (for more information see [1] and [2]).
> Both variants support the 128, 196, and 256 bit key lengths.
>
> The new new ciphers can be configured with:
> - aes-128-gcm, aes-196-gcm and aes-256-gcm for ENCR_AES_GCM_16
> - aes-128-gcm-12, aes-196-gcm-12 and aes-256-gcm-12 for ENCR_AES_GCM_12
>
> It would be nice if we could get some interop testing with different IKEv2
> implementations.  I have so far successfully tested strongswan <-> iked and
> of course iked <-> iked.
>
> Feedback welcome ;)
>
> [1] https://tools.ietf.org/html/rfc5282
> [2] https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml#ikev2-parameters-5
>

whoops, previous diff was broken.

Index: crypto.c
===================================================================
RCS file: /cvs/src/sbin/iked/crypto.c,v
retrieving revision 1.27
diff -u -p -r1.27 crypto.c
--- crypto.c 14 May 2020 15:08:30 -0000 1.27
+++ crypto.c 14 May 2020 20:43:27 -0000
@@ -92,7 +92,7 @@ hash_new(uint8_t type, uint16_t id)
  struct iked_hash *hash;
  const EVP_MD *md = NULL;
  HMAC_CTX *ctx = NULL;
- int length = 0, fixedkey = 0, trunc = 0;
+ int length = 0, fixedkey = 0, trunc = 0, isaead = 0;
 
  switch (type) {
  case IKEV2_XFORMTYPE_PRF:
@@ -156,6 +156,14 @@ hash_new(uint8_t type, uint16_t id)
  length = SHA512_DIGEST_LENGTH;
  trunc = 32;
  break;
+ case IKEV2_XFORMAUTH_AES_GCM_12:
+ length = 12;
+ isaead = 1;
+ break;
+ case IKEV2_XFORMAUTH_AES_GCM_16:
+ length = 16;
+ isaead = 1;
+ break;
  case IKEV2_XFORMAUTH_NONE:
  case IKEV2_XFORMAUTH_DES_MAC:
  case IKEV2_XFORMAUTH_KPDK_MD5:
@@ -177,7 +185,7 @@ hash_new(uint8_t type, uint16_t id)
     print_map(id, ikev2_xformtype_map));
  break;
  }
- if (md == NULL)
+ if (!isaead && md == NULL)
  return (NULL);
 
  if ((hash = calloc(1, sizeof(*hash))) == NULL) {
@@ -192,6 +200,10 @@ hash_new(uint8_t type, uint16_t id)
  hash->hash_trunc = trunc;
  hash->hash_length = length;
  hash->hash_fixedkey = fixedkey;
+ hash->hash_isaead = isaead;
+
+ if (isaead)
+ return (hash);
 
  if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
  log_debug("%s: alloc hash ctx", __func__);
@@ -276,6 +288,7 @@ cipher_new(uint8_t type, uint16_t id, ui
  const EVP_CIPHER *cipher = NULL;
  EVP_CIPHER_CTX *ctx = NULL;
  int length = 0, fixedkey = 0, ivlength = 0;
+ int saltlength = 0, authid = 0;
 
  switch (type) {
  case IKEV2_XFORMTYPE_ENCR:
@@ -309,6 +322,39 @@ cipher_new(uint8_t type, uint16_t id, ui
  ivlength = EVP_CIPHER_iv_length(cipher);
  fixedkey = EVP_CIPHER_key_length(cipher);
  break;
+ case IKEV2_XFORMENCR_AES_GCM_16:
+ case IKEV2_XFORMENCR_AES_GCM_12:
+ switch (id_length) {
+ case 128:
+ cipher = EVP_aes_128_gcm();
+ break;
+ case 192:
+ cipher = EVP_aes_192_gcm();
+ break;
+ case 256:
+ cipher = EVP_aes_256_gcm();
+ break;
+ default:
+ log_debug("%s: invalid key length %d"
+    " for cipher %s", __func__, id_length,
+    print_map(id, ikev2_xformencr_map));
+ break;
+ }
+ if (cipher == NULL)
+ break;
+ switch(id) {
+ case IKEV2_XFORMENCR_AES_GCM_16:
+ authid = IKEV2_XFORMAUTH_AES_GCM_16;
+ break;
+ case IKEV2_XFORMENCR_AES_GCM_12:
+ authid = IKEV2_XFORMAUTH_AES_GCM_12;
+ break;
+ }
+ length = EVP_CIPHER_block_size(cipher);
+ ivlength = 8;
+ saltlength = 4;
+ fixedkey = EVP_CIPHER_key_length(cipher) + saltlength;
+ break;
  case IKEV2_XFORMENCR_DES_IV64:
  case IKEV2_XFORMENCR_DES:
  case IKEV2_XFORMENCR_RC5:
@@ -346,6 +392,8 @@ cipher_new(uint8_t type, uint16_t id, ui
  encr->encr_length = length;
  encr->encr_fixedkey = fixedkey;
  encr->encr_ivlength = ivlength ? ivlength : length;
+ encr->encr_saltlength = saltlength;
+ encr->encr_authid = authid;
 
  if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
  log_debug("%s: alloc cipher ctx", __func__);
@@ -392,6 +440,20 @@ cipher_setiv(struct iked_cipher *encr, v
  return (encr->encr_iv);
 }
 
+int
+cipher_settag(struct iked_cipher *encr, uint8_t *data, size_t len)
+{
+ return (EVP_CIPHER_CTX_ctrl(encr->encr_ctx,
+    EVP_CTRL_GCM_SET_TAG, len, data) != 1);
+}
+
+int
+cipher_gettag(struct iked_cipher *encr, uint8_t *data, size_t len)
+{
+ return (EVP_CIPHER_CTX_ctrl(encr->encr_ctx,
+    EVP_CTRL_GCM_GET_TAG, len, data) != 1);
+}
+
 void
 cipher_free(struct iked_cipher *encr)
 {
@@ -409,11 +471,33 @@ cipher_free(struct iked_cipher *encr)
 int
 cipher_init(struct iked_cipher *encr, int enc)
 {
+ struct ibuf *nonce = NULL;
+ int ret = -1;
+
  if (EVP_CipherInit_ex(encr->encr_ctx, encr->encr_priv, NULL,
-    ibuf_data(encr->encr_key), ibuf_data(encr->encr_iv), enc) != 1)
+    NULL, NULL, enc) != 1)
  return (-1);
+ if (encr->encr_saltlength > 0) {
+ /* For AEADs the nonce is salt + IV  (see RFC5282) */
+ nonce = ibuf_new(ibuf_data(encr->encr_key) +
+    ibuf_size(encr->encr_key) - encr->encr_saltlength,
+    encr->encr_saltlength);
+ if (nonce == NULL)
+ return (-1);
+ if (ibuf_add(nonce, ibuf_data(encr->encr_iv) , ibuf_size(encr->encr_iv)) != 0)
+ goto done;
+ if (EVP_CipherInit_ex(encr->encr_ctx, NULL, NULL,
+    ibuf_data(encr->encr_key), ibuf_data(nonce), enc) != 1)
+ goto done;
+ } else
+ if (EVP_CipherInit_ex(encr->encr_ctx, NULL, NULL,
+    ibuf_data(encr->encr_key), ibuf_data(encr->encr_iv), enc) != 1)
+ return (-1);
  EVP_CIPHER_CTX_set_padding(encr->encr_ctx, 0);
- return (0);
+ ret = 0;
+ done:
+ ibuf_free(nonce);
+ return (ret);
 }
 
 int
@@ -426,6 +510,20 @@ int
 cipher_init_decrypt(struct iked_cipher *encr)
 {
  return (cipher_init(encr, 0));
+}
+
+void
+cipher_aad(struct iked_cipher *encr, void *in, size_t inlen,
+    size_t *outlen)
+{
+ int olen = 0;
+
+ if (EVP_CipherUpdate(encr->encr_ctx, NULL, &olen, in, inlen) != 1) {
+ ca_sslerror(__func__);
+ *outlen = 0;
+ return;
+ }
+ *outlen = (size_t)olen;
 }
 
 int
Index: iked.conf.5
===================================================================
RCS file: /cvs/src/sbin/iked/iked.conf.5,v
retrieving revision 1.68
diff -u -p -r1.68 iked.conf.5
--- iked.conf.5 1 May 2020 17:44:02 -0000 1.68
+++ iked.conf.5 14 May 2020 20:43:27 -0000
@@ -861,12 +861,15 @@ keyword:
 .It Li aes-128 Ta "128 bits" Ta ""
 .It Li aes-192 Ta "192 bits" Ta ""
 .It Li aes-256 Ta "256 bits" Ta ""
+.It Li aes-128-gcm Ta "160 bits" Ta ""
+.It Li aes-192-gcm Ta "224 bits" Ta ""
+.It Li aes-256-gcm Ta "288 bits" Ta ""
+.It Li aes-128-gcm-12 Ta "160 bits" Ta "[IKE only]"
+.It Li aes-192-gcm-12 Ta "224 bits" Ta "[IKE only]"
+.It Li aes-256-gcm-12 Ta "288 bits" Ta "[IKE only]"
 .It Li aes-128-ctr Ta "160 bits" Ta "[ESP only]"
 .It Li aes-192-ctr Ta "224 bits" Ta "[ESP only]"
 .It Li aes-256-ctr Ta "288 bits" Ta "[ESP only]"
-.It Li aes-128-gcm Ta "160 bits" Ta "[ESP only]"
-.It Li aes-192-gcm Ta "224 bits" Ta "[ESP only]"
-.It Li aes-256-gcm Ta "288 bits" Ta "[ESP only]"
 .It Li blowfish Ta "160 bits" Ta "[ESP only]"
 .It Li cast Ta "128 bits" Ta "[ESP only]"
 .It Li chacha20-poly1305 Ta "288 bits" Ta "[ESP only]"
Index: iked.h
===================================================================
RCS file: /cvs/src/sbin/iked/iked.h,v
retrieving revision 1.150
diff -u -p -r1.150 iked.h
--- iked.h 14 May 2020 15:08:30 -0000 1.150
+++ iked.h 14 May 2020 20:43:27 -0000
@@ -309,6 +309,7 @@ struct iked_hash {
  size_t hash_length; /* Output length */
  size_t hash_trunc; /* Truncate the output length */
  struct iked_hash *hash_prf; /* PRF pointer */
+ int hash_isaead;
 };
 
 struct iked_cipher {
@@ -321,6 +322,8 @@ struct iked_cipher {
  struct ibuf *encr_iv; /* Initialization Vector */
  size_t encr_ivlength; /* IV length */
  size_t encr_length; /* Block length */
+ size_t encr_saltlength; /* IV salt length */
+ uint16_t encr_authid; /* ID of associated authentication */
 };
 
 struct iked_dsa {
@@ -841,10 +844,13 @@ struct ibuf *
  cipher_setkey(struct iked_cipher *, void *, size_t);
 struct ibuf *
  cipher_setiv(struct iked_cipher *, void *, size_t);
+int cipher_settag(struct iked_cipher *, uint8_t *, size_t);
+int cipher_gettag(struct iked_cipher *, uint8_t *, size_t);
 void cipher_free(struct iked_cipher *);
 int cipher_init(struct iked_cipher *, int);
 int cipher_init_encrypt(struct iked_cipher *);
 int cipher_init_decrypt(struct iked_cipher *);
+void cipher_aad(struct iked_cipher *, void *, size_t, size_t *);
 int cipher_update(struct iked_cipher *, void *, size_t, void *, size_t *);
 int cipher_final(struct iked_cipher *);
 size_t cipher_length(struct iked_cipher *);
@@ -933,7 +939,8 @@ int ikev2_msg_send(struct iked *, struc
 int ikev2_msg_send_encrypt(struct iked *, struct iked_sa *,
     struct ibuf **, uint8_t, uint8_t, int);
 struct ibuf
- *ikev2_msg_encrypt(struct iked *, struct iked_sa *, struct ibuf *);
+ *ikev2_msg_encrypt(struct iked *, struct iked_sa *, struct ibuf *,
+    struct ibuf *);
 struct ibuf *
  ikev2_msg_decrypt(struct iked *, struct iked_sa *,
     struct ibuf *, struct ibuf *);
@@ -1129,5 +1136,6 @@ void print_policy(struct iked_policy *)
 size_t keylength_xf(unsigned int, unsigned int, unsigned int);
 size_t noncelength_xf(unsigned int, unsigned int);
 int cmdline_symset(char *);
+int encxf_noauth(unsigned int);
 
 #endif /* IKED_H */
Index: ikev2.c
===================================================================
RCS file: /cvs/src/sbin/iked/ikev2.c,v
retrieving revision 1.225
diff -u -p -r1.225 ikev2.c
--- ikev2.c 11 May 2020 20:11:35 -0000 1.225
+++ ikev2.c 14 May 2020 20:43:29 -0000
@@ -4473,7 +4473,7 @@ ikev2_send_informational(struct iked *en
  goto done;
 
  /* Encrypt message and add as an E payload */
- if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) {
+ if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) {
  log_debug("%s: encryption failed", __func__);
  goto done;
  }
@@ -4666,6 +4666,16 @@ ikev2_sa_initiator(struct iked *env, str
  }
  }
 
+ /* For AEAD ciphers integrity is implicit */
+ if (sa->sa_encr->encr_authid && sa->sa_integr == NULL) {
+ if ((sa->sa_integr = hash_new(IKEV2_XFORMTYPE_INTEGR,
+    sa->sa_encr->encr_authid)) == NULL) {
+ log_info("%s: failed to get AEAD integr",
+    SPI_SA(sa, __func__));
+ return (-1);
+ }
+ }
+
  if (sa->sa_prf == NULL) {
  if ((xform = config_findtransform(&sa->sa_proposals,
     IKEV2_XFORMTYPE_PRF, 0)) == NULL) {
@@ -4840,6 +4850,16 @@ ikev2_sa_responder(struct iked *env, str
  }
  }
 
+ /* For AEAD ciphers integrity is implicit */
+ if (sa->sa_encr->encr_authid && sa->sa_integr == NULL) {
+ if ((sa->sa_integr = hash_new(IKEV2_XFORMTYPE_INTEGR,
+    sa->sa_encr->encr_authid)) == NULL) {
+ log_info("%s: failed to get AEAD integr",
+    SPI_SA(sa, __func__));
+ return (-1);
+ }
+ }
+
  if (sa->sa_prf == NULL) {
  if ((xform = config_findtransform(&sa->sa_proposals,
     IKEV2_XFORMTYPE_PRF, 0)) == NULL) {
@@ -4883,6 +4903,7 @@ ikev2_sa_keys(struct iked *env, struct i
  size_t nonceminlen, ilen, rlen, tmplen;
  uint64_t ispi, rspi;
  int ret = -1;
+ int isaead = 0;
 
  ninr = dhsecret = skeyseed = s = t = NULL;
 
@@ -4895,6 +4916,9 @@ ikev2_sa_keys(struct iked *env, struct i
  return (-1);
  }
 
+ /* For AEADs no auth keys are required (see RFC 5282) */
+ isaead = !!integr->hash_isaead;
+
  if (prf->hash_fixedkey)
  nonceminlen = prf->hash_fixedkey;
  else
@@ -5026,13 +5050,13 @@ ikev2_sa_keys(struct iked *env, struct i
  * Get the size of the key material we need and the number
  * of rounds we need to run the prf+ function.
  */
- ilen = hash_length(prf) + /* SK_d */
-    hash_keylength(integr) + /* SK_ai */
-    hash_keylength(integr) + /* SK_ar */
-    cipher_keylength(encr) + /* SK_ei */
-    cipher_keylength(encr) + /* SK_er */
-    hash_keylength(prf) + /* SK_pi */
-    hash_keylength(prf); /* SK_pr */
+ ilen = hash_length(prf) + /* SK_d */
+    (isaead ? 0 : hash_keylength(integr)) + /* SK_ai */
+    (isaead ? 0 : hash_keylength(integr)) + /* SK_ar */
+    cipher_keylength(encr) + /* SK_ei */
+    cipher_keylength(encr) + /* SK_er */
+    hash_keylength(prf) + /* SK_pi */
+    hash_keylength(prf); /* SK_pr */
 
  if ((t = ikev2_prfplus(prf, skeyseed, s, ilen)) == NULL) {
  log_info("%s: failed to get IKE SA key material",
@@ -5042,8 +5066,10 @@ ikev2_sa_keys(struct iked *env, struct i
 
  /* ibuf_get() returns a new buffer from the next read offset */
  if ((sa->sa_key_d = ibuf_get(t, hash_length(prf))) == NULL ||
-    (sa->sa_key_iauth = ibuf_get(t, hash_keylength(integr))) == NULL ||
-    (sa->sa_key_rauth = ibuf_get(t, hash_keylength(integr))) == NULL ||
+    (!isaead &&
+    (sa->sa_key_iauth = ibuf_get(t, hash_keylength(integr))) == NULL) ||
+    (!isaead &&
+    (sa->sa_key_rauth = ibuf_get(t, hash_keylength(integr))) == NULL) ||
     (sa->sa_key_iencr = ibuf_get(t, cipher_keylength(encr))) == NULL ||
     (sa->sa_key_rencr = ibuf_get(t, cipher_keylength(encr))) == NULL ||
     (sa->sa_key_iprf = ibuf_get(t, hash_length(prf))) == NULL ||
@@ -5055,12 +5081,16 @@ ikev2_sa_keys(struct iked *env, struct i
  log_debug("%s: SK_d with %zu bytes", __func__,
     ibuf_length(sa->sa_key_d));
  print_hex(sa->sa_key_d->buf, 0, ibuf_length(sa->sa_key_d));
- log_debug("%s: SK_ai with %zu bytes", __func__,
-    ibuf_length(sa->sa_key_iauth));
- print_hex(sa->sa_key_iauth->buf, 0, ibuf_length(sa->sa_key_iauth));
- log_debug("%s: SK_ar with %zu bytes", __func__,
-    ibuf_length(sa->sa_key_rauth));
- print_hex(sa->sa_key_rauth->buf, 0, ibuf_length(sa->sa_key_rauth));
+ if (!isaead) {
+ log_debug("%s: SK_ai with %zu bytes", __func__,
+    ibuf_length(sa->sa_key_iauth));
+ print_hex(sa->sa_key_iauth->buf, 0,
+    ibuf_length(sa->sa_key_iauth));
+ log_debug("%s: SK_ar with %zu bytes", __func__,
+    ibuf_length(sa->sa_key_rauth));
+ print_hex(sa->sa_key_rauth->buf, 0,
+    ibuf_length(sa->sa_key_rauth));
+ }
  log_debug("%s: SK_ei with %zu bytes", __func__,
     ibuf_length(sa->sa_key_iencr));
  print_hex(sa->sa_key_iencr->buf, 0, ibuf_length(sa->sa_key_iencr));
Index: ikev2.h
===================================================================
RCS file: /cvs/src/sbin/iked/ikev2.h,v
retrieving revision 1.32
diff -u -p -r1.32 ikev2.h
--- ikev2.h 28 Apr 2020 15:18:52 -0000 1.32
+++ ikev2.h 14 May 2020 20:43:30 -0000
@@ -224,6 +224,9 @@ extern struct iked_constmap ikev2_xformp
 #define IKEV2_XFORMAUTH_HMAC_SHA2_256_128 12 /* RFC4868 */
 #define IKEV2_XFORMAUTH_HMAC_SHA2_384_192 13 /* RFC4868 */
 #define IKEV2_XFORMAUTH_HMAC_SHA2_512_256 14 /* RFC4868 */
+#define IKEV2_XFORMAUTH_AES_GCM_8 2018 /* private */
+#define IKEV2_XFORMAUTH_AES_GCM_12 2019 /* private */
+#define IKEV2_XFORMAUTH_AES_GCM_16 2020 /* private */
 
 extern struct iked_constmap ikev2_xformauth_map[];
 
Index: ikev2_msg.c
===================================================================
RCS file: /cvs/src/sbin/iked/ikev2_msg.c,v
retrieving revision 1.67
diff -u -p -r1.67 ikev2_msg.c
--- ikev2_msg.c 14 May 2020 15:08:30 -0000 1.67
+++ ikev2_msg.c 14 May 2020 20:43:30 -0000
@@ -51,6 +51,8 @@ void ikev2_msg_retransmit_timeout(struc
 int ikev2_check_frag_oversize(struct iked_sa *sa, struct ibuf *buf);
 int ikev2_send_encrypted_fragments(struct iked *env, struct iked_sa *sa,
     struct ibuf *in,uint8_t exchange, uint8_t firstpayload, int response);
+int ikev2_msg_encrypt_prepare(struct iked_sa *, struct ikev2_payload *,
+    struct ibuf*, struct ibuf *, struct ike_header *, uint8_t, int);
 
 void
 ikev2_msg_cb(int fd, short event, void *arg)
@@ -329,8 +331,45 @@ ikev2_msg_id(struct iked *env, struct ik
  return (id);
 }
 
+/*
+ * Calculate the final sizes of the IKEv2 header and the encrypted payload
+ * header.  This must be done before encryption to make sure the correct
+ * headers are authenticated.
+ */
+int
+ikev2_msg_encrypt_prepare(struct iked_sa *sa, struct ikev2_payload *pld,
+    struct ibuf *buf, struct ibuf *e, struct ike_header *hdr,
+    uint8_t firstpayload, int fragmentation)
+{
+ size_t len, ivlen, encrlen, integrlen, blocklen, pldlen, outlen;
+
+ if (sa == NULL ||
+    sa->sa_encr == NULL ||
+    sa->sa_integr == NULL) {
+ log_debug("%s: invalid SA", __func__);
+ return (-1);
+ }
+
+ len = ibuf_size(e);
+ blocklen = cipher_length(sa->sa_encr);
+ integrlen = hash_length(sa->sa_integr);
+ ivlen = cipher_ivlength(sa->sa_encr);
+ encrlen = roundup(len + 1, blocklen);
+ outlen = cipher_outlength(sa->sa_encr, encrlen);
+ pldlen = ivlen + outlen + integrlen +
+    (fragmentation ? sizeof(struct ikev2_frag_payload) : 0);
+
+ if (ikev2_next_payload(pld, pldlen, firstpayload) == -1)
+ return (-1);
+ if (ikev2_set_header(hdr, ibuf_size(buf) + pldlen - sizeof(*hdr)) == -1)
+ return (-1);
+
+ return (0);
+}
+
 struct ibuf *
-ikev2_msg_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf *src)
+ikev2_msg_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf *src,
+    struct ibuf *aad)
 {
  size_t len, ivlen, encrlen, integrlen, blocklen,
     outlen;
@@ -390,12 +429,23 @@ ikev2_msg_encrypt(struct iked *env, stru
  goto done;
 
  outlen = ibuf_size(out);
+
+ /* Add AAD for AEAD ciphers */
+ if (sa->sa_integr->hash_isaead)
+ cipher_aad(sa->sa_encr, ibuf_data(aad),
+    ibuf_length(aad), &outlen);
+
  if (cipher_update(sa->sa_encr, ibuf_data(src), encrlen,
     ibuf_data(out), &outlen) == -1) {
  log_info("%s: error updating cipher.", __func__);
  goto done;
  }
 
+ if (cipher_final(sa->sa_encr) == -1) {
+ log_info("%s: encryption failed.", __func__);
+ goto done;
+ }
+
  if (outlen && ibuf_add(dst, ibuf_data(out), outlen) != 0)
  goto done;
 
@@ -429,18 +479,13 @@ ikev2_msg_integr(struct iked *env, struc
  print_hex(ibuf_data(src), 0, ibuf_size(src));
 
  if (sa == NULL ||
+    sa->sa_encr == NULL ||
     sa->sa_integr == NULL) {
  log_debug("%s: invalid SA", __func__);
  return (-1);
  }
 
- if (sa->sa_hdr.sh_initiator)
- integr = sa->sa_key_iauth;
- else
- integr = sa->sa_key_rauth;
-
  integrlen = hash_length(sa->sa_integr);
-
  log_debug("%s: integrity checksum length %zu", __func__,
     integrlen);
 
@@ -450,21 +495,33 @@ ikev2_msg_integr(struct iked *env, struc
  if ((tmp = ibuf_new(NULL, hash_keylength(sa->sa_integr))) == NULL)
  goto done;
 
- hash_setkey(sa->sa_integr, ibuf_data(integr), ibuf_size(integr));
- hash_init(sa->sa_integr);
- hash_update(sa->sa_integr, ibuf_data(src),
-    ibuf_size(src) - integrlen);
- hash_final(sa->sa_integr, ibuf_data(tmp), &tmplen);
+ if (!sa->sa_integr->hash_isaead) {
+ if (sa->sa_hdr.sh_initiator)
+ integr = sa->sa_key_iauth;
+ else
+ integr = sa->sa_key_rauth;
+
+ hash_setkey(sa->sa_integr, ibuf_data(integr),
+    ibuf_size(integr));
+ hash_init(sa->sa_integr);
+ hash_update(sa->sa_integr, ibuf_data(src),
+    ibuf_size(src) - integrlen);
+ hash_final(sa->sa_integr, ibuf_data(tmp), &tmplen);
 
- if (tmplen != integrlen) {
- log_debug("%s: hash failure", __func__);
- goto done;
+ if (tmplen != integrlen) {
+ log_debug("%s: hash failure", __func__);
+ goto done;
+ }
+ } else {
+ /* Append AEAD tag */
+ if (cipher_gettag(sa->sa_encr, ibuf_data(tmp), ibuf_size(tmp)))
+ goto done;
  }
 
  if ((ptr = ibuf_seek(src,
     ibuf_size(src) - integrlen, integrlen)) == NULL)
  goto done;
- memcpy(ptr, ibuf_data(tmp), tmplen);
+ memcpy(ptr, ibuf_data(tmp), integrlen);
 
  print_hex(ibuf_data(tmp), 0, ibuf_size(tmp));
 
@@ -481,7 +538,7 @@ ikev2_msg_decrypt(struct iked *env, stru
 {
  ssize_t ivlen, encrlen, integrlen, blocklen,
     outlen, tmplen;
- uint8_t pad = 0, *ptr;
+ uint8_t pad = 0, *ptr, *integrdata;
  struct ibuf *integr, *encr, *tmp = NULL, *out = NULL;
  off_t ivoff, encroff, integroff;
 
@@ -524,25 +581,30 @@ ikev2_msg_decrypt(struct iked *env, stru
  /*
  * Validate packet checksum
  */
- if ((tmp = ibuf_new(NULL, ibuf_length(integr))) == NULL)
- goto done;
+ if (!sa->sa_integr->hash_isaead) {
+ if ((tmp = ibuf_new(NULL, ibuf_length(integr))) == NULL)
+ goto done;
 
- hash_setkey(sa->sa_integr, integr->buf, ibuf_length(integr));
- hash_init(sa->sa_integr);
- hash_update(sa->sa_integr, ibuf_data(msg),
-    ibuf_size(msg) - integrlen);
- hash_final(sa->sa_integr, tmp->buf, &tmplen);
+ hash_setkey(sa->sa_integr, integr->buf, ibuf_length(integr));
+ hash_init(sa->sa_integr);
+ hash_update(sa->sa_integr, ibuf_data(msg),
+    ibuf_size(msg) - integrlen);
+ hash_final(sa->sa_integr, tmp->buf, &tmplen);
 
- if (memcmp(tmp->buf, ibuf_data(src) + integroff, integrlen) != 0) {
- log_debug("%s: integrity check failed", __func__);
- goto done;
- }
+ integrdata = ibuf_seek(src, integroff, integrlen);
+ if (integrdata == NULL)
+ goto done;
+ if (memcmp(tmp->buf, integrdata, integrlen) != 0) {
+ log_debug("%s: integrity check failed", __func__);
+ goto done;
+ }
 
- log_debug("%s: integrity check succeeded", __func__);
- print_hex(tmp->buf, 0, tmplen);
+ log_debug("%s: integrity check succeeded", __func__);
+ print_hex(tmp->buf, 0, tmplen);
 
- ibuf_release(tmp);
- tmp = NULL;
+ ibuf_release(tmp);
+ tmp = NULL;
+ }
 
  /*
  * Decrypt the payload and strip any padding
@@ -559,10 +621,31 @@ ikev2_msg_decrypt(struct iked *env, stru
  goto done;
  }
 
+ /* Set AEAD tag */
+ if (sa->sa_integr->hash_isaead) {
+ integrdata = ibuf_seek(src, integroff, integrlen);
+ if (integrdata == NULL)
+ goto done;
+ if (cipher_settag(sa->sa_encr, integrdata, integrlen)) {
+ log_info("%s: failed to set tag.", __func__);
+ goto done;
+ }
+ }
+
  if ((out = ibuf_new(NULL, cipher_outlength(sa->sa_encr,
     encrlen))) == NULL)
  goto done;
 
+ /*
+ * Add additional authenticated data for AEAD ciphers
+ */
+ if (sa->sa_integr->hash_isaead) {
+ log_debug("%s: AAD length %zu", __func__, ibuf_length(msg) - ibuf_length(src));
+ print_hex(ibuf_data(msg), 0, ibuf_length(msg) - ibuf_length(src));
+ cipher_aad(sa->sa_encr, ibuf_data(msg),
+    ibuf_length(msg) - ibuf_length(src), &outlen);
+ }
+
  if ((outlen = ibuf_length(out)) != 0) {
  if (cipher_update(sa->sa_encr, ibuf_data(src) + encroff,
     encrlen, ibuf_data(out), &outlen) == -1) {
@@ -574,6 +657,11 @@ ikev2_msg_decrypt(struct iked *env, stru
  pad = *ptr;
  }
 
+ if (cipher_final(sa->sa_encr) == -1) {
+ log_info("%s: decryption failed.", __func__);
+ goto done;
+ }
+
  log_debug("%s: decrypted payload length %zd/%zd padding %d",
     __func__, outlen, encrlen, pad);
  print_hex(ibuf_data(out), 0, ibuf_size(out));
@@ -598,6 +686,13 @@ ikev2_check_frag_oversize(struct iked_sa
  size_t max;
  size_t ivlen, integrlen, blocklen;
 
+ if (sa == NULL ||
+    sa->sa_encr == NULL ||
+    sa->sa_integr == NULL) {
+ log_debug("%s: invalid SA", __func__);
+ return (-1);
+ }
+
  sa_fam = ((struct sockaddr *)&sa->sa_local.addr)->sa_family;
 
  max = sa_fam == AF_INET ? IKEV2_MAXLEN_IPV4_FRAG
@@ -642,18 +737,16 @@ ikev2_msg_send_encrypt(struct iked *env,
  if ((pld = ikev2_add_payload(buf)) == NULL)
  goto done;
 
+ if (ikev2_msg_encrypt_prepare(sa, pld, buf, e, hdr, firstpayload, 0) == -1)
+ goto done;
+
  /* Encrypt message and add as an E payload */
- if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) {
+ if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) {
  log_debug("%s: encryption failed", __func__);
  goto done;
  }
  if (ibuf_cat(buf, e) != 0)
  goto done;
- if (ikev2_next_payload(pld, ibuf_size(e), firstpayload) == -1)
- goto done;
-
- if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1)
- goto done;
 
  /* Add integrity checksum (HMAC) */
  if (ikev2_msg_integr(env, sa, buf) != 0) {
@@ -694,6 +787,13 @@ ikev2_send_encrypted_fragments(struct ik
  uint32_t msgid;
  int ret = -1;
 
+ if (sa == NULL ||
+    sa->sa_encr == NULL ||
+    sa->sa_integr == NULL) {
+ log_debug("%s: invalid SA", __func__);
+ goto done;
+ }
+
  sa_fam = ((struct sockaddr *)&sa->sa_local.addr)->sa_family;
 
  left = ibuf_length(in);
@@ -743,18 +843,16 @@ ikev2_send_encrypted_fragments(struct ik
  if ((e = ibuf_new(data, MIN(left, max_len))) == NULL) {
  goto done;
  }
- if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) {
+
+ if (ikev2_msg_encrypt_prepare(sa, pld, buf, e, hdr,
+    firstpayload, 1) == -1)
+ goto done;
+
+ if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) {
  log_debug("%s: encryption failed", __func__);
  goto done;
  }
  if (ibuf_cat(buf, e) != 0)
- goto done;
-
- if (ikev2_next_payload(pld, ibuf_size(e) + sizeof(*frag),
-    firstpayload) == -1)
- goto done;
-
- if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1)
  goto done;
 
  /* Add integrity checksum (HMAC) */
Index: parse.y
===================================================================
RCS file: /cvs/src/sbin/iked/parse.y,v
retrieving revision 1.99
diff -u -p -r1.99 parse.y
--- parse.y 30 Apr 2020 21:11:13 -0000 1.99
+++ parse.y 14 May 2020 20:43:30 -0000
@@ -198,6 +198,12 @@ const struct ipsec_xf ikeencxfs[] = {
  { "aes-128", IKEV2_XFORMENCR_AES_CBC, 16, 16 },
  { "aes-192", IKEV2_XFORMENCR_AES_CBC, 24, 24 },
  { "aes-256", IKEV2_XFORMENCR_AES_CBC, 32, 32 },
+ { "aes-128-gcm-12", IKEV2_XFORMENCR_AES_GCM_12, 16, 16, 4, 1 },
+ { "aes-192-gcm-12", IKEV2_XFORMENCR_AES_GCM_12, 24, 24, 4, 1 },
+ { "aes-256-gcm-12", IKEV2_XFORMENCR_AES_GCM_12, 32, 32, 4, 1 },
+ { "aes-128-gcm", IKEV2_XFORMENCR_AES_GCM_16, 16, 16, 4, 1 },
+ { "aes-192-gcm", IKEV2_XFORMENCR_AES_GCM_16, 24, 24, 4, 1 },
+ { "aes-256-gcm", IKEV2_XFORMENCR_AES_GCM_16, 32, 32, 4, 1 },
  { NULL }
 };
 
@@ -2417,6 +2423,17 @@ print_xf(unsigned int id, unsigned int l
  return ("unknown");
 }
 
+int
+encxf_noauth(unsigned int id)
+{
+ int i;
+
+ for (i = 0; ikeencxfs[i].name != NULL; i++)
+ if (ikeencxfs[i].id == id)
+ return ikeencxfs[i].noauth;
+ return (0);
+}
+
 size_t
 keylength_xf(unsigned int saproto, unsigned int type, unsigned int id)
 {
@@ -2852,21 +2869,37 @@ create_ike(char *name, int af, uint8_t i
  pol.pol_nproposals++;
  } else {
  for (i = 0; i < ike_sa->nxfs; i++) {
+ noauth = 0;
+ for (j = 0; j < ike_sa->xfs[i]->nencxf; j++) {
+ if (ike_sa->xfs[i]->encxf[j]->noauth)
+ noauth++;
+ }
  if (ike_sa->xfs[i]->nesnxf) {
  yyerror("cannot use ESN with ikesa.");
  goto done;
  }
+ if (noauth && noauth != ike_sa->xfs[i]->nencxf) {
+ yyerror("cannot mix encryption transforms with "
+    "implicit and non-implicit authentication");
+ goto done;
+ }
+ if (noauth && ike_sa->xfs[i]->nauthxf) {
+ yyerror("authentication is implicit for given "
+    "encryption transforms");
+ goto done;
+ }
 
  if ((p = calloc(1, sizeof(*p))) == NULL)
  err(1, "%s", __func__);
 
  xf = NULL;
  xfi = 0;
- copy_transforms(IKEV2_XFORMTYPE_INTEGR,
-    ike_sa->xfs[i]->authxf,
-    ike_sa->xfs[i]->nauthxf, &xf, &xfi,
-    ikev2_default_ike_transforms,
-    ikev2_default_nike_transforms);
+ if (!ike_sa->xfs[i]->nencxf || !noauth)
+ copy_transforms(IKEV2_XFORMTYPE_INTEGR,
+    ike_sa->xfs[i]->authxf,
+    ike_sa->xfs[i]->nauthxf, &xf, &xfi,
+    ikev2_default_ike_transforms,
+    ikev2_default_nike_transforms);
  copy_transforms(IKEV2_XFORMTYPE_ENCR,
     ike_sa->xfs[i]->encxf,
     ike_sa->xfs[i]->nencxf, &xf, &xfi,
Index: policy.c
===================================================================
RCS file: /cvs/src/sbin/iked/policy.c,v
retrieving revision 1.62
diff -u -p -r1.62 policy.c
--- policy.c 13 May 2020 23:03:20 -0000 1.62
+++ policy.c 14 May 2020 20:43:30 -0000
@@ -774,7 +774,7 @@ proposals_match(struct iked_proposal *lo
     struct iked_transform **xforms, int rekey)
 {
  struct iked_transform *tpeer, *tlocal;
- unsigned int i, j, type, score, requiredh = 0;
+ unsigned int i, j, type, score, requiredh = 0, noauth = 0;
  uint8_t protoid = peer->prop_protoid;
  uint8_t peerxfs[IKEV2_XFORMTYPE_MAX];
 
@@ -782,8 +782,18 @@ proposals_match(struct iked_proposal *lo
 
  for (i = 0; i < peer->prop_nxforms; i++) {
  tpeer = peer->prop_xforms + i;
+ /* If any of the ENC transforms is an AEAD, ignore auth */
+ if (tpeer->xform_type == IKEV2_XFORMTYPE_ENCR &&
+    encxf_noauth(tpeer->xform_id))
+ noauth = 1;
+ }
+
+ for (i = 0; i < peer->prop_nxforms; i++) {
+ tpeer = peer->prop_xforms + i;
  if (tpeer->xform_type > IKEV2_XFORMTYPE_MAX)
  continue;
+ if (noauth && tpeer->xform_type == IKEV2_XFORMTYPE_INTEGR)
+ return (0);
 
  /*
  * Record all transform types from the peer's proposal,
@@ -832,7 +842,8 @@ proposals_match(struct iked_proposal *lo
  for (i = score = 0; i < IKEV2_XFORMTYPE_MAX; i++) {
  if (protoid == IKEV2_SAPROTO_IKE && xforms[i] == NULL &&
     (i == IKEV2_XFORMTYPE_ENCR || i == IKEV2_XFORMTYPE_PRF ||
-     i == IKEV2_XFORMTYPE_INTEGR || i == IKEV2_XFORMTYPE_DH)) {
+    (!noauth && i == IKEV2_XFORMTYPE_INTEGR) ||
+    i == IKEV2_XFORMTYPE_DH)) {
  score = 0;
  break;
  } else if (protoid == IKEV2_SAPROTO_AH && xforms[i] == NULL &&

Reply | Threaded
Open this post in threaded view
|

Re: iked(8): AES_GCM ciphers for IKE

Stephan Mending
On Thu, May 14, 2020 at 10:47:52PM +0200, Tobias Heider wrote:

> On Thu, May 14, 2020 at 10:07:30PM +0200, Tobias Heider wrote:
> > Hi,
> >
> > currently iked(8) supports AES-GCM only for ESP.
> > The diff below adds the ENCR_AES_GCM_16 and ENCR_AES_GCM_12 variants for IKE.
> > (for more information see [1] and [2]).
> > Both variants support the 128, 196, and 256 bit key lengths.
> >
> > The new new ciphers can be configured with:
> > - aes-128-gcm, aes-196-gcm and aes-256-gcm for ENCR_AES_GCM_16
> > - aes-128-gcm-12, aes-196-gcm-12 and aes-256-gcm-12 for ENCR_AES_GCM_12
> >
> > It would be nice if we could get some interop testing with different IKEv2
> > implementations.  I have so far successfully tested strongswan <-> iked and
> > of course iked <-> iked.
> >
> > Feedback welcome ;)
> >
> > [1] https://tools.ietf.org/html/rfc5282
> > [2] https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml#ikev2-parameters-5
> >
>
> whoops, previous diff was broken.
>
> Index: crypto.c
> ===================================================================
> RCS file: /cvs/src/sbin/iked/crypto.c,v
> retrieving revision 1.27
> diff -u -p -r1.27 crypto.c
> --- crypto.c 14 May 2020 15:08:30 -0000 1.27
> +++ crypto.c 14 May 2020 20:43:27 -0000
> @@ -92,7 +92,7 @@ hash_new(uint8_t type, uint16_t id)
>   struct iked_hash *hash;
>   const EVP_MD *md = NULL;
>   HMAC_CTX *ctx = NULL;
> - int length = 0, fixedkey = 0, trunc = 0;
> + int length = 0, fixedkey = 0, trunc = 0, isaead = 0;
>  
>   switch (type) {
>   case IKEV2_XFORMTYPE_PRF:
> @@ -156,6 +156,14 @@ hash_new(uint8_t type, uint16_t id)
>   length = SHA512_DIGEST_LENGTH;
>   trunc = 32;
>   break;
> + case IKEV2_XFORMAUTH_AES_GCM_12:
> + length = 12;
> + isaead = 1;
> + break;
> + case IKEV2_XFORMAUTH_AES_GCM_16:
> + length = 16;
> + isaead = 1;
> + break;
>   case IKEV2_XFORMAUTH_NONE:
>   case IKEV2_XFORMAUTH_DES_MAC:
>   case IKEV2_XFORMAUTH_KPDK_MD5:
> @@ -177,7 +185,7 @@ hash_new(uint8_t type, uint16_t id)
>      print_map(id, ikev2_xformtype_map));
>   break;
>   }
> - if (md == NULL)
> + if (!isaead && md == NULL)
>   return (NULL);
>  
>   if ((hash = calloc(1, sizeof(*hash))) == NULL) {
> @@ -192,6 +200,10 @@ hash_new(uint8_t type, uint16_t id)
>   hash->hash_trunc = trunc;
>   hash->hash_length = length;
>   hash->hash_fixedkey = fixedkey;
> + hash->hash_isaead = isaead;
> +
> + if (isaead)
> + return (hash);
>  
>   if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
>   log_debug("%s: alloc hash ctx", __func__);
> @@ -276,6 +288,7 @@ cipher_new(uint8_t type, uint16_t id, ui
>   const EVP_CIPHER *cipher = NULL;
>   EVP_CIPHER_CTX *ctx = NULL;
>   int length = 0, fixedkey = 0, ivlength = 0;
> + int saltlength = 0, authid = 0;
>  
>   switch (type) {
>   case IKEV2_XFORMTYPE_ENCR:
> @@ -309,6 +322,39 @@ cipher_new(uint8_t type, uint16_t id, ui
>   ivlength = EVP_CIPHER_iv_length(cipher);
>   fixedkey = EVP_CIPHER_key_length(cipher);
>   break;
> + case IKEV2_XFORMENCR_AES_GCM_16:
> + case IKEV2_XFORMENCR_AES_GCM_12:
> + switch (id_length) {
> + case 128:
> + cipher = EVP_aes_128_gcm();
> + break;
> + case 192:
> + cipher = EVP_aes_192_gcm();
> + break;
> + case 256:
> + cipher = EVP_aes_256_gcm();
> + break;
> + default:
> + log_debug("%s: invalid key length %d"
> +    " for cipher %s", __func__, id_length,
> +    print_map(id, ikev2_xformencr_map));
> + break;
> + }
> + if (cipher == NULL)
> + break;
> + switch(id) {
> + case IKEV2_XFORMENCR_AES_GCM_16:
> + authid = IKEV2_XFORMAUTH_AES_GCM_16;
> + break;
> + case IKEV2_XFORMENCR_AES_GCM_12:
> + authid = IKEV2_XFORMAUTH_AES_GCM_12;
> + break;
> + }
> + length = EVP_CIPHER_block_size(cipher);
> + ivlength = 8;
> + saltlength = 4;
> + fixedkey = EVP_CIPHER_key_length(cipher) + saltlength;
> + break;
>   case IKEV2_XFORMENCR_DES_IV64:
>   case IKEV2_XFORMENCR_DES:
>   case IKEV2_XFORMENCR_RC5:
> @@ -346,6 +392,8 @@ cipher_new(uint8_t type, uint16_t id, ui
>   encr->encr_length = length;
>   encr->encr_fixedkey = fixedkey;
>   encr->encr_ivlength = ivlength ? ivlength : length;
> + encr->encr_saltlength = saltlength;
> + encr->encr_authid = authid;
>  
>   if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
>   log_debug("%s: alloc cipher ctx", __func__);
> @@ -392,6 +440,20 @@ cipher_setiv(struct iked_cipher *encr, v
>   return (encr->encr_iv);
>  }
>  
> +int
> +cipher_settag(struct iked_cipher *encr, uint8_t *data, size_t len)
> +{
> + return (EVP_CIPHER_CTX_ctrl(encr->encr_ctx,
> +    EVP_CTRL_GCM_SET_TAG, len, data) != 1);
> +}
> +
> +int
> +cipher_gettag(struct iked_cipher *encr, uint8_t *data, size_t len)
> +{
> + return (EVP_CIPHER_CTX_ctrl(encr->encr_ctx,
> +    EVP_CTRL_GCM_GET_TAG, len, data) != 1);
> +}
> +
>  void
>  cipher_free(struct iked_cipher *encr)
>  {
> @@ -409,11 +471,33 @@ cipher_free(struct iked_cipher *encr)
>  int
>  cipher_init(struct iked_cipher *encr, int enc)
>  {
> + struct ibuf *nonce = NULL;
> + int ret = -1;
> +
>   if (EVP_CipherInit_ex(encr->encr_ctx, encr->encr_priv, NULL,
> -    ibuf_data(encr->encr_key), ibuf_data(encr->encr_iv), enc) != 1)
> +    NULL, NULL, enc) != 1)
>   return (-1);
> + if (encr->encr_saltlength > 0) {
> + /* For AEADs the nonce is salt + IV  (see RFC5282) */
> + nonce = ibuf_new(ibuf_data(encr->encr_key) +
> +    ibuf_size(encr->encr_key) - encr->encr_saltlength,
> +    encr->encr_saltlength);
> + if (nonce == NULL)
> + return (-1);
> + if (ibuf_add(nonce, ibuf_data(encr->encr_iv) , ibuf_size(encr->encr_iv)) != 0)
> + goto done;
> + if (EVP_CipherInit_ex(encr->encr_ctx, NULL, NULL,
> +    ibuf_data(encr->encr_key), ibuf_data(nonce), enc) != 1)
> + goto done;
> + } else
> + if (EVP_CipherInit_ex(encr->encr_ctx, NULL, NULL,
> +    ibuf_data(encr->encr_key), ibuf_data(encr->encr_iv), enc) != 1)
> + return (-1);
>   EVP_CIPHER_CTX_set_padding(encr->encr_ctx, 0);
> - return (0);
> + ret = 0;
> + done:
> + ibuf_free(nonce);
> + return (ret);
>  }
>  
>  int
> @@ -426,6 +510,20 @@ int
>  cipher_init_decrypt(struct iked_cipher *encr)
>  {
>   return (cipher_init(encr, 0));
> +}
> +
> +void
> +cipher_aad(struct iked_cipher *encr, void *in, size_t inlen,
> +    size_t *outlen)
> +{
> + int olen = 0;
> +
> + if (EVP_CipherUpdate(encr->encr_ctx, NULL, &olen, in, inlen) != 1) {
> + ca_sslerror(__func__);
> + *outlen = 0;
> + return;
> + }
> + *outlen = (size_t)olen;
>  }
>  
>  int
> Index: iked.conf.5
> ===================================================================
> RCS file: /cvs/src/sbin/iked/iked.conf.5,v
> retrieving revision 1.68
> diff -u -p -r1.68 iked.conf.5
> --- iked.conf.5 1 May 2020 17:44:02 -0000 1.68
> +++ iked.conf.5 14 May 2020 20:43:27 -0000
> @@ -861,12 +861,15 @@ keyword:
>  .It Li aes-128 Ta "128 bits" Ta ""
>  .It Li aes-192 Ta "192 bits" Ta ""
>  .It Li aes-256 Ta "256 bits" Ta ""
> +.It Li aes-128-gcm Ta "160 bits" Ta ""
> +.It Li aes-192-gcm Ta "224 bits" Ta ""
> +.It Li aes-256-gcm Ta "288 bits" Ta ""
> +.It Li aes-128-gcm-12 Ta "160 bits" Ta "[IKE only]"
> +.It Li aes-192-gcm-12 Ta "224 bits" Ta "[IKE only]"
> +.It Li aes-256-gcm-12 Ta "288 bits" Ta "[IKE only]"
>  .It Li aes-128-ctr Ta "160 bits" Ta "[ESP only]"
>  .It Li aes-192-ctr Ta "224 bits" Ta "[ESP only]"
>  .It Li aes-256-ctr Ta "288 bits" Ta "[ESP only]"
> -.It Li aes-128-gcm Ta "160 bits" Ta "[ESP only]"
> -.It Li aes-192-gcm Ta "224 bits" Ta "[ESP only]"
> -.It Li aes-256-gcm Ta "288 bits" Ta "[ESP only]"
>  .It Li blowfish Ta "160 bits" Ta "[ESP only]"
>  .It Li cast Ta "128 bits" Ta "[ESP only]"
>  .It Li chacha20-poly1305 Ta "288 bits" Ta "[ESP only]"
> Index: iked.h
> ===================================================================
> RCS file: /cvs/src/sbin/iked/iked.h,v
> retrieving revision 1.150
> diff -u -p -r1.150 iked.h
> --- iked.h 14 May 2020 15:08:30 -0000 1.150
> +++ iked.h 14 May 2020 20:43:27 -0000
> @@ -309,6 +309,7 @@ struct iked_hash {
>   size_t hash_length; /* Output length */
>   size_t hash_trunc; /* Truncate the output length */
>   struct iked_hash *hash_prf; /* PRF pointer */
> + int hash_isaead;
>  };
>  
>  struct iked_cipher {
> @@ -321,6 +322,8 @@ struct iked_cipher {
>   struct ibuf *encr_iv; /* Initialization Vector */
>   size_t encr_ivlength; /* IV length */
>   size_t encr_length; /* Block length */
> + size_t encr_saltlength; /* IV salt length */
> + uint16_t encr_authid; /* ID of associated authentication */
>  };
>  
>  struct iked_dsa {
> @@ -841,10 +844,13 @@ struct ibuf *
>   cipher_setkey(struct iked_cipher *, void *, size_t);
>  struct ibuf *
>   cipher_setiv(struct iked_cipher *, void *, size_t);
> +int cipher_settag(struct iked_cipher *, uint8_t *, size_t);
> +int cipher_gettag(struct iked_cipher *, uint8_t *, size_t);
>  void cipher_free(struct iked_cipher *);
>  int cipher_init(struct iked_cipher *, int);
>  int cipher_init_encrypt(struct iked_cipher *);
>  int cipher_init_decrypt(struct iked_cipher *);
> +void cipher_aad(struct iked_cipher *, void *, size_t, size_t *);
>  int cipher_update(struct iked_cipher *, void *, size_t, void *, size_t *);
>  int cipher_final(struct iked_cipher *);
>  size_t cipher_length(struct iked_cipher *);
> @@ -933,7 +939,8 @@ int ikev2_msg_send(struct iked *, struc
>  int ikev2_msg_send_encrypt(struct iked *, struct iked_sa *,
>      struct ibuf **, uint8_t, uint8_t, int);
>  struct ibuf
> - *ikev2_msg_encrypt(struct iked *, struct iked_sa *, struct ibuf *);
> + *ikev2_msg_encrypt(struct iked *, struct iked_sa *, struct ibuf *,
> +    struct ibuf *);
>  struct ibuf *
>   ikev2_msg_decrypt(struct iked *, struct iked_sa *,
>      struct ibuf *, struct ibuf *);
> @@ -1129,5 +1136,6 @@ void print_policy(struct iked_policy *)
>  size_t keylength_xf(unsigned int, unsigned int, unsigned int);
>  size_t noncelength_xf(unsigned int, unsigned int);
>  int cmdline_symset(char *);
> +int encxf_noauth(unsigned int);
>  
>  #endif /* IKED_H */
> Index: ikev2.c
> ===================================================================
> RCS file: /cvs/src/sbin/iked/ikev2.c,v
> retrieving revision 1.225
> diff -u -p -r1.225 ikev2.c
> --- ikev2.c 11 May 2020 20:11:35 -0000 1.225
> +++ ikev2.c 14 May 2020 20:43:29 -0000
> @@ -4473,7 +4473,7 @@ ikev2_send_informational(struct iked *en
>   goto done;
>  
>   /* Encrypt message and add as an E payload */
> - if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) {
> + if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) {
>   log_debug("%s: encryption failed", __func__);
>   goto done;
>   }
> @@ -4666,6 +4666,16 @@ ikev2_sa_initiator(struct iked *env, str
>   }
>   }
>  
> + /* For AEAD ciphers integrity is implicit */
> + if (sa->sa_encr->encr_authid && sa->sa_integr == NULL) {
> + if ((sa->sa_integr = hash_new(IKEV2_XFORMTYPE_INTEGR,
> +    sa->sa_encr->encr_authid)) == NULL) {
> + log_info("%s: failed to get AEAD integr",
> +    SPI_SA(sa, __func__));
> + return (-1);
> + }
> + }
> +
>   if (sa->sa_prf == NULL) {
>   if ((xform = config_findtransform(&sa->sa_proposals,
>      IKEV2_XFORMTYPE_PRF, 0)) == NULL) {
> @@ -4840,6 +4850,16 @@ ikev2_sa_responder(struct iked *env, str
>   }
>   }
>  
> + /* For AEAD ciphers integrity is implicit */
> + if (sa->sa_encr->encr_authid && sa->sa_integr == NULL) {
> + if ((sa->sa_integr = hash_new(IKEV2_XFORMTYPE_INTEGR,
> +    sa->sa_encr->encr_authid)) == NULL) {
> + log_info("%s: failed to get AEAD integr",
> +    SPI_SA(sa, __func__));
> + return (-1);
> + }
> + }
> +
>   if (sa->sa_prf == NULL) {
>   if ((xform = config_findtransform(&sa->sa_proposals,
>      IKEV2_XFORMTYPE_PRF, 0)) == NULL) {
> @@ -4883,6 +4903,7 @@ ikev2_sa_keys(struct iked *env, struct i
>   size_t nonceminlen, ilen, rlen, tmplen;
>   uint64_t ispi, rspi;
>   int ret = -1;
> + int isaead = 0;
>  
>   ninr = dhsecret = skeyseed = s = t = NULL;
>  
> @@ -4895,6 +4916,9 @@ ikev2_sa_keys(struct iked *env, struct i
>   return (-1);
>   }
>  
> + /* For AEADs no auth keys are required (see RFC 5282) */
> + isaead = !!integr->hash_isaead;
> +
>   if (prf->hash_fixedkey)
>   nonceminlen = prf->hash_fixedkey;
>   else
> @@ -5026,13 +5050,13 @@ ikev2_sa_keys(struct iked *env, struct i
>   * Get the size of the key material we need and the number
>   * of rounds we need to run the prf+ function.
>   */
> - ilen = hash_length(prf) + /* SK_d */
> -    hash_keylength(integr) + /* SK_ai */
> -    hash_keylength(integr) + /* SK_ar */
> -    cipher_keylength(encr) + /* SK_ei */
> -    cipher_keylength(encr) + /* SK_er */
> -    hash_keylength(prf) + /* SK_pi */
> -    hash_keylength(prf); /* SK_pr */
> + ilen = hash_length(prf) + /* SK_d */
> +    (isaead ? 0 : hash_keylength(integr)) + /* SK_ai */
> +    (isaead ? 0 : hash_keylength(integr)) + /* SK_ar */
> +    cipher_keylength(encr) + /* SK_ei */
> +    cipher_keylength(encr) + /* SK_er */
> +    hash_keylength(prf) + /* SK_pi */
> +    hash_keylength(prf); /* SK_pr */
>  
>   if ((t = ikev2_prfplus(prf, skeyseed, s, ilen)) == NULL) {
>   log_info("%s: failed to get IKE SA key material",
> @@ -5042,8 +5066,10 @@ ikev2_sa_keys(struct iked *env, struct i
>  
>   /* ibuf_get() returns a new buffer from the next read offset */
>   if ((sa->sa_key_d = ibuf_get(t, hash_length(prf))) == NULL ||
> -    (sa->sa_key_iauth = ibuf_get(t, hash_keylength(integr))) == NULL ||
> -    (sa->sa_key_rauth = ibuf_get(t, hash_keylength(integr))) == NULL ||
> +    (!isaead &&
> +    (sa->sa_key_iauth = ibuf_get(t, hash_keylength(integr))) == NULL) ||
> +    (!isaead &&
> +    (sa->sa_key_rauth = ibuf_get(t, hash_keylength(integr))) == NULL) ||
>      (sa->sa_key_iencr = ibuf_get(t, cipher_keylength(encr))) == NULL ||
>      (sa->sa_key_rencr = ibuf_get(t, cipher_keylength(encr))) == NULL ||
>      (sa->sa_key_iprf = ibuf_get(t, hash_length(prf))) == NULL ||
> @@ -5055,12 +5081,16 @@ ikev2_sa_keys(struct iked *env, struct i
>   log_debug("%s: SK_d with %zu bytes", __func__,
>      ibuf_length(sa->sa_key_d));
>   print_hex(sa->sa_key_d->buf, 0, ibuf_length(sa->sa_key_d));
> - log_debug("%s: SK_ai with %zu bytes", __func__,
> -    ibuf_length(sa->sa_key_iauth));
> - print_hex(sa->sa_key_iauth->buf, 0, ibuf_length(sa->sa_key_iauth));
> - log_debug("%s: SK_ar with %zu bytes", __func__,
> -    ibuf_length(sa->sa_key_rauth));
> - print_hex(sa->sa_key_rauth->buf, 0, ibuf_length(sa->sa_key_rauth));
> + if (!isaead) {
> + log_debug("%s: SK_ai with %zu bytes", __func__,
> +    ibuf_length(sa->sa_key_iauth));
> + print_hex(sa->sa_key_iauth->buf, 0,
> +    ibuf_length(sa->sa_key_iauth));
> + log_debug("%s: SK_ar with %zu bytes", __func__,
> +    ibuf_length(sa->sa_key_rauth));
> + print_hex(sa->sa_key_rauth->buf, 0,
> +    ibuf_length(sa->sa_key_rauth));
> + }
>   log_debug("%s: SK_ei with %zu bytes", __func__,
>      ibuf_length(sa->sa_key_iencr));
>   print_hex(sa->sa_key_iencr->buf, 0, ibuf_length(sa->sa_key_iencr));
> Index: ikev2.h
> ===================================================================
> RCS file: /cvs/src/sbin/iked/ikev2.h,v
> retrieving revision 1.32
> diff -u -p -r1.32 ikev2.h
> --- ikev2.h 28 Apr 2020 15:18:52 -0000 1.32
> +++ ikev2.h 14 May 2020 20:43:30 -0000
> @@ -224,6 +224,9 @@ extern struct iked_constmap ikev2_xformp
>  #define IKEV2_XFORMAUTH_HMAC_SHA2_256_128 12 /* RFC4868 */
>  #define IKEV2_XFORMAUTH_HMAC_SHA2_384_192 13 /* RFC4868 */
>  #define IKEV2_XFORMAUTH_HMAC_SHA2_512_256 14 /* RFC4868 */
> +#define IKEV2_XFORMAUTH_AES_GCM_8 2018 /* private */
> +#define IKEV2_XFORMAUTH_AES_GCM_12 2019 /* private */
> +#define IKEV2_XFORMAUTH_AES_GCM_16 2020 /* private */
>  
>  extern struct iked_constmap ikev2_xformauth_map[];
>  
> Index: ikev2_msg.c
> ===================================================================
> RCS file: /cvs/src/sbin/iked/ikev2_msg.c,v
> retrieving revision 1.67
> diff -u -p -r1.67 ikev2_msg.c
> --- ikev2_msg.c 14 May 2020 15:08:30 -0000 1.67
> +++ ikev2_msg.c 14 May 2020 20:43:30 -0000
> @@ -51,6 +51,8 @@ void ikev2_msg_retransmit_timeout(struc
>  int ikev2_check_frag_oversize(struct iked_sa *sa, struct ibuf *buf);
>  int ikev2_send_encrypted_fragments(struct iked *env, struct iked_sa *sa,
>      struct ibuf *in,uint8_t exchange, uint8_t firstpayload, int response);
> +int ikev2_msg_encrypt_prepare(struct iked_sa *, struct ikev2_payload *,
> +    struct ibuf*, struct ibuf *, struct ike_header *, uint8_t, int);
>  
>  void
>  ikev2_msg_cb(int fd, short event, void *arg)
> @@ -329,8 +331,45 @@ ikev2_msg_id(struct iked *env, struct ik
>   return (id);
>  }
>  
> +/*
> + * Calculate the final sizes of the IKEv2 header and the encrypted payload
> + * header.  This must be done before encryption to make sure the correct
> + * headers are authenticated.
> + */
> +int
> +ikev2_msg_encrypt_prepare(struct iked_sa *sa, struct ikev2_payload *pld,
> +    struct ibuf *buf, struct ibuf *e, struct ike_header *hdr,
> +    uint8_t firstpayload, int fragmentation)
> +{
> + size_t len, ivlen, encrlen, integrlen, blocklen, pldlen, outlen;
> +
> + if (sa == NULL ||
> +    sa->sa_encr == NULL ||
> +    sa->sa_integr == NULL) {
> + log_debug("%s: invalid SA", __func__);
> + return (-1);
> + }
> +
> + len = ibuf_size(e);
> + blocklen = cipher_length(sa->sa_encr);
> + integrlen = hash_length(sa->sa_integr);
> + ivlen = cipher_ivlength(sa->sa_encr);
> + encrlen = roundup(len + 1, blocklen);
> + outlen = cipher_outlength(sa->sa_encr, encrlen);
> + pldlen = ivlen + outlen + integrlen +
> +    (fragmentation ? sizeof(struct ikev2_frag_payload) : 0);
> +
> + if (ikev2_next_payload(pld, pldlen, firstpayload) == -1)
> + return (-1);
> + if (ikev2_set_header(hdr, ibuf_size(buf) + pldlen - sizeof(*hdr)) == -1)
> + return (-1);
> +
> + return (0);
> +}
> +
>  struct ibuf *
> -ikev2_msg_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf *src)
> +ikev2_msg_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf *src,
> +    struct ibuf *aad)
>  {
>   size_t len, ivlen, encrlen, integrlen, blocklen,
>      outlen;
> @@ -390,12 +429,23 @@ ikev2_msg_encrypt(struct iked *env, stru
>   goto done;
>  
>   outlen = ibuf_size(out);
> +
> + /* Add AAD for AEAD ciphers */
> + if (sa->sa_integr->hash_isaead)
> + cipher_aad(sa->sa_encr, ibuf_data(aad),
> +    ibuf_length(aad), &outlen);
> +
>   if (cipher_update(sa->sa_encr, ibuf_data(src), encrlen,
>      ibuf_data(out), &outlen) == -1) {
>   log_info("%s: error updating cipher.", __func__);
>   goto done;
>   }
>  
> + if (cipher_final(sa->sa_encr) == -1) {
> + log_info("%s: encryption failed.", __func__);
> + goto done;
> + }
> +
>   if (outlen && ibuf_add(dst, ibuf_data(out), outlen) != 0)
>   goto done;
>  
> @@ -429,18 +479,13 @@ ikev2_msg_integr(struct iked *env, struc
>   print_hex(ibuf_data(src), 0, ibuf_size(src));
>  
>   if (sa == NULL ||
> +    sa->sa_encr == NULL ||
>      sa->sa_integr == NULL) {
>   log_debug("%s: invalid SA", __func__);
>   return (-1);
>   }
>  
> - if (sa->sa_hdr.sh_initiator)
> - integr = sa->sa_key_iauth;
> - else
> - integr = sa->sa_key_rauth;
> -
>   integrlen = hash_length(sa->sa_integr);
> -
>   log_debug("%s: integrity checksum length %zu", __func__,
>      integrlen);
>  
> @@ -450,21 +495,33 @@ ikev2_msg_integr(struct iked *env, struc
>   if ((tmp = ibuf_new(NULL, hash_keylength(sa->sa_integr))) == NULL)
>   goto done;
>  
> - hash_setkey(sa->sa_integr, ibuf_data(integr), ibuf_size(integr));
> - hash_init(sa->sa_integr);
> - hash_update(sa->sa_integr, ibuf_data(src),
> -    ibuf_size(src) - integrlen);
> - hash_final(sa->sa_integr, ibuf_data(tmp), &tmplen);
> + if (!sa->sa_integr->hash_isaead) {
> + if (sa->sa_hdr.sh_initiator)
> + integr = sa->sa_key_iauth;
> + else
> + integr = sa->sa_key_rauth;
> +
> + hash_setkey(sa->sa_integr, ibuf_data(integr),
> +    ibuf_size(integr));
> + hash_init(sa->sa_integr);
> + hash_update(sa->sa_integr, ibuf_data(src),
> +    ibuf_size(src) - integrlen);
> + hash_final(sa->sa_integr, ibuf_data(tmp), &tmplen);
>  
> - if (tmplen != integrlen) {
> - log_debug("%s: hash failure", __func__);
> - goto done;
> + if (tmplen != integrlen) {
> + log_debug("%s: hash failure", __func__);
> + goto done;
> + }
> + } else {
> + /* Append AEAD tag */
> + if (cipher_gettag(sa->sa_encr, ibuf_data(tmp), ibuf_size(tmp)))
> + goto done;
>   }
>  
>   if ((ptr = ibuf_seek(src,
>      ibuf_size(src) - integrlen, integrlen)) == NULL)
>   goto done;
> - memcpy(ptr, ibuf_data(tmp), tmplen);
> + memcpy(ptr, ibuf_data(tmp), integrlen);
>  
>   print_hex(ibuf_data(tmp), 0, ibuf_size(tmp));
>  
> @@ -481,7 +538,7 @@ ikev2_msg_decrypt(struct iked *env, stru
>  {
>   ssize_t ivlen, encrlen, integrlen, blocklen,
>      outlen, tmplen;
> - uint8_t pad = 0, *ptr;
> + uint8_t pad = 0, *ptr, *integrdata;
>   struct ibuf *integr, *encr, *tmp = NULL, *out = NULL;
>   off_t ivoff, encroff, integroff;
>  
> @@ -524,25 +581,30 @@ ikev2_msg_decrypt(struct iked *env, stru
>   /*
>   * Validate packet checksum
>   */
> - if ((tmp = ibuf_new(NULL, ibuf_length(integr))) == NULL)
> - goto done;
> + if (!sa->sa_integr->hash_isaead) {
> + if ((tmp = ibuf_new(NULL, ibuf_length(integr))) == NULL)
> + goto done;
>  
> - hash_setkey(sa->sa_integr, integr->buf, ibuf_length(integr));
> - hash_init(sa->sa_integr);
> - hash_update(sa->sa_integr, ibuf_data(msg),
> -    ibuf_size(msg) - integrlen);
> - hash_final(sa->sa_integr, tmp->buf, &tmplen);
> + hash_setkey(sa->sa_integr, integr->buf, ibuf_length(integr));
> + hash_init(sa->sa_integr);
> + hash_update(sa->sa_integr, ibuf_data(msg),
> +    ibuf_size(msg) - integrlen);
> + hash_final(sa->sa_integr, tmp->buf, &tmplen);
>  
> - if (memcmp(tmp->buf, ibuf_data(src) + integroff, integrlen) != 0) {
> - log_debug("%s: integrity check failed", __func__);
> - goto done;
> - }
> + integrdata = ibuf_seek(src, integroff, integrlen);
> + if (integrdata == NULL)
> + goto done;
> + if (memcmp(tmp->buf, integrdata, integrlen) != 0) {
> + log_debug("%s: integrity check failed", __func__);
> + goto done;
> + }
>  
> - log_debug("%s: integrity check succeeded", __func__);
> - print_hex(tmp->buf, 0, tmplen);
> + log_debug("%s: integrity check succeeded", __func__);
> + print_hex(tmp->buf, 0, tmplen);
>  
> - ibuf_release(tmp);
> - tmp = NULL;
> + ibuf_release(tmp);
> + tmp = NULL;
> + }
>  
>   /*
>   * Decrypt the payload and strip any padding
> @@ -559,10 +621,31 @@ ikev2_msg_decrypt(struct iked *env, stru
>   goto done;
>   }
>  
> + /* Set AEAD tag */
> + if (sa->sa_integr->hash_isaead) {
> + integrdata = ibuf_seek(src, integroff, integrlen);
> + if (integrdata == NULL)
> + goto done;
> + if (cipher_settag(sa->sa_encr, integrdata, integrlen)) {
> + log_info("%s: failed to set tag.", __func__);
> + goto done;
> + }
> + }
> +
>   if ((out = ibuf_new(NULL, cipher_outlength(sa->sa_encr,
>      encrlen))) == NULL)
>   goto done;
>  
> + /*
> + * Add additional authenticated data for AEAD ciphers
> + */
> + if (sa->sa_integr->hash_isaead) {
> + log_debug("%s: AAD length %zu", __func__, ibuf_length(msg) - ibuf_length(src));
> + print_hex(ibuf_data(msg), 0, ibuf_length(msg) - ibuf_length(src));
> + cipher_aad(sa->sa_encr, ibuf_data(msg),
> +    ibuf_length(msg) - ibuf_length(src), &outlen);
> + }
> +
>   if ((outlen = ibuf_length(out)) != 0) {
>   if (cipher_update(sa->sa_encr, ibuf_data(src) + encroff,
>      encrlen, ibuf_data(out), &outlen) == -1) {
> @@ -574,6 +657,11 @@ ikev2_msg_decrypt(struct iked *env, stru
>   pad = *ptr;
>   }
>  
> + if (cipher_final(sa->sa_encr) == -1) {
> + log_info("%s: decryption failed.", __func__);
> + goto done;
> + }
> +
>   log_debug("%s: decrypted payload length %zd/%zd padding %d",
>      __func__, outlen, encrlen, pad);
>   print_hex(ibuf_data(out), 0, ibuf_size(out));
> @@ -598,6 +686,13 @@ ikev2_check_frag_oversize(struct iked_sa
>   size_t max;
>   size_t ivlen, integrlen, blocklen;
>  
> + if (sa == NULL ||
> +    sa->sa_encr == NULL ||
> +    sa->sa_integr == NULL) {
> + log_debug("%s: invalid SA", __func__);
> + return (-1);
> + }
> +
>   sa_fam = ((struct sockaddr *)&sa->sa_local.addr)->sa_family;
>  
>   max = sa_fam == AF_INET ? IKEV2_MAXLEN_IPV4_FRAG
> @@ -642,18 +737,16 @@ ikev2_msg_send_encrypt(struct iked *env,
>   if ((pld = ikev2_add_payload(buf)) == NULL)
>   goto done;
>  
> + if (ikev2_msg_encrypt_prepare(sa, pld, buf, e, hdr, firstpayload, 0) == -1)
> + goto done;
> +
>   /* Encrypt message and add as an E payload */
> - if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) {
> + if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) {
>   log_debug("%s: encryption failed", __func__);
>   goto done;
>   }
>   if (ibuf_cat(buf, e) != 0)
>   goto done;
> - if (ikev2_next_payload(pld, ibuf_size(e), firstpayload) == -1)
> - goto done;
> -
> - if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1)
> - goto done;
>  
>   /* Add integrity checksum (HMAC) */
>   if (ikev2_msg_integr(env, sa, buf) != 0) {
> @@ -694,6 +787,13 @@ ikev2_send_encrypted_fragments(struct ik
>   uint32_t msgid;
>   int ret = -1;
>  
> + if (sa == NULL ||
> +    sa->sa_encr == NULL ||
> +    sa->sa_integr == NULL) {
> + log_debug("%s: invalid SA", __func__);
> + goto done;
> + }
> +
>   sa_fam = ((struct sockaddr *)&sa->sa_local.addr)->sa_family;
>  
>   left = ibuf_length(in);
> @@ -743,18 +843,16 @@ ikev2_send_encrypted_fragments(struct ik
>   if ((e = ibuf_new(data, MIN(left, max_len))) == NULL) {
>   goto done;
>   }
> - if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) {
> +
> + if (ikev2_msg_encrypt_prepare(sa, pld, buf, e, hdr,
> +    firstpayload, 1) == -1)
> + goto done;
> +
> + if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) {
>   log_debug("%s: encryption failed", __func__);
>   goto done;
>   }
>   if (ibuf_cat(buf, e) != 0)
> - goto done;
> -
> - if (ikev2_next_payload(pld, ibuf_size(e) + sizeof(*frag),
> -    firstpayload) == -1)
> - goto done;
> -
> - if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1)
>   goto done;
>  
>   /* Add integrity checksum (HMAC) */
> Index: parse.y
> ===================================================================
> RCS file: /cvs/src/sbin/iked/parse.y,v
> retrieving revision 1.99
> diff -u -p -r1.99 parse.y
> --- parse.y 30 Apr 2020 21:11:13 -0000 1.99
> +++ parse.y 14 May 2020 20:43:30 -0000
> @@ -198,6 +198,12 @@ const struct ipsec_xf ikeencxfs[] = {
>   { "aes-128", IKEV2_XFORMENCR_AES_CBC, 16, 16 },
>   { "aes-192", IKEV2_XFORMENCR_AES_CBC, 24, 24 },
>   { "aes-256", IKEV2_XFORMENCR_AES_CBC, 32, 32 },
> + { "aes-128-gcm-12", IKEV2_XFORMENCR_AES_GCM_12, 16, 16, 4, 1 },
> + { "aes-192-gcm-12", IKEV2_XFORMENCR_AES_GCM_12, 24, 24, 4, 1 },
> + { "aes-256-gcm-12", IKEV2_XFORMENCR_AES_GCM_12, 32, 32, 4, 1 },
> + { "aes-128-gcm", IKEV2_XFORMENCR_AES_GCM_16, 16, 16, 4, 1 },
> + { "aes-192-gcm", IKEV2_XFORMENCR_AES_GCM_16, 24, 24, 4, 1 },
> + { "aes-256-gcm", IKEV2_XFORMENCR_AES_GCM_16, 32, 32, 4, 1 },
>   { NULL }
>  };
>  
> @@ -2417,6 +2423,17 @@ print_xf(unsigned int id, unsigned int l
>   return ("unknown");
>  }
>  
> +int
> +encxf_noauth(unsigned int id)
> +{
> + int i;
> +
> + for (i = 0; ikeencxfs[i].name != NULL; i++)
> + if (ikeencxfs[i].id == id)
> + return ikeencxfs[i].noauth;
> + return (0);
> +}
> +
>  size_t
>  keylength_xf(unsigned int saproto, unsigned int type, unsigned int id)
>  {
> @@ -2852,21 +2869,37 @@ create_ike(char *name, int af, uint8_t i
>   pol.pol_nproposals++;
>   } else {
>   for (i = 0; i < ike_sa->nxfs; i++) {
> + noauth = 0;
> + for (j = 0; j < ike_sa->xfs[i]->nencxf; j++) {
> + if (ike_sa->xfs[i]->encxf[j]->noauth)
> + noauth++;
> + }
>   if (ike_sa->xfs[i]->nesnxf) {
>   yyerror("cannot use ESN with ikesa.");
>   goto done;
>   }
> + if (noauth && noauth != ike_sa->xfs[i]->nencxf) {
> + yyerror("cannot mix encryption transforms with "
> +    "implicit and non-implicit authentication");
> + goto done;
> + }
> + if (noauth && ike_sa->xfs[i]->nauthxf) {
> + yyerror("authentication is implicit for given "
> +    "encryption transforms");
> + goto done;
> + }
>  
>   if ((p = calloc(1, sizeof(*p))) == NULL)
>   err(1, "%s", __func__);
>  
>   xf = NULL;
>   xfi = 0;
> - copy_transforms(IKEV2_XFORMTYPE_INTEGR,
> -    ike_sa->xfs[i]->authxf,
> -    ike_sa->xfs[i]->nauthxf, &xf, &xfi,
> -    ikev2_default_ike_transforms,
> -    ikev2_default_nike_transforms);
> + if (!ike_sa->xfs[i]->nencxf || !noauth)
> + copy_transforms(IKEV2_XFORMTYPE_INTEGR,
> +    ike_sa->xfs[i]->authxf,
> +    ike_sa->xfs[i]->nauthxf, &xf, &xfi,
> +    ikev2_default_ike_transforms,
> +    ikev2_default_nike_transforms);
>   copy_transforms(IKEV2_XFORMTYPE_ENCR,
>      ike_sa->xfs[i]->encxf,
>      ike_sa->xfs[i]->nencxf, &xf, &xfi,
> Index: policy.c
> ===================================================================
> RCS file: /cvs/src/sbin/iked/policy.c,v
> retrieving revision 1.62
> diff -u -p -r1.62 policy.c
> --- policy.c 13 May 2020 23:03:20 -0000 1.62
> +++ policy.c 14 May 2020 20:43:30 -0000
> @@ -774,7 +774,7 @@ proposals_match(struct iked_proposal *lo
>      struct iked_transform **xforms, int rekey)
>  {
>   struct iked_transform *tpeer, *tlocal;
> - unsigned int i, j, type, score, requiredh = 0;
> + unsigned int i, j, type, score, requiredh = 0, noauth = 0;
>   uint8_t protoid = peer->prop_protoid;
>   uint8_t peerxfs[IKEV2_XFORMTYPE_MAX];
>  
> @@ -782,8 +782,18 @@ proposals_match(struct iked_proposal *lo
>  
>   for (i = 0; i < peer->prop_nxforms; i++) {
>   tpeer = peer->prop_xforms + i;
> + /* If any of the ENC transforms is an AEAD, ignore auth */
> + if (tpeer->xform_type == IKEV2_XFORMTYPE_ENCR &&
> +    encxf_noauth(tpeer->xform_id))
> + noauth = 1;
> + }
> +
> + for (i = 0; i < peer->prop_nxforms; i++) {
> + tpeer = peer->prop_xforms + i;
>   if (tpeer->xform_type > IKEV2_XFORMTYPE_MAX)
>   continue;
> + if (noauth && tpeer->xform_type == IKEV2_XFORMTYPE_INTEGR)
> + return (0);
>  
>   /*
>   * Record all transform types from the peer's proposal,
> @@ -832,7 +842,8 @@ proposals_match(struct iked_proposal *lo
>   for (i = score = 0; i < IKEV2_XFORMTYPE_MAX; i++) {
>   if (protoid == IKEV2_SAPROTO_IKE && xforms[i] == NULL &&
>      (i == IKEV2_XFORMTYPE_ENCR || i == IKEV2_XFORMTYPE_PRF ||
> -     i == IKEV2_XFORMTYPE_INTEGR || i == IKEV2_XFORMTYPE_DH)) {
> +    (!noauth && i == IKEV2_XFORMTYPE_INTEGR) ||
> +    i == IKEV2_XFORMTYPE_DH)) {
>   score = 0;
>   break;
>   } else if (protoid == IKEV2_SAPROTO_AH && xforms[i] == NULL &&
>
Hi Tobias,
I tried to apply your diff but it fails at some points. I haven't yet dug deeper why.
Will take a look tomorrow.
You'll find the .rej files and the output of patch(1) attached.

Best regards,
Stephan


crypto.c.rej (1K) Download Attachment
iked.h.rej (704 bytes) Download Attachment
ikev2_msg.c.rej (595 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: iked(8): AES_GCM ciphers for IKE

Tobias Heider-2
Looks like you are missing the previous commit:
https://marc.info/?l=openbsd-cvs&m=158946893417378&w=2

Reply | Threaded
Open this post in threaded view
|

Re: iked(8): AES_GCM ciphers for IKE

Tobias Heider-2
In reply to this post by Tobias Heider-2
On Thu, May 14, 2020 at 10:47:52PM +0200, Tobias Heider wrote:

> On Thu, May 14, 2020 at 10:07:30PM +0200, Tobias Heider wrote:
> > Hi,
> >
> > currently iked(8) supports AES-GCM only for ESP.
> > The diff below adds the ENCR_AES_GCM_16 and ENCR_AES_GCM_12 variants for IKE.
> > (for more information see [1] and [2]).
> > Both variants support the 128, 196, and 256 bit key lengths.
> >
> > The new new ciphers can be configured with:
> > - aes-128-gcm, aes-196-gcm and aes-256-gcm for ENCR_AES_GCM_16
> > - aes-128-gcm-12, aes-196-gcm-12 and aes-256-gcm-12 for ENCR_AES_GCM_12

Small typo: it's 192, not 196.

> >
> > It would be nice if we could get some interop testing with different IKEv2
> > implementations.  I have so far successfully tested strongswan <-> iked and
> > of course iked <-> iked.
> >
> > Feedback welcome ;)
> >
> > [1] https://tools.ietf.org/html/rfc5282
> > [2] https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml#ikev2-parameters-5
> >
>
> whoops, previous diff was broken.
>

Another update because it seems parse_xf matches substrings instead of the
full transform type name, which means I had to change the order of ikeencxfs
members or 'aes-128-gcm' will always match 'aes-128-gcm-12' ...

Index: crypto.c
===================================================================
RCS file: /cvs/src/sbin/iked/crypto.c,v
retrieving revision 1.27
diff -u -p -r1.27 crypto.c
--- crypto.c 14 May 2020 15:08:30 -0000 1.27
+++ crypto.c 14 May 2020 23:55:13 -0000
@@ -92,7 +92,7 @@ hash_new(uint8_t type, uint16_t id)
  struct iked_hash *hash;
  const EVP_MD *md = NULL;
  HMAC_CTX *ctx = NULL;
- int length = 0, fixedkey = 0, trunc = 0;
+ int length = 0, fixedkey = 0, trunc = 0, isaead = 0;
 
  switch (type) {
  case IKEV2_XFORMTYPE_PRF:
@@ -156,6 +156,14 @@ hash_new(uint8_t type, uint16_t id)
  length = SHA512_DIGEST_LENGTH;
  trunc = 32;
  break;
+ case IKEV2_XFORMAUTH_AES_GCM_12:
+ length = 12;
+ isaead = 1;
+ break;
+ case IKEV2_XFORMAUTH_AES_GCM_16:
+ length = 16;
+ isaead = 1;
+ break;
  case IKEV2_XFORMAUTH_NONE:
  case IKEV2_XFORMAUTH_DES_MAC:
  case IKEV2_XFORMAUTH_KPDK_MD5:
@@ -177,7 +185,7 @@ hash_new(uint8_t type, uint16_t id)
     print_map(id, ikev2_xformtype_map));
  break;
  }
- if (md == NULL)
+ if (!isaead && md == NULL)
  return (NULL);
 
  if ((hash = calloc(1, sizeof(*hash))) == NULL) {
@@ -192,6 +200,10 @@ hash_new(uint8_t type, uint16_t id)
  hash->hash_trunc = trunc;
  hash->hash_length = length;
  hash->hash_fixedkey = fixedkey;
+ hash->hash_isaead = isaead;
+
+ if (isaead)
+ return (hash);
 
  if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
  log_debug("%s: alloc hash ctx", __func__);
@@ -276,6 +288,7 @@ cipher_new(uint8_t type, uint16_t id, ui
  const EVP_CIPHER *cipher = NULL;
  EVP_CIPHER_CTX *ctx = NULL;
  int length = 0, fixedkey = 0, ivlength = 0;
+ int saltlength = 0, authid = 0;
 
  switch (type) {
  case IKEV2_XFORMTYPE_ENCR:
@@ -309,6 +322,39 @@ cipher_new(uint8_t type, uint16_t id, ui
  ivlength = EVP_CIPHER_iv_length(cipher);
  fixedkey = EVP_CIPHER_key_length(cipher);
  break;
+ case IKEV2_XFORMENCR_AES_GCM_16:
+ case IKEV2_XFORMENCR_AES_GCM_12:
+ switch (id_length) {
+ case 128:
+ cipher = EVP_aes_128_gcm();
+ break;
+ case 192:
+ cipher = EVP_aes_192_gcm();
+ break;
+ case 256:
+ cipher = EVP_aes_256_gcm();
+ break;
+ default:
+ log_debug("%s: invalid key length %d"
+    " for cipher %s", __func__, id_length,
+    print_map(id, ikev2_xformencr_map));
+ break;
+ }
+ if (cipher == NULL)
+ break;
+ switch(id) {
+ case IKEV2_XFORMENCR_AES_GCM_16:
+ authid = IKEV2_XFORMAUTH_AES_GCM_16;
+ break;
+ case IKEV2_XFORMENCR_AES_GCM_12:
+ authid = IKEV2_XFORMAUTH_AES_GCM_12;
+ break;
+ }
+ length = EVP_CIPHER_block_size(cipher);
+ ivlength = 8;
+ saltlength = 4;
+ fixedkey = EVP_CIPHER_key_length(cipher) + saltlength;
+ break;
  case IKEV2_XFORMENCR_DES_IV64:
  case IKEV2_XFORMENCR_DES:
  case IKEV2_XFORMENCR_RC5:
@@ -346,6 +392,8 @@ cipher_new(uint8_t type, uint16_t id, ui
  encr->encr_length = length;
  encr->encr_fixedkey = fixedkey;
  encr->encr_ivlength = ivlength ? ivlength : length;
+ encr->encr_saltlength = saltlength;
+ encr->encr_authid = authid;
 
  if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
  log_debug("%s: alloc cipher ctx", __func__);
@@ -392,6 +440,20 @@ cipher_setiv(struct iked_cipher *encr, v
  return (encr->encr_iv);
 }
 
+int
+cipher_settag(struct iked_cipher *encr, uint8_t *data, size_t len)
+{
+ return (EVP_CIPHER_CTX_ctrl(encr->encr_ctx,
+    EVP_CTRL_GCM_SET_TAG, len, data) != 1);
+}
+
+int
+cipher_gettag(struct iked_cipher *encr, uint8_t *data, size_t len)
+{
+ return (EVP_CIPHER_CTX_ctrl(encr->encr_ctx,
+    EVP_CTRL_GCM_GET_TAG, len, data) != 1);
+}
+
 void
 cipher_free(struct iked_cipher *encr)
 {
@@ -409,11 +471,33 @@ cipher_free(struct iked_cipher *encr)
 int
 cipher_init(struct iked_cipher *encr, int enc)
 {
+ struct ibuf *nonce = NULL;
+ int ret = -1;
+
  if (EVP_CipherInit_ex(encr->encr_ctx, encr->encr_priv, NULL,
-    ibuf_data(encr->encr_key), ibuf_data(encr->encr_iv), enc) != 1)
+    NULL, NULL, enc) != 1)
  return (-1);
+ if (encr->encr_saltlength > 0) {
+ /* For AEADs the nonce is salt + IV  (see RFC5282) */
+ nonce = ibuf_new(ibuf_data(encr->encr_key) +
+    ibuf_size(encr->encr_key) - encr->encr_saltlength,
+    encr->encr_saltlength);
+ if (nonce == NULL)
+ return (-1);
+ if (ibuf_add(nonce, ibuf_data(encr->encr_iv) , ibuf_size(encr->encr_iv)) != 0)
+ goto done;
+ if (EVP_CipherInit_ex(encr->encr_ctx, NULL, NULL,
+    ibuf_data(encr->encr_key), ibuf_data(nonce), enc) != 1)
+ goto done;
+ } else
+ if (EVP_CipherInit_ex(encr->encr_ctx, NULL, NULL,
+    ibuf_data(encr->encr_key), ibuf_data(encr->encr_iv), enc) != 1)
+ return (-1);
  EVP_CIPHER_CTX_set_padding(encr->encr_ctx, 0);
- return (0);
+ ret = 0;
+ done:
+ ibuf_free(nonce);
+ return (ret);
 }
 
 int
@@ -426,6 +510,20 @@ int
 cipher_init_decrypt(struct iked_cipher *encr)
 {
  return (cipher_init(encr, 0));
+}
+
+void
+cipher_aad(struct iked_cipher *encr, void *in, size_t inlen,
+    size_t *outlen)
+{
+ int olen = 0;
+
+ if (EVP_CipherUpdate(encr->encr_ctx, NULL, &olen, in, inlen) != 1) {
+ ca_sslerror(__func__);
+ *outlen = 0;
+ return;
+ }
+ *outlen = (size_t)olen;
 }
 
 int
Index: iked.conf.5
===================================================================
RCS file: /cvs/src/sbin/iked/iked.conf.5,v
retrieving revision 1.68
diff -u -p -r1.68 iked.conf.5
--- iked.conf.5 1 May 2020 17:44:02 -0000 1.68
+++ iked.conf.5 14 May 2020 23:55:13 -0000
@@ -861,12 +861,15 @@ keyword:
 .It Li aes-128 Ta "128 bits" Ta ""
 .It Li aes-192 Ta "192 bits" Ta ""
 .It Li aes-256 Ta "256 bits" Ta ""
+.It Li aes-128-gcm Ta "160 bits" Ta ""
+.It Li aes-192-gcm Ta "224 bits" Ta ""
+.It Li aes-256-gcm Ta "288 bits" Ta ""
+.It Li aes-128-gcm-12 Ta "160 bits" Ta "[IKE only]"
+.It Li aes-192-gcm-12 Ta "224 bits" Ta "[IKE only]"
+.It Li aes-256-gcm-12 Ta "288 bits" Ta "[IKE only]"
 .It Li aes-128-ctr Ta "160 bits" Ta "[ESP only]"
 .It Li aes-192-ctr Ta "224 bits" Ta "[ESP only]"
 .It Li aes-256-ctr Ta "288 bits" Ta "[ESP only]"
-.It Li aes-128-gcm Ta "160 bits" Ta "[ESP only]"
-.It Li aes-192-gcm Ta "224 bits" Ta "[ESP only]"
-.It Li aes-256-gcm Ta "288 bits" Ta "[ESP only]"
 .It Li blowfish Ta "160 bits" Ta "[ESP only]"
 .It Li cast Ta "128 bits" Ta "[ESP only]"
 .It Li chacha20-poly1305 Ta "288 bits" Ta "[ESP only]"
Index: iked.h
===================================================================
RCS file: /cvs/src/sbin/iked/iked.h,v
retrieving revision 1.150
diff -u -p -r1.150 iked.h
--- iked.h 14 May 2020 15:08:30 -0000 1.150
+++ iked.h 14 May 2020 23:55:13 -0000
@@ -309,6 +309,7 @@ struct iked_hash {
  size_t hash_length; /* Output length */
  size_t hash_trunc; /* Truncate the output length */
  struct iked_hash *hash_prf; /* PRF pointer */
+ int hash_isaead;
 };
 
 struct iked_cipher {
@@ -321,6 +322,8 @@ struct iked_cipher {
  struct ibuf *encr_iv; /* Initialization Vector */
  size_t encr_ivlength; /* IV length */
  size_t encr_length; /* Block length */
+ size_t encr_saltlength; /* IV salt length */
+ uint16_t encr_authid; /* ID of associated authentication */
 };
 
 struct iked_dsa {
@@ -841,10 +844,13 @@ struct ibuf *
  cipher_setkey(struct iked_cipher *, void *, size_t);
 struct ibuf *
  cipher_setiv(struct iked_cipher *, void *, size_t);
+int cipher_settag(struct iked_cipher *, uint8_t *, size_t);
+int cipher_gettag(struct iked_cipher *, uint8_t *, size_t);
 void cipher_free(struct iked_cipher *);
 int cipher_init(struct iked_cipher *, int);
 int cipher_init_encrypt(struct iked_cipher *);
 int cipher_init_decrypt(struct iked_cipher *);
+void cipher_aad(struct iked_cipher *, void *, size_t, size_t *);
 int cipher_update(struct iked_cipher *, void *, size_t, void *, size_t *);
 int cipher_final(struct iked_cipher *);
 size_t cipher_length(struct iked_cipher *);
@@ -933,7 +939,8 @@ int ikev2_msg_send(struct iked *, struc
 int ikev2_msg_send_encrypt(struct iked *, struct iked_sa *,
     struct ibuf **, uint8_t, uint8_t, int);
 struct ibuf
- *ikev2_msg_encrypt(struct iked *, struct iked_sa *, struct ibuf *);
+ *ikev2_msg_encrypt(struct iked *, struct iked_sa *, struct ibuf *,
+    struct ibuf *);
 struct ibuf *
  ikev2_msg_decrypt(struct iked *, struct iked_sa *,
     struct ibuf *, struct ibuf *);
@@ -1129,5 +1136,6 @@ void print_policy(struct iked_policy *)
 size_t keylength_xf(unsigned int, unsigned int, unsigned int);
 size_t noncelength_xf(unsigned int, unsigned int);
 int cmdline_symset(char *);
+int encxf_noauth(unsigned int);
 
 #endif /* IKED_H */
Index: ikev2.c
===================================================================
RCS file: /cvs/src/sbin/iked/ikev2.c,v
retrieving revision 1.225
diff -u -p -r1.225 ikev2.c
--- ikev2.c 11 May 2020 20:11:35 -0000 1.225
+++ ikev2.c 14 May 2020 23:55:14 -0000
@@ -4473,7 +4473,7 @@ ikev2_send_informational(struct iked *en
  goto done;
 
  /* Encrypt message and add as an E payload */
- if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) {
+ if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) {
  log_debug("%s: encryption failed", __func__);
  goto done;
  }
@@ -4666,6 +4666,16 @@ ikev2_sa_initiator(struct iked *env, str
  }
  }
 
+ /* For AEAD ciphers integrity is implicit */
+ if (sa->sa_encr->encr_authid && sa->sa_integr == NULL) {
+ if ((sa->sa_integr = hash_new(IKEV2_XFORMTYPE_INTEGR,
+    sa->sa_encr->encr_authid)) == NULL) {
+ log_info("%s: failed to get AEAD integr",
+    SPI_SA(sa, __func__));
+ return (-1);
+ }
+ }
+
  if (sa->sa_prf == NULL) {
  if ((xform = config_findtransform(&sa->sa_proposals,
     IKEV2_XFORMTYPE_PRF, 0)) == NULL) {
@@ -4840,6 +4850,16 @@ ikev2_sa_responder(struct iked *env, str
  }
  }
 
+ /* For AEAD ciphers integrity is implicit */
+ if (sa->sa_encr->encr_authid && sa->sa_integr == NULL) {
+ if ((sa->sa_integr = hash_new(IKEV2_XFORMTYPE_INTEGR,
+    sa->sa_encr->encr_authid)) == NULL) {
+ log_info("%s: failed to get AEAD integr",
+    SPI_SA(sa, __func__));
+ return (-1);
+ }
+ }
+
  if (sa->sa_prf == NULL) {
  if ((xform = config_findtransform(&sa->sa_proposals,
     IKEV2_XFORMTYPE_PRF, 0)) == NULL) {
@@ -4883,6 +4903,7 @@ ikev2_sa_keys(struct iked *env, struct i
  size_t nonceminlen, ilen, rlen, tmplen;
  uint64_t ispi, rspi;
  int ret = -1;
+ int isaead = 0;
 
  ninr = dhsecret = skeyseed = s = t = NULL;
 
@@ -4895,6 +4916,9 @@ ikev2_sa_keys(struct iked *env, struct i
  return (-1);
  }
 
+ /* For AEADs no auth keys are required (see RFC 5282) */
+ isaead = !!integr->hash_isaead;
+
  if (prf->hash_fixedkey)
  nonceminlen = prf->hash_fixedkey;
  else
@@ -5026,13 +5050,13 @@ ikev2_sa_keys(struct iked *env, struct i
  * Get the size of the key material we need and the number
  * of rounds we need to run the prf+ function.
  */
- ilen = hash_length(prf) + /* SK_d */
-    hash_keylength(integr) + /* SK_ai */
-    hash_keylength(integr) + /* SK_ar */
-    cipher_keylength(encr) + /* SK_ei */
-    cipher_keylength(encr) + /* SK_er */
-    hash_keylength(prf) + /* SK_pi */
-    hash_keylength(prf); /* SK_pr */
+ ilen = hash_length(prf) + /* SK_d */
+    (isaead ? 0 : hash_keylength(integr)) + /* SK_ai */
+    (isaead ? 0 : hash_keylength(integr)) + /* SK_ar */
+    cipher_keylength(encr) + /* SK_ei */
+    cipher_keylength(encr) + /* SK_er */
+    hash_keylength(prf) + /* SK_pi */
+    hash_keylength(prf); /* SK_pr */
 
  if ((t = ikev2_prfplus(prf, skeyseed, s, ilen)) == NULL) {
  log_info("%s: failed to get IKE SA key material",
@@ -5042,8 +5066,10 @@ ikev2_sa_keys(struct iked *env, struct i
 
  /* ibuf_get() returns a new buffer from the next read offset */
  if ((sa->sa_key_d = ibuf_get(t, hash_length(prf))) == NULL ||
-    (sa->sa_key_iauth = ibuf_get(t, hash_keylength(integr))) == NULL ||
-    (sa->sa_key_rauth = ibuf_get(t, hash_keylength(integr))) == NULL ||
+    (!isaead &&
+    (sa->sa_key_iauth = ibuf_get(t, hash_keylength(integr))) == NULL) ||
+    (!isaead &&
+    (sa->sa_key_rauth = ibuf_get(t, hash_keylength(integr))) == NULL) ||
     (sa->sa_key_iencr = ibuf_get(t, cipher_keylength(encr))) == NULL ||
     (sa->sa_key_rencr = ibuf_get(t, cipher_keylength(encr))) == NULL ||
     (sa->sa_key_iprf = ibuf_get(t, hash_length(prf))) == NULL ||
@@ -5055,12 +5081,16 @@ ikev2_sa_keys(struct iked *env, struct i
  log_debug("%s: SK_d with %zu bytes", __func__,
     ibuf_length(sa->sa_key_d));
  print_hex(sa->sa_key_d->buf, 0, ibuf_length(sa->sa_key_d));
- log_debug("%s: SK_ai with %zu bytes", __func__,
-    ibuf_length(sa->sa_key_iauth));
- print_hex(sa->sa_key_iauth->buf, 0, ibuf_length(sa->sa_key_iauth));
- log_debug("%s: SK_ar with %zu bytes", __func__,
-    ibuf_length(sa->sa_key_rauth));
- print_hex(sa->sa_key_rauth->buf, 0, ibuf_length(sa->sa_key_rauth));
+ if (!isaead) {
+ log_debug("%s: SK_ai with %zu bytes", __func__,
+    ibuf_length(sa->sa_key_iauth));
+ print_hex(sa->sa_key_iauth->buf, 0,
+    ibuf_length(sa->sa_key_iauth));
+ log_debug("%s: SK_ar with %zu bytes", __func__,
+    ibuf_length(sa->sa_key_rauth));
+ print_hex(sa->sa_key_rauth->buf, 0,
+    ibuf_length(sa->sa_key_rauth));
+ }
  log_debug("%s: SK_ei with %zu bytes", __func__,
     ibuf_length(sa->sa_key_iencr));
  print_hex(sa->sa_key_iencr->buf, 0, ibuf_length(sa->sa_key_iencr));
Index: ikev2.h
===================================================================
RCS file: /cvs/src/sbin/iked/ikev2.h,v
retrieving revision 1.32
diff -u -p -r1.32 ikev2.h
--- ikev2.h 28 Apr 2020 15:18:52 -0000 1.32
+++ ikev2.h 14 May 2020 23:55:14 -0000
@@ -224,6 +224,9 @@ extern struct iked_constmap ikev2_xformp
 #define IKEV2_XFORMAUTH_HMAC_SHA2_256_128 12 /* RFC4868 */
 #define IKEV2_XFORMAUTH_HMAC_SHA2_384_192 13 /* RFC4868 */
 #define IKEV2_XFORMAUTH_HMAC_SHA2_512_256 14 /* RFC4868 */
+#define IKEV2_XFORMAUTH_AES_GCM_8 2018 /* private */
+#define IKEV2_XFORMAUTH_AES_GCM_12 2019 /* private */
+#define IKEV2_XFORMAUTH_AES_GCM_16 2020 /* private */
 
 extern struct iked_constmap ikev2_xformauth_map[];
 
Index: ikev2_msg.c
===================================================================
RCS file: /cvs/src/sbin/iked/ikev2_msg.c,v
retrieving revision 1.67
diff -u -p -r1.67 ikev2_msg.c
--- ikev2_msg.c 14 May 2020 15:08:30 -0000 1.67
+++ ikev2_msg.c 14 May 2020 23:55:14 -0000
@@ -51,6 +51,8 @@ void ikev2_msg_retransmit_timeout(struc
 int ikev2_check_frag_oversize(struct iked_sa *sa, struct ibuf *buf);
 int ikev2_send_encrypted_fragments(struct iked *env, struct iked_sa *sa,
     struct ibuf *in,uint8_t exchange, uint8_t firstpayload, int response);
+int ikev2_msg_encrypt_prepare(struct iked_sa *, struct ikev2_payload *,
+    struct ibuf*, struct ibuf *, struct ike_header *, uint8_t, int);
 
 void
 ikev2_msg_cb(int fd, short event, void *arg)
@@ -329,8 +331,45 @@ ikev2_msg_id(struct iked *env, struct ik
  return (id);
 }
 
+/*
+ * Calculate the final sizes of the IKEv2 header and the encrypted payload
+ * header.  This must be done before encryption to make sure the correct
+ * headers are authenticated.
+ */
+int
+ikev2_msg_encrypt_prepare(struct iked_sa *sa, struct ikev2_payload *pld,
+    struct ibuf *buf, struct ibuf *e, struct ike_header *hdr,
+    uint8_t firstpayload, int fragmentation)
+{
+ size_t len, ivlen, encrlen, integrlen, blocklen, pldlen, outlen;
+
+ if (sa == NULL ||
+    sa->sa_encr == NULL ||
+    sa->sa_integr == NULL) {
+ log_debug("%s: invalid SA", __func__);
+ return (-1);
+ }
+
+ len = ibuf_size(e);
+ blocklen = cipher_length(sa->sa_encr);
+ integrlen = hash_length(sa->sa_integr);
+ ivlen = cipher_ivlength(sa->sa_encr);
+ encrlen = roundup(len + 1, blocklen);
+ outlen = cipher_outlength(sa->sa_encr, encrlen);
+ pldlen = ivlen + outlen + integrlen +
+    (fragmentation ? sizeof(struct ikev2_frag_payload) : 0);
+
+ if (ikev2_next_payload(pld, pldlen, firstpayload) == -1)
+ return (-1);
+ if (ikev2_set_header(hdr, ibuf_size(buf) + pldlen - sizeof(*hdr)) == -1)
+ return (-1);
+
+ return (0);
+}
+
 struct ibuf *
-ikev2_msg_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf *src)
+ikev2_msg_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf *src,
+    struct ibuf *aad)
 {
  size_t len, ivlen, encrlen, integrlen, blocklen,
     outlen;
@@ -390,12 +429,23 @@ ikev2_msg_encrypt(struct iked *env, stru
  goto done;
 
  outlen = ibuf_size(out);
+
+ /* Add AAD for AEAD ciphers */
+ if (sa->sa_integr->hash_isaead)
+ cipher_aad(sa->sa_encr, ibuf_data(aad),
+    ibuf_length(aad), &outlen);
+
  if (cipher_update(sa->sa_encr, ibuf_data(src), encrlen,
     ibuf_data(out), &outlen) == -1) {
  log_info("%s: error updating cipher.", __func__);
  goto done;
  }
 
+ if (cipher_final(sa->sa_encr) == -1) {
+ log_info("%s: encryption failed.", __func__);
+ goto done;
+ }
+
  if (outlen && ibuf_add(dst, ibuf_data(out), outlen) != 0)
  goto done;
 
@@ -429,18 +479,13 @@ ikev2_msg_integr(struct iked *env, struc
  print_hex(ibuf_data(src), 0, ibuf_size(src));
 
  if (sa == NULL ||
+    sa->sa_encr == NULL ||
     sa->sa_integr == NULL) {
  log_debug("%s: invalid SA", __func__);
  return (-1);
  }
 
- if (sa->sa_hdr.sh_initiator)
- integr = sa->sa_key_iauth;
- else
- integr = sa->sa_key_rauth;
-
  integrlen = hash_length(sa->sa_integr);
-
  log_debug("%s: integrity checksum length %zu", __func__,
     integrlen);
 
@@ -450,21 +495,33 @@ ikev2_msg_integr(struct iked *env, struc
  if ((tmp = ibuf_new(NULL, hash_keylength(sa->sa_integr))) == NULL)
  goto done;
 
- hash_setkey(sa->sa_integr, ibuf_data(integr), ibuf_size(integr));
- hash_init(sa->sa_integr);
- hash_update(sa->sa_integr, ibuf_data(src),
-    ibuf_size(src) - integrlen);
- hash_final(sa->sa_integr, ibuf_data(tmp), &tmplen);
+ if (!sa->sa_integr->hash_isaead) {
+ if (sa->sa_hdr.sh_initiator)
+ integr = sa->sa_key_iauth;
+ else
+ integr = sa->sa_key_rauth;
+
+ hash_setkey(sa->sa_integr, ibuf_data(integr),
+    ibuf_size(integr));
+ hash_init(sa->sa_integr);
+ hash_update(sa->sa_integr, ibuf_data(src),
+    ibuf_size(src) - integrlen);
+ hash_final(sa->sa_integr, ibuf_data(tmp), &tmplen);
 
- if (tmplen != integrlen) {
- log_debug("%s: hash failure", __func__);
- goto done;
+ if (tmplen != integrlen) {
+ log_debug("%s: hash failure", __func__);
+ goto done;
+ }
+ } else {
+ /* Append AEAD tag */
+ if (cipher_gettag(sa->sa_encr, ibuf_data(tmp), ibuf_size(tmp)))
+ goto done;
  }
 
  if ((ptr = ibuf_seek(src,
     ibuf_size(src) - integrlen, integrlen)) == NULL)
  goto done;
- memcpy(ptr, ibuf_data(tmp), tmplen);
+ memcpy(ptr, ibuf_data(tmp), integrlen);
 
  print_hex(ibuf_data(tmp), 0, ibuf_size(tmp));
 
@@ -481,7 +538,7 @@ ikev2_msg_decrypt(struct iked *env, stru
 {
  ssize_t ivlen, encrlen, integrlen, blocklen,
     outlen, tmplen;
- uint8_t pad = 0, *ptr;
+ uint8_t pad = 0, *ptr, *integrdata;
  struct ibuf *integr, *encr, *tmp = NULL, *out = NULL;
  off_t ivoff, encroff, integroff;
 
@@ -524,25 +581,30 @@ ikev2_msg_decrypt(struct iked *env, stru
  /*
  * Validate packet checksum
  */
- if ((tmp = ibuf_new(NULL, ibuf_length(integr))) == NULL)
- goto done;
+ if (!sa->sa_integr->hash_isaead) {
+ if ((tmp = ibuf_new(NULL, ibuf_length(integr))) == NULL)
+ goto done;
 
- hash_setkey(sa->sa_integr, integr->buf, ibuf_length(integr));
- hash_init(sa->sa_integr);
- hash_update(sa->sa_integr, ibuf_data(msg),
-    ibuf_size(msg) - integrlen);
- hash_final(sa->sa_integr, tmp->buf, &tmplen);
+ hash_setkey(sa->sa_integr, integr->buf, ibuf_length(integr));
+ hash_init(sa->sa_integr);
+ hash_update(sa->sa_integr, ibuf_data(msg),
+    ibuf_size(msg) - integrlen);
+ hash_final(sa->sa_integr, tmp->buf, &tmplen);
 
- if (memcmp(tmp->buf, ibuf_data(src) + integroff, integrlen) != 0) {
- log_debug("%s: integrity check failed", __func__);
- goto done;
- }
+ integrdata = ibuf_seek(src, integroff, integrlen);
+ if (integrdata == NULL)
+ goto done;
+ if (memcmp(tmp->buf, integrdata, integrlen) != 0) {
+ log_debug("%s: integrity check failed", __func__);
+ goto done;
+ }
 
- log_debug("%s: integrity check succeeded", __func__);
- print_hex(tmp->buf, 0, tmplen);
+ log_debug("%s: integrity check succeeded", __func__);
+ print_hex(tmp->buf, 0, tmplen);
 
- ibuf_release(tmp);
- tmp = NULL;
+ ibuf_release(tmp);
+ tmp = NULL;
+ }
 
  /*
  * Decrypt the payload and strip any padding
@@ -559,10 +621,31 @@ ikev2_msg_decrypt(struct iked *env, stru
  goto done;
  }
 
+ /* Set AEAD tag */
+ if (sa->sa_integr->hash_isaead) {
+ integrdata = ibuf_seek(src, integroff, integrlen);
+ if (integrdata == NULL)
+ goto done;
+ if (cipher_settag(sa->sa_encr, integrdata, integrlen)) {
+ log_info("%s: failed to set tag.", __func__);
+ goto done;
+ }
+ }
+
  if ((out = ibuf_new(NULL, cipher_outlength(sa->sa_encr,
     encrlen))) == NULL)
  goto done;
 
+ /*
+ * Add additional authenticated data for AEAD ciphers
+ */
+ if (sa->sa_integr->hash_isaead) {
+ log_debug("%s: AAD length %zu", __func__, ibuf_length(msg) - ibuf_length(src));
+ print_hex(ibuf_data(msg), 0, ibuf_length(msg) - ibuf_length(src));
+ cipher_aad(sa->sa_encr, ibuf_data(msg),
+    ibuf_length(msg) - ibuf_length(src), &outlen);
+ }
+
  if ((outlen = ibuf_length(out)) != 0) {
  if (cipher_update(sa->sa_encr, ibuf_data(src) + encroff,
     encrlen, ibuf_data(out), &outlen) == -1) {
@@ -574,6 +657,11 @@ ikev2_msg_decrypt(struct iked *env, stru
  pad = *ptr;
  }
 
+ if (cipher_final(sa->sa_encr) == -1) {
+ log_info("%s: decryption failed.", __func__);
+ goto done;
+ }
+
  log_debug("%s: decrypted payload length %zd/%zd padding %d",
     __func__, outlen, encrlen, pad);
  print_hex(ibuf_data(out), 0, ibuf_size(out));
@@ -598,6 +686,13 @@ ikev2_check_frag_oversize(struct iked_sa
  size_t max;
  size_t ivlen, integrlen, blocklen;
 
+ if (sa == NULL ||
+    sa->sa_encr == NULL ||
+    sa->sa_integr == NULL) {
+ log_debug("%s: invalid SA", __func__);
+ return (-1);
+ }
+
  sa_fam = ((struct sockaddr *)&sa->sa_local.addr)->sa_family;
 
  max = sa_fam == AF_INET ? IKEV2_MAXLEN_IPV4_FRAG
@@ -642,18 +737,16 @@ ikev2_msg_send_encrypt(struct iked *env,
  if ((pld = ikev2_add_payload(buf)) == NULL)
  goto done;
 
+ if (ikev2_msg_encrypt_prepare(sa, pld, buf, e, hdr, firstpayload, 0) == -1)
+ goto done;
+
  /* Encrypt message and add as an E payload */
- if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) {
+ if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) {
  log_debug("%s: encryption failed", __func__);
  goto done;
  }
  if (ibuf_cat(buf, e) != 0)
  goto done;
- if (ikev2_next_payload(pld, ibuf_size(e), firstpayload) == -1)
- goto done;
-
- if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1)
- goto done;
 
  /* Add integrity checksum (HMAC) */
  if (ikev2_msg_integr(env, sa, buf) != 0) {
@@ -694,6 +787,13 @@ ikev2_send_encrypted_fragments(struct ik
  uint32_t msgid;
  int ret = -1;
 
+ if (sa == NULL ||
+    sa->sa_encr == NULL ||
+    sa->sa_integr == NULL) {
+ log_debug("%s: invalid SA", __func__);
+ goto done;
+ }
+
  sa_fam = ((struct sockaddr *)&sa->sa_local.addr)->sa_family;
 
  left = ibuf_length(in);
@@ -743,18 +843,16 @@ ikev2_send_encrypted_fragments(struct ik
  if ((e = ibuf_new(data, MIN(left, max_len))) == NULL) {
  goto done;
  }
- if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) {
+
+ if (ikev2_msg_encrypt_prepare(sa, pld, buf, e, hdr,
+    firstpayload, 1) == -1)
+ goto done;
+
+ if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) {
  log_debug("%s: encryption failed", __func__);
  goto done;
  }
  if (ibuf_cat(buf, e) != 0)
- goto done;
-
- if (ikev2_next_payload(pld, ibuf_size(e) + sizeof(*frag),
-    firstpayload) == -1)
- goto done;
-
- if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1)
  goto done;
 
  /* Add integrity checksum (HMAC) */
Index: parse.y
===================================================================
RCS file: /cvs/src/sbin/iked/parse.y,v
retrieving revision 1.99
diff -u -p -r1.99 parse.y
--- parse.y 30 Apr 2020 21:11:13 -0000 1.99
+++ parse.y 14 May 2020 23:55:14 -0000
@@ -198,6 +198,12 @@ const struct ipsec_xf ikeencxfs[] = {
  { "aes-128", IKEV2_XFORMENCR_AES_CBC, 16, 16 },
  { "aes-192", IKEV2_XFORMENCR_AES_CBC, 24, 24 },
  { "aes-256", IKEV2_XFORMENCR_AES_CBC, 32, 32 },
+ { "aes-128-gcm", IKEV2_XFORMENCR_AES_GCM_16, 16, 16, 4, 1 },
+ { "aes-192-gcm", IKEV2_XFORMENCR_AES_GCM_16, 24, 24, 4, 1 },
+ { "aes-256-gcm", IKEV2_XFORMENCR_AES_GCM_16, 32, 32, 4, 1 },
+ { "aes-128-gcm-12", IKEV2_XFORMENCR_AES_GCM_12, 16, 16, 4, 1 },
+ { "aes-192-gcm-12", IKEV2_XFORMENCR_AES_GCM_12, 24, 24, 4, 1 },
+ { "aes-256-gcm-12", IKEV2_XFORMENCR_AES_GCM_12, 32, 32, 4, 1 },
  { NULL }
 };
 
@@ -2417,6 +2423,17 @@ print_xf(unsigned int id, unsigned int l
  return ("unknown");
 }
 
+int
+encxf_noauth(unsigned int id)
+{
+ int i;
+
+ for (i = 0; ikeencxfs[i].name != NULL; i++)
+ if (ikeencxfs[i].id == id)
+ return ikeencxfs[i].noauth;
+ return (0);
+}
+
 size_t
 keylength_xf(unsigned int saproto, unsigned int type, unsigned int id)
 {
@@ -2852,21 +2869,37 @@ create_ike(char *name, int af, uint8_t i
  pol.pol_nproposals++;
  } else {
  for (i = 0; i < ike_sa->nxfs; i++) {
+ noauth = 0;
+ for (j = 0; j < ike_sa->xfs[i]->nencxf; j++) {
+ if (ike_sa->xfs[i]->encxf[j]->noauth)
+ noauth++;
+ }
  if (ike_sa->xfs[i]->nesnxf) {
  yyerror("cannot use ESN with ikesa.");
  goto done;
  }
+ if (noauth && noauth != ike_sa->xfs[i]->nencxf) {
+ yyerror("cannot mix encryption transforms with "
+    "implicit and non-implicit authentication");
+ goto done;
+ }
+ if (noauth && ike_sa->xfs[i]->nauthxf) {
+ yyerror("authentication is implicit for given "
+    "encryption transforms");
+ goto done;
+ }
 
  if ((p = calloc(1, sizeof(*p))) == NULL)
  err(1, "%s", __func__);
 
  xf = NULL;
  xfi = 0;
- copy_transforms(IKEV2_XFORMTYPE_INTEGR,
-    ike_sa->xfs[i]->authxf,
-    ike_sa->xfs[i]->nauthxf, &xf, &xfi,
-    ikev2_default_ike_transforms,
-    ikev2_default_nike_transforms);
+ if (!ike_sa->xfs[i]->nencxf || !noauth)
+ copy_transforms(IKEV2_XFORMTYPE_INTEGR,
+    ike_sa->xfs[i]->authxf,
+    ike_sa->xfs[i]->nauthxf, &xf, &xfi,
+    ikev2_default_ike_transforms,
+    ikev2_default_nike_transforms);
  copy_transforms(IKEV2_XFORMTYPE_ENCR,
     ike_sa->xfs[i]->encxf,
     ike_sa->xfs[i]->nencxf, &xf, &xfi,
Index: policy.c
===================================================================
RCS file: /cvs/src/sbin/iked/policy.c,v
retrieving revision 1.62
diff -u -p -r1.62 policy.c
--- policy.c 13 May 2020 23:03:20 -0000 1.62
+++ policy.c 14 May 2020 23:55:14 -0000
@@ -774,7 +774,7 @@ proposals_match(struct iked_proposal *lo
     struct iked_transform **xforms, int rekey)
 {
  struct iked_transform *tpeer, *tlocal;
- unsigned int i, j, type, score, requiredh = 0;
+ unsigned int i, j, type, score, requiredh = 0, noauth = 0;
  uint8_t protoid = peer->prop_protoid;
  uint8_t peerxfs[IKEV2_XFORMTYPE_MAX];
 
@@ -782,8 +782,18 @@ proposals_match(struct iked_proposal *lo
 
  for (i = 0; i < peer->prop_nxforms; i++) {
  tpeer = peer->prop_xforms + i;
+ /* If any of the ENC transforms is an AEAD, ignore auth */
+ if (tpeer->xform_type == IKEV2_XFORMTYPE_ENCR &&
+    encxf_noauth(tpeer->xform_id))
+ noauth = 1;
+ }
+
+ for (i = 0; i < peer->prop_nxforms; i++) {
+ tpeer = peer->prop_xforms + i;
  if (tpeer->xform_type > IKEV2_XFORMTYPE_MAX)
  continue;
+ if (noauth && tpeer->xform_type == IKEV2_XFORMTYPE_INTEGR)
+ return (0);
 
  /*
  * Record all transform types from the peer's proposal,
@@ -832,7 +842,8 @@ proposals_match(struct iked_proposal *lo
  for (i = score = 0; i < IKEV2_XFORMTYPE_MAX; i++) {
  if (protoid == IKEV2_SAPROTO_IKE && xforms[i] == NULL &&
     (i == IKEV2_XFORMTYPE_ENCR || i == IKEV2_XFORMTYPE_PRF ||
-     i == IKEV2_XFORMTYPE_INTEGR || i == IKEV2_XFORMTYPE_DH)) {
+    (!noauth && i == IKEV2_XFORMTYPE_INTEGR) ||
+    i == IKEV2_XFORMTYPE_DH)) {
  score = 0;
  break;
  } else if (protoid == IKEV2_SAPROTO_AH && xforms[i] == NULL &&

Reply | Threaded
Open this post in threaded view
|

Re: iked(8): AES_GCM ciphers for IKE

Stephan Mending
On Fri, May 15, 2020 at 01:59:35AM +0200, Tobias Heider wrote:

> On Thu, May 14, 2020 at 10:47:52PM +0200, Tobias Heider wrote:
> > On Thu, May 14, 2020 at 10:07:30PM +0200, Tobias Heider wrote:
> > > Hi,
> > >
> > > currently iked(8) supports AES-GCM only for ESP.
> > > The diff below adds the ENCR_AES_GCM_16 and ENCR_AES_GCM_12 variants for IKE.
> > > (for more information see [1] and [2]).
> > > Both variants support the 128, 196, and 256 bit key lengths.
> > >
> > > The new new ciphers can be configured with:
> > > - aes-128-gcm, aes-196-gcm and aes-256-gcm for ENCR_AES_GCM_16
> > > - aes-128-gcm-12, aes-196-gcm-12 and aes-256-gcm-12 for ENCR_AES_GCM_12
>
> Small typo: it's 192, not 196.
>
> > >
> > > It would be nice if we could get some interop testing with different IKEv2
> > > implementations.  I have so far successfully tested strongswan <-> iked and
> > > of course iked <-> iked.
> > >
> > > Feedback welcome ;)
> > >
> > > [1] https://tools.ietf.org/html/rfc5282
> > > [2] https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml#ikev2-parameters-5
> > >
> >
> > whoops, previous diff was broken.
> >
>
> Another update because it seems parse_xf matches substrings instead of the
> full transform type name, which means I had to change the order of ikeencxfs
> members or 'aes-128-gcm' will always match 'aes-128-gcm-12' ...
>
> Index: crypto.c
> ===================================================================
> RCS file: /cvs/src/sbin/iked/crypto.c,v
> retrieving revision 1.27
> diff -u -p -r1.27 crypto.c
> --- crypto.c 14 May 2020 15:08:30 -0000 1.27
> +++ crypto.c 14 May 2020 23:55:13 -0000
> @@ -92,7 +92,7 @@ hash_new(uint8_t type, uint16_t id)
>   struct iked_hash *hash;
>   const EVP_MD *md = NULL;
>   HMAC_CTX *ctx = NULL;
> - int length = 0, fixedkey = 0, trunc = 0;
> + int length = 0, fixedkey = 0, trunc = 0, isaead = 0;
>  
>   switch (type) {
>   case IKEV2_XFORMTYPE_PRF:
> @@ -156,6 +156,14 @@ hash_new(uint8_t type, uint16_t id)
>   length = SHA512_DIGEST_LENGTH;
>   trunc = 32;
>   break;
> + case IKEV2_XFORMAUTH_AES_GCM_12:
> + length = 12;
> + isaead = 1;
> + break;
> + case IKEV2_XFORMAUTH_AES_GCM_16:
> + length = 16;
> + isaead = 1;
> + break;
>   case IKEV2_XFORMAUTH_NONE:
>   case IKEV2_XFORMAUTH_DES_MAC:
>   case IKEV2_XFORMAUTH_KPDK_MD5:
> @@ -177,7 +185,7 @@ hash_new(uint8_t type, uint16_t id)
>      print_map(id, ikev2_xformtype_map));
>   break;
>   }
> - if (md == NULL)
> + if (!isaead && md == NULL)
>   return (NULL);
>  
>   if ((hash = calloc(1, sizeof(*hash))) == NULL) {
> @@ -192,6 +200,10 @@ hash_new(uint8_t type, uint16_t id)
>   hash->hash_trunc = trunc;
>   hash->hash_length = length;
>   hash->hash_fixedkey = fixedkey;
> + hash->hash_isaead = isaead;
> +
> + if (isaead)
> + return (hash);
>  
>   if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
>   log_debug("%s: alloc hash ctx", __func__);
> @@ -276,6 +288,7 @@ cipher_new(uint8_t type, uint16_t id, ui
>   const EVP_CIPHER *cipher = NULL;
>   EVP_CIPHER_CTX *ctx = NULL;
>   int length = 0, fixedkey = 0, ivlength = 0;
> + int saltlength = 0, authid = 0;
>  
>   switch (type) {
>   case IKEV2_XFORMTYPE_ENCR:
> @@ -309,6 +322,39 @@ cipher_new(uint8_t type, uint16_t id, ui
>   ivlength = EVP_CIPHER_iv_length(cipher);
>   fixedkey = EVP_CIPHER_key_length(cipher);
>   break;
> + case IKEV2_XFORMENCR_AES_GCM_16:
> + case IKEV2_XFORMENCR_AES_GCM_12:
> + switch (id_length) {
> + case 128:
> + cipher = EVP_aes_128_gcm();
> + break;
> + case 192:
> + cipher = EVP_aes_192_gcm();
> + break;
> + case 256:
> + cipher = EVP_aes_256_gcm();
> + break;
> + default:
> + log_debug("%s: invalid key length %d"
> +    " for cipher %s", __func__, id_length,
> +    print_map(id, ikev2_xformencr_map));
> + break;
> + }
> + if (cipher == NULL)
> + break;
> + switch(id) {
> + case IKEV2_XFORMENCR_AES_GCM_16:
> + authid = IKEV2_XFORMAUTH_AES_GCM_16;
> + break;
> + case IKEV2_XFORMENCR_AES_GCM_12:
> + authid = IKEV2_XFORMAUTH_AES_GCM_12;
> + break;
> + }
> + length = EVP_CIPHER_block_size(cipher);
> + ivlength = 8;
> + saltlength = 4;
> + fixedkey = EVP_CIPHER_key_length(cipher) + saltlength;
> + break;
>   case IKEV2_XFORMENCR_DES_IV64:
>   case IKEV2_XFORMENCR_DES:
>   case IKEV2_XFORMENCR_RC5:
> @@ -346,6 +392,8 @@ cipher_new(uint8_t type, uint16_t id, ui
>   encr->encr_length = length;
>   encr->encr_fixedkey = fixedkey;
>   encr->encr_ivlength = ivlength ? ivlength : length;
> + encr->encr_saltlength = saltlength;
> + encr->encr_authid = authid;
>  
>   if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
>   log_debug("%s: alloc cipher ctx", __func__);
> @@ -392,6 +440,20 @@ cipher_setiv(struct iked_cipher *encr, v
>   return (encr->encr_iv);
>  }
>  
> +int
> +cipher_settag(struct iked_cipher *encr, uint8_t *data, size_t len)
> +{
> + return (EVP_CIPHER_CTX_ctrl(encr->encr_ctx,
> +    EVP_CTRL_GCM_SET_TAG, len, data) != 1);
> +}
> +
> +int
> +cipher_gettag(struct iked_cipher *encr, uint8_t *data, size_t len)
> +{
> + return (EVP_CIPHER_CTX_ctrl(encr->encr_ctx,
> +    EVP_CTRL_GCM_GET_TAG, len, data) != 1);
> +}
> +
>  void
>  cipher_free(struct iked_cipher *encr)
>  {
> @@ -409,11 +471,33 @@ cipher_free(struct iked_cipher *encr)
>  int
>  cipher_init(struct iked_cipher *encr, int enc)
>  {
> + struct ibuf *nonce = NULL;
> + int ret = -1;
> +
>   if (EVP_CipherInit_ex(encr->encr_ctx, encr->encr_priv, NULL,
> -    ibuf_data(encr->encr_key), ibuf_data(encr->encr_iv), enc) != 1)
> +    NULL, NULL, enc) != 1)
>   return (-1);
> + if (encr->encr_saltlength > 0) {
> + /* For AEADs the nonce is salt + IV  (see RFC5282) */
> + nonce = ibuf_new(ibuf_data(encr->encr_key) +
> +    ibuf_size(encr->encr_key) - encr->encr_saltlength,
> +    encr->encr_saltlength);
> + if (nonce == NULL)
> + return (-1);
> + if (ibuf_add(nonce, ibuf_data(encr->encr_iv) , ibuf_size(encr->encr_iv)) != 0)
> + goto done;
> + if (EVP_CipherInit_ex(encr->encr_ctx, NULL, NULL,
> +    ibuf_data(encr->encr_key), ibuf_data(nonce), enc) != 1)
> + goto done;
> + } else
> + if (EVP_CipherInit_ex(encr->encr_ctx, NULL, NULL,
> +    ibuf_data(encr->encr_key), ibuf_data(encr->encr_iv), enc) != 1)
> + return (-1);
>   EVP_CIPHER_CTX_set_padding(encr->encr_ctx, 0);
> - return (0);
> + ret = 0;
> + done:
> + ibuf_free(nonce);
> + return (ret);
>  }
>  
>  int
> @@ -426,6 +510,20 @@ int
>  cipher_init_decrypt(struct iked_cipher *encr)
>  {
>   return (cipher_init(encr, 0));
> +}
> +
> +void
> +cipher_aad(struct iked_cipher *encr, void *in, size_t inlen,
> +    size_t *outlen)
> +{
> + int olen = 0;
> +
> + if (EVP_CipherUpdate(encr->encr_ctx, NULL, &olen, in, inlen) != 1) {
> + ca_sslerror(__func__);
> + *outlen = 0;
> + return;
> + }
> + *outlen = (size_t)olen;
>  }
>  
>  int
> Index: iked.conf.5
> ===================================================================
> RCS file: /cvs/src/sbin/iked/iked.conf.5,v
> retrieving revision 1.68
> diff -u -p -r1.68 iked.conf.5
> --- iked.conf.5 1 May 2020 17:44:02 -0000 1.68
> +++ iked.conf.5 14 May 2020 23:55:13 -0000
> @@ -861,12 +861,15 @@ keyword:
>  .It Li aes-128 Ta "128 bits" Ta ""
>  .It Li aes-192 Ta "192 bits" Ta ""
>  .It Li aes-256 Ta "256 bits" Ta ""
> +.It Li aes-128-gcm Ta "160 bits" Ta ""
> +.It Li aes-192-gcm Ta "224 bits" Ta ""
> +.It Li aes-256-gcm Ta "288 bits" Ta ""
> +.It Li aes-128-gcm-12 Ta "160 bits" Ta "[IKE only]"
> +.It Li aes-192-gcm-12 Ta "224 bits" Ta "[IKE only]"
> +.It Li aes-256-gcm-12 Ta "288 bits" Ta "[IKE only]"
>  .It Li aes-128-ctr Ta "160 bits" Ta "[ESP only]"
>  .It Li aes-192-ctr Ta "224 bits" Ta "[ESP only]"
>  .It Li aes-256-ctr Ta "288 bits" Ta "[ESP only]"
> -.It Li aes-128-gcm Ta "160 bits" Ta "[ESP only]"
> -.It Li aes-192-gcm Ta "224 bits" Ta "[ESP only]"
> -.It Li aes-256-gcm Ta "288 bits" Ta "[ESP only]"
>  .It Li blowfish Ta "160 bits" Ta "[ESP only]"
>  .It Li cast Ta "128 bits" Ta "[ESP only]"
>  .It Li chacha20-poly1305 Ta "288 bits" Ta "[ESP only]"
> Index: iked.h
> ===================================================================
> RCS file: /cvs/src/sbin/iked/iked.h,v
> retrieving revision 1.150
> diff -u -p -r1.150 iked.h
> --- iked.h 14 May 2020 15:08:30 -0000 1.150
> +++ iked.h 14 May 2020 23:55:13 -0000
> @@ -309,6 +309,7 @@ struct iked_hash {
>   size_t hash_length; /* Output length */
>   size_t hash_trunc; /* Truncate the output length */
>   struct iked_hash *hash_prf; /* PRF pointer */
> + int hash_isaead;
>  };
>  
>  struct iked_cipher {
> @@ -321,6 +322,8 @@ struct iked_cipher {
>   struct ibuf *encr_iv; /* Initialization Vector */
>   size_t encr_ivlength; /* IV length */
>   size_t encr_length; /* Block length */
> + size_t encr_saltlength; /* IV salt length */
> + uint16_t encr_authid; /* ID of associated authentication */
>  };
>  
>  struct iked_dsa {
> @@ -841,10 +844,13 @@ struct ibuf *
>   cipher_setkey(struct iked_cipher *, void *, size_t);
>  struct ibuf *
>   cipher_setiv(struct iked_cipher *, void *, size_t);
> +int cipher_settag(struct iked_cipher *, uint8_t *, size_t);
> +int cipher_gettag(struct iked_cipher *, uint8_t *, size_t);
>  void cipher_free(struct iked_cipher *);
>  int cipher_init(struct iked_cipher *, int);
>  int cipher_init_encrypt(struct iked_cipher *);
>  int cipher_init_decrypt(struct iked_cipher *);
> +void cipher_aad(struct iked_cipher *, void *, size_t, size_t *);
>  int cipher_update(struct iked_cipher *, void *, size_t, void *, size_t *);
>  int cipher_final(struct iked_cipher *);
>  size_t cipher_length(struct iked_cipher *);
> @@ -933,7 +939,8 @@ int ikev2_msg_send(struct iked *, struc
>  int ikev2_msg_send_encrypt(struct iked *, struct iked_sa *,
>      struct ibuf **, uint8_t, uint8_t, int);
>  struct ibuf
> - *ikev2_msg_encrypt(struct iked *, struct iked_sa *, struct ibuf *);
> + *ikev2_msg_encrypt(struct iked *, struct iked_sa *, struct ibuf *,
> +    struct ibuf *);
>  struct ibuf *
>   ikev2_msg_decrypt(struct iked *, struct iked_sa *,
>      struct ibuf *, struct ibuf *);
> @@ -1129,5 +1136,6 @@ void print_policy(struct iked_policy *)
>  size_t keylength_xf(unsigned int, unsigned int, unsigned int);
>  size_t noncelength_xf(unsigned int, unsigned int);
>  int cmdline_symset(char *);
> +int encxf_noauth(unsigned int);
>  
>  #endif /* IKED_H */
> Index: ikev2.c
> ===================================================================
> RCS file: /cvs/src/sbin/iked/ikev2.c,v
> retrieving revision 1.225
> diff -u -p -r1.225 ikev2.c
> --- ikev2.c 11 May 2020 20:11:35 -0000 1.225
> +++ ikev2.c 14 May 2020 23:55:14 -0000
> @@ -4473,7 +4473,7 @@ ikev2_send_informational(struct iked *en
>   goto done;
>  
>   /* Encrypt message and add as an E payload */
> - if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) {
> + if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) {
>   log_debug("%s: encryption failed", __func__);
>   goto done;
>   }
> @@ -4666,6 +4666,16 @@ ikev2_sa_initiator(struct iked *env, str
>   }
>   }
>  
> + /* For AEAD ciphers integrity is implicit */
> + if (sa->sa_encr->encr_authid && sa->sa_integr == NULL) {
> + if ((sa->sa_integr = hash_new(IKEV2_XFORMTYPE_INTEGR,
> +    sa->sa_encr->encr_authid)) == NULL) {
> + log_info("%s: failed to get AEAD integr",
> +    SPI_SA(sa, __func__));
> + return (-1);
> + }
> + }
> +
>   if (sa->sa_prf == NULL) {
>   if ((xform = config_findtransform(&sa->sa_proposals,
>      IKEV2_XFORMTYPE_PRF, 0)) == NULL) {
> @@ -4840,6 +4850,16 @@ ikev2_sa_responder(struct iked *env, str
>   }
>   }
>  
> + /* For AEAD ciphers integrity is implicit */
> + if (sa->sa_encr->encr_authid && sa->sa_integr == NULL) {
> + if ((sa->sa_integr = hash_new(IKEV2_XFORMTYPE_INTEGR,
> +    sa->sa_encr->encr_authid)) == NULL) {
> + log_info("%s: failed to get AEAD integr",
> +    SPI_SA(sa, __func__));
> + return (-1);
> + }
> + }
> +
>   if (sa->sa_prf == NULL) {
>   if ((xform = config_findtransform(&sa->sa_proposals,
>      IKEV2_XFORMTYPE_PRF, 0)) == NULL) {
> @@ -4883,6 +4903,7 @@ ikev2_sa_keys(struct iked *env, struct i
>   size_t nonceminlen, ilen, rlen, tmplen;
>   uint64_t ispi, rspi;
>   int ret = -1;
> + int isaead = 0;
>  
>   ninr = dhsecret = skeyseed = s = t = NULL;
>  
> @@ -4895,6 +4916,9 @@ ikev2_sa_keys(struct iked *env, struct i
>   return (-1);
>   }
>  
> + /* For AEADs no auth keys are required (see RFC 5282) */
> + isaead = !!integr->hash_isaead;
> +
>   if (prf->hash_fixedkey)
>   nonceminlen = prf->hash_fixedkey;
>   else
> @@ -5026,13 +5050,13 @@ ikev2_sa_keys(struct iked *env, struct i
>   * Get the size of the key material we need and the number
>   * of rounds we need to run the prf+ function.
>   */
> - ilen = hash_length(prf) + /* SK_d */
> -    hash_keylength(integr) + /* SK_ai */
> -    hash_keylength(integr) + /* SK_ar */
> -    cipher_keylength(encr) + /* SK_ei */
> -    cipher_keylength(encr) + /* SK_er */
> -    hash_keylength(prf) + /* SK_pi */
> -    hash_keylength(prf); /* SK_pr */
> + ilen = hash_length(prf) + /* SK_d */
> +    (isaead ? 0 : hash_keylength(integr)) + /* SK_ai */
> +    (isaead ? 0 : hash_keylength(integr)) + /* SK_ar */
> +    cipher_keylength(encr) + /* SK_ei */
> +    cipher_keylength(encr) + /* SK_er */
> +    hash_keylength(prf) + /* SK_pi */
> +    hash_keylength(prf); /* SK_pr */
>  
>   if ((t = ikev2_prfplus(prf, skeyseed, s, ilen)) == NULL) {
>   log_info("%s: failed to get IKE SA key material",
> @@ -5042,8 +5066,10 @@ ikev2_sa_keys(struct iked *env, struct i
>  
>   /* ibuf_get() returns a new buffer from the next read offset */
>   if ((sa->sa_key_d = ibuf_get(t, hash_length(prf))) == NULL ||
> -    (sa->sa_key_iauth = ibuf_get(t, hash_keylength(integr))) == NULL ||
> -    (sa->sa_key_rauth = ibuf_get(t, hash_keylength(integr))) == NULL ||
> +    (!isaead &&
> +    (sa->sa_key_iauth = ibuf_get(t, hash_keylength(integr))) == NULL) ||
> +    (!isaead &&
> +    (sa->sa_key_rauth = ibuf_get(t, hash_keylength(integr))) == NULL) ||
>      (sa->sa_key_iencr = ibuf_get(t, cipher_keylength(encr))) == NULL ||
>      (sa->sa_key_rencr = ibuf_get(t, cipher_keylength(encr))) == NULL ||
>      (sa->sa_key_iprf = ibuf_get(t, hash_length(prf))) == NULL ||
> @@ -5055,12 +5081,16 @@ ikev2_sa_keys(struct iked *env, struct i
>   log_debug("%s: SK_d with %zu bytes", __func__,
>      ibuf_length(sa->sa_key_d));
>   print_hex(sa->sa_key_d->buf, 0, ibuf_length(sa->sa_key_d));
> - log_debug("%s: SK_ai with %zu bytes", __func__,
> -    ibuf_length(sa->sa_key_iauth));
> - print_hex(sa->sa_key_iauth->buf, 0, ibuf_length(sa->sa_key_iauth));
> - log_debug("%s: SK_ar with %zu bytes", __func__,
> -    ibuf_length(sa->sa_key_rauth));
> - print_hex(sa->sa_key_rauth->buf, 0, ibuf_length(sa->sa_key_rauth));
> + if (!isaead) {
> + log_debug("%s: SK_ai with %zu bytes", __func__,
> +    ibuf_length(sa->sa_key_iauth));
> + print_hex(sa->sa_key_iauth->buf, 0,
> +    ibuf_length(sa->sa_key_iauth));
> + log_debug("%s: SK_ar with %zu bytes", __func__,
> +    ibuf_length(sa->sa_key_rauth));
> + print_hex(sa->sa_key_rauth->buf, 0,
> +    ibuf_length(sa->sa_key_rauth));
> + }
>   log_debug("%s: SK_ei with %zu bytes", __func__,
>      ibuf_length(sa->sa_key_iencr));
>   print_hex(sa->sa_key_iencr->buf, 0, ibuf_length(sa->sa_key_iencr));
> Index: ikev2.h
> ===================================================================
> RCS file: /cvs/src/sbin/iked/ikev2.h,v
> retrieving revision 1.32
> diff -u -p -r1.32 ikev2.h
> --- ikev2.h 28 Apr 2020 15:18:52 -0000 1.32
> +++ ikev2.h 14 May 2020 23:55:14 -0000
> @@ -224,6 +224,9 @@ extern struct iked_constmap ikev2_xformp
>  #define IKEV2_XFORMAUTH_HMAC_SHA2_256_128 12 /* RFC4868 */
>  #define IKEV2_XFORMAUTH_HMAC_SHA2_384_192 13 /* RFC4868 */
>  #define IKEV2_XFORMAUTH_HMAC_SHA2_512_256 14 /* RFC4868 */
> +#define IKEV2_XFORMAUTH_AES_GCM_8 2018 /* private */
> +#define IKEV2_XFORMAUTH_AES_GCM_12 2019 /* private */
> +#define IKEV2_XFORMAUTH_AES_GCM_16 2020 /* private */
>  
>  extern struct iked_constmap ikev2_xformauth_map[];
>  
> Index: ikev2_msg.c
> ===================================================================
> RCS file: /cvs/src/sbin/iked/ikev2_msg.c,v
> retrieving revision 1.67
> diff -u -p -r1.67 ikev2_msg.c
> --- ikev2_msg.c 14 May 2020 15:08:30 -0000 1.67
> +++ ikev2_msg.c 14 May 2020 23:55:14 -0000
> @@ -51,6 +51,8 @@ void ikev2_msg_retransmit_timeout(struc
>  int ikev2_check_frag_oversize(struct iked_sa *sa, struct ibuf *buf);
>  int ikev2_send_encrypted_fragments(struct iked *env, struct iked_sa *sa,
>      struct ibuf *in,uint8_t exchange, uint8_t firstpayload, int response);
> +int ikev2_msg_encrypt_prepare(struct iked_sa *, struct ikev2_payload *,
> +    struct ibuf*, struct ibuf *, struct ike_header *, uint8_t, int);
>  
>  void
>  ikev2_msg_cb(int fd, short event, void *arg)
> @@ -329,8 +331,45 @@ ikev2_msg_id(struct iked *env, struct ik
>   return (id);
>  }
>  
> +/*
> + * Calculate the final sizes of the IKEv2 header and the encrypted payload
> + * header.  This must be done before encryption to make sure the correct
> + * headers are authenticated.
> + */
> +int
> +ikev2_msg_encrypt_prepare(struct iked_sa *sa, struct ikev2_payload *pld,
> +    struct ibuf *buf, struct ibuf *e, struct ike_header *hdr,
> +    uint8_t firstpayload, int fragmentation)
> +{
> + size_t len, ivlen, encrlen, integrlen, blocklen, pldlen, outlen;
> +
> + if (sa == NULL ||
> +    sa->sa_encr == NULL ||
> +    sa->sa_integr == NULL) {
> + log_debug("%s: invalid SA", __func__);
> + return (-1);
> + }
> +
> + len = ibuf_size(e);
> + blocklen = cipher_length(sa->sa_encr);
> + integrlen = hash_length(sa->sa_integr);
> + ivlen = cipher_ivlength(sa->sa_encr);
> + encrlen = roundup(len + 1, blocklen);
> + outlen = cipher_outlength(sa->sa_encr, encrlen);
> + pldlen = ivlen + outlen + integrlen +
> +    (fragmentation ? sizeof(struct ikev2_frag_payload) : 0);
> +
> + if (ikev2_next_payload(pld, pldlen, firstpayload) == -1)
> + return (-1);
> + if (ikev2_set_header(hdr, ibuf_size(buf) + pldlen - sizeof(*hdr)) == -1)
> + return (-1);
> +
> + return (0);
> +}
> +
>  struct ibuf *
> -ikev2_msg_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf *src)
> +ikev2_msg_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf *src,
> +    struct ibuf *aad)
>  {
>   size_t len, ivlen, encrlen, integrlen, blocklen,
>      outlen;
> @@ -390,12 +429,23 @@ ikev2_msg_encrypt(struct iked *env, stru
>   goto done;
>  
>   outlen = ibuf_size(out);
> +
> + /* Add AAD for AEAD ciphers */
> + if (sa->sa_integr->hash_isaead)
> + cipher_aad(sa->sa_encr, ibuf_data(aad),
> +    ibuf_length(aad), &outlen);
> +
>   if (cipher_update(sa->sa_encr, ibuf_data(src), encrlen,
>      ibuf_data(out), &outlen) == -1) {
>   log_info("%s: error updating cipher.", __func__);
>   goto done;
>   }
>  
> + if (cipher_final(sa->sa_encr) == -1) {
> + log_info("%s: encryption failed.", __func__);
> + goto done;
> + }
> +
>   if (outlen && ibuf_add(dst, ibuf_data(out), outlen) != 0)
>   goto done;
>  
> @@ -429,18 +479,13 @@ ikev2_msg_integr(struct iked *env, struc
>   print_hex(ibuf_data(src), 0, ibuf_size(src));
>  
>   if (sa == NULL ||
> +    sa->sa_encr == NULL ||
>      sa->sa_integr == NULL) {
>   log_debug("%s: invalid SA", __func__);
>   return (-1);
>   }
>  
> - if (sa->sa_hdr.sh_initiator)
> - integr = sa->sa_key_iauth;
> - else
> - integr = sa->sa_key_rauth;
> -
>   integrlen = hash_length(sa->sa_integr);
> -
>   log_debug("%s: integrity checksum length %zu", __func__,
>      integrlen);
>  
> @@ -450,21 +495,33 @@ ikev2_msg_integr(struct iked *env, struc
>   if ((tmp = ibuf_new(NULL, hash_keylength(sa->sa_integr))) == NULL)
>   goto done;
>  
> - hash_setkey(sa->sa_integr, ibuf_data(integr), ibuf_size(integr));
> - hash_init(sa->sa_integr);
> - hash_update(sa->sa_integr, ibuf_data(src),
> -    ibuf_size(src) - integrlen);
> - hash_final(sa->sa_integr, ibuf_data(tmp), &tmplen);
> + if (!sa->sa_integr->hash_isaead) {
> + if (sa->sa_hdr.sh_initiator)
> + integr = sa->sa_key_iauth;
> + else
> + integr = sa->sa_key_rauth;
> +
> + hash_setkey(sa->sa_integr, ibuf_data(integr),
> +    ibuf_size(integr));
> + hash_init(sa->sa_integr);
> + hash_update(sa->sa_integr, ibuf_data(src),
> +    ibuf_size(src) - integrlen);
> + hash_final(sa->sa_integr, ibuf_data(tmp), &tmplen);
>  
> - if (tmplen != integrlen) {
> - log_debug("%s: hash failure", __func__);
> - goto done;
> + if (tmplen != integrlen) {
> + log_debug("%s: hash failure", __func__);
> + goto done;
> + }
> + } else {
> + /* Append AEAD tag */
> + if (cipher_gettag(sa->sa_encr, ibuf_data(tmp), ibuf_size(tmp)))
> + goto done;
>   }
>  
>   if ((ptr = ibuf_seek(src,
>      ibuf_size(src) - integrlen, integrlen)) == NULL)
>   goto done;
> - memcpy(ptr, ibuf_data(tmp), tmplen);
> + memcpy(ptr, ibuf_data(tmp), integrlen);
>  
>   print_hex(ibuf_data(tmp), 0, ibuf_size(tmp));
>  
> @@ -481,7 +538,7 @@ ikev2_msg_decrypt(struct iked *env, stru
>  {
>   ssize_t ivlen, encrlen, integrlen, blocklen,
>      outlen, tmplen;
> - uint8_t pad = 0, *ptr;
> + uint8_t pad = 0, *ptr, *integrdata;
>   struct ibuf *integr, *encr, *tmp = NULL, *out = NULL;
>   off_t ivoff, encroff, integroff;
>  
> @@ -524,25 +581,30 @@ ikev2_msg_decrypt(struct iked *env, stru
>   /*
>   * Validate packet checksum
>   */
> - if ((tmp = ibuf_new(NULL, ibuf_length(integr))) == NULL)
> - goto done;
> + if (!sa->sa_integr->hash_isaead) {
> + if ((tmp = ibuf_new(NULL, ibuf_length(integr))) == NULL)
> + goto done;
>  
> - hash_setkey(sa->sa_integr, integr->buf, ibuf_length(integr));
> - hash_init(sa->sa_integr);
> - hash_update(sa->sa_integr, ibuf_data(msg),
> -    ibuf_size(msg) - integrlen);
> - hash_final(sa->sa_integr, tmp->buf, &tmplen);
> + hash_setkey(sa->sa_integr, integr->buf, ibuf_length(integr));
> + hash_init(sa->sa_integr);
> + hash_update(sa->sa_integr, ibuf_data(msg),
> +    ibuf_size(msg) - integrlen);
> + hash_final(sa->sa_integr, tmp->buf, &tmplen);
>  
> - if (memcmp(tmp->buf, ibuf_data(src) + integroff, integrlen) != 0) {
> - log_debug("%s: integrity check failed", __func__);
> - goto done;
> - }
> + integrdata = ibuf_seek(src, integroff, integrlen);
> + if (integrdata == NULL)
> + goto done;
> + if (memcmp(tmp->buf, integrdata, integrlen) != 0) {
> + log_debug("%s: integrity check failed", __func__);
> + goto done;
> + }
>  
> - log_debug("%s: integrity check succeeded", __func__);
> - print_hex(tmp->buf, 0, tmplen);
> + log_debug("%s: integrity check succeeded", __func__);
> + print_hex(tmp->buf, 0, tmplen);
>  
> - ibuf_release(tmp);
> - tmp = NULL;
> + ibuf_release(tmp);
> + tmp = NULL;
> + }
>  
>   /*
>   * Decrypt the payload and strip any padding
> @@ -559,10 +621,31 @@ ikev2_msg_decrypt(struct iked *env, stru
>   goto done;
>   }
>  
> + /* Set AEAD tag */
> + if (sa->sa_integr->hash_isaead) {
> + integrdata = ibuf_seek(src, integroff, integrlen);
> + if (integrdata == NULL)
> + goto done;
> + if (cipher_settag(sa->sa_encr, integrdata, integrlen)) {
> + log_info("%s: failed to set tag.", __func__);
> + goto done;
> + }
> + }
> +
>   if ((out = ibuf_new(NULL, cipher_outlength(sa->sa_encr,
>      encrlen))) == NULL)
>   goto done;
>  
> + /*
> + * Add additional authenticated data for AEAD ciphers
> + */
> + if (sa->sa_integr->hash_isaead) {
> + log_debug("%s: AAD length %zu", __func__, ibuf_length(msg) - ibuf_length(src));
> + print_hex(ibuf_data(msg), 0, ibuf_length(msg) - ibuf_length(src));
> + cipher_aad(sa->sa_encr, ibuf_data(msg),
> +    ibuf_length(msg) - ibuf_length(src), &outlen);
> + }
> +
>   if ((outlen = ibuf_length(out)) != 0) {
>   if (cipher_update(sa->sa_encr, ibuf_data(src) + encroff,
>      encrlen, ibuf_data(out), &outlen) == -1) {
> @@ -574,6 +657,11 @@ ikev2_msg_decrypt(struct iked *env, stru
>   pad = *ptr;
>   }
>  
> + if (cipher_final(sa->sa_encr) == -1) {
> + log_info("%s: decryption failed.", __func__);
> + goto done;
> + }
> +
>   log_debug("%s: decrypted payload length %zd/%zd padding %d",
>      __func__, outlen, encrlen, pad);
>   print_hex(ibuf_data(out), 0, ibuf_size(out));
> @@ -598,6 +686,13 @@ ikev2_check_frag_oversize(struct iked_sa
>   size_t max;
>   size_t ivlen, integrlen, blocklen;
>  
> + if (sa == NULL ||
> +    sa->sa_encr == NULL ||
> +    sa->sa_integr == NULL) {
> + log_debug("%s: invalid SA", __func__);
> + return (-1);
> + }
> +
>   sa_fam = ((struct sockaddr *)&sa->sa_local.addr)->sa_family;
>  
>   max = sa_fam == AF_INET ? IKEV2_MAXLEN_IPV4_FRAG
> @@ -642,18 +737,16 @@ ikev2_msg_send_encrypt(struct iked *env,
>   if ((pld = ikev2_add_payload(buf)) == NULL)
>   goto done;
>  
> + if (ikev2_msg_encrypt_prepare(sa, pld, buf, e, hdr, firstpayload, 0) == -1)
> + goto done;
> +
>   /* Encrypt message and add as an E payload */
> - if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) {
> + if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) {
>   log_debug("%s: encryption failed", __func__);
>   goto done;
>   }
>   if (ibuf_cat(buf, e) != 0)
>   goto done;
> - if (ikev2_next_payload(pld, ibuf_size(e), firstpayload) == -1)
> - goto done;
> -
> - if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1)
> - goto done;
>  
>   /* Add integrity checksum (HMAC) */
>   if (ikev2_msg_integr(env, sa, buf) != 0) {
> @@ -694,6 +787,13 @@ ikev2_send_encrypted_fragments(struct ik
>   uint32_t msgid;
>   int ret = -1;
>  
> + if (sa == NULL ||
> +    sa->sa_encr == NULL ||
> +    sa->sa_integr == NULL) {
> + log_debug("%s: invalid SA", __func__);
> + goto done;
> + }
> +
>   sa_fam = ((struct sockaddr *)&sa->sa_local.addr)->sa_family;
>  
>   left = ibuf_length(in);
> @@ -743,18 +843,16 @@ ikev2_send_encrypted_fragments(struct ik
>   if ((e = ibuf_new(data, MIN(left, max_len))) == NULL) {
>   goto done;
>   }
> - if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) {
> +
> + if (ikev2_msg_encrypt_prepare(sa, pld, buf, e, hdr,
> +    firstpayload, 1) == -1)
> + goto done;
> +
> + if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) {
>   log_debug("%s: encryption failed", __func__);
>   goto done;
>   }
>   if (ibuf_cat(buf, e) != 0)
> - goto done;
> -
> - if (ikev2_next_payload(pld, ibuf_size(e) + sizeof(*frag),
> -    firstpayload) == -1)
> - goto done;
> -
> - if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1)
>   goto done;
>  
>   /* Add integrity checksum (HMAC) */
> Index: parse.y
> ===================================================================
> RCS file: /cvs/src/sbin/iked/parse.y,v
> retrieving revision 1.99
> diff -u -p -r1.99 parse.y
> --- parse.y 30 Apr 2020 21:11:13 -0000 1.99
> +++ parse.y 14 May 2020 23:55:14 -0000
> @@ -198,6 +198,12 @@ const struct ipsec_xf ikeencxfs[] = {
>   { "aes-128", IKEV2_XFORMENCR_AES_CBC, 16, 16 },
>   { "aes-192", IKEV2_XFORMENCR_AES_CBC, 24, 24 },
>   { "aes-256", IKEV2_XFORMENCR_AES_CBC, 32, 32 },
> + { "aes-128-gcm", IKEV2_XFORMENCR_AES_GCM_16, 16, 16, 4, 1 },
> + { "aes-192-gcm", IKEV2_XFORMENCR_AES_GCM_16, 24, 24, 4, 1 },
> + { "aes-256-gcm", IKEV2_XFORMENCR_AES_GCM_16, 32, 32, 4, 1 },
> + { "aes-128-gcm-12", IKEV2_XFORMENCR_AES_GCM_12, 16, 16, 4, 1 },
> + { "aes-192-gcm-12", IKEV2_XFORMENCR_AES_GCM_12, 24, 24, 4, 1 },
> + { "aes-256-gcm-12", IKEV2_XFORMENCR_AES_GCM_12, 32, 32, 4, 1 },
>   { NULL }
>  };
>  
> @@ -2417,6 +2423,17 @@ print_xf(unsigned int id, unsigned int l
>   return ("unknown");
>  }
>  
> +int
> +encxf_noauth(unsigned int id)
> +{
> + int i;
> +
> + for (i = 0; ikeencxfs[i].name != NULL; i++)
> + if (ikeencxfs[i].id == id)
> + return ikeencxfs[i].noauth;
> + return (0);
> +}
> +
>  size_t
>  keylength_xf(unsigned int saproto, unsigned int type, unsigned int id)
>  {
> @@ -2852,21 +2869,37 @@ create_ike(char *name, int af, uint8_t i
>   pol.pol_nproposals++;
>   } else {
>   for (i = 0; i < ike_sa->nxfs; i++) {
> + noauth = 0;
> + for (j = 0; j < ike_sa->xfs[i]->nencxf; j++) {
> + if (ike_sa->xfs[i]->encxf[j]->noauth)
> + noauth++;
> + }
>   if (ike_sa->xfs[i]->nesnxf) {
>   yyerror("cannot use ESN with ikesa.");
>   goto done;
>   }
> + if (noauth && noauth != ike_sa->xfs[i]->nencxf) {
> + yyerror("cannot mix encryption transforms with "
> +    "implicit and non-implicit authentication");
> + goto done;
> + }
> + if (noauth && ike_sa->xfs[i]->nauthxf) {
> + yyerror("authentication is implicit for given "
> +    "encryption transforms");
> + goto done;
> + }
>  
>   if ((p = calloc(1, sizeof(*p))) == NULL)
>   err(1, "%s", __func__);
>  
>   xf = NULL;
>   xfi = 0;
> - copy_transforms(IKEV2_XFORMTYPE_INTEGR,
> -    ike_sa->xfs[i]->authxf,
> -    ike_sa->xfs[i]->nauthxf, &xf, &xfi,
> -    ikev2_default_ike_transforms,
> -    ikev2_default_nike_transforms);
> + if (!ike_sa->xfs[i]->nencxf || !noauth)
> + copy_transforms(IKEV2_XFORMTYPE_INTEGR,
> +    ike_sa->xfs[i]->authxf,
> +    ike_sa->xfs[i]->nauthxf, &xf, &xfi,
> +    ikev2_default_ike_transforms,
> +    ikev2_default_nike_transforms);
>   copy_transforms(IKEV2_XFORMTYPE_ENCR,
>      ike_sa->xfs[i]->encxf,
>      ike_sa->xfs[i]->nencxf, &xf, &xfi,
> Index: policy.c
> ===================================================================
> RCS file: /cvs/src/sbin/iked/policy.c,v
> retrieving revision 1.62
> diff -u -p -r1.62 policy.c
> --- policy.c 13 May 2020 23:03:20 -0000 1.62
> +++ policy.c 14 May 2020 23:55:14 -0000
> @@ -774,7 +774,7 @@ proposals_match(struct iked_proposal *lo
>      struct iked_transform **xforms, int rekey)
>  {
>   struct iked_transform *tpeer, *tlocal;
> - unsigned int i, j, type, score, requiredh = 0;
> + unsigned int i, j, type, score, requiredh = 0, noauth = 0;
>   uint8_t protoid = peer->prop_protoid;
>   uint8_t peerxfs[IKEV2_XFORMTYPE_MAX];
>  
> @@ -782,8 +782,18 @@ proposals_match(struct iked_proposal *lo
>  
>   for (i = 0; i < peer->prop_nxforms; i++) {
>   tpeer = peer->prop_xforms + i;
> + /* If any of the ENC transforms is an AEAD, ignore auth */
> + if (tpeer->xform_type == IKEV2_XFORMTYPE_ENCR &&
> +    encxf_noauth(tpeer->xform_id))
> + noauth = 1;
> + }
> +
> + for (i = 0; i < peer->prop_nxforms; i++) {
> + tpeer = peer->prop_xforms + i;
>   if (tpeer->xform_type > IKEV2_XFORMTYPE_MAX)
>   continue;
> + if (noauth && tpeer->xform_type == IKEV2_XFORMTYPE_INTEGR)
> + return (0);
>  
>   /*
>   * Record all transform types from the peer's proposal,
> @@ -832,7 +842,8 @@ proposals_match(struct iked_proposal *lo
>   for (i = score = 0; i < IKEV2_XFORMTYPE_MAX; i++) {
>   if (protoid == IKEV2_SAPROTO_IKE && xforms[i] == NULL &&
>      (i == IKEV2_XFORMTYPE_ENCR || i == IKEV2_XFORMTYPE_PRF ||
> -     i == IKEV2_XFORMTYPE_INTEGR || i == IKEV2_XFORMTYPE_DH)) {
> +    (!noauth && i == IKEV2_XFORMTYPE_INTEGR) ||
> +    i == IKEV2_XFORMTYPE_DH)) {
>   score = 0;
>   break;
>   } else if (protoid == IKEV2_SAPROTO_AH && xforms[i] == NULL &&
>

Hi Tobias,
your diff is working without a problem. Have been running on it since 1,5 days on different machines. Though my feedback
won't be of big use for you. I only run on iked<-->iked and iked<-->strongswan connections.

Anyway. Big thank you for implementing this diff! I greatly appreciate it being now able to have AEAD for IKE ! :)

Have a nice  day !

Best regards,
Stephan

Reply | Threaded
Open this post in threaded view
|

Re: iked(8): AES_GCM ciphers for IKE

Christian Weisgerber
In reply to this post by Tobias Heider-2
Tobias Heider:

> currently iked(8) supports AES-GCM only for ESP.
> The diff below adds the ENCR_AES_GCM_16 and ENCR_AES_GCM_12 variants for IKE.
> (for more information see [1] and [2]).
> Both variants support the 128, 196, and 256 bit key lengths.
>
> The new new ciphers can be configured with:
> - aes-128-gcm, aes-196-gcm and aes-256-gcm for ENCR_AES_GCM_16
> - aes-128-gcm-12, aes-196-gcm-12 and aes-256-gcm-12 for ENCR_AES_GCM_12

Is there a compelling reason to implement the GCM_12 variants?

I remember that truncating integrity tags is problematic for GCM.
That probably doesn't matter for small IKE exchanges, but then again
four extra bytes per packet don't matter either.  According to
RFC5282, full length tags MUST be supported anyway, truncated ones
are optional.

RFC5282 also says that AES-192 is NOT RECOMMENDED.

So I think only aes-128-gcm and aes-256-gcm should be added.  While
adding the other variants is simple, there is no value in supporting
them; they just add more configuration buttons.

--
Christian "naddy" Weisgerber                          [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: iked(8): AES_GCM ciphers for IKE

Tobias Heider-2
On Sat, May 16, 2020 at 02:24:45PM +0200, Christian Weisgerber wrote:

> Tobias Heider:
>
> > currently iked(8) supports AES-GCM only for ESP.
> > The diff below adds the ENCR_AES_GCM_16 and ENCR_AES_GCM_12 variants for IKE.
> > (for more information see [1] and [2]).
> > Both variants support the 128, 196, and 256 bit key lengths.
> >
> > The new new ciphers can be configured with:
> > - aes-128-gcm, aes-196-gcm and aes-256-gcm for ENCR_AES_GCM_16
> > - aes-128-gcm-12, aes-196-gcm-12 and aes-256-gcm-12 for ENCR_AES_GCM_12
>
> Is there a compelling reason to implement the GCM_12 variants?

Only that it's actually used in the wild.  I added them because I am forced
to have them for compatibility.  Dropping them would force me to maintain
a local diff, which I would of course like to avoid.

> I remember that truncating integrity tags is problematic for GCM.
> That probably doesn't matter for small IKE exchanges, but then again
> four extra bytes per packet don't matter either.  According to
> RFC5282, full length tags MUST be supported anyway, truncated ones
> are optional.

I know NIST recommends a limit in message size and operations per key
for the 32 and 64 bit truncated versions in [1].  The AES_GCM_12 tag
seems to be long enough to make attacks considerably harder (although
I agree that the non truncated version should always be preferred).

>
> RFC5282 also says that AES-192 is NOT RECOMMENDED.
>

Fair enough, I think we can live without those.

- Tobias

[1] https://csrc.nist.gov/publications/detail/sp/800-38d/final

Reply | Threaded
Open this post in threaded view
|

Re: iked(8): AES_GCM ciphers for IKE

Remi Locherer
In reply to this post by Tobias Heider-2
On Fri, May 15, 2020 at 01:59:35AM +0200, Tobias Heider wrote:

> On Thu, May 14, 2020 at 10:47:52PM +0200, Tobias Heider wrote:
> > On Thu, May 14, 2020 at 10:07:30PM +0200, Tobias Heider wrote:
> > > Hi,
> > >
> > > currently iked(8) supports AES-GCM only for ESP.
> > > The diff below adds the ENCR_AES_GCM_16 and ENCR_AES_GCM_12 variants for IKE.
> > > (for more information see [1] and [2]).
> > > Both variants support the 128, 196, and 256 bit key lengths.
> > >
> > > The new new ciphers can be configured with:
> > > - aes-128-gcm, aes-196-gcm and aes-256-gcm for ENCR_AES_GCM_16
> > > - aes-128-gcm-12, aes-196-gcm-12 and aes-256-gcm-12 for ENCR_AES_GCM_12
>
> Small typo: it's 192, not 196.
>
> > >
> > > It would be nice if we could get some interop testing with different IKEv2
> > > implementations.  I have so far successfully tested strongswan <-> iked and
> > > of course iked <-> iked.
> > >
> > > Feedback welcome ;)

It works with a Juniper SRX on the other side.

I tested with this iked.conf:

ikev2 "srx1" active esp \
        from 192.168.100.0/24 to 192.168.111.0/24 \
        local 10.0.0.2 peer 10.0.0.1 \
        ikesa enc aes-128-gcm group ecp256 \
        childsa enc aes-128-gcm group ecp256 \
        srcid 10.0.0.2 dstid 10.0.0.1 \
        psk "Secret1"


> > >
> > > [1] https://tools.ietf.org/html/rfc5282
> > > [2] https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml#ikev2-parameters-5
> > >
> >
> > whoops, previous diff was broken.
> >
>
> Another update because it seems parse_xf matches substrings instead of the
> full transform type name, which means I had to change the order of ikeencxfs
> members or 'aes-128-gcm' will always match 'aes-128-gcm-12' ...
>
> Index: crypto.c
> ===================================================================
> RCS file: /cvs/src/sbin/iked/crypto.c,v
> retrieving revision 1.27
> diff -u -p -r1.27 crypto.c
> --- crypto.c 14 May 2020 15:08:30 -0000 1.27
> +++ crypto.c 14 May 2020 23:55:13 -0000
> @@ -92,7 +92,7 @@ hash_new(uint8_t type, uint16_t id)
>   struct iked_hash *hash;
>   const EVP_MD *md = NULL;
>   HMAC_CTX *ctx = NULL;
> - int length = 0, fixedkey = 0, trunc = 0;
> + int length = 0, fixedkey = 0, trunc = 0, isaead = 0;
>  
>   switch (type) {
>   case IKEV2_XFORMTYPE_PRF:
> @@ -156,6 +156,14 @@ hash_new(uint8_t type, uint16_t id)
>   length = SHA512_DIGEST_LENGTH;
>   trunc = 32;
>   break;
> + case IKEV2_XFORMAUTH_AES_GCM_12:
> + length = 12;
> + isaead = 1;
> + break;
> + case IKEV2_XFORMAUTH_AES_GCM_16:
> + length = 16;
> + isaead = 1;
> + break;
>   case IKEV2_XFORMAUTH_NONE:
>   case IKEV2_XFORMAUTH_DES_MAC:
>   case IKEV2_XFORMAUTH_KPDK_MD5:
> @@ -177,7 +185,7 @@ hash_new(uint8_t type, uint16_t id)
>      print_map(id, ikev2_xformtype_map));
>   break;
>   }
> - if (md == NULL)
> + if (!isaead && md == NULL)
>   return (NULL);
>  
>   if ((hash = calloc(1, sizeof(*hash))) == NULL) {
> @@ -192,6 +200,10 @@ hash_new(uint8_t type, uint16_t id)
>   hash->hash_trunc = trunc;
>   hash->hash_length = length;
>   hash->hash_fixedkey = fixedkey;
> + hash->hash_isaead = isaead;
> +
> + if (isaead)
> + return (hash);
>  
>   if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
>   log_debug("%s: alloc hash ctx", __func__);
> @@ -276,6 +288,7 @@ cipher_new(uint8_t type, uint16_t id, ui
>   const EVP_CIPHER *cipher = NULL;
>   EVP_CIPHER_CTX *ctx = NULL;
>   int length = 0, fixedkey = 0, ivlength = 0;
> + int saltlength = 0, authid = 0;
>  
>   switch (type) {
>   case IKEV2_XFORMTYPE_ENCR:
> @@ -309,6 +322,39 @@ cipher_new(uint8_t type, uint16_t id, ui
>   ivlength = EVP_CIPHER_iv_length(cipher);
>   fixedkey = EVP_CIPHER_key_length(cipher);
>   break;
> + case IKEV2_XFORMENCR_AES_GCM_16:
> + case IKEV2_XFORMENCR_AES_GCM_12:
> + switch (id_length) {
> + case 128:
> + cipher = EVP_aes_128_gcm();
> + break;
> + case 192:
> + cipher = EVP_aes_192_gcm();
> + break;
> + case 256:
> + cipher = EVP_aes_256_gcm();
> + break;
> + default:
> + log_debug("%s: invalid key length %d"
> +    " for cipher %s", __func__, id_length,
> +    print_map(id, ikev2_xformencr_map));
> + break;
> + }
> + if (cipher == NULL)
> + break;
> + switch(id) {
> + case IKEV2_XFORMENCR_AES_GCM_16:
> + authid = IKEV2_XFORMAUTH_AES_GCM_16;
> + break;
> + case IKEV2_XFORMENCR_AES_GCM_12:
> + authid = IKEV2_XFORMAUTH_AES_GCM_12;
> + break;
> + }
> + length = EVP_CIPHER_block_size(cipher);
> + ivlength = 8;
> + saltlength = 4;
> + fixedkey = EVP_CIPHER_key_length(cipher) + saltlength;
> + break;
>   case IKEV2_XFORMENCR_DES_IV64:
>   case IKEV2_XFORMENCR_DES:
>   case IKEV2_XFORMENCR_RC5:
> @@ -346,6 +392,8 @@ cipher_new(uint8_t type, uint16_t id, ui
>   encr->encr_length = length;
>   encr->encr_fixedkey = fixedkey;
>   encr->encr_ivlength = ivlength ? ivlength : length;
> + encr->encr_saltlength = saltlength;
> + encr->encr_authid = authid;
>  
>   if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
>   log_debug("%s: alloc cipher ctx", __func__);
> @@ -392,6 +440,20 @@ cipher_setiv(struct iked_cipher *encr, v
>   return (encr->encr_iv);
>  }
>  
> +int
> +cipher_settag(struct iked_cipher *encr, uint8_t *data, size_t len)
> +{
> + return (EVP_CIPHER_CTX_ctrl(encr->encr_ctx,
> +    EVP_CTRL_GCM_SET_TAG, len, data) != 1);
> +}
> +
> +int
> +cipher_gettag(struct iked_cipher *encr, uint8_t *data, size_t len)
> +{
> + return (EVP_CIPHER_CTX_ctrl(encr->encr_ctx,
> +    EVP_CTRL_GCM_GET_TAG, len, data) != 1);
> +}
> +
>  void
>  cipher_free(struct iked_cipher *encr)
>  {
> @@ -409,11 +471,33 @@ cipher_free(struct iked_cipher *encr)
>  int
>  cipher_init(struct iked_cipher *encr, int enc)
>  {
> + struct ibuf *nonce = NULL;
> + int ret = -1;
> +
>   if (EVP_CipherInit_ex(encr->encr_ctx, encr->encr_priv, NULL,
> -    ibuf_data(encr->encr_key), ibuf_data(encr->encr_iv), enc) != 1)
> +    NULL, NULL, enc) != 1)
>   return (-1);
> + if (encr->encr_saltlength > 0) {
> + /* For AEADs the nonce is salt + IV  (see RFC5282) */
> + nonce = ibuf_new(ibuf_data(encr->encr_key) +
> +    ibuf_size(encr->encr_key) - encr->encr_saltlength,
> +    encr->encr_saltlength);
> + if (nonce == NULL)
> + return (-1);
> + if (ibuf_add(nonce, ibuf_data(encr->encr_iv) , ibuf_size(encr->encr_iv)) != 0)
> + goto done;
> + if (EVP_CipherInit_ex(encr->encr_ctx, NULL, NULL,
> +    ibuf_data(encr->encr_key), ibuf_data(nonce), enc) != 1)
> + goto done;
> + } else
> + if (EVP_CipherInit_ex(encr->encr_ctx, NULL, NULL,
> +    ibuf_data(encr->encr_key), ibuf_data(encr->encr_iv), enc) != 1)
> + return (-1);
>   EVP_CIPHER_CTX_set_padding(encr->encr_ctx, 0);
> - return (0);
> + ret = 0;
> + done:
> + ibuf_free(nonce);
> + return (ret);
>  }
>  
>  int
> @@ -426,6 +510,20 @@ int
>  cipher_init_decrypt(struct iked_cipher *encr)
>  {
>   return (cipher_init(encr, 0));
> +}
> +
> +void
> +cipher_aad(struct iked_cipher *encr, void *in, size_t inlen,
> +    size_t *outlen)
> +{
> + int olen = 0;
> +
> + if (EVP_CipherUpdate(encr->encr_ctx, NULL, &olen, in, inlen) != 1) {
> + ca_sslerror(__func__);
> + *outlen = 0;
> + return;
> + }
> + *outlen = (size_t)olen;
>  }
>  
>  int
> Index: iked.conf.5
> ===================================================================
> RCS file: /cvs/src/sbin/iked/iked.conf.5,v
> retrieving revision 1.68
> diff -u -p -r1.68 iked.conf.5
> --- iked.conf.5 1 May 2020 17:44:02 -0000 1.68
> +++ iked.conf.5 14 May 2020 23:55:13 -0000
> @@ -861,12 +861,15 @@ keyword:
>  .It Li aes-128 Ta "128 bits" Ta ""
>  .It Li aes-192 Ta "192 bits" Ta ""
>  .It Li aes-256 Ta "256 bits" Ta ""
> +.It Li aes-128-gcm Ta "160 bits" Ta ""
> +.It Li aes-192-gcm Ta "224 bits" Ta ""
> +.It Li aes-256-gcm Ta "288 bits" Ta ""
> +.It Li aes-128-gcm-12 Ta "160 bits" Ta "[IKE only]"
> +.It Li aes-192-gcm-12 Ta "224 bits" Ta "[IKE only]"
> +.It Li aes-256-gcm-12 Ta "288 bits" Ta "[IKE only]"
>  .It Li aes-128-ctr Ta "160 bits" Ta "[ESP only]"
>  .It Li aes-192-ctr Ta "224 bits" Ta "[ESP only]"
>  .It Li aes-256-ctr Ta "288 bits" Ta "[ESP only]"
> -.It Li aes-128-gcm Ta "160 bits" Ta "[ESP only]"
> -.It Li aes-192-gcm Ta "224 bits" Ta "[ESP only]"
> -.It Li aes-256-gcm Ta "288 bits" Ta "[ESP only]"
>  .It Li blowfish Ta "160 bits" Ta "[ESP only]"
>  .It Li cast Ta "128 bits" Ta "[ESP only]"
>  .It Li chacha20-poly1305 Ta "288 bits" Ta "[ESP only]"
> Index: iked.h
> ===================================================================
> RCS file: /cvs/src/sbin/iked/iked.h,v
> retrieving revision 1.150
> diff -u -p -r1.150 iked.h
> --- iked.h 14 May 2020 15:08:30 -0000 1.150
> +++ iked.h 14 May 2020 23:55:13 -0000
> @@ -309,6 +309,7 @@ struct iked_hash {
>   size_t hash_length; /* Output length */
>   size_t hash_trunc; /* Truncate the output length */
>   struct iked_hash *hash_prf; /* PRF pointer */
> + int hash_isaead;
>  };
>  
>  struct iked_cipher {
> @@ -321,6 +322,8 @@ struct iked_cipher {
>   struct ibuf *encr_iv; /* Initialization Vector */
>   size_t encr_ivlength; /* IV length */
>   size_t encr_length; /* Block length */
> + size_t encr_saltlength; /* IV salt length */
> + uint16_t encr_authid; /* ID of associated authentication */
>  };
>  
>  struct iked_dsa {
> @@ -841,10 +844,13 @@ struct ibuf *
>   cipher_setkey(struct iked_cipher *, void *, size_t);
>  struct ibuf *
>   cipher_setiv(struct iked_cipher *, void *, size_t);
> +int cipher_settag(struct iked_cipher *, uint8_t *, size_t);
> +int cipher_gettag(struct iked_cipher *, uint8_t *, size_t);
>  void cipher_free(struct iked_cipher *);
>  int cipher_init(struct iked_cipher *, int);
>  int cipher_init_encrypt(struct iked_cipher *);
>  int cipher_init_decrypt(struct iked_cipher *);
> +void cipher_aad(struct iked_cipher *, void *, size_t, size_t *);
>  int cipher_update(struct iked_cipher *, void *, size_t, void *, size_t *);
>  int cipher_final(struct iked_cipher *);
>  size_t cipher_length(struct iked_cipher *);
> @@ -933,7 +939,8 @@ int ikev2_msg_send(struct iked *, struc
>  int ikev2_msg_send_encrypt(struct iked *, struct iked_sa *,
>      struct ibuf **, uint8_t, uint8_t, int);
>  struct ibuf
> - *ikev2_msg_encrypt(struct iked *, struct iked_sa *, struct ibuf *);
> + *ikev2_msg_encrypt(struct iked *, struct iked_sa *, struct ibuf *,
> +    struct ibuf *);
>  struct ibuf *
>   ikev2_msg_decrypt(struct iked *, struct iked_sa *,
>      struct ibuf *, struct ibuf *);
> @@ -1129,5 +1136,6 @@ void print_policy(struct iked_policy *)
>  size_t keylength_xf(unsigned int, unsigned int, unsigned int);
>  size_t noncelength_xf(unsigned int, unsigned int);
>  int cmdline_symset(char *);
> +int encxf_noauth(unsigned int);
>  
>  #endif /* IKED_H */
> Index: ikev2.c
> ===================================================================
> RCS file: /cvs/src/sbin/iked/ikev2.c,v
> retrieving revision 1.225
> diff -u -p -r1.225 ikev2.c
> --- ikev2.c 11 May 2020 20:11:35 -0000 1.225
> +++ ikev2.c 14 May 2020 23:55:14 -0000
> @@ -4473,7 +4473,7 @@ ikev2_send_informational(struct iked *en
>   goto done;
>  
>   /* Encrypt message and add as an E payload */
> - if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) {
> + if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) {
>   log_debug("%s: encryption failed", __func__);
>   goto done;
>   }
> @@ -4666,6 +4666,16 @@ ikev2_sa_initiator(struct iked *env, str
>   }
>   }
>  
> + /* For AEAD ciphers integrity is implicit */
> + if (sa->sa_encr->encr_authid && sa->sa_integr == NULL) {
> + if ((sa->sa_integr = hash_new(IKEV2_XFORMTYPE_INTEGR,
> +    sa->sa_encr->encr_authid)) == NULL) {
> + log_info("%s: failed to get AEAD integr",
> +    SPI_SA(sa, __func__));
> + return (-1);
> + }
> + }
> +
>   if (sa->sa_prf == NULL) {
>   if ((xform = config_findtransform(&sa->sa_proposals,
>      IKEV2_XFORMTYPE_PRF, 0)) == NULL) {
> @@ -4840,6 +4850,16 @@ ikev2_sa_responder(struct iked *env, str
>   }
>   }
>  
> + /* For AEAD ciphers integrity is implicit */
> + if (sa->sa_encr->encr_authid && sa->sa_integr == NULL) {
> + if ((sa->sa_integr = hash_new(IKEV2_XFORMTYPE_INTEGR,
> +    sa->sa_encr->encr_authid)) == NULL) {
> + log_info("%s: failed to get AEAD integr",
> +    SPI_SA(sa, __func__));
> + return (-1);
> + }
> + }
> +
>   if (sa->sa_prf == NULL) {
>   if ((xform = config_findtransform(&sa->sa_proposals,
>      IKEV2_XFORMTYPE_PRF, 0)) == NULL) {
> @@ -4883,6 +4903,7 @@ ikev2_sa_keys(struct iked *env, struct i
>   size_t nonceminlen, ilen, rlen, tmplen;
>   uint64_t ispi, rspi;
>   int ret = -1;
> + int isaead = 0;
>  
>   ninr = dhsecret = skeyseed = s = t = NULL;
>  
> @@ -4895,6 +4916,9 @@ ikev2_sa_keys(struct iked *env, struct i
>   return (-1);
>   }
>  
> + /* For AEADs no auth keys are required (see RFC 5282) */
> + isaead = !!integr->hash_isaead;
> +
>   if (prf->hash_fixedkey)
>   nonceminlen = prf->hash_fixedkey;
>   else
> @@ -5026,13 +5050,13 @@ ikev2_sa_keys(struct iked *env, struct i
>   * Get the size of the key material we need and the number
>   * of rounds we need to run the prf+ function.
>   */
> - ilen = hash_length(prf) + /* SK_d */
> -    hash_keylength(integr) + /* SK_ai */
> -    hash_keylength(integr) + /* SK_ar */
> -    cipher_keylength(encr) + /* SK_ei */
> -    cipher_keylength(encr) + /* SK_er */
> -    hash_keylength(prf) + /* SK_pi */
> -    hash_keylength(prf); /* SK_pr */
> + ilen = hash_length(prf) + /* SK_d */
> +    (isaead ? 0 : hash_keylength(integr)) + /* SK_ai */
> +    (isaead ? 0 : hash_keylength(integr)) + /* SK_ar */
> +    cipher_keylength(encr) + /* SK_ei */
> +    cipher_keylength(encr) + /* SK_er */
> +    hash_keylength(prf) + /* SK_pi */
> +    hash_keylength(prf); /* SK_pr */
>  
>   if ((t = ikev2_prfplus(prf, skeyseed, s, ilen)) == NULL) {
>   log_info("%s: failed to get IKE SA key material",
> @@ -5042,8 +5066,10 @@ ikev2_sa_keys(struct iked *env, struct i
>  
>   /* ibuf_get() returns a new buffer from the next read offset */
>   if ((sa->sa_key_d = ibuf_get(t, hash_length(prf))) == NULL ||
> -    (sa->sa_key_iauth = ibuf_get(t, hash_keylength(integr))) == NULL ||
> -    (sa->sa_key_rauth = ibuf_get(t, hash_keylength(integr))) == NULL ||
> +    (!isaead &&
> +    (sa->sa_key_iauth = ibuf_get(t, hash_keylength(integr))) == NULL) ||
> +    (!isaead &&
> +    (sa->sa_key_rauth = ibuf_get(t, hash_keylength(integr))) == NULL) ||
>      (sa->sa_key_iencr = ibuf_get(t, cipher_keylength(encr))) == NULL ||
>      (sa->sa_key_rencr = ibuf_get(t, cipher_keylength(encr))) == NULL ||
>      (sa->sa_key_iprf = ibuf_get(t, hash_length(prf))) == NULL ||
> @@ -5055,12 +5081,16 @@ ikev2_sa_keys(struct iked *env, struct i
>   log_debug("%s: SK_d with %zu bytes", __func__,
>      ibuf_length(sa->sa_key_d));
>   print_hex(sa->sa_key_d->buf, 0, ibuf_length(sa->sa_key_d));
> - log_debug("%s: SK_ai with %zu bytes", __func__,
> -    ibuf_length(sa->sa_key_iauth));
> - print_hex(sa->sa_key_iauth->buf, 0, ibuf_length(sa->sa_key_iauth));
> - log_debug("%s: SK_ar with %zu bytes", __func__,
> -    ibuf_length(sa->sa_key_rauth));
> - print_hex(sa->sa_key_rauth->buf, 0, ibuf_length(sa->sa_key_rauth));
> + if (!isaead) {
> + log_debug("%s: SK_ai with %zu bytes", __func__,
> +    ibuf_length(sa->sa_key_iauth));
> + print_hex(sa->sa_key_iauth->buf, 0,
> +    ibuf_length(sa->sa_key_iauth));
> + log_debug("%s: SK_ar with %zu bytes", __func__,
> +    ibuf_length(sa->sa_key_rauth));
> + print_hex(sa->sa_key_rauth->buf, 0,
> +    ibuf_length(sa->sa_key_rauth));
> + }
>   log_debug("%s: SK_ei with %zu bytes", __func__,
>      ibuf_length(sa->sa_key_iencr));
>   print_hex(sa->sa_key_iencr->buf, 0, ibuf_length(sa->sa_key_iencr));
> Index: ikev2.h
> ===================================================================
> RCS file: /cvs/src/sbin/iked/ikev2.h,v
> retrieving revision 1.32
> diff -u -p -r1.32 ikev2.h
> --- ikev2.h 28 Apr 2020 15:18:52 -0000 1.32
> +++ ikev2.h 14 May 2020 23:55:14 -0000
> @@ -224,6 +224,9 @@ extern struct iked_constmap ikev2_xformp
>  #define IKEV2_XFORMAUTH_HMAC_SHA2_256_128 12 /* RFC4868 */
>  #define IKEV2_XFORMAUTH_HMAC_SHA2_384_192 13 /* RFC4868 */
>  #define IKEV2_XFORMAUTH_HMAC_SHA2_512_256 14 /* RFC4868 */
> +#define IKEV2_XFORMAUTH_AES_GCM_8 2018 /* private */
> +#define IKEV2_XFORMAUTH_AES_GCM_12 2019 /* private */
> +#define IKEV2_XFORMAUTH_AES_GCM_16 2020 /* private */
>  
>  extern struct iked_constmap ikev2_xformauth_map[];
>  
> Index: ikev2_msg.c
> ===================================================================
> RCS file: /cvs/src/sbin/iked/ikev2_msg.c,v
> retrieving revision 1.67
> diff -u -p -r1.67 ikev2_msg.c
> --- ikev2_msg.c 14 May 2020 15:08:30 -0000 1.67
> +++ ikev2_msg.c 14 May 2020 23:55:14 -0000
> @@ -51,6 +51,8 @@ void ikev2_msg_retransmit_timeout(struc
>  int ikev2_check_frag_oversize(struct iked_sa *sa, struct ibuf *buf);
>  int ikev2_send_encrypted_fragments(struct iked *env, struct iked_sa *sa,
>      struct ibuf *in,uint8_t exchange, uint8_t firstpayload, int response);
> +int ikev2_msg_encrypt_prepare(struct iked_sa *, struct ikev2_payload *,
> +    struct ibuf*, struct ibuf *, struct ike_header *, uint8_t, int);
>  
>  void
>  ikev2_msg_cb(int fd, short event, void *arg)
> @@ -329,8 +331,45 @@ ikev2_msg_id(struct iked *env, struct ik
>   return (id);
>  }
>  
> +/*
> + * Calculate the final sizes of the IKEv2 header and the encrypted payload
> + * header.  This must be done before encryption to make sure the correct
> + * headers are authenticated.
> + */
> +int
> +ikev2_msg_encrypt_prepare(struct iked_sa *sa, struct ikev2_payload *pld,
> +    struct ibuf *buf, struct ibuf *e, struct ike_header *hdr,
> +    uint8_t firstpayload, int fragmentation)
> +{
> + size_t len, ivlen, encrlen, integrlen, blocklen, pldlen, outlen;
> +
> + if (sa == NULL ||
> +    sa->sa_encr == NULL ||
> +    sa->sa_integr == NULL) {
> + log_debug("%s: invalid SA", __func__);
> + return (-1);
> + }
> +
> + len = ibuf_size(e);
> + blocklen = cipher_length(sa->sa_encr);
> + integrlen = hash_length(sa->sa_integr);
> + ivlen = cipher_ivlength(sa->sa_encr);
> + encrlen = roundup(len + 1, blocklen);
> + outlen = cipher_outlength(sa->sa_encr, encrlen);
> + pldlen = ivlen + outlen + integrlen +
> +    (fragmentation ? sizeof(struct ikev2_frag_payload) : 0);
> +
> + if (ikev2_next_payload(pld, pldlen, firstpayload) == -1)
> + return (-1);
> + if (ikev2_set_header(hdr, ibuf_size(buf) + pldlen - sizeof(*hdr)) == -1)
> + return (-1);
> +
> + return (0);
> +}
> +
>  struct ibuf *
> -ikev2_msg_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf *src)
> +ikev2_msg_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf *src,
> +    struct ibuf *aad)
>  {
>   size_t len, ivlen, encrlen, integrlen, blocklen,
>      outlen;
> @@ -390,12 +429,23 @@ ikev2_msg_encrypt(struct iked *env, stru
>   goto done;
>  
>   outlen = ibuf_size(out);
> +
> + /* Add AAD for AEAD ciphers */
> + if (sa->sa_integr->hash_isaead)
> + cipher_aad(sa->sa_encr, ibuf_data(aad),
> +    ibuf_length(aad), &outlen);
> +
>   if (cipher_update(sa->sa_encr, ibuf_data(src), encrlen,
>      ibuf_data(out), &outlen) == -1) {
>   log_info("%s: error updating cipher.", __func__);
>   goto done;
>   }
>  
> + if (cipher_final(sa->sa_encr) == -1) {
> + log_info("%s: encryption failed.", __func__);
> + goto done;
> + }
> +
>   if (outlen && ibuf_add(dst, ibuf_data(out), outlen) != 0)
>   goto done;
>  
> @@ -429,18 +479,13 @@ ikev2_msg_integr(struct iked *env, struc
>   print_hex(ibuf_data(src), 0, ibuf_size(src));
>  
>   if (sa == NULL ||
> +    sa->sa_encr == NULL ||
>      sa->sa_integr == NULL) {
>   log_debug("%s: invalid SA", __func__);
>   return (-1);
>   }
>  
> - if (sa->sa_hdr.sh_initiator)
> - integr = sa->sa_key_iauth;
> - else
> - integr = sa->sa_key_rauth;
> -
>   integrlen = hash_length(sa->sa_integr);
> -
>   log_debug("%s: integrity checksum length %zu", __func__,
>      integrlen);
>  
> @@ -450,21 +495,33 @@ ikev2_msg_integr(struct iked *env, struc
>   if ((tmp = ibuf_new(NULL, hash_keylength(sa->sa_integr))) == NULL)
>   goto done;
>  
> - hash_setkey(sa->sa_integr, ibuf_data(integr), ibuf_size(integr));
> - hash_init(sa->sa_integr);
> - hash_update(sa->sa_integr, ibuf_data(src),
> -    ibuf_size(src) - integrlen);
> - hash_final(sa->sa_integr, ibuf_data(tmp), &tmplen);
> + if (!sa->sa_integr->hash_isaead) {
> + if (sa->sa_hdr.sh_initiator)
> + integr = sa->sa_key_iauth;
> + else
> + integr = sa->sa_key_rauth;
> +
> + hash_setkey(sa->sa_integr, ibuf_data(integr),
> +    ibuf_size(integr));
> + hash_init(sa->sa_integr);
> + hash_update(sa->sa_integr, ibuf_data(src),
> +    ibuf_size(src) - integrlen);
> + hash_final(sa->sa_integr, ibuf_data(tmp), &tmplen);
>  
> - if (tmplen != integrlen) {
> - log_debug("%s: hash failure", __func__);
> - goto done;
> + if (tmplen != integrlen) {
> + log_debug("%s: hash failure", __func__);
> + goto done;
> + }
> + } else {
> + /* Append AEAD tag */
> + if (cipher_gettag(sa->sa_encr, ibuf_data(tmp), ibuf_size(tmp)))
> + goto done;
>   }
>  
>   if ((ptr = ibuf_seek(src,
>      ibuf_size(src) - integrlen, integrlen)) == NULL)
>   goto done;
> - memcpy(ptr, ibuf_data(tmp), tmplen);
> + memcpy(ptr, ibuf_data(tmp), integrlen);
>  
>   print_hex(ibuf_data(tmp), 0, ibuf_size(tmp));
>  
> @@ -481,7 +538,7 @@ ikev2_msg_decrypt(struct iked *env, stru
>  {
>   ssize_t ivlen, encrlen, integrlen, blocklen,
>      outlen, tmplen;
> - uint8_t pad = 0, *ptr;
> + uint8_t pad = 0, *ptr, *integrdata;
>   struct ibuf *integr, *encr, *tmp = NULL, *out = NULL;
>   off_t ivoff, encroff, integroff;
>  
> @@ -524,25 +581,30 @@ ikev2_msg_decrypt(struct iked *env, stru
>   /*
>   * Validate packet checksum
>   */
> - if ((tmp = ibuf_new(NULL, ibuf_length(integr))) == NULL)
> - goto done;
> + if (!sa->sa_integr->hash_isaead) {
> + if ((tmp = ibuf_new(NULL, ibuf_length(integr))) == NULL)
> + goto done;
>  
> - hash_setkey(sa->sa_integr, integr->buf, ibuf_length(integr));
> - hash_init(sa->sa_integr);
> - hash_update(sa->sa_integr, ibuf_data(msg),
> -    ibuf_size(msg) - integrlen);
> - hash_final(sa->sa_integr, tmp->buf, &tmplen);
> + hash_setkey(sa->sa_integr, integr->buf, ibuf_length(integr));
> + hash_init(sa->sa_integr);
> + hash_update(sa->sa_integr, ibuf_data(msg),
> +    ibuf_size(msg) - integrlen);
> + hash_final(sa->sa_integr, tmp->buf, &tmplen);
>  
> - if (memcmp(tmp->buf, ibuf_data(src) + integroff, integrlen) != 0) {
> - log_debug("%s: integrity check failed", __func__);
> - goto done;
> - }
> + integrdata = ibuf_seek(src, integroff, integrlen);
> + if (integrdata == NULL)
> + goto done;
> + if (memcmp(tmp->buf, integrdata, integrlen) != 0) {
> + log_debug("%s: integrity check failed", __func__);
> + goto done;
> + }
>  
> - log_debug("%s: integrity check succeeded", __func__);
> - print_hex(tmp->buf, 0, tmplen);
> + log_debug("%s: integrity check succeeded", __func__);
> + print_hex(tmp->buf, 0, tmplen);
>  
> - ibuf_release(tmp);
> - tmp = NULL;
> + ibuf_release(tmp);
> + tmp = NULL;
> + }
>  
>   /*
>   * Decrypt the payload and strip any padding
> @@ -559,10 +621,31 @@ ikev2_msg_decrypt(struct iked *env, stru
>   goto done;
>   }
>  
> + /* Set AEAD tag */
> + if (sa->sa_integr->hash_isaead) {
> + integrdata = ibuf_seek(src, integroff, integrlen);
> + if (integrdata == NULL)
> + goto done;
> + if (cipher_settag(sa->sa_encr, integrdata, integrlen)) {
> + log_info("%s: failed to set tag.", __func__);
> + goto done;
> + }
> + }
> +
>   if ((out = ibuf_new(NULL, cipher_outlength(sa->sa_encr,
>      encrlen))) == NULL)
>   goto done;
>  
> + /*
> + * Add additional authenticated data for AEAD ciphers
> + */
> + if (sa->sa_integr->hash_isaead) {
> + log_debug("%s: AAD length %zu", __func__, ibuf_length(msg) - ibuf_length(src));
> + print_hex(ibuf_data(msg), 0, ibuf_length(msg) - ibuf_length(src));
> + cipher_aad(sa->sa_encr, ibuf_data(msg),
> +    ibuf_length(msg) - ibuf_length(src), &outlen);
> + }
> +
>   if ((outlen = ibuf_length(out)) != 0) {
>   if (cipher_update(sa->sa_encr, ibuf_data(src) + encroff,
>      encrlen, ibuf_data(out), &outlen) == -1) {
> @@ -574,6 +657,11 @@ ikev2_msg_decrypt(struct iked *env, stru
>   pad = *ptr;
>   }
>  
> + if (cipher_final(sa->sa_encr) == -1) {
> + log_info("%s: decryption failed.", __func__);
> + goto done;
> + }
> +
>   log_debug("%s: decrypted payload length %zd/%zd padding %d",
>      __func__, outlen, encrlen, pad);
>   print_hex(ibuf_data(out), 0, ibuf_size(out));
> @@ -598,6 +686,13 @@ ikev2_check_frag_oversize(struct iked_sa
>   size_t max;
>   size_t ivlen, integrlen, blocklen;
>  
> + if (sa == NULL ||
> +    sa->sa_encr == NULL ||
> +    sa->sa_integr == NULL) {
> + log_debug("%s: invalid SA", __func__);
> + return (-1);
> + }
> +
>   sa_fam = ((struct sockaddr *)&sa->sa_local.addr)->sa_family;
>  
>   max = sa_fam == AF_INET ? IKEV2_MAXLEN_IPV4_FRAG
> @@ -642,18 +737,16 @@ ikev2_msg_send_encrypt(struct iked *env,
>   if ((pld = ikev2_add_payload(buf)) == NULL)
>   goto done;
>  
> + if (ikev2_msg_encrypt_prepare(sa, pld, buf, e, hdr, firstpayload, 0) == -1)
> + goto done;
> +
>   /* Encrypt message and add as an E payload */
> - if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) {
> + if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) {
>   log_debug("%s: encryption failed", __func__);
>   goto done;
>   }
>   if (ibuf_cat(buf, e) != 0)
>   goto done;
> - if (ikev2_next_payload(pld, ibuf_size(e), firstpayload) == -1)
> - goto done;
> -
> - if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1)
> - goto done;
>  
>   /* Add integrity checksum (HMAC) */
>   if (ikev2_msg_integr(env, sa, buf) != 0) {
> @@ -694,6 +787,13 @@ ikev2_send_encrypted_fragments(struct ik
>   uint32_t msgid;
>   int ret = -1;
>  
> + if (sa == NULL ||
> +    sa->sa_encr == NULL ||
> +    sa->sa_integr == NULL) {
> + log_debug("%s: invalid SA", __func__);
> + goto done;
> + }
> +
>   sa_fam = ((struct sockaddr *)&sa->sa_local.addr)->sa_family;
>  
>   left = ibuf_length(in);
> @@ -743,18 +843,16 @@ ikev2_send_encrypted_fragments(struct ik
>   if ((e = ibuf_new(data, MIN(left, max_len))) == NULL) {
>   goto done;
>   }
> - if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) {
> +
> + if (ikev2_msg_encrypt_prepare(sa, pld, buf, e, hdr,
> +    firstpayload, 1) == -1)
> + goto done;
> +
> + if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) {
>   log_debug("%s: encryption failed", __func__);
>   goto done;
>   }
>   if (ibuf_cat(buf, e) != 0)
> - goto done;
> -
> - if (ikev2_next_payload(pld, ibuf_size(e) + sizeof(*frag),
> -    firstpayload) == -1)
> - goto done;
> -
> - if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1)
>   goto done;
>  
>   /* Add integrity checksum (HMAC) */
> Index: parse.y
> ===================================================================
> RCS file: /cvs/src/sbin/iked/parse.y,v
> retrieving revision 1.99
> diff -u -p -r1.99 parse.y
> --- parse.y 30 Apr 2020 21:11:13 -0000 1.99
> +++ parse.y 14 May 2020 23:55:14 -0000
> @@ -198,6 +198,12 @@ const struct ipsec_xf ikeencxfs[] = {
>   { "aes-128", IKEV2_XFORMENCR_AES_CBC, 16, 16 },
>   { "aes-192", IKEV2_XFORMENCR_AES_CBC, 24, 24 },
>   { "aes-256", IKEV2_XFORMENCR_AES_CBC, 32, 32 },
> + { "aes-128-gcm", IKEV2_XFORMENCR_AES_GCM_16, 16, 16, 4, 1 },
> + { "aes-192-gcm", IKEV2_XFORMENCR_AES_GCM_16, 24, 24, 4, 1 },
> + { "aes-256-gcm", IKEV2_XFORMENCR_AES_GCM_16, 32, 32, 4, 1 },
> + { "aes-128-gcm-12", IKEV2_XFORMENCR_AES_GCM_12, 16, 16, 4, 1 },
> + { "aes-192-gcm-12", IKEV2_XFORMENCR_AES_GCM_12, 24, 24, 4, 1 },
> + { "aes-256-gcm-12", IKEV2_XFORMENCR_AES_GCM_12, 32, 32, 4, 1 },
>   { NULL }
>  };
>  
> @@ -2417,6 +2423,17 @@ print_xf(unsigned int id, unsigned int l
>   return ("unknown");
>  }
>  
> +int
> +encxf_noauth(unsigned int id)
> +{
> + int i;
> +
> + for (i = 0; ikeencxfs[i].name != NULL; i++)
> + if (ikeencxfs[i].id == id)
> + return ikeencxfs[i].noauth;
> + return (0);
> +}
> +
>  size_t
>  keylength_xf(unsigned int saproto, unsigned int type, unsigned int id)
>  {
> @@ -2852,21 +2869,37 @@ create_ike(char *name, int af, uint8_t i
>   pol.pol_nproposals++;
>   } else {
>   for (i = 0; i < ike_sa->nxfs; i++) {
> + noauth = 0;
> + for (j = 0; j < ike_sa->xfs[i]->nencxf; j++) {
> + if (ike_sa->xfs[i]->encxf[j]->noauth)
> + noauth++;
> + }
>   if (ike_sa->xfs[i]->nesnxf) {
>   yyerror("cannot use ESN with ikesa.");
>   goto done;
>   }
> + if (noauth && noauth != ike_sa->xfs[i]->nencxf) {
> + yyerror("cannot mix encryption transforms with "
> +    "implicit and non-implicit authentication");
> + goto done;
> + }
> + if (noauth && ike_sa->xfs[i]->nauthxf) {
> + yyerror("authentication is implicit for given "
> +    "encryption transforms");
> + goto done;
> + }
>  
>   if ((p = calloc(1, sizeof(*p))) == NULL)
>   err(1, "%s", __func__);
>  
>   xf = NULL;
>   xfi = 0;
> - copy_transforms(IKEV2_XFORMTYPE_INTEGR,
> -    ike_sa->xfs[i]->authxf,
> -    ike_sa->xfs[i]->nauthxf, &xf, &xfi,
> -    ikev2_default_ike_transforms,
> -    ikev2_default_nike_transforms);
> + if (!ike_sa->xfs[i]->nencxf || !noauth)
> + copy_transforms(IKEV2_XFORMTYPE_INTEGR,
> +    ike_sa->xfs[i]->authxf,
> +    ike_sa->xfs[i]->nauthxf, &xf, &xfi,
> +    ikev2_default_ike_transforms,
> +    ikev2_default_nike_transforms);
>   copy_transforms(IKEV2_XFORMTYPE_ENCR,
>      ike_sa->xfs[i]->encxf,
>      ike_sa->xfs[i]->nencxf, &xf, &xfi,
> Index: policy.c
> ===================================================================
> RCS file: /cvs/src/sbin/iked/policy.c,v
> retrieving revision 1.62
> diff -u -p -r1.62 policy.c
> --- policy.c 13 May 2020 23:03:20 -0000 1.62
> +++ policy.c 14 May 2020 23:55:14 -0000
> @@ -774,7 +774,7 @@ proposals_match(struct iked_proposal *lo
>      struct iked_transform **xforms, int rekey)
>  {
>   struct iked_transform *tpeer, *tlocal;
> - unsigned int i, j, type, score, requiredh = 0;
> + unsigned int i, j, type, score, requiredh = 0, noauth = 0;
>   uint8_t protoid = peer->prop_protoid;
>   uint8_t peerxfs[IKEV2_XFORMTYPE_MAX];
>  
> @@ -782,8 +782,18 @@ proposals_match(struct iked_proposal *lo
>  
>   for (i = 0; i < peer->prop_nxforms; i++) {
>   tpeer = peer->prop_xforms + i;
> + /* If any of the ENC transforms is an AEAD, ignore auth */
> + if (tpeer->xform_type == IKEV2_XFORMTYPE_ENCR &&
> +    encxf_noauth(tpeer->xform_id))
> + noauth = 1;
> + }
> +
> + for (i = 0; i < peer->prop_nxforms; i++) {
> + tpeer = peer->prop_xforms + i;
>   if (tpeer->xform_type > IKEV2_XFORMTYPE_MAX)
>   continue;
> + if (noauth && tpeer->xform_type == IKEV2_XFORMTYPE_INTEGR)
> + return (0);
>  
>   /*
>   * Record all transform types from the peer's proposal,
> @@ -832,7 +842,8 @@ proposals_match(struct iked_proposal *lo
>   for (i = score = 0; i < IKEV2_XFORMTYPE_MAX; i++) {
>   if (protoid == IKEV2_SAPROTO_IKE && xforms[i] == NULL &&
>      (i == IKEV2_XFORMTYPE_ENCR || i == IKEV2_XFORMTYPE_PRF ||
> -     i == IKEV2_XFORMTYPE_INTEGR || i == IKEV2_XFORMTYPE_DH)) {
> +    (!noauth && i == IKEV2_XFORMTYPE_INTEGR) ||
> +    i == IKEV2_XFORMTYPE_DH)) {
>   score = 0;
>   break;
>   } else if (protoid == IKEV2_SAPROTO_AH && xforms[i] == NULL &&
>

Reply | Threaded
Open this post in threaded view
|

Re: iked(8): AES_GCM ciphers for IKE

Tobias Heider-2
In reply to this post by Tobias Heider-2
On Thu, May 14, 2020 at 10:07:30PM +0200, Tobias Heider wrote:

> Hi,
>
> currently iked(8) supports AES-GCM only for ESP.
> The diff below adds the ENCR_AES_GCM_16 and ENCR_AES_GCM_12 variants for IKE.
> (for more information see [1] and [2]).
> Both variants support the 128, 196, and 256 bit key lengths.
>
> The new new ciphers can be configured with:
> - aes-128-gcm, aes-196-gcm and aes-256-gcm for ENCR_AES_GCM_16
> - aes-128-gcm-12, aes-196-gcm-12 and aes-256-gcm-12 for ENCR_AES_GCM_12
>
> It would be nice if we could get some interop testing with different IKEv2
> implementations.  I have so far successfully tested strongswan <-> iked and
> of course iked <-> iked.
>
> Feedback welcome ;)
>
> [1] https://tools.ietf.org/html/rfc5282
> [2] https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml#ikev2-parameters-5

Here is the latest revision of the diff with a few more comments and without aes-192-gcm.
We have so far successfully tested against iked, strongswan and juniper srx.

ok?

Index: crypto.c
===================================================================
RCS file: /cvs/src/sbin/iked/crypto.c,v
retrieving revision 1.27
diff -u -p -r1.27 crypto.c
--- crypto.c 14 May 2020 15:08:30 -0000 1.27
+++ crypto.c 26 May 2020 12:58:28 -0000
@@ -92,7 +92,7 @@ hash_new(uint8_t type, uint16_t id)
  struct iked_hash *hash;
  const EVP_MD *md = NULL;
  HMAC_CTX *ctx = NULL;
- int length = 0, fixedkey = 0, trunc = 0;
+ int length = 0, fixedkey = 0, trunc = 0, isaead = 0;
 
  switch (type) {
  case IKEV2_XFORMTYPE_PRF:
@@ -156,6 +156,14 @@ hash_new(uint8_t type, uint16_t id)
  length = SHA512_DIGEST_LENGTH;
  trunc = 32;
  break;
+ case IKEV2_XFORMAUTH_AES_GCM_12:
+ length = 12;
+ isaead = 1;
+ break;
+ case IKEV2_XFORMAUTH_AES_GCM_16:
+ length = 16;
+ isaead = 1;
+ break;
  case IKEV2_XFORMAUTH_NONE:
  case IKEV2_XFORMAUTH_DES_MAC:
  case IKEV2_XFORMAUTH_KPDK_MD5:
@@ -177,7 +185,7 @@ hash_new(uint8_t type, uint16_t id)
     print_map(id, ikev2_xformtype_map));
  break;
  }
- if (md == NULL)
+ if (!isaead && md == NULL)
  return (NULL);
 
  if ((hash = calloc(1, sizeof(*hash))) == NULL) {
@@ -192,6 +200,10 @@ hash_new(uint8_t type, uint16_t id)
  hash->hash_trunc = trunc;
  hash->hash_length = length;
  hash->hash_fixedkey = fixedkey;
+ hash->hash_isaead = isaead;
+
+ if (isaead)
+ return (hash);
 
  if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
  log_debug("%s: alloc hash ctx", __func__);
@@ -276,6 +288,7 @@ cipher_new(uint8_t type, uint16_t id, ui
  const EVP_CIPHER *cipher = NULL;
  EVP_CIPHER_CTX *ctx = NULL;
  int length = 0, fixedkey = 0, ivlength = 0;
+ int saltlength = 0, authid = 0;
 
  switch (type) {
  case IKEV2_XFORMTYPE_ENCR:
@@ -309,6 +322,36 @@ cipher_new(uint8_t type, uint16_t id, ui
  ivlength = EVP_CIPHER_iv_length(cipher);
  fixedkey = EVP_CIPHER_key_length(cipher);
  break;
+ case IKEV2_XFORMENCR_AES_GCM_16:
+ case IKEV2_XFORMENCR_AES_GCM_12:
+ switch (id_length) {
+ case 128:
+ cipher = EVP_aes_128_gcm();
+ break;
+ case 256:
+ cipher = EVP_aes_256_gcm();
+ break;
+ default:
+ log_debug("%s: invalid key length %d"
+    " for cipher %s", __func__, id_length,
+    print_map(id, ikev2_xformencr_map));
+ break;
+ }
+ if (cipher == NULL)
+ break;
+ switch(id) {
+ case IKEV2_XFORMENCR_AES_GCM_16:
+ authid = IKEV2_XFORMAUTH_AES_GCM_16;
+ break;
+ case IKEV2_XFORMENCR_AES_GCM_12:
+ authid = IKEV2_XFORMAUTH_AES_GCM_12;
+ break;
+ }
+ length = EVP_CIPHER_block_size(cipher);
+ ivlength = 8;
+ saltlength = 4;
+ fixedkey = EVP_CIPHER_key_length(cipher) + saltlength;
+ break;
  case IKEV2_XFORMENCR_DES_IV64:
  case IKEV2_XFORMENCR_DES:
  case IKEV2_XFORMENCR_RC5:
@@ -346,6 +389,8 @@ cipher_new(uint8_t type, uint16_t id, ui
  encr->encr_length = length;
  encr->encr_fixedkey = fixedkey;
  encr->encr_ivlength = ivlength ? ivlength : length;
+ encr->encr_saltlength = saltlength;
+ encr->encr_authid = authid;
 
  if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
  log_debug("%s: alloc cipher ctx", __func__);
@@ -392,6 +437,20 @@ cipher_setiv(struct iked_cipher *encr, v
  return (encr->encr_iv);
 }
 
+int
+cipher_settag(struct iked_cipher *encr, uint8_t *data, size_t len)
+{
+ return (EVP_CIPHER_CTX_ctrl(encr->encr_ctx,
+    EVP_CTRL_GCM_SET_TAG, len, data) != 1);
+}
+
+int
+cipher_gettag(struct iked_cipher *encr, uint8_t *data, size_t len)
+{
+ return (EVP_CIPHER_CTX_ctrl(encr->encr_ctx,
+    EVP_CTRL_GCM_GET_TAG, len, data) != 1);
+}
+
 void
 cipher_free(struct iked_cipher *encr)
 {
@@ -409,11 +468,33 @@ cipher_free(struct iked_cipher *encr)
 int
 cipher_init(struct iked_cipher *encr, int enc)
 {
+ struct ibuf *nonce = NULL;
+ int ret = -1;
+
  if (EVP_CipherInit_ex(encr->encr_ctx, encr->encr_priv, NULL,
-    ibuf_data(encr->encr_key), ibuf_data(encr->encr_iv), enc) != 1)
+    NULL, NULL, enc) != 1)
  return (-1);
+ if (encr->encr_saltlength > 0) {
+ /* For AEADs the nonce is salt + IV  (see RFC5282) */
+ nonce = ibuf_new(ibuf_data(encr->encr_key) +
+    ibuf_size(encr->encr_key) - encr->encr_saltlength,
+    encr->encr_saltlength);
+ if (nonce == NULL)
+ return (-1);
+ if (ibuf_add(nonce, ibuf_data(encr->encr_iv) , ibuf_size(encr->encr_iv)) != 0)
+ goto done;
+ if (EVP_CipherInit_ex(encr->encr_ctx, NULL, NULL,
+    ibuf_data(encr->encr_key), ibuf_data(nonce), enc) != 1)
+ goto done;
+ } else
+ if (EVP_CipherInit_ex(encr->encr_ctx, NULL, NULL,
+    ibuf_data(encr->encr_key), ibuf_data(encr->encr_iv), enc) != 1)
+ return (-1);
  EVP_CIPHER_CTX_set_padding(encr->encr_ctx, 0);
- return (0);
+ ret = 0;
+ done:
+ ibuf_free(nonce);
+ return (ret);
 }
 
 int
@@ -426,6 +507,20 @@ int
 cipher_init_decrypt(struct iked_cipher *encr)
 {
  return (cipher_init(encr, 0));
+}
+
+void
+cipher_aad(struct iked_cipher *encr, void *in, size_t inlen,
+    size_t *outlen)
+{
+ int olen = 0;
+
+ if (EVP_CipherUpdate(encr->encr_ctx, NULL, &olen, in, inlen) != 1) {
+ ca_sslerror(__func__);
+ *outlen = 0;
+ return;
+ }
+ *outlen = (size_t)olen;
 }
 
 int
Index: iked.conf.5
===================================================================
RCS file: /cvs/src/sbin/iked/iked.conf.5,v
retrieving revision 1.68
diff -u -p -r1.68 iked.conf.5
--- iked.conf.5 1 May 2020 17:44:02 -0000 1.68
+++ iked.conf.5 26 May 2020 12:58:28 -0000
@@ -864,9 +864,11 @@ keyword:
 .It Li aes-128-ctr Ta "160 bits" Ta "[ESP only]"
 .It Li aes-192-ctr Ta "224 bits" Ta "[ESP only]"
 .It Li aes-256-ctr Ta "288 bits" Ta "[ESP only]"
-.It Li aes-128-gcm Ta "160 bits" Ta "[ESP only]"
+.It Li aes-128-gcm Ta "160 bits" Ta ""
 .It Li aes-192-gcm Ta "224 bits" Ta "[ESP only]"
-.It Li aes-256-gcm Ta "288 bits" Ta "[ESP only]"
+.It Li aes-256-gcm Ta "288 bits" Ta ""
+.It Li aes-128-gcm-12 Ta "160 bits" Ta "[IKE only]"
+.It Li aes-256-gcm-12 Ta "288 bits" Ta "[IKE only]"
 .It Li blowfish Ta "160 bits" Ta "[ESP only]"
 .It Li cast Ta "128 bits" Ta "[ESP only]"
 .It Li chacha20-poly1305 Ta "288 bits" Ta "[ESP only]"
Index: iked.h
===================================================================
RCS file: /cvs/src/sbin/iked/iked.h,v
retrieving revision 1.150
diff -u -p -r1.150 iked.h
--- iked.h 14 May 2020 15:08:30 -0000 1.150
+++ iked.h 26 May 2020 12:58:28 -0000
@@ -309,6 +309,7 @@ struct iked_hash {
  size_t hash_length; /* Output length */
  size_t hash_trunc; /* Truncate the output length */
  struct iked_hash *hash_prf; /* PRF pointer */
+ int hash_isaead;
 };
 
 struct iked_cipher {
@@ -321,6 +322,8 @@ struct iked_cipher {
  struct ibuf *encr_iv; /* Initialization Vector */
  size_t encr_ivlength; /* IV length */
  size_t encr_length; /* Block length */
+ size_t encr_saltlength; /* IV salt length */
+ uint16_t encr_authid; /* ID of associated authentication */
 };
 
 struct iked_dsa {
@@ -841,10 +844,13 @@ struct ibuf *
  cipher_setkey(struct iked_cipher *, void *, size_t);
 struct ibuf *
  cipher_setiv(struct iked_cipher *, void *, size_t);
+int cipher_settag(struct iked_cipher *, uint8_t *, size_t);
+int cipher_gettag(struct iked_cipher *, uint8_t *, size_t);
 void cipher_free(struct iked_cipher *);
 int cipher_init(struct iked_cipher *, int);
 int cipher_init_encrypt(struct iked_cipher *);
 int cipher_init_decrypt(struct iked_cipher *);
+void cipher_aad(struct iked_cipher *, void *, size_t, size_t *);
 int cipher_update(struct iked_cipher *, void *, size_t, void *, size_t *);
 int cipher_final(struct iked_cipher *);
 size_t cipher_length(struct iked_cipher *);
@@ -933,7 +939,8 @@ int ikev2_msg_send(struct iked *, struc
 int ikev2_msg_send_encrypt(struct iked *, struct iked_sa *,
     struct ibuf **, uint8_t, uint8_t, int);
 struct ibuf
- *ikev2_msg_encrypt(struct iked *, struct iked_sa *, struct ibuf *);
+ *ikev2_msg_encrypt(struct iked *, struct iked_sa *, struct ibuf *,
+    struct ibuf *);
 struct ibuf *
  ikev2_msg_decrypt(struct iked *, struct iked_sa *,
     struct ibuf *, struct ibuf *);
@@ -1129,5 +1136,6 @@ void print_policy(struct iked_policy *)
 size_t keylength_xf(unsigned int, unsigned int, unsigned int);
 size_t noncelength_xf(unsigned int, unsigned int);
 int cmdline_symset(char *);
+int encxf_noauth(unsigned int);
 
 #endif /* IKED_H */
Index: ikev2.c
===================================================================
RCS file: /cvs/src/sbin/iked/ikev2.c,v
retrieving revision 1.225
diff -u -p -r1.225 ikev2.c
--- ikev2.c 11 May 2020 20:11:35 -0000 1.225
+++ ikev2.c 26 May 2020 12:58:33 -0000
@@ -4473,7 +4473,7 @@ ikev2_send_informational(struct iked *en
  goto done;
 
  /* Encrypt message and add as an E payload */
- if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) {
+ if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) {
  log_debug("%s: encryption failed", __func__);
  goto done;
  }
@@ -4666,6 +4666,16 @@ ikev2_sa_initiator(struct iked *env, str
  }
  }
 
+ /* For AEAD ciphers integrity is implicit */
+ if (sa->sa_encr->encr_authid && sa->sa_integr == NULL) {
+ if ((sa->sa_integr = hash_new(IKEV2_XFORMTYPE_INTEGR,
+    sa->sa_encr->encr_authid)) == NULL) {
+ log_info("%s: failed to get AEAD integr",
+    SPI_SA(sa, __func__));
+ return (-1);
+ }
+ }
+
  if (sa->sa_prf == NULL) {
  if ((xform = config_findtransform(&sa->sa_proposals,
     IKEV2_XFORMTYPE_PRF, 0)) == NULL) {
@@ -4840,6 +4850,16 @@ ikev2_sa_responder(struct iked *env, str
  }
  }
 
+ /* For AEAD ciphers integrity is implicit */
+ if (sa->sa_encr->encr_authid && sa->sa_integr == NULL) {
+ if ((sa->sa_integr = hash_new(IKEV2_XFORMTYPE_INTEGR,
+    sa->sa_encr->encr_authid)) == NULL) {
+ log_info("%s: failed to get AEAD integr",
+    SPI_SA(sa, __func__));
+ return (-1);
+ }
+ }
+
  if (sa->sa_prf == NULL) {
  if ((xform = config_findtransform(&sa->sa_proposals,
     IKEV2_XFORMTYPE_PRF, 0)) == NULL) {
@@ -4883,6 +4903,7 @@ ikev2_sa_keys(struct iked *env, struct i
  size_t nonceminlen, ilen, rlen, tmplen;
  uint64_t ispi, rspi;
  int ret = -1;
+ int isaead = 0;
 
  ninr = dhsecret = skeyseed = s = t = NULL;
 
@@ -4895,6 +4916,9 @@ ikev2_sa_keys(struct iked *env, struct i
  return (-1);
  }
 
+ /* For AEADs no auth keys are required (see RFC 5282) */
+ isaead = !!integr->hash_isaead;
+
  if (prf->hash_fixedkey)
  nonceminlen = prf->hash_fixedkey;
  else
@@ -5026,13 +5050,13 @@ ikev2_sa_keys(struct iked *env, struct i
  * Get the size of the key material we need and the number
  * of rounds we need to run the prf+ function.
  */
- ilen = hash_length(prf) + /* SK_d */
-    hash_keylength(integr) + /* SK_ai */
-    hash_keylength(integr) + /* SK_ar */
-    cipher_keylength(encr) + /* SK_ei */
-    cipher_keylength(encr) + /* SK_er */
-    hash_keylength(prf) + /* SK_pi */
-    hash_keylength(prf); /* SK_pr */
+ ilen = hash_length(prf) + /* SK_d */
+    (isaead ? 0 : hash_keylength(integr)) + /* SK_ai */
+    (isaead ? 0 : hash_keylength(integr)) + /* SK_ar */
+    cipher_keylength(encr) + /* SK_ei */
+    cipher_keylength(encr) + /* SK_er */
+    hash_keylength(prf) + /* SK_pi */
+    hash_keylength(prf); /* SK_pr */
 
  if ((t = ikev2_prfplus(prf, skeyseed, s, ilen)) == NULL) {
  log_info("%s: failed to get IKE SA key material",
@@ -5042,8 +5066,10 @@ ikev2_sa_keys(struct iked *env, struct i
 
  /* ibuf_get() returns a new buffer from the next read offset */
  if ((sa->sa_key_d = ibuf_get(t, hash_length(prf))) == NULL ||
-    (sa->sa_key_iauth = ibuf_get(t, hash_keylength(integr))) == NULL ||
-    (sa->sa_key_rauth = ibuf_get(t, hash_keylength(integr))) == NULL ||
+    (!isaead &&
+    (sa->sa_key_iauth = ibuf_get(t, hash_keylength(integr))) == NULL) ||
+    (!isaead &&
+    (sa->sa_key_rauth = ibuf_get(t, hash_keylength(integr))) == NULL) ||
     (sa->sa_key_iencr = ibuf_get(t, cipher_keylength(encr))) == NULL ||
     (sa->sa_key_rencr = ibuf_get(t, cipher_keylength(encr))) == NULL ||
     (sa->sa_key_iprf = ibuf_get(t, hash_length(prf))) == NULL ||
@@ -5055,12 +5081,16 @@ ikev2_sa_keys(struct iked *env, struct i
  log_debug("%s: SK_d with %zu bytes", __func__,
     ibuf_length(sa->sa_key_d));
  print_hex(sa->sa_key_d->buf, 0, ibuf_length(sa->sa_key_d));
- log_debug("%s: SK_ai with %zu bytes", __func__,
-    ibuf_length(sa->sa_key_iauth));
- print_hex(sa->sa_key_iauth->buf, 0, ibuf_length(sa->sa_key_iauth));
- log_debug("%s: SK_ar with %zu bytes", __func__,
-    ibuf_length(sa->sa_key_rauth));
- print_hex(sa->sa_key_rauth->buf, 0, ibuf_length(sa->sa_key_rauth));
+ if (!isaead) {
+ log_debug("%s: SK_ai with %zu bytes", __func__,
+    ibuf_length(sa->sa_key_iauth));
+ print_hex(sa->sa_key_iauth->buf, 0,
+    ibuf_length(sa->sa_key_iauth));
+ log_debug("%s: SK_ar with %zu bytes", __func__,
+    ibuf_length(sa->sa_key_rauth));
+ print_hex(sa->sa_key_rauth->buf, 0,
+    ibuf_length(sa->sa_key_rauth));
+ }
  log_debug("%s: SK_ei with %zu bytes", __func__,
     ibuf_length(sa->sa_key_iencr));
  print_hex(sa->sa_key_iencr->buf, 0, ibuf_length(sa->sa_key_iencr));
Index: ikev2.h
===================================================================
RCS file: /cvs/src/sbin/iked/ikev2.h,v
retrieving revision 1.32
diff -u -p -r1.32 ikev2.h
--- ikev2.h 28 Apr 2020 15:18:52 -0000 1.32
+++ ikev2.h 26 May 2020 12:58:34 -0000
@@ -225,6 +225,11 @@ extern struct iked_constmap ikev2_xformp
 #define IKEV2_XFORMAUTH_HMAC_SHA2_384_192 13 /* RFC4868 */
 #define IKEV2_XFORMAUTH_HMAC_SHA2_512_256 14 /* RFC4868 */
 
+/* Placeholders for AEAD ciphers (only used internally) */
+#define IKEV2_XFORMAUTH_AES_GCM_8 2018 /* internal */
+#define IKEV2_XFORMAUTH_AES_GCM_12 2019 /* internal */
+#define IKEV2_XFORMAUTH_AES_GCM_16 2020 /* internal */
+
 extern struct iked_constmap ikev2_xformauth_map[];
 
 #define IKEV2_XFORMDH_NONE 0 /* No DH */
Index: ikev2_msg.c
===================================================================
RCS file: /cvs/src/sbin/iked/ikev2_msg.c,v
retrieving revision 1.67
diff -u -p -r1.67 ikev2_msg.c
--- ikev2_msg.c 14 May 2020 15:08:30 -0000 1.67
+++ ikev2_msg.c 26 May 2020 12:58:34 -0000
@@ -51,6 +51,8 @@ void ikev2_msg_retransmit_timeout(struc
 int ikev2_check_frag_oversize(struct iked_sa *sa, struct ibuf *buf);
 int ikev2_send_encrypted_fragments(struct iked *env, struct iked_sa *sa,
     struct ibuf *in,uint8_t exchange, uint8_t firstpayload, int response);
+int ikev2_msg_encrypt_prepare(struct iked_sa *, struct ikev2_payload *,
+    struct ibuf*, struct ibuf *, struct ike_header *, uint8_t, int);
 
 void
 ikev2_msg_cb(int fd, short event, void *arg)
@@ -329,8 +331,45 @@ ikev2_msg_id(struct iked *env, struct ik
  return (id);
 }
 
+/*
+ * Calculate the final sizes of the IKEv2 header and the encrypted payload
+ * header.  This must be done before encryption to make sure the correct
+ * headers are authenticated.
+ */
+int
+ikev2_msg_encrypt_prepare(struct iked_sa *sa, struct ikev2_payload *pld,
+    struct ibuf *buf, struct ibuf *e, struct ike_header *hdr,
+    uint8_t firstpayload, int fragmentation)
+{
+ size_t len, ivlen, encrlen, integrlen, blocklen, pldlen, outlen;
+
+ if (sa == NULL ||
+    sa->sa_encr == NULL ||
+    sa->sa_integr == NULL) {
+ log_debug("%s: invalid SA", __func__);
+ return (-1);
+ }
+
+ len = ibuf_size(e);
+ blocklen = cipher_length(sa->sa_encr);
+ integrlen = hash_length(sa->sa_integr);
+ ivlen = cipher_ivlength(sa->sa_encr);
+ encrlen = roundup(len + 1, blocklen);
+ outlen = cipher_outlength(sa->sa_encr, encrlen);
+ pldlen = ivlen + outlen + integrlen +
+    (fragmentation ? sizeof(struct ikev2_frag_payload) : 0);
+
+ if (ikev2_next_payload(pld, pldlen, firstpayload) == -1)
+ return (-1);
+ if (ikev2_set_header(hdr, ibuf_size(buf) + pldlen - sizeof(*hdr)) == -1)
+ return (-1);
+
+ return (0);
+}
+
 struct ibuf *
-ikev2_msg_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf *src)
+ikev2_msg_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf *src,
+    struct ibuf *aad)
 {
  size_t len, ivlen, encrlen, integrlen, blocklen,
     outlen;
@@ -390,12 +429,23 @@ ikev2_msg_encrypt(struct iked *env, stru
  goto done;
 
  outlen = ibuf_size(out);
+
+ /* Add AAD for AEAD ciphers */
+ if (sa->sa_integr->hash_isaead)
+ cipher_aad(sa->sa_encr, ibuf_data(aad),
+    ibuf_length(aad), &outlen);
+
  if (cipher_update(sa->sa_encr, ibuf_data(src), encrlen,
     ibuf_data(out), &outlen) == -1) {
  log_info("%s: error updating cipher.", __func__);
  goto done;
  }
 
+ if (cipher_final(sa->sa_encr) == -1) {
+ log_info("%s: encryption failed.", __func__);
+ goto done;
+ }
+
  if (outlen && ibuf_add(dst, ibuf_data(out), outlen) != 0)
  goto done;
 
@@ -429,18 +479,13 @@ ikev2_msg_integr(struct iked *env, struc
  print_hex(ibuf_data(src), 0, ibuf_size(src));
 
  if (sa == NULL ||
+    sa->sa_encr == NULL ||
     sa->sa_integr == NULL) {
  log_debug("%s: invalid SA", __func__);
  return (-1);
  }
 
- if (sa->sa_hdr.sh_initiator)
- integr = sa->sa_key_iauth;
- else
- integr = sa->sa_key_rauth;
-
  integrlen = hash_length(sa->sa_integr);
-
  log_debug("%s: integrity checksum length %zu", __func__,
     integrlen);
 
@@ -450,21 +495,33 @@ ikev2_msg_integr(struct iked *env, struc
  if ((tmp = ibuf_new(NULL, hash_keylength(sa->sa_integr))) == NULL)
  goto done;
 
- hash_setkey(sa->sa_integr, ibuf_data(integr), ibuf_size(integr));
- hash_init(sa->sa_integr);
- hash_update(sa->sa_integr, ibuf_data(src),
-    ibuf_size(src) - integrlen);
- hash_final(sa->sa_integr, ibuf_data(tmp), &tmplen);
+ if (!sa->sa_integr->hash_isaead) {
+ if (sa->sa_hdr.sh_initiator)
+ integr = sa->sa_key_iauth;
+ else
+ integr = sa->sa_key_rauth;
+
+ hash_setkey(sa->sa_integr, ibuf_data(integr),
+    ibuf_size(integr));
+ hash_init(sa->sa_integr);
+ hash_update(sa->sa_integr, ibuf_data(src),
+    ibuf_size(src) - integrlen);
+ hash_final(sa->sa_integr, ibuf_data(tmp), &tmplen);
 
- if (tmplen != integrlen) {
- log_debug("%s: hash failure", __func__);
- goto done;
+ if (tmplen != integrlen) {
+ log_debug("%s: hash failure", __func__);
+ goto done;
+ }
+ } else {
+ /* Append AEAD tag */
+ if (cipher_gettag(sa->sa_encr, ibuf_data(tmp), ibuf_size(tmp)))
+ goto done;
  }
 
  if ((ptr = ibuf_seek(src,
     ibuf_size(src) - integrlen, integrlen)) == NULL)
  goto done;
- memcpy(ptr, ibuf_data(tmp), tmplen);
+ memcpy(ptr, ibuf_data(tmp), integrlen);
 
  print_hex(ibuf_data(tmp), 0, ibuf_size(tmp));
 
@@ -481,7 +538,7 @@ ikev2_msg_decrypt(struct iked *env, stru
 {
  ssize_t ivlen, encrlen, integrlen, blocklen,
     outlen, tmplen;
- uint8_t pad = 0, *ptr;
+ uint8_t pad = 0, *ptr, *integrdata;
  struct ibuf *integr, *encr, *tmp = NULL, *out = NULL;
  off_t ivoff, encroff, integroff;
 
@@ -524,25 +581,30 @@ ikev2_msg_decrypt(struct iked *env, stru
  /*
  * Validate packet checksum
  */
- if ((tmp = ibuf_new(NULL, ibuf_length(integr))) == NULL)
- goto done;
+ if (!sa->sa_integr->hash_isaead) {
+ if ((tmp = ibuf_new(NULL, ibuf_length(integr))) == NULL)
+ goto done;
 
- hash_setkey(sa->sa_integr, integr->buf, ibuf_length(integr));
- hash_init(sa->sa_integr);
- hash_update(sa->sa_integr, ibuf_data(msg),
-    ibuf_size(msg) - integrlen);
- hash_final(sa->sa_integr, tmp->buf, &tmplen);
+ hash_setkey(sa->sa_integr, integr->buf, ibuf_length(integr));
+ hash_init(sa->sa_integr);
+ hash_update(sa->sa_integr, ibuf_data(msg),
+    ibuf_size(msg) - integrlen);
+ hash_final(sa->sa_integr, tmp->buf, &tmplen);
 
- if (memcmp(tmp->buf, ibuf_data(src) + integroff, integrlen) != 0) {
- log_debug("%s: integrity check failed", __func__);
- goto done;
- }
+ integrdata = ibuf_seek(src, integroff, integrlen);
+ if (integrdata == NULL)
+ goto done;
+ if (memcmp(tmp->buf, integrdata, integrlen) != 0) {
+ log_debug("%s: integrity check failed", __func__);
+ goto done;
+ }
 
- log_debug("%s: integrity check succeeded", __func__);
- print_hex(tmp->buf, 0, tmplen);
+ log_debug("%s: integrity check succeeded", __func__);
+ print_hex(tmp->buf, 0, tmplen);
 
- ibuf_release(tmp);
- tmp = NULL;
+ ibuf_release(tmp);
+ tmp = NULL;
+ }
 
  /*
  * Decrypt the payload and strip any padding
@@ -559,10 +621,31 @@ ikev2_msg_decrypt(struct iked *env, stru
  goto done;
  }
 
+ /* Set AEAD tag */
+ if (sa->sa_integr->hash_isaead) {
+ integrdata = ibuf_seek(src, integroff, integrlen);
+ if (integrdata == NULL)
+ goto done;
+ if (cipher_settag(sa->sa_encr, integrdata, integrlen)) {
+ log_info("%s: failed to set tag.", __func__);
+ goto done;
+ }
+ }
+
  if ((out = ibuf_new(NULL, cipher_outlength(sa->sa_encr,
     encrlen))) == NULL)
  goto done;
 
+ /*
+ * Add additional authenticated data for AEAD ciphers
+ */
+ if (sa->sa_integr->hash_isaead) {
+ log_debug("%s: AAD length %zu", __func__, ibuf_length(msg) - ibuf_length(src));
+ print_hex(ibuf_data(msg), 0, ibuf_length(msg) - ibuf_length(src));
+ cipher_aad(sa->sa_encr, ibuf_data(msg),
+    ibuf_length(msg) - ibuf_length(src), &outlen);
+ }
+
  if ((outlen = ibuf_length(out)) != 0) {
  if (cipher_update(sa->sa_encr, ibuf_data(src) + encroff,
     encrlen, ibuf_data(out), &outlen) == -1) {
@@ -574,6 +657,11 @@ ikev2_msg_decrypt(struct iked *env, stru
  pad = *ptr;
  }
 
+ if (cipher_final(sa->sa_encr) == -1) {
+ log_info("%s: decryption failed.", __func__);
+ goto done;
+ }
+
  log_debug("%s: decrypted payload length %zd/%zd padding %d",
     __func__, outlen, encrlen, pad);
  print_hex(ibuf_data(out), 0, ibuf_size(out));
@@ -598,6 +686,13 @@ ikev2_check_frag_oversize(struct iked_sa
  size_t max;
  size_t ivlen, integrlen, blocklen;
 
+ if (sa == NULL ||
+    sa->sa_encr == NULL ||
+    sa->sa_integr == NULL) {
+ log_debug("%s: invalid SA", __func__);
+ return (-1);
+ }
+
  sa_fam = ((struct sockaddr *)&sa->sa_local.addr)->sa_family;
 
  max = sa_fam == AF_INET ? IKEV2_MAXLEN_IPV4_FRAG
@@ -642,18 +737,16 @@ ikev2_msg_send_encrypt(struct iked *env,
  if ((pld = ikev2_add_payload(buf)) == NULL)
  goto done;
 
+ if (ikev2_msg_encrypt_prepare(sa, pld, buf, e, hdr, firstpayload, 0) == -1)
+ goto done;
+
  /* Encrypt message and add as an E payload */
- if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) {
+ if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) {
  log_debug("%s: encryption failed", __func__);
  goto done;
  }
  if (ibuf_cat(buf, e) != 0)
  goto done;
- if (ikev2_next_payload(pld, ibuf_size(e), firstpayload) == -1)
- goto done;
-
- if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1)
- goto done;
 
  /* Add integrity checksum (HMAC) */
  if (ikev2_msg_integr(env, sa, buf) != 0) {
@@ -694,6 +787,13 @@ ikev2_send_encrypted_fragments(struct ik
  uint32_t msgid;
  int ret = -1;
 
+ if (sa == NULL ||
+    sa->sa_encr == NULL ||
+    sa->sa_integr == NULL) {
+ log_debug("%s: invalid SA", __func__);
+ goto done;
+ }
+
  sa_fam = ((struct sockaddr *)&sa->sa_local.addr)->sa_family;
 
  left = ibuf_length(in);
@@ -743,18 +843,16 @@ ikev2_send_encrypted_fragments(struct ik
  if ((e = ibuf_new(data, MIN(left, max_len))) == NULL) {
  goto done;
  }
- if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) {
+
+ if (ikev2_msg_encrypt_prepare(sa, pld, buf, e, hdr,
+    firstpayload, 1) == -1)
+ goto done;
+
+ if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) {
  log_debug("%s: encryption failed", __func__);
  goto done;
  }
  if (ibuf_cat(buf, e) != 0)
- goto done;
-
- if (ikev2_next_payload(pld, ibuf_size(e) + sizeof(*frag),
-    firstpayload) == -1)
- goto done;
-
- if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1)
  goto done;
 
  /* Add integrity checksum (HMAC) */
Index: parse.y
===================================================================
RCS file: /cvs/src/sbin/iked/parse.y,v
retrieving revision 1.99
diff -u -p -r1.99 parse.y
--- parse.y 30 Apr 2020 21:11:13 -0000 1.99
+++ parse.y 26 May 2020 12:58:35 -0000
@@ -198,6 +198,10 @@ const struct ipsec_xf ikeencxfs[] = {
  { "aes-128", IKEV2_XFORMENCR_AES_CBC, 16, 16 },
  { "aes-192", IKEV2_XFORMENCR_AES_CBC, 24, 24 },
  { "aes-256", IKEV2_XFORMENCR_AES_CBC, 32, 32 },
+ { "aes-128-gcm", IKEV2_XFORMENCR_AES_GCM_16, 16, 16, 4, 1 },
+ { "aes-256-gcm", IKEV2_XFORMENCR_AES_GCM_16, 32, 32, 4, 1 },
+ { "aes-128-gcm-12", IKEV2_XFORMENCR_AES_GCM_12, 16, 16, 4, 1 },
+ { "aes-256-gcm-12", IKEV2_XFORMENCR_AES_GCM_12, 32, 32, 4, 1 },
  { NULL }
 };
 
@@ -2417,6 +2421,17 @@ print_xf(unsigned int id, unsigned int l
  return ("unknown");
 }
 
+int
+encxf_noauth(unsigned int id)
+{
+ int i;
+
+ for (i = 0; ikeencxfs[i].name != NULL; i++)
+ if (ikeencxfs[i].id == id)
+ return ikeencxfs[i].noauth;
+ return (0);
+}
+
 size_t
 keylength_xf(unsigned int saproto, unsigned int type, unsigned int id)
 {
@@ -2852,21 +2867,37 @@ create_ike(char *name, int af, uint8_t i
  pol.pol_nproposals++;
  } else {
  for (i = 0; i < ike_sa->nxfs; i++) {
+ noauth = 0;
+ for (j = 0; j < ike_sa->xfs[i]->nencxf; j++) {
+ if (ike_sa->xfs[i]->encxf[j]->noauth)
+ noauth++;
+ }
  if (ike_sa->xfs[i]->nesnxf) {
  yyerror("cannot use ESN with ikesa.");
  goto done;
  }
+ if (noauth && noauth != ike_sa->xfs[i]->nencxf) {
+ yyerror("cannot mix encryption transforms with "
+    "implicit and non-implicit authentication");
+ goto done;
+ }
+ if (noauth && ike_sa->xfs[i]->nauthxf) {
+ yyerror("authentication is implicit for given "
+    "encryption transforms");
+ goto done;
+ }
 
  if ((p = calloc(1, sizeof(*p))) == NULL)
  err(1, "%s", __func__);
 
  xf = NULL;
  xfi = 0;
- copy_transforms(IKEV2_XFORMTYPE_INTEGR,
-    ike_sa->xfs[i]->authxf,
-    ike_sa->xfs[i]->nauthxf, &xf, &xfi,
-    ikev2_default_ike_transforms,
-    ikev2_default_nike_transforms);
+ if (!ike_sa->xfs[i]->nencxf || !noauth)
+ copy_transforms(IKEV2_XFORMTYPE_INTEGR,
+    ike_sa->xfs[i]->authxf,
+    ike_sa->xfs[i]->nauthxf, &xf, &xfi,
+    ikev2_default_ike_transforms,
+    ikev2_default_nike_transforms);
  copy_transforms(IKEV2_XFORMTYPE_ENCR,
     ike_sa->xfs[i]->encxf,
     ike_sa->xfs[i]->nencxf, &xf, &xfi,
Index: policy.c
===================================================================
RCS file: /cvs/src/sbin/iked/policy.c,v
retrieving revision 1.62
diff -u -p -r1.62 policy.c
--- policy.c 13 May 2020 23:03:20 -0000 1.62
+++ policy.c 26 May 2020 12:58:35 -0000
@@ -774,7 +774,7 @@ proposals_match(struct iked_proposal *lo
     struct iked_transform **xforms, int rekey)
 {
  struct iked_transform *tpeer, *tlocal;
- unsigned int i, j, type, score, requiredh = 0;
+ unsigned int i, j, type, score, requiredh = 0, noauth = 0;
  uint8_t protoid = peer->prop_protoid;
  uint8_t peerxfs[IKEV2_XFORMTYPE_MAX];
 
@@ -782,8 +782,18 @@ proposals_match(struct iked_proposal *lo
 
  for (i = 0; i < peer->prop_nxforms; i++) {
  tpeer = peer->prop_xforms + i;
+ /* If any of the ENC transforms is an AEAD, ignore auth */
+ if (tpeer->xform_type == IKEV2_XFORMTYPE_ENCR &&
+    encxf_noauth(tpeer->xform_id))
+ noauth = 1;
+ }
+
+ for (i = 0; i < peer->prop_nxforms; i++) {
+ tpeer = peer->prop_xforms + i;
  if (tpeer->xform_type > IKEV2_XFORMTYPE_MAX)
  continue;
+ if (noauth && tpeer->xform_type == IKEV2_XFORMTYPE_INTEGR)
+ return (0);
 
  /*
  * Record all transform types from the peer's proposal,
@@ -832,7 +842,8 @@ proposals_match(struct iked_proposal *lo
  for (i = score = 0; i < IKEV2_XFORMTYPE_MAX; i++) {
  if (protoid == IKEV2_SAPROTO_IKE && xforms[i] == NULL &&
     (i == IKEV2_XFORMTYPE_ENCR || i == IKEV2_XFORMTYPE_PRF ||
-     i == IKEV2_XFORMTYPE_INTEGR || i == IKEV2_XFORMTYPE_DH)) {
+    (!noauth && i == IKEV2_XFORMTYPE_INTEGR) ||
+    i == IKEV2_XFORMTYPE_DH)) {
  score = 0;
  break;
  } else if (protoid == IKEV2_SAPROTO_AH && xforms[i] == NULL &&