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
@@ -409,11 +471,33 @@
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 @@
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
@@ -841,10 +844,13 @@
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 *);
@@ -390,12 +429,23 @@
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;