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;