Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 25 May 2019 07:26:30 +0000 (UTC)
From:      Sean Eric Fagan <sef@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r348268 - in head/sys: conf crypto/aesni modules/aesni
Message-ID:  <201905250726.x4P7QUBd050347@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: sef
Date: Sat May 25 07:26:30 2019
New Revision: 348268
URL: https://svnweb.freebsd.org/changeset/base/348268

Log:
  Add an AESNI-optimized version of the CCM/CBC cryptographic and authentication
  code.  The primary client of this is probably going to be ZFS encryption.
  
  Reviewed by:	jhb, cem
  Sponsored by:	iXsystems Inc, Kithrup Enterprises
  Differential Revision:	https://reviews.freebsd.org/D19298

Added:
  head/sys/crypto/aesni/aesni_ccm.c   (contents, props changed)
Modified:
  head/sys/conf/files.amd64
  head/sys/conf/files.i386
  head/sys/crypto/aesni/aesni.c
  head/sys/crypto/aesni/aesni.h
  head/sys/crypto/aesni/aesni_wrap.c
  head/sys/modules/aesni/Makefile

Modified: head/sys/conf/files.amd64
==============================================================================
--- head/sys/conf/files.amd64	Sat May 25 04:56:06 2019	(r348267)
+++ head/sys/conf/files.amd64	Sat May 25 07:26:30 2019	(r348268)
@@ -175,6 +175,11 @@ aesni_ghash.o			optional aesni				\
 	compile-with	"${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc} ${WERROR} ${NO_WCAST_QUAL} ${PROF} -mmmx -msse -msse4 -maes -mpclmul ${.IMPSRC}" \
 	no-implicit-rule						\
 	clean		"aesni_ghash.o"
+aesni_ccm.o			optional aesni				\
+	dependency	"$S/crypto/aesni/aesni_ccm.c"			\
+	compile-with	"${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc} ${WERROR} ${NO_WCAST_QUAL} ${PROF} -mmmx -msse -msse4 -maes -mpclmul ${.IMPSRC}" \
+	no-implicit-rule						\
+	clean		"aesni_ccm.o"
 aesni_wrap.o			optional aesni				\
 	dependency	"$S/crypto/aesni/aesni_wrap.c"			\
 	compile-with	"${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc} ${WERROR} ${NO_WCAST_QUAL} ${PROF} -mmmx -msse -msse4 -maes ${.IMPSRC}" \

Modified: head/sys/conf/files.i386
==============================================================================
--- head/sys/conf/files.i386	Sat May 25 04:56:06 2019	(r348267)
+++ head/sys/conf/files.i386	Sat May 25 07:26:30 2019	(r348268)
@@ -127,6 +127,11 @@ aesni_ghash.o			optional aesni				\
 	compile-with	"${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc} ${WERROR} ${NO_WCAST_QUAL} ${PROF} -mmmx -msse -msse4 -maes -mpclmul ${.IMPSRC}" \
 	no-implicit-rule						\
 	clean		"aesni_ghash.o"
+aesni_ccm.o			optional aesni				\
+	dependency	"$S/crypto/aesni/aesni_ccm.c"			\
+	compile-with	"${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc} ${WERROR} ${NO_WCAST_QUAL} ${PROF} -mmmx -msse -msse4 -maes -mpclmul ${.IMPSRC}" \
+	no-implicit-rule						\
+	clean		"aesni_ccm.o"
 aesni_wrap.o			optional aesni				\
 	dependency	"$S/crypto/aesni/aesni_wrap.c"			\
 	compile-with	"${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc} ${WERROR} ${NO_WCAST_QUAL} ${PROF} -mmmx -msse -msse4 -maes ${.IMPSRC}" \

Modified: head/sys/crypto/aesni/aesni.c
==============================================================================
--- head/sys/crypto/aesni/aesni.c	Sat May 25 04:56:06 2019	(r348267)
+++ head/sys/crypto/aesni/aesni.c	Sat May 25 07:26:30 2019	(r348268)
@@ -132,9 +132,10 @@ aesni_probe(device_t dev)
 		return (EINVAL);
 	} else if (has_aes && has_sha)
 		device_set_desc(dev,
-		    "AES-CBC,AES-XTS,AES-GCM,AES-ICM,SHA1,SHA256");
+		    "AES-CBC,AES-CCM,AES-GCM,AES-ICM,AES-XTS,SHA1,SHA256");
 	else if (has_aes)
-		device_set_desc(dev, "AES-CBC,AES-XTS,AES-GCM,AES-ICM");
+		device_set_desc(dev,
+		    "AES-CBC,AES-CCM,AES-GCM,AES-ICM,AES-XTS");
 	else
 		device_set_desc(dev, "SHA1,SHA256");
 
@@ -194,6 +195,8 @@ aesni_attach(device_t dev)
 		crypto_register(sc->cid, CRYPTO_AES_192_NIST_GMAC, 0, 0);
 		crypto_register(sc->cid, CRYPTO_AES_256_NIST_GMAC, 0, 0);
 		crypto_register(sc->cid, CRYPTO_AES_XTS, 0, 0);
+		crypto_register(sc->cid, CRYPTO_AES_CCM_16, 0, 0);
+		crypto_register(sc->cid, CRYPTO_AES_CCM_CBC_MAC, 0, 0);
 	}
 	if (sc->has_sha) {
 		crypto_register(sc->cid, CRYPTO_SHA1, 0, 0);
@@ -227,6 +230,7 @@ aesni_newsession(device_t dev, crypto_session_t cses, 
 	struct aesni_session *ses;
 	struct cryptoini *encini, *authini;
 	bool gcm_hash, gcm;
+	bool cbc_hash, ccm;
 	int error;
 
 	KASSERT(cses != NULL, ("EDOOFUS"));
@@ -243,10 +247,17 @@ aesni_newsession(device_t dev, crypto_session_t cses, 
 	encini = NULL;
 	gcm = false;
 	gcm_hash = false;
+	ccm = cbc_hash = false;
+
 	for (; cri != NULL; cri = cri->cri_next) {
 		switch (cri->cri_alg) {
 		case CRYPTO_AES_NIST_GCM_16:
-			gcm = true;
+		case CRYPTO_AES_CCM_16:
+			if (cri->cri_alg == CRYPTO_AES_NIST_GCM_16) {
+				gcm = true;
+			} else if (cri->cri_alg == CRYPTO_AES_CCM_16) {
+				ccm = true;
+			}
 			/* FALLTHROUGH */
 		case CRYPTO_AES_CBC:
 		case CRYPTO_AES_ICM:
@@ -259,6 +270,10 @@ aesni_newsession(device_t dev, crypto_session_t cses, 
 			}
 			encini = cri;
 			break;
+		case CRYPTO_AES_CCM_CBC_MAC:
+			cbc_hash = true;
+			authini = cri;
+			break;
 		case CRYPTO_AES_128_NIST_GMAC:
 		case CRYPTO_AES_192_NIST_GMAC:
 		case CRYPTO_AES_256_NIST_GMAC:
@@ -266,7 +281,12 @@ aesni_newsession(device_t dev, crypto_session_t cses, 
 			 * nothing to do here, maybe in the future cache some
 			 * values for GHASH
 			 */
+			if (authini != NULL) {
+				CRYPTDEB("authini already set");
+				return (EINVAL);
+			}
 			gcm_hash = true;
+			authini = cri;
 			break;
 		case CRYPTO_SHA1:
 		case CRYPTO_SHA1_HMAC:
@@ -296,9 +316,16 @@ unhandled:
 	 * GMAC algorithms are only supported with simultaneous GCM.  Likewise
 	 * GCM is not supported without GMAC.
 	 */
-	if (gcm_hash != gcm)
+	if (gcm_hash != gcm) {
+		CRYPTDEB("gcm_hash != gcm");
 		return (EINVAL);
+	}
 
+	if (cbc_hash != ccm) {
+		CRYPTDEB("cbc_hash != ccm");
+		return (EINVAL);
+	}
+
 	if (encini != NULL)
 		ses->algo = encini->cri_alg;
 	if (authini != NULL)
@@ -339,6 +366,7 @@ aesni_process(device_t dev, struct cryptop *crp, int h
 	for (crd = crp->crp_desc; crd != NULL; crd = crd->crd_next) {
 		switch (crd->crd_alg) {
 		case CRYPTO_AES_NIST_GCM_16:
+		case CRYPTO_AES_CCM_16:
 			needauth = 1;
 			/* FALLTHROUGH */
 		case CRYPTO_AES_CBC:
@@ -354,6 +382,7 @@ aesni_process(device_t dev, struct cryptop *crp, int h
 		case CRYPTO_AES_128_NIST_GMAC:
 		case CRYPTO_AES_192_NIST_GMAC:
 		case CRYPTO_AES_256_NIST_GMAC:
+		case CRYPTO_AES_CCM_CBC_MAC:
 		case CRYPTO_SHA1:
 		case CRYPTO_SHA1_HMAC:
 		case CRYPTO_SHA2_224:
@@ -648,6 +677,7 @@ aesni_cipher_process(struct aesni_session *ses, struct
 
 	if (enccrd != NULL) {
 		if ((enccrd->crd_alg == CRYPTO_AES_ICM ||
+		    enccrd->crd_alg == CRYPTO_AES_CCM_16 ||
 		    enccrd->crd_alg == CRYPTO_AES_NIST_GCM_16) &&
 		    (enccrd->crd_flags & CRD_F_IV_EXPLICIT) == 0)
 			return (EINVAL);
@@ -701,8 +731,9 @@ aesni_cipher_crypt(struct aesni_session *ses, struct c
 	int error, ivlen;
 	bool encflag, allocated, authallocated;
 
-	KASSERT(ses->algo != CRYPTO_AES_NIST_GCM_16 || authcrd != NULL,
-	    ("AES_NIST_GCM_16 must include MAC descriptor"));
+	KASSERT((ses->algo != CRYPTO_AES_NIST_GCM_16 &&
+		ses->algo != CRYPTO_AES_CCM_16) || authcrd != NULL,
+	    ("AES_NIST_GCM_16/AES_CCM_16  must include MAC descriptor"));
 
 	ivlen = 0;
 	authbuf = NULL;
@@ -712,7 +743,8 @@ aesni_cipher_crypt(struct aesni_session *ses, struct c
 		return (ENOMEM);
 
 	authallocated = false;
-	if (ses->algo == CRYPTO_AES_NIST_GCM_16) {
+	if (ses->algo == CRYPTO_AES_NIST_GCM_16 ||
+	    ses->algo == CRYPTO_AES_CCM_16) {
 		authbuf = aesni_cipher_alloc(authcrd, crp, &authallocated);
 		if (authbuf == NULL) {
 			error = ENOMEM;
@@ -738,6 +770,7 @@ aesni_cipher_crypt(struct aesni_session *ses, struct c
 		ivlen = 8;
 		break;
 	case CRYPTO_AES_NIST_GCM_16:
+	case CRYPTO_AES_CCM_16:
 		ivlen = 12;	/* should support arbitarily larger */
 		break;
 	}
@@ -787,7 +820,7 @@ aesni_cipher_crypt(struct aesni_session *ses, struct c
 	case CRYPTO_AES_NIST_GCM_16:
 		if (!encflag)
 			crypto_copydata(crp->crp_flags, crp->crp_buf,
-			    authcrd->crd_inject, GMAC_DIGEST_LEN, tag);
+			    authcrd->crd_inject, sizeof(tag), tag);
 		else
 			bzero(tag, sizeof tag);
 
@@ -798,7 +831,7 @@ aesni_cipher_crypt(struct aesni_session *ses, struct c
 
 			if (authcrd != NULL)
 				crypto_copyback(crp->crp_flags, crp->crp_buf,
-				    authcrd->crd_inject, GMAC_DIGEST_LEN, tag);
+				    authcrd->crd_inject, sizeof(tag), tag);
 		} else {
 			if (!AES_GCM_decrypt(buf, buf, authbuf, iv, tag,
 			    enccrd->crd_len, authcrd->crd_len, ivlen,
@@ -806,9 +839,28 @@ aesni_cipher_crypt(struct aesni_session *ses, struct c
 				error = EBADMSG;
 		}
 		break;
+	case CRYPTO_AES_CCM_16:
+		if (!encflag)
+			crypto_copydata(crp->crp_flags, crp->crp_buf,
+			    authcrd->crd_inject, sizeof(tag), tag);
+		else
+			bzero(tag, sizeof tag);
+		if (encflag) {
+			AES_CCM_encrypt(buf, buf, authbuf, iv, tag,
+			    enccrd->crd_len, authcrd->crd_len, ivlen,
+			    ses->enc_schedule, ses->rounds);
+			if (authcrd != NULL)
+				crypto_copyback(crp->crp_flags, crp->crp_buf,
+				    authcrd->crd_inject, sizeof(tag), tag);
+		} else {
+			if (!AES_CCM_decrypt(buf, buf, authbuf, iv, tag,
+			    enccrd->crd_len, authcrd->crd_len, ivlen,
+			    ses->enc_schedule, ses->rounds))
+				error = EBADMSG;
+		}
+		break;
 	}
-
-	if (allocated)
+	if (allocated && error == 0)
 		crypto_copyback(crp->crp_flags, crp->crp_buf, enccrd->crd_skip,
 		    enccrd->crd_len, buf);
 

Modified: head/sys/crypto/aesni/aesni.h
==============================================================================
--- head/sys/crypto/aesni/aesni.h	Sat May 25 04:56:06 2019	(r348267)
+++ head/sys/crypto/aesni/aesni.h	Sat May 25 07:26:30 2019	(r348268)
@@ -111,6 +111,15 @@ int AES_GCM_decrypt(const unsigned char *in, unsigned 
     const unsigned char *tag, uint32_t nbytes, uint32_t abytes, int ibytes,
     const unsigned char *key, int nr);
 
+/* CCM + CBC-MAC functions */
+void AES_CCM_encrypt(const unsigned char *in, unsigned char *out,
+    const unsigned char *addt, const unsigned char *ivec,
+    unsigned char *tag, uint32_t nbytes, uint32_t abytes, int ibytes,
+    const unsigned char *key, int nr);
+int AES_CCM_decrypt(const unsigned char *in, unsigned char *out,
+    const unsigned char *addt, const unsigned char *ivec,
+    const unsigned char *tag, uint32_t nbytes, uint32_t abytes, int ibytes,
+    const unsigned char *key, int nr);
 int aesni_cipher_setup_common(struct aesni_session *ses, const uint8_t *key,
     int keylen);
 

Added: head/sys/crypto/aesni/aesni_ccm.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/crypto/aesni/aesni_ccm.c	Sat May 25 07:26:30 2019	(r348268)
@@ -0,0 +1,439 @@
+/*-
+ * Copyright (c) 2014 The FreeBSD Foundation
+ * Copyright (c) 2018 iXsystems, Inc
+ * All rights reserved.
+ *
+ * This software was developed by John-Mark Gurney under
+ * the sponsorship of the FreeBSD Foundation and
+ * Rubicon Communications, LLC (Netgate).
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ *	$FreeBSD$
+ *
+ * This file implements AES-CCM+CBC-MAC, as described
+ * at https://tools.ietf.org/html/rfc3610, using Intel's
+ * AES-NI instructions.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/endian.h>
+#include <sys/param.h>
+
+#include <sys/systm.h>
+#include <crypto/aesni/aesni.h>
+#include <crypto/aesni/aesni_os.h>
+#include <crypto/aesni/aesencdec.h>
+#define AESNI_ENC(d, k, nr)	aesni_enc(nr-1, (const __m128i*)k, d)
+
+#include <wmmintrin.h>
+#include <emmintrin.h>
+#include <smmintrin.h>
+
+/*
+ * Encrypt a single 128-bit block after
+ * doing an xor.  This is also used to
+ * decrypt (yay symmetric encryption).
+ */
+static inline __m128i
+xor_and_encrypt(__m128i a, __m128i b, const unsigned char *k, int nr)
+{
+	__m128 retval = _mm_xor_si128(a, b);
+
+	retval = AESNI_ENC(retval, k, nr);
+	return (retval);
+}
+
+/*
+ * Put value at the end of block, starting at offset.
+ * (This goes backwards, putting bytes in *until* it
+ * reaches offset.)
+ */
+static void
+append_int(size_t value, __m128i *block, size_t offset)
+{
+	int indx = sizeof(*block) - 1;
+	uint8_t *bp = (uint8_t*)block;
+
+	while (indx > (sizeof(*block) - offset)) {
+		bp[indx] = value & 0xff;
+		indx--;
+		value >>= 8;
+	}
+}
+
+/*
+ * Start the CBC-MAC process.  This handles the auth data.
+ */
+static __m128i
+cbc_mac_start(const unsigned char *auth_data, size_t auth_len,
+	     const unsigned char *nonce, size_t nonce_len,
+	     const unsigned char *key, int nr,
+	     size_t data_len, size_t tag_len)
+{
+	__m128i cbc_block, staging_block;
+	uint8_t *byte_ptr;
+	/* This defines where the message length goes */
+	int L = sizeof(__m128i) - 1 - nonce_len;
+
+	/*
+	 * Set up B0 here.  This has the flags byte,
+	 * followed by the nonce, followed by the
+	 * length of the message.
+	 */
+	cbc_block = _mm_setzero_si128();
+	byte_ptr = (uint8_t*)&cbc_block;
+	byte_ptr[0] = ((auth_len > 0) ? 1 : 0) * 64 |
+		(((tag_len - 2) / 2) * 8) |
+		(L - 1);
+	bcopy(nonce, byte_ptr + 1, nonce_len);
+	append_int(data_len, &cbc_block, L+1);
+	cbc_block = AESNI_ENC(cbc_block, key, nr);
+
+	if (auth_len != 0) {
+		/*
+		 * We need to start by appending the length descriptor.
+		 */
+		uint32_t auth_amt;
+		size_t copy_amt;
+		const uint8_t *auth_ptr = auth_data;
+
+		staging_block = _mm_setzero_si128();
+
+		/*
+		 * The current OCF calling convention means that
+		 * there can never be more than 4g of authentication
+		 * data, so we don't handle the 0xffff case.
+		 */
+		KASSERT(auth_len < (1ULL << 32),
+		    ("%s: auth_len (%zu) larger than 4GB",
+			__FUNCTION__, auth_len));
+
+		if (auth_len < ((1 << 16) - (1 << 8))) {
+			/*
+			 * If the auth data length is less than
+			 * 0xff00, we don't need to encode a length
+			 * specifier, just the length of the auth
+			 * data.
+			 */
+			be16enc(&staging_block, auth_len);
+			auth_amt = 2;
+		} else if (auth_len < (1ULL << 32)) {
+			/*
+			 * Two bytes for the length prefix, and then
+			 * four bytes for the length.  This makes a total
+			 * of 6 bytes to describe the auth data length.
+			 */
+			be16enc(&staging_block, 0xfffe);
+			be32enc((char*)&staging_block + 2, auth_len);
+			auth_amt = 6;
+		} else
+			panic("%s: auth len too large", __FUNCTION__);
+
+		/*
+		 * Need to copy abytes into blocks.  The first block is
+		 * already partially filled, by auth_amt, so we need
+		 * to handle that.  The last block needs to be zero padded.
+		 */
+		copy_amt = MIN(auth_len - auth_amt,
+		    sizeof(staging_block) - auth_amt);
+		byte_ptr = (uint8_t*)&staging_block;
+		bcopy(auth_ptr, &byte_ptr[auth_amt], copy_amt);
+		auth_ptr += copy_amt;
+
+		cbc_block = xor_and_encrypt(cbc_block, staging_block, key, nr);
+		
+		while (auth_ptr < auth_data + auth_len) {
+			copy_amt = MIN((auth_data + auth_len) - auth_ptr,
+			    sizeof(staging_block));
+			if (copy_amt < sizeof(staging_block))
+				bzero(&staging_block, sizeof(staging_block));
+			bcopy(auth_ptr, &staging_block, copy_amt);
+			cbc_block = xor_and_encrypt(cbc_block, staging_block,
+			    key, nr);
+			auth_ptr += copy_amt;
+		}
+	}
+	return (cbc_block);
+}
+
+/*
+ * Implement AES CCM+CBC-MAC encryption and authentication.
+ *
+ * A couple of notes:
+ * The specification allows for a different number of tag lengths;
+ * however, they're always truncated from 16 bytes, and the tag
+ * length isn't passed in.  (This could be fixed by changing the
+ * code in aesni.c:aesni_cipher_crypt().)
+ * Similarly, although the nonce length is passed in, the
+ * OpenCrypto API that calls us doesn't have a way to set the nonce
+ * other than by having different crypto algorithm types.  As a result,
+ * this is currently always called with nlen=12; this means that we
+ * also have a maximum message length of 16 megabytes.  And similarly,
+ * since abytes is limited to a 32 bit value here, the AAD is
+ * limited to 4 gigabytes or less.
+ */
+void
+AES_CCM_encrypt(const unsigned char *in, unsigned char *out,
+		const unsigned char *addt, const unsigned char *nonce,
+		unsigned char *tag, uint32_t nbytes, uint32_t abytes, int nlen,
+		const unsigned char *key, int nr)
+{
+	static const int tag_length = 16;	/* 128 bits */
+	int L;
+	int counter = 1;	/* S0 has 0, S1 has 1 */
+	size_t copy_amt, total = 0;
+	uint8_t *byte_ptr;
+	__m128i s0, rolling_mac, s_x, staging_block;
+
+	if (nbytes == 0 && abytes == 0)
+		return;
+
+	/* NIST 800-38c section A.1 says n is [7, 13]. */
+	if (nlen < 7 || nlen > 13)
+		panic("%s: bad nonce length %d", __FUNCTION__, nlen);
+
+	/*
+	 * We need to know how many bytes to use to describe
+	 * the length of the data.  Normally, nlen should be
+	 * 12, which leaves us 3 bytes to do that -- 16mbytes of
+	 * data to encrypt.  But it can be longer or shorter;
+	 * this impacts the length of the message.
+	 */
+	L = sizeof(__m128i) - 1 - nlen;
+
+	/*
+	 * Now, this shouldn't happen, but let's make sure that
+	 * the data length isn't too big.
+	 */
+	KASSERT(nbytes <= ((1 << (8 * L)) - 1),
+	    ("%s: nbytes is %u, but length field is %d bytes",
+		__FUNCTION__, nbytes, L));
+
+	/*
+	 * Clear out the blocks
+	 */
+	s0 = _mm_setzero_si128();
+
+	rolling_mac = cbc_mac_start(addt, abytes, nonce, nlen,
+	    key, nr, nbytes, tag_length);
+
+	/* s0 has flags, nonce, and then 0 */
+	byte_ptr = (uint8_t*)&s0;
+	byte_ptr[0] = L - 1;	/* but the flags byte only has L' */
+	bcopy(nonce, &byte_ptr[1], nlen);
+
+	/*
+	 * Now to cycle through the rest of the data.
+	 */
+	bcopy(&s0, &s_x, sizeof(s0));
+
+	while (total < nbytes) {
+		/*
+		 * Copy the plain-text data into staging_block.
+		 * This may need to be zero-padded.
+		 */
+		copy_amt = MIN(nbytes - total, sizeof(staging_block));
+		bcopy(in+total, &staging_block, copy_amt);
+		if (copy_amt < sizeof(staging_block)) {
+			byte_ptr = (uint8_t*)&staging_block;
+			bzero(&byte_ptr[copy_amt],
+			    sizeof(staging_block) - copy_amt);
+		}
+		rolling_mac = xor_and_encrypt(rolling_mac, staging_block,
+		    key, nr);
+		/* Put the counter into the s_x block */
+		append_int(counter++, &s_x, L+1);
+		/* Encrypt that */
+		__m128i X = AESNI_ENC(s_x, key, nr);
+		/* XOR the plain-text with the encrypted counter block */
+		staging_block = _mm_xor_si128(staging_block, X);
+		/* And copy it out */
+		bcopy(&staging_block, out+total, copy_amt);
+		total += copy_amt;
+	}
+	/*
+	 * Allegedly done with it!  Except for the tag.
+	 */
+	s0 = AESNI_ENC(s0, key, nr);
+	staging_block = _mm_xor_si128(s0, rolling_mac);
+	bcopy(&staging_block, tag, tag_length);
+	explicit_bzero(&s0, sizeof(s0));
+	explicit_bzero(&staging_block, sizeof(staging_block));
+	explicit_bzero(&s_x, sizeof(s_x));
+	explicit_bzero(&rolling_mac, sizeof(rolling_mac));
+}
+
+/*
+ * Implement AES CCM+CBC-MAC decryption and authentication.
+ * Returns 0 on failure, 1 on success.
+ *
+ * The primary difference here is that each encrypted block
+ * needs to be hashed&encrypted after it is decrypted (since
+ * the CBC-MAC is based on the plain text).  This means that
+ * we do the decryption twice -- first to verify the tag,
+ * and second to decrypt and copy it out.
+ *
+ * To avoid annoying code copying, we implement the main
+ * loop as a separate function.
+ *
+ * Call with out as NULL to not store the decrypted results;
+ * call with hashp as NULL to not run the authentication.
+ * Calling with neither as NULL does the decryption and
+ * authentication as a single pass (which is not allowed
+ * per the specification, really).
+ *
+ * If hashp is non-NULL, it points to the post-AAD computed
+ * checksum.
+ */
+static void
+decrypt_loop(const unsigned char *in, unsigned char *out, size_t nbytes,
+    __m128i s0, size_t nonce_length, __m128i *macp,
+    const unsigned char *key, int nr)
+{
+	size_t total = 0;
+	__m128i s_x = s0, mac_block;
+	int counter = 1;
+	const size_t L = sizeof(__m128i) - 1 - nonce_length;
+	__m128i pad_block, staging_block;
+
+	/*
+	 * The starting mac (post AAD, if any).
+	 */
+	if (macp != NULL)
+		mac_block = *macp;
+	
+	while (total < nbytes) {
+		size_t copy_amt = MIN(nbytes - total, sizeof(staging_block));
+
+		if (copy_amt < sizeof(staging_block)) {
+			staging_block = _mm_setzero_si128();
+		}
+		bcopy(in+total, &staging_block, copy_amt);
+
+		/*
+		 * staging_block has the current block of input data,
+		 * zero-padded if necessary.  This is used in computing
+		 * both the decrypted data, and the authentication tag.
+		 */
+		append_int(counter++, &s_x, L+1);
+		/*
+		 * The tag is computed based on the decrypted data.
+		 */
+		pad_block = AESNI_ENC(s_x, key, nr);
+		if (copy_amt < sizeof(staging_block)) {
+			/*
+			 * Need to pad out pad_block with 0.
+			 * (staging_block was set to 0's above.)
+			 */
+			uint8_t *end_of_buffer = (uint8_t*)&pad_block;
+			bzero(end_of_buffer + copy_amt,
+			    sizeof(pad_block) - copy_amt);
+		}
+		staging_block = _mm_xor_si128(staging_block, pad_block);
+
+		if (out)
+			bcopy(&staging_block, out+total, copy_amt);
+
+		if (macp)
+			mac_block = xor_and_encrypt(mac_block, staging_block,
+			    key, nr);
+		total += copy_amt;
+	}
+
+	if (macp)
+		*macp = mac_block;
+
+	explicit_bzero(&pad_block, sizeof(pad_block));
+	explicit_bzero(&staging_block, sizeof(staging_block));
+	explicit_bzero(&mac_block, sizeof(mac_block));
+}
+
+/*
+ * The exposed decryption routine.  This is practically a
+ * copy of the encryption routine, except that the order
+ * in which the tag is created is changed.
+ * XXX combine the two functions at some point!
+ */
+int
+AES_CCM_decrypt(const unsigned char *in, unsigned char *out,
+		const unsigned char *addt, const unsigned char *nonce,
+		const unsigned char *tag, uint32_t nbytes, uint32_t abytes, int nlen,
+		const unsigned char *key, int nr)
+{
+	static const int tag_length = 16;	/* 128 bits */
+	int L;
+	__m128i s0, rolling_mac, staging_block;
+	uint8_t *byte_ptr;
+
+	if (nbytes == 0 && abytes == 0)
+		return (1);	// No message means no decryption!
+	if (nlen < 0 || nlen > 15)
+		panic("%s: bad nonce length %d", __FUNCTION__, nlen);
+
+	/*
+	 * We need to know how many bytes to use to describe
+	 * the length of the data.  Normally, nlen should be
+	 * 12, which leaves us 3 bytes to do that -- 16mbytes of
+	 * data to encrypt.  But it can be longer or shorter.
+	 */
+	L = sizeof(__m128i) - 1 - nlen;
+
+	/*
+	 * Now, this shouldn't happen, but let's make sure that
+	 * the data length isn't too big.
+	 */
+	if (nbytes > ((1 << (8 * L)) - 1))
+		panic("%s: nbytes is %u, but length field is %d bytes",
+		      __FUNCTION__, nbytes, L);
+	/*
+	 * Clear out the blocks
+	 */
+	s0 = _mm_setzero_si128();
+
+	rolling_mac = cbc_mac_start(addt, abytes, nonce, nlen,
+	    key, nr, nbytes, tag_length);
+	/* s0 has flags, nonce, and then 0 */
+	byte_ptr = (uint8_t*)&s0;
+	byte_ptr[0] = L-1;	/* but the flags byte only has L' */
+	bcopy(nonce, &byte_ptr[1], nlen);
+
+	/*
+	 * Now to cycle through the rest of the data.
+	 */
+	decrypt_loop(in, NULL, nbytes, s0, nlen, &rolling_mac, key, nr);
+
+	/*
+	 * Compare the tag.
+	 */
+	staging_block = _mm_xor_si128(AESNI_ENC(s0, key, nr), rolling_mac);
+	if (timingsafe_bcmp(&staging_block, tag, tag_length) != 0) {
+		return (0);
+	}
+
+	/*
+	 * Push out the decryption results this time.
+	 */
+	decrypt_loop(in, out, nbytes, s0, nlen, NULL, key, nr);
+	return (1);
+}

Modified: head/sys/crypto/aesni/aesni_wrap.c
==============================================================================
--- head/sys/crypto/aesni/aesni_wrap.c	Sat May 25 04:56:06 2019	(r348267)
+++ head/sys/crypto/aesni/aesni_wrap.c	Sat May 25 07:26:30 2019	(r348268)
@@ -446,6 +446,7 @@ aesni_cipher_setup_common(struct aesni_session *ses, c
 	switch (ses->algo) {
 	case CRYPTO_AES_ICM:
 	case CRYPTO_AES_NIST_GCM_16:
+	case CRYPTO_AES_CCM_16:
 		decsched = 0;
 		/* FALLTHROUGH */
 	case CRYPTO_AES_CBC:

Modified: head/sys/modules/aesni/Makefile
==============================================================================
--- head/sys/modules/aesni/Makefile	Sat May 25 04:56:06 2019	(r348267)
+++ head/sys/modules/aesni/Makefile	Sat May 25 07:26:30 2019	(r348268)
@@ -8,16 +8,20 @@ SRCS=	aesni.c
 SRCS+=	aeskeys_${MACHINE_CPUARCH}.S
 SRCS+=	device_if.h bus_if.h opt_bus.h cryptodev_if.h
 
-OBJS+=	aesni_ghash.o aesni_wrap.o
+OBJS+=	aesni_ghash.o aesni_wrap.o aesni_ccm.o
 OBJS+=	intel_sha1.o intel_sha256.o
 
 # Remove -nostdinc so we can get the intrinsics.
 aesni_ghash.o: aesni_ghash.c
-	# XXX - gcc won't understand -mpclmul
 	${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc} ${WERROR} ${PROF} \
 	     -mmmx -msse -msse4 -maes -mpclmul ${.IMPSRC}
 	${CTFCONVERT_CMD}
 
+aesni_ccm.o: aesni_ccm.c
+	${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc} ${WERROR} ${PROF} \
+	     -mmmx -msse -msse4 -maes -mpclmul ${.IMPSRC}
+	${CTFCONVERT_CMD}
+
 aesni_wrap.o: aesni_wrap.c
 	${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc} ${WERROR} ${PROF} \
 	     -mmmx -msse -msse4 -maes ${.IMPSRC}
@@ -35,6 +39,7 @@ intel_sha256.o: intel_sha256.c
 
 aesni_ghash.o: aesni.h
 aesni_wrap.o: aesni.h
+aesni_ccm.o: aesni.h
 intel_sha1.o: sha_sse.h immintrin.h shaintrin.h tmmintrin.h xmmintrin.h
 intel_sha256.o: sha_sse.h immintrin.h shaintrin.h tmmintrin.h xmmintrin.h
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201905250726.x4P7QUBd050347>