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