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 via email to