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 &&

Reply via email to