summaryrefslogtreecommitdiff
path: root/sbin/iked
diff options
context:
space:
mode:
authortobhe <tobhe@cvs.openbsd.org>2020-05-26 20:24:32 +0000
committertobhe <tobhe@cvs.openbsd.org>2020-05-26 20:24:32 +0000
commitcc559da5d680c6935ddd5e7063a50457a916aaa8 (patch)
tree6c575f26d0b86c817eed2e59e99d0acc25804aed /sbin/iked
parenta6b0173c5eb6d2749e4e0a290de5af813e858d39 (diff)
Add AES-GCM mode ciphers (IANA IDs 19 and 20) for IKEv2.
They can be configured with the new ikesa enc options aes-128-gcm, aes-256-gcm, aes-128-gcm-12 and aes-256-gcm-12. Tested with Strongswan by Stephan Mendling and myself Tested with Juniper SRX by remi@ ok sthen@, patrick@
Diffstat (limited to 'sbin/iked')
-rw-r--r--sbin/iked/crypto.c105
-rw-r--r--sbin/iked/iked.conf.510
-rw-r--r--sbin/iked/iked.h12
-rw-r--r--sbin/iked/ikev2.c64
-rw-r--r--sbin/iked/ikev2.h7
-rw-r--r--sbin/iked/ikev2_msg.c195
-rw-r--r--sbin/iked/parse.y43
-rw-r--r--sbin/iked/policy.c17
8 files changed, 367 insertions, 86 deletions
diff --git a/sbin/iked/crypto.c b/sbin/iked/crypto.c
index 2218e6a3dd0..66df3cafbee 100644
--- a/sbin/iked/crypto.c
+++ b/sbin/iked/crypto.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: crypto.c,v 1.27 2020/05/14 15:08:30 tobhe Exp $ */
+/* $OpenBSD: crypto.c,v 1.28 2020/05/26 20:24:31 tobhe Exp $ */
/*
* Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -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, uint16_t id_length)
const EVP_CIPHER *cipher = NULL;
EVP_CIPHER_CTX *ctx = NULL;
int length = 0, fixedkey = 0, ivlength = 0;
+ int saltlength = 0, authid = 0;
switch (type) {
case IKEV2_XFORMTYPE_ENCR:
@@ -309,6 +322,36 @@ cipher_new(uint8_t type, uint16_t id, uint16_t id_length)
ivlength = EVP_CIPHER_iv_length(cipher);
fixedkey = EVP_CIPHER_key_length(cipher);
break;
+ case IKEV2_XFORMENCR_AES_GCM_16:
+ case IKEV2_XFORMENCR_AES_GCM_12:
+ switch (id_length) {
+ case 128:
+ cipher = EVP_aes_128_gcm();
+ break;
+ case 256:
+ cipher = EVP_aes_256_gcm();
+ break;
+ default:
+ log_debug("%s: invalid key length %d"
+ " for cipher %s", __func__, id_length,
+ print_map(id, ikev2_xformencr_map));
+ break;
+ }
+ if (cipher == NULL)
+ break;
+ switch(id) {
+ case IKEV2_XFORMENCR_AES_GCM_16:
+ authid = IKEV2_XFORMAUTH_AES_GCM_16;
+ break;
+ case IKEV2_XFORMENCR_AES_GCM_12:
+ authid = IKEV2_XFORMAUTH_AES_GCM_12;
+ break;
+ }
+ length = EVP_CIPHER_block_size(cipher);
+ ivlength = 8;
+ saltlength = 4;
+ fixedkey = EVP_CIPHER_key_length(cipher) + saltlength;
+ break;
case IKEV2_XFORMENCR_DES_IV64:
case IKEV2_XFORMENCR_DES:
case IKEV2_XFORMENCR_RC5:
@@ -346,6 +389,8 @@ cipher_new(uint8_t type, uint16_t id, uint16_t id_length)
encr->encr_length = length;
encr->encr_fixedkey = fixedkey;
encr->encr_ivlength = ivlength ? ivlength : length;
+ encr->encr_saltlength = saltlength;
+ encr->encr_authid = authid;
if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
log_debug("%s: alloc cipher ctx", __func__);
@@ -392,6 +437,20 @@ cipher_setiv(struct iked_cipher *encr, void *iv, size_t len)
return (encr->encr_iv);
}
+int
+cipher_settag(struct iked_cipher *encr, uint8_t *data, size_t len)
+{
+ return (EVP_CIPHER_CTX_ctrl(encr->encr_ctx,
+ EVP_CTRL_GCM_SET_TAG, len, data) != 1);
+}
+
+int
+cipher_gettag(struct iked_cipher *encr, uint8_t *data, size_t len)
+{
+ return (EVP_CIPHER_CTX_ctrl(encr->encr_ctx,
+ EVP_CTRL_GCM_GET_TAG, len, data) != 1);
+}
+
void
cipher_free(struct iked_cipher *encr)
{
@@ -409,11 +468,33 @@ cipher_free(struct iked_cipher *encr)
int
cipher_init(struct iked_cipher *encr, int enc)
{
+ struct ibuf *nonce = NULL;
+ int ret = -1;
+
if (EVP_CipherInit_ex(encr->encr_ctx, encr->encr_priv, NULL,
- ibuf_data(encr->encr_key), ibuf_data(encr->encr_iv), enc) != 1)
+ NULL, NULL, enc) != 1)
return (-1);
+ if (encr->encr_saltlength > 0) {
+ /* For AEADs the nonce is salt + IV (see RFC5282) */
+ nonce = ibuf_new(ibuf_data(encr->encr_key) +
+ ibuf_size(encr->encr_key) - encr->encr_saltlength,
+ encr->encr_saltlength);
+ if (nonce == NULL)
+ return (-1);
+ if (ibuf_add(nonce, ibuf_data(encr->encr_iv) , ibuf_size(encr->encr_iv)) != 0)
+ goto done;
+ if (EVP_CipherInit_ex(encr->encr_ctx, NULL, NULL,
+ ibuf_data(encr->encr_key), ibuf_data(nonce), enc) != 1)
+ goto done;
+ } else
+ if (EVP_CipherInit_ex(encr->encr_ctx, NULL, NULL,
+ ibuf_data(encr->encr_key), ibuf_data(encr->encr_iv), enc) != 1)
+ return (-1);
EVP_CIPHER_CTX_set_padding(encr->encr_ctx, 0);
- return (0);
+ ret = 0;
+ done:
+ ibuf_free(nonce);
+ return (ret);
}
int
@@ -428,6 +509,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
cipher_update(struct iked_cipher *encr, void *in, size_t inlen,
void *out, size_t *outlen)
diff --git a/sbin/iked/iked.conf.5 b/sbin/iked/iked.conf.5
index 26df204822e..654f408d4e7 100644
--- a/sbin/iked/iked.conf.5
+++ b/sbin/iked/iked.conf.5
@@ -1,4 +1,4 @@
-.\" $OpenBSD: iked.conf.5,v 1.68 2020/05/01 17:44:02 tobhe Exp $
+.\" $OpenBSD: iked.conf.5,v 1.69 2020/05/26 20:24:31 tobhe Exp $
.\"
.\" Copyright (c) 2010 - 2014 Reyk Floeter <reyk@openbsd.org>
.\" Copyright (c) 2004 Mathieu Sauve-Frankel All rights reserved.
@@ -15,7 +15,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: May 1 2020 $
+.Dd $Mdocdate: May 26 2020 $
.Dt IKED.CONF 5
.Os
.Sh NAME
@@ -864,9 +864,11 @@ keyword:
.It Li aes-128-ctr Ta "160 bits" Ta "[ESP only]"
.It Li aes-192-ctr Ta "224 bits" Ta "[ESP only]"
.It Li aes-256-ctr Ta "288 bits" Ta "[ESP only]"
-.It Li aes-128-gcm Ta "160 bits" Ta "[ESP only]"
+.It Li aes-128-gcm Ta "160 bits" Ta ""
.It Li aes-192-gcm Ta "224 bits" Ta "[ESP only]"
-.It Li aes-256-gcm Ta "288 bits" Ta "[ESP only]"
+.It Li aes-256-gcm Ta "288 bits" Ta ""
+.It Li aes-128-gcm-12 Ta "160 bits" Ta "[IKE only]"
+.It Li aes-256-gcm-12 Ta "288 bits" Ta "[IKE only]"
.It Li blowfish Ta "160 bits" Ta "[ESP only]"
.It Li cast Ta "128 bits" Ta "[ESP only]"
.It Li chacha20-poly1305 Ta "288 bits" Ta "[ESP only]"
diff --git a/sbin/iked/iked.h b/sbin/iked/iked.h
index f93fe35cb05..866f342f4a0 100644
--- a/sbin/iked/iked.h
+++ b/sbin/iked/iked.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: iked.h,v 1.150 2020/05/14 15:08:30 tobhe Exp $ */
+/* $OpenBSD: iked.h,v 1.151 2020/05/26 20:24:31 tobhe Exp $ */
/*
* Copyright (c) 2019 Tobias Heider <tobias.heider@stusta.de>
@@ -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 *, struct iked_message *);
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 */
diff --git a/sbin/iked/ikev2.c b/sbin/iked/ikev2.c
index ebc89de0339..5db382cbe92 100644
--- a/sbin/iked/ikev2.c
+++ b/sbin/iked/ikev2.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ikev2.c,v 1.225 2020/05/11 20:11:35 tobhe Exp $ */
+/* $OpenBSD: ikev2.c,v 1.226 2020/05/26 20:24:31 tobhe Exp $ */
/*
* Copyright (c) 2019 Tobias Heider <tobias.heider@stusta.de>
@@ -4473,7 +4473,7 @@ ikev2_send_informational(struct iked *env, struct iked_message *msg)
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, struct iked_sa *sa,
}
}
+ /* 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, struct iked_sa *sa, struct iked_sa *osa,
}
}
+ /* 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 iked_sa *sa, struct ibuf *key)
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 iked_sa *sa, struct ibuf *key)
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 iked_sa *sa, struct ibuf *key)
* 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 iked_sa *sa, struct ibuf *key)
/* 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 iked_sa *sa, struct ibuf *key)
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));
diff --git a/sbin/iked/ikev2.h b/sbin/iked/ikev2.h
index 09a28a4397b..074928f8699 100644
--- a/sbin/iked/ikev2.h
+++ b/sbin/iked/ikev2.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ikev2.h,v 1.32 2020/04/28 15:18:52 tobhe Exp $ */
+/* $OpenBSD: ikev2.h,v 1.33 2020/05/26 20:24:31 tobhe Exp $ */
/*
* Copyright (c) 2019 Tobias Heider <tobias.heider@stusta.de>
@@ -225,6 +225,11 @@ extern struct iked_constmap ikev2_xformprf_map[];
#define IKEV2_XFORMAUTH_HMAC_SHA2_384_192 13 /* RFC4868 */
#define IKEV2_XFORMAUTH_HMAC_SHA2_512_256 14 /* RFC4868 */
+/* Placeholders for AEAD ciphers (only used internally) */
+#define IKEV2_XFORMAUTH_AES_GCM_8 2018 /* internal */
+#define IKEV2_XFORMAUTH_AES_GCM_12 2019 /* internal */
+#define IKEV2_XFORMAUTH_AES_GCM_16 2020 /* internal */
+
extern struct iked_constmap ikev2_xformauth_map[];
#define IKEV2_XFORMDH_NONE 0 /* No DH */
diff --git a/sbin/iked/ikev2_msg.c b/sbin/iked/ikev2_msg.c
index bdd88abb678..cc76dfde3f5 100644
--- a/sbin/iked/ikev2_msg.c
+++ b/sbin/iked/ikev2_msg.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ikev2_msg.c,v 1.67 2020/05/14 15:08:30 tobhe Exp $ */
+/* $OpenBSD: ikev2_msg.c,v 1.68 2020/05/26 20:24:31 tobhe Exp $ */
/*
* Copyright (c) 2019 Tobias Heider <tobias.heider@stusta.de>
@@ -51,6 +51,8 @@ void ikev2_msg_retransmit_timeout(struct iked *, void *);
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,46 @@ ikev2_msg_id(struct iked *env, struct iked_sa *sa)
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;
+
+ if (ikev2_next_payload(pld,
+ pldlen + (fragmentation ? sizeof(struct ikev2_frag_payload) : 0),
+ 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 +430,23 @@ ikev2_msg_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf *src)
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 +480,13 @@ ikev2_msg_integr(struct iked *env, struct iked_sa *sa, struct ibuf *src)
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 +496,33 @@ ikev2_msg_integr(struct iked *env, struct iked_sa *sa, struct ibuf *src)
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 (tmplen != integrlen) {
- log_debug("%s: hash failure", __func__);
- goto done;
+ 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;
+ }
+ } 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 +539,7 @@ ikev2_msg_decrypt(struct iked *env, struct iked_sa *sa,
{
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 +582,30 @@ ikev2_msg_decrypt(struct iked *env, struct iked_sa *sa,
/*
* 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 +622,31 @@ ikev2_msg_decrypt(struct iked *env, struct iked_sa *sa,
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 +658,11 @@ ikev2_msg_decrypt(struct iked *env, struct iked_sa *sa,
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 +687,13 @@ ikev2_check_frag_oversize(struct iked_sa *sa, struct ibuf *buf) {
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 +738,16 @@ ikev2_msg_send_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf **ep,
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 +788,13 @@ ikev2_send_encrypted_fragments(struct iked *env, struct iked_sa *sa,
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,20 +844,18 @@ ikev2_send_encrypted_fragments(struct iked *env, struct iked_sa *sa,
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) */
if (ikev2_msg_integr(env, sa, buf) != 0) {
log_debug("%s: integrity checksum failed", __func__);
diff --git a/sbin/iked/parse.y b/sbin/iked/parse.y
index 71bdbc0ab24..2cb00c29833 100644
--- a/sbin/iked/parse.y
+++ b/sbin/iked/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.99 2020/04/30 21:11:13 tobhe Exp $ */
+/* $OpenBSD: parse.y,v 1.100 2020/05/26 20:24:31 tobhe Exp $ */
/*
* Copyright (c) 2019 Tobias Heider <tobias.heider@stusta.de>
@@ -198,6 +198,10 @@ const struct ipsec_xf ikeencxfs[] = {
{ "aes-128", IKEV2_XFORMENCR_AES_CBC, 16, 16 },
{ "aes-192", IKEV2_XFORMENCR_AES_CBC, 24, 24 },
{ "aes-256", IKEV2_XFORMENCR_AES_CBC, 32, 32 },
+ { "aes-128-gcm", IKEV2_XFORMENCR_AES_GCM_16, 16, 16, 4, 1 },
+ { "aes-256-gcm", IKEV2_XFORMENCR_AES_GCM_16, 32, 32, 4, 1 },
+ { "aes-128-gcm-12", IKEV2_XFORMENCR_AES_GCM_12, 16, 16, 4, 1 },
+ { "aes-256-gcm-12", IKEV2_XFORMENCR_AES_GCM_12, 32, 32, 4, 1 },
{ NULL }
};
@@ -2417,6 +2421,17 @@ print_xf(unsigned int id, unsigned int length, const struct ipsec_xf xfs[])
return ("unknown");
}
+int
+encxf_noauth(unsigned int id)
+{
+ int i;
+
+ for (i = 0; ikeencxfs[i].name != NULL; i++)
+ if (ikeencxfs[i].id == id)
+ return ikeencxfs[i].noauth;
+ return (0);
+}
+
size_t
keylength_xf(unsigned int saproto, unsigned int type, unsigned int id)
{
@@ -2852,21 +2867,37 @@ create_ike(char *name, int af, uint8_t ipproto,
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,
diff --git a/sbin/iked/policy.c b/sbin/iked/policy.c
index 735284af401..8992f37bd99 100644
--- a/sbin/iked/policy.c
+++ b/sbin/iked/policy.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: policy.c,v 1.62 2020/05/13 23:03:20 tobhe Exp $ */
+/* $OpenBSD: policy.c,v 1.63 2020/05/26 20:24:31 tobhe Exp $ */
/*
* Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -774,7 +774,7 @@ proposals_match(struct iked_proposal *local, struct iked_proposal *peer,
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 *local, struct iked_proposal *peer,
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 *local, struct iked_proposal *peer,
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 &&