From 0b2903bf0a54225395eaa6386172183e8c494c13 Mon Sep 17 00:00:00 2001 From: Luiz Otavio O Souza Date: Tue, 15 Sep 2015 17:09:29 -0500 Subject: MFC r285336: Add support for AES modes to IPSec. These modes work both in software only mode and with hardware support on systems that have AESNI instructions. Differential Revision: D2936 Reviewed by: jmg, eri, cognet Sponsored by: Rubicon Communications (Netgate) TAG: IPSEC-HEAD Issue: #4841 --- sys/netipsec/xform_ah.c | 35 ++++-------- sys/netipsec/xform_esp.c | 137 ++++++++++++++++++++++++++++++----------------- 2 files changed, 99 insertions(+), 73 deletions(-) (limited to 'sys/netipsec') diff --git a/sys/netipsec/xform_ah.c b/sys/netipsec/xform_ah.c index 5274ea2..4feb851 100644 --- a/sys/netipsec/xform_ah.c +++ b/sys/netipsec/xform_ah.c @@ -80,11 +80,11 @@ (((sav)->flags & SADB_X_EXT_OLD) ? \ sizeof (struct ah) : sizeof (struct ah) + sizeof (u_int32_t)) /* - * Return authenticator size in bytes. The old protocol is known - * to use a fixed 16-byte authenticator. The new algorithm use 12-byte - * authenticator. + * Return authenticator size in bytes, based on a field in the + * algorithm descriptor. */ -#define AUTHSIZE(sav) ah_authsize(sav) +#define AUTHSIZE(sav) \ + ((sav->flags & SADB_X_EXT_OLD) ? 16 : (sav)->tdb_authalgxform->hashsize) VNET_DEFINE(int, ah_enable) = 1; /* control flow of packets with AH */ VNET_DEFINE(int, ah_cleartos) = 1; /* clear ip_tos when doing AH calc */ @@ -110,27 +110,6 @@ static unsigned char ipseczeroes[256]; /* larger than an ip6 extension hdr */ static int ah_input_cb(struct cryptop*); static int ah_output_cb(struct cryptop*); -static int -ah_authsize(struct secasvar *sav) -{ - - IPSEC_ASSERT(sav != NULL, ("%s: sav == NULL", __func__)); - - if (sav->flags & SADB_X_EXT_OLD) - return 16; - - switch (sav->alg_auth) { - case SADB_X_AALG_SHA2_256: - return 16; - case SADB_X_AALG_SHA2_384: - return 24; - case SADB_X_AALG_SHA2_512: - return 32; - default: - return AH_HMAC_HASHLEN; - } - /* NOTREACHED */ -} /* * NB: this is public for use by the PF_KEY support. */ @@ -158,6 +137,12 @@ ah_algorithm_lookup(int alg) return &auth_hash_hmac_sha2_384; case SADB_X_AALG_SHA2_512: return &auth_hash_hmac_sha2_512; + case SADB_X_AALG_AES128GMAC: + return &auth_hash_nist_gmac_aes_128; + case SADB_X_AALG_AES192GMAC: + return &auth_hash_nist_gmac_aes_192; + case SADB_X_AALG_AES256GMAC: + return &auth_hash_nist_gmac_aes_256; } return NULL; } diff --git a/sys/netipsec/xform_esp.c b/sys/netipsec/xform_esp.c index b5a22ba..a47404b 100644 --- a/sys/netipsec/xform_esp.c +++ b/sys/netipsec/xform_esp.c @@ -119,6 +119,12 @@ esp_algorithm_lookup(int alg) return &enc_xform_null; case SADB_X_EALG_CAMELLIACBC: return &enc_xform_camellia; + case SADB_X_EALG_AESCTR: + return &enc_xform_aes_icm; + case SADB_X_EALG_AESGCM16: + return &enc_xform_aes_nist_gcm; + case SADB_X_EALG_AESGMAC: + return &enc_xform_aes_nist_gmac; } return NULL; } @@ -196,7 +202,7 @@ esp_init(struct secasvar *sav, struct xformsw *xsp) * the ESP header will be processed incorrectly. The * compromise is to force it to zero here. */ - sav->ivlen = (txform == &enc_xform_null ? 0 : txform->blocksize); + sav->ivlen = (txform == &enc_xform_null ? 0 : txform->ivsize); sav->iv = (caddr_t) malloc(sav->ivlen, M_XDATA, M_WAITOK); key_randomfill(sav->iv, sav->ivlen); /*XXX*/ @@ -213,11 +219,45 @@ esp_init(struct secasvar *sav, struct xformsw *xsp) sav->tdb_xform = xsp; sav->tdb_encalgxform = txform; + /* + * Whenever AES-GCM is used for encryption, one + * of the AES authentication algorithms is chosen + * as well, based on the key size. + */ + if (sav->alg_enc == SADB_X_EALG_AESGCM16) { + switch (keylen) { + case AES_128_HMAC_KEY_LEN: + sav->alg_auth = SADB_X_AALG_AES128GMAC; + sav->tdb_authalgxform = &auth_hash_nist_gmac_aes_128; + break; + case AES_192_HMAC_KEY_LEN: + sav->alg_auth = SADB_X_AALG_AES192GMAC; + sav->tdb_authalgxform = &auth_hash_nist_gmac_aes_192; + break; + case AES_256_HMAC_KEY_LEN: + sav->alg_auth = SADB_X_AALG_AES256GMAC; + sav->tdb_authalgxform = &auth_hash_nist_gmac_aes_256; + break; + default: + DPRINTF(("%s: invalid key length %u" + "for algorithm %s\n", __func__, + keylen, txform->name)); + return EINVAL; + } + bzero(&cria, sizeof(cria)); + cria.cri_alg = sav->tdb_authalgxform->type; + cria.cri_klen = _KEYBITS(sav->key_enc) + 4; + cria.cri_key = sav->key_enc->key_data; + } + /* Initialize crypto session. */ bzero(&crie, sizeof (crie)); crie.cri_alg = sav->tdb_encalgxform->type; crie.cri_klen = _KEYBITS(sav->key_enc); crie.cri_key = sav->key_enc->key_data; + if (sav->alg_enc == SADB_X_EALG_AESGCM16) + arc4rand(crie.cri_iv, sav->ivlen, 0); + /* XXX Rounds ? */ if (sav->tdb_authalgxform && sav->tdb_encalgxform) { @@ -287,7 +327,6 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff) m_freem(m); return EINVAL; } - /* XXX don't pullup, just copy header */ IP6_EXTHDR_GET(esp, struct newesp *, m, skip, sizeof (struct newesp)); @@ -300,18 +339,7 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff) else hlen = sizeof (struct newesp) + sav->ivlen; /* Authenticator hash size */ - if (esph != NULL) { - switch (esph->type) { - case CRYPTO_SHA2_256_HMAC: - case CRYPTO_SHA2_384_HMAC: - case CRYPTO_SHA2_512_HMAC: - alen = esph->hashsize/2; - break; - default: - alen = AH_HMAC_HASHLEN; - break; - } - } + alen = esph ? esph->hashsize : 0; /* * Verify payload length is multiple of encryption algorithm @@ -323,13 +351,15 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff) */ plen = m->m_pkthdr.len - (skip + hlen + alen); if ((plen & (espx->blocksize - 1)) || (plen <= 0)) { - DPRINTF(("%s: payload of %d octets not a multiple of %d octets," - " SA %s/%08lx\n", __func__, - plen, espx->blocksize, ipsec_address(&sav->sah->saidx.dst, - buf, sizeof(buf)), (u_long) ntohl(sav->spi))); - ESPSTAT_INC(esps_badilen); - m_freem(m); - return EINVAL; + if (!espx || sav->alg_enc != SADB_X_EALG_AESGCM16) { + DPRINTF(("%s: payload of %d octets not a multiple of %d octets," + " SA %s/%08lx\n", __func__, + plen, espx->blocksize, ipsec_address(&sav->sah->saidx.dst, + buf, sizeof(buf)), (u_long) ntohl(sav->spi))); + ESPSTAT_INC(esps_badilen); + m_freem(m); + return EINVAL; + } } /* @@ -375,12 +405,20 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff) /* Authentication descriptor */ crda->crd_skip = skip; - crda->crd_len = m->m_pkthdr.len - (skip + alen); + if (espx && espx->type == CRYPTO_AES_NIST_GCM_16) + crda->crd_len = hlen - sav->ivlen; + else + crda->crd_len = m->m_pkthdr.len - (skip + alen); crda->crd_inject = m->m_pkthdr.len - alen; crda->crd_alg = esph->type; - crda->crd_key = sav->key_auth->key_data; - crda->crd_klen = _KEYBITS(sav->key_auth); + if (espx && (espx->type == CRYPTO_AES_NIST_GCM_16)) { + crda->crd_key = sav->key_enc->key_data; + crda->crd_klen = _KEYBITS(sav->key_enc); + } else { + crda->crd_key = sav->key_auth->key_data; + crda->crd_klen = _KEYBITS(sav->key_auth); + } /* Copy the authenticator */ m_copydata(m, m->m_pkthdr.len - alen, alen, @@ -418,6 +456,9 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff) crde->crd_alg = espx->type; crde->crd_key = sav->key_enc->key_data; crde->crd_klen = _KEYBITS(sav->key_enc); + if (espx && (espx->type == CRYPTO_AES_NIST_GCM_16)) + crde->crd_flags |= CRD_F_IV_EXPLICIT; + /* XXX Rounds ? */ return (crypto_dispatch(crp)); @@ -487,16 +528,7 @@ esp_input_cb(struct cryptop *crp) /* If authentication was performed, check now. */ if (esph != NULL) { - switch (esph->type) { - case CRYPTO_SHA2_256_HMAC: - case CRYPTO_SHA2_384_HMAC: - case CRYPTO_SHA2_512_HMAC: - alen = esph->hashsize/2; - break; - default: - alen = AH_HMAC_HASHLEN; - break; - } + alen = esph->hashsize; AHSTAT_INC(ahs_hist[sav->alg_auth]); /* Copy the authenticator from the packet */ m_copydata(m, m->m_pkthdr.len - alen, alen, aalg); @@ -661,22 +693,13 @@ esp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp, * NB: The null encoding transform has a blocksize of 4 * so that headers are properly aligned. */ - blks = espx->blocksize; /* IV blocksize */ + blks = espx->ivsize; /* IV blocksize */ /* XXX clamp padding length a la KAME??? */ padding = ((blks - ((rlen + 2) % blks)) % blks) + 2; if (esph) - switch (esph->type) { - case CRYPTO_SHA2_256_HMAC: - case CRYPTO_SHA2_384_HMAC: - case CRYPTO_SHA2_512_HMAC: - alen = esph->hashsize/2; - break; - default: - alen = AH_HMAC_HASHLEN; - break; - } + alen = esph->hashsize; else alen = 0; @@ -704,6 +727,8 @@ esp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp, error = EPFNOSUPPORT; goto bad; } + DPRINTF(("%s: skip %d hlen %d rlen %d padding %d alen %d blksd %d\n", + __func__, skip, hlen, rlen, padding, alen, blks)); if (skip + hlen + rlen + padding + alen > maxpacketsize) { DPRINTF(("%s: packet in SA %s/%08lx got too big " "(len %u, max len %u)\n", __func__, @@ -818,6 +843,8 @@ esp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp, crde->crd_alg = espx->type; crde->crd_key = sav->key_enc->key_data; crde->crd_klen = _KEYBITS(sav->key_enc); + if (espx->type == CRYPTO_AES_NIST_GCM_16) + crde->crd_flags |= CRD_F_IV_EXPLICIT; /* XXX Rounds ? */ } else crda = crp->crp_desc; @@ -852,13 +879,22 @@ esp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp, if (esph) { /* Authentication descriptor. */ crda->crd_skip = skip; - crda->crd_len = m->m_pkthdr.len - (skip + alen); + if (espx && espx->type == CRYPTO_AES_NIST_GCM_16) + crda->crd_len = hlen - sav->ivlen; + else + crda->crd_len = m->m_pkthdr.len - (skip + alen); crda->crd_inject = m->m_pkthdr.len - alen; /* Authentication operation. */ crda->crd_alg = esph->type; - crda->crd_key = sav->key_auth->key_data; - crda->crd_klen = _KEYBITS(sav->key_auth); + if (espx && espx->type == CRYPTO_AES_NIST_GCM_16) { + crda->crd_key = sav->key_enc->key_data; + crda->crd_klen = _KEYBITS(sav->key_enc); + } else { + crda->crd_key = sav->key_auth->key_data; + crda->crd_klen = _KEYBITS(sav->key_auth); + } + } return crypto_dispatch(crp); @@ -951,6 +987,11 @@ esp_output_cb(struct cryptop *crp) case CRYPTO_SHA2_512_HMAC: alen = esph->hashsize/2; break; + case CRYPTO_AES_128_GMAC: + case CRYPTO_AES_192_GMAC: + case CRYPTO_AES_256_GMAC: + alen = esph->hashsize; + break; default: alen = AH_HMAC_HASHLEN; break; -- cgit v1.1