Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F105803945
D17066.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
59 KB
Referenced Files
None
Subscribers
None
D17066.diff
View Options
Index: sys/conf/files
===================================================================
--- sys/conf/files
+++ sys/conf/files
@@ -4832,6 +4832,8 @@
compile-with "${NORMAL_C} -I$S/contrib/libsodium/src/libsodium/include -I$S/crypto/libsodium"
crypto/libsodium/utils.c optional crypto \
compile-with "${NORMAL_C} -I$S/contrib/libsodium/src/libsodium/include -I$S/crypto/libsodium"
+opencrypto/ccm-cbc.c optional crypto
+opencrypto/xform_cbc_mac.c optional crypto
rpc/auth_none.c optional krpc | nfslockd | nfscl | nfsd
rpc/auth_unix.c optional krpc | nfslockd | nfscl | nfsd
rpc/authunix_prot.c optional krpc | nfslockd | nfscl | nfsd
Index: sys/conf/files.amd64
===================================================================
--- sys/conf/files.amd64
+++ sys/conf/files.amd64
@@ -175,6 +175,11 @@
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}" \
Index: sys/conf/files.i386
===================================================================
--- sys/conf/files.i386
+++ sys/conf/files.i386
@@ -127,6 +127,11 @@
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}" \
Index: sys/crypto/aesni/aesni.h
===================================================================
--- sys/crypto/aesni/aesni.h
+++ sys/crypto/aesni/aesni.h
@@ -111,6 +111,15 @@
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);
Index: sys/crypto/aesni/aesni.c
===================================================================
--- sys/crypto/aesni/aesni.c
+++ sys/crypto/aesni/aesni.c
@@ -131,9 +131,9 @@
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-XTS,AES-GCM,AES-ICM,AES-CCM,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-XTS,AES-GCM,AES-ICM,AES-CCM");
else
device_set_desc(dev, "SHA1,SHA256");
@@ -193,6 +193,10 @@
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_128_CCM_CBC_MAC, 0, 0);
+ crypto_register(sc->cid, CRYPTO_AES_192_CCM_CBC_MAC, 0, 0);
+ crypto_register(sc->cid, CRYPTO_AES_256_CCM_CBC_MAC, 0, 0);
}
if (sc->has_sha) {
crypto_register(sc->cid, CRYPTO_SHA1, 0, 0);
@@ -226,6 +230,7 @@
struct aesni_session *ses;
struct cryptoini *encini, *authini;
bool gcm_hash, gcm;
+ bool cbc_hash, ccm;
int error;
KASSERT(cses != NULL, ("EDOOFUS"));
@@ -242,10 +247,17 @@
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:
@@ -258,6 +270,12 @@
}
encini = cri;
break;
+ case CRYPTO_AES_128_CCM_CBC_MAC:
+ case CRYPTO_AES_192_CCM_CBC_MAC:
+ case CRYPTO_AES_256_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,6 +284,7 @@
* values for GHASH
*/
gcm_hash = true;
+ authini = cri;
break;
case CRYPTO_SHA1:
case CRYPTO_SHA1_HMAC:
@@ -295,9 +314,16 @@
* 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)
@@ -338,6 +364,7 @@
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:
@@ -353,6 +380,9 @@
case CRYPTO_AES_128_NIST_GMAC:
case CRYPTO_AES_192_NIST_GMAC:
case CRYPTO_AES_256_NIST_GMAC:
+ case CRYPTO_AES_128_CCM_CBC_MAC:
+ case CRYPTO_AES_192_CCM_CBC_MAC:
+ case CRYPTO_AES_256_CCM_CBC_MAC:
case CRYPTO_SHA1:
case CRYPTO_SHA1_HMAC:
case CRYPTO_SHA2_224:
@@ -399,13 +429,45 @@
return (error);
}
+/*
+ * Find an iovec in the given uio that contains a
+ * <offset, length> vector. To qualify, the vector
+ * must be entirely contained with a single iovec.
+ * If it is found, return the address; otherwise,
+ * return NULL.
+ */
+static void *
+find_vector(struct uio *uio, size_t start, size_t length)
+{
+ int indx;
+ size_t curr_offset = 0, end = start + length;
+
+ for (indx = 0;
+ indx < uio->uio_iovcnt && curr_offset <= start;
+ indx++) {
+ /*
+ * See if <start, length> is in the range
+ * of <curr_offset, uio->iov[indx].iov_len>
+ */
+ struct iovec *iov = &uio->uio_iov[indx];
+ if (curr_offset <= start &&
+ ((curr_offset + iov->iov_len) >= end)) {
+ size_t offset = start - curr_offset;
+ uint8_t *retval = iov->iov_base;
+ return (void*)(retval + offset);
+ }
+ curr_offset += iov->iov_len;
+ }
+ return NULL;
+
+}
+
static uint8_t *
aesni_cipher_alloc(struct cryptodesc *enccrd, struct cryptop *crp,
bool *allocated)
{
struct mbuf *m;
struct uio *uio;
- struct iovec *iov;
uint8_t *addr;
if (crp->crp_flags & CRYPTO_F_IMBUF) {
@@ -415,10 +477,18 @@
addr = mtod(m, uint8_t *);
} else if (crp->crp_flags & CRYPTO_F_IOV) {
uio = (struct uio *)crp->crp_buf;
- if (uio->uio_iovcnt != 1)
- goto alloc;
- iov = uio->uio_iov;
- addr = (uint8_t *)iov->iov_base;
+ /*
+ * If the data range we need is entirely
+ * contained within one iovec, we should
+ * use that, instead of trying to allocate
+ * memory.
+ */
+ addr = find_vector(uio, enccrd->crd_skip, enccrd->crd_len);
+ if (addr != NULL) {
+ *allocated = 0;
+ return (addr);
+ }
+ goto alloc;
} else
addr = (uint8_t *)crp->crp_buf;
*allocated = false;
@@ -662,6 +732,7 @@
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);
@@ -715,8 +786,9 @@
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;
@@ -726,7 +798,8 @@
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;
@@ -752,6 +825,7 @@
ivlen = 8;
break;
case CRYPTO_AES_NIST_GCM_16:
+ case CRYPTO_AES_CCM_16:
ivlen = 12; /* should support arbitarily larger */
break;
}
@@ -820,9 +894,28 @@
error = EBADMSG;
}
break;
+ case CRYPTO_AES_CCM_16:
+ if (!encflag)
+ crypto_copydata(crp->crp_flags, crp->crp_buf,
+ authcrd->crd_inject, GMAC_DIGEST_LEN, 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, GMAC_DIGEST_LEN, 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);
Index: sys/crypto/aesni/aesni_ccm.c
===================================================================
--- sys/crypto/aesni/aesni_ccm.c
+++ sys/crypto/aesni/aesni_ccm.c
@@ -0,0 +1,596 @@
+/*-
+ * 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>
+
+#ifdef _KERNEL
+#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)
+#else
+#include <stdio.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <strings.h>
+#include <err.h>
+#endif
+
+#include <wmmintrin.h>
+#include <emmintrin.h>
+#include <smmintrin.h>
+
+typedef union {
+ __m128i block;
+ uint8_t bytes[sizeof(__m128i)];
+} aes_block_t;
+
+#ifndef _KERNEL
+static void
+panic(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ verrx(1, fmt, ap);
+ va_end(ap);
+}
+#endif
+
+#ifdef CRYPTO_DEBUG
+static void
+PrintBlock(const char *label, __m128i b)
+{
+ uint8_t *ptr = (uint8_t*)&b;
+ printf("%s: ", label);
+ for (size_t i = 0; i < sizeof(b); i++)
+ printf("%02x ", ptr[i]);
+ printf("\n");
+}
+#endif
+
+#ifdef STANDALONE
+static void PrintHex(const void *, size_t);
+#endif
+
+#ifndef _KERNEL
+/*
+ * Convenience wrapper to do AES encryption.
+ */
+static inline __m128i
+aes_encrypt(__m128i data, const unsigned char *k, int nr)
+{
+ int i;
+ __m128 retval = data;
+ const __m128i *key = (const void*)k;
+ retval = _mm_xor_si128(retval, key[0]);
+ for (i = 1; i < nr; i++) {
+ retval = _mm_aesenc_si128(retval, key[i]);
+ }
+ retval = _mm_aesenclast_si128(retval, key[nr]);
+ return retval;
+}
+#endif
+
+/*
+ * 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);
+#ifdef CRYPTO_DEBUG
+ PrintBlock("\ta\t", a);
+ PrintBlock("\tb\t", b);
+ PrintBlock("\tresult\t", retval);
+#endif
+ 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)
+{
+ aes_block_t retval, temp_block;
+ /* 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.
+ */
+ retval.block = _mm_setzero_si128();
+ retval.bytes[0] = (auth_len ? 1 : 0) * 64 |
+ (((tag_len - 2) / 2) * 8) |
+ (L - 1);
+ bcopy(nonce, &retval.bytes[1], nonce_len);
+ append_int(data_len, &retval.block, L+1);
+#ifdef CRYPTO_DEBUG
+ PrintBlock("Plain B0", retval.block);
+#endif
+ retval.block = AESNI_ENC(retval.block, key, nr);
+
+ if (auth_len) {
+ /*
+ * We need to start by appending the length descriptor.
+ */
+ uint32_t auth_amt;
+ size_t copy_amt;
+ const uint8_t *auth_ptr = auth_data;
+
+ temp_block.block = _mm_setzero_si128();
+
+ if (auth_len < ((1<<16) - (1<<8))) {
+ uint16_t *ip = (uint16_t*)&temp_block;
+ *ip = htobe16(auth_len);
+ auth_amt = 2;
+ } else {
+ /*
+ * The current calling convention means that
+ * there can never be more than 4g of authentication
+ * data, so we don't handle the 0xffff case.
+ */
+ uint32_t *ip = (uint32_t*)&temp_block.bytes[2];
+ temp_block.bytes[0] = 0xff;
+ temp_block.bytes[1] = 0xfe;
+ *ip = htobe32(auth_len);
+ auth_amt = 2 + sizeof(*ip);
+ }
+ /*
+ * 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(temp_block) - auth_amt);
+ bcopy(auth_ptr, &temp_block.bytes[auth_amt], copy_amt);
+ auth_ptr += copy_amt;
+
+ retval.block = xor_and_encrypt(retval.block, temp_block.block, key, nr);
+
+ while (auth_ptr < auth_data + auth_len) {
+ copy_amt = MIN((auth_data + auth_len) - auth_ptr, sizeof(temp_block));
+ if (copy_amt < sizeof(retval))
+ bzero(&temp_block, sizeof(temp_block));
+ bcopy(auth_ptr, &temp_block, copy_amt);
+ retval.block = xor_and_encrypt(retval.block, temp_block.block, key, nr);
+ auth_ptr += copy_amt;
+ }
+ }
+ return retval.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 16MBytes. And similarly,
+ * since abyes is limited to a 32 bit value here, the AAD is
+ * limited to 4gbytes 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;
+
+ aes_block_t s0, last_block, current_block, s_x, temp_block;
+
+#ifdef CRYPTO_DEBUG
+ printf("%s(%p, %p, %p, %p, %p, %u, %u, %d, %p, %d)\n",
+ __FUNCTION__, in, out, addt, nonce, tag, nbytes, abytes, nlen, key, nr);
+#endif
+
+ if (nbytes == 0)
+ return;
+ 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;
+ * 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.
+ */
+ if (nbytes > ((1 << (8 * L)) - 1))
+ panic("%s: nbytes is %u, but length field is %d bytes",
+ __FUNCTION__, nbytes, L);
+ /*
+ * Clear out the blocks
+ */
+ explicit_bzero(&s0, sizeof(s0));
+ explicit_bzero(¤t_block, sizeof(current_block));
+
+ last_block.block = cbc_mac_start(addt, abytes, nonce, nlen,
+ key, nr, nbytes, tag_length);
+
+ /* s0 has flags, nonce, and then 0 */
+ s0.bytes[0] = L-1; /* but the flags byte only has L' */
+ bcopy(nonce, &s0.bytes[1], nlen);
+#ifdef CRYPTO_DEBUG
+ PrintBlock("s0", s0.block);
+#endif
+
+ /*
+ * Now to cycle through the rest of the data.
+ */
+ bcopy(&s0, &s_x, sizeof(s0));
+
+ while (total < nbytes) {
+ /*
+ * Copy the plain-text data into temp_block.
+ * This may need to be zero-padded.
+ */
+ copy_amt = MIN(nbytes - total, sizeof(temp_block));
+ bcopy(in+total, &temp_block, copy_amt);
+ if (copy_amt < sizeof(temp_block)) {
+ bzero(&temp_block.bytes[copy_amt],
+ sizeof(temp_block) - copy_amt);
+ }
+#ifdef CRYPTO_DEBUG
+ PrintBlock("Plain text", temp_block.block);
+#endif
+ last_block.block = xor_and_encrypt(last_block.block,
+ temp_block.block, key, nr);
+ /* Put the counter into the s_x block */
+ append_int(counter++, &s_x.block, L+1);
+ /* Encrypt that */
+ __m128i X = AESNI_ENC(s_x.block, key, nr);
+ /* XOR the plain-text with the encrypted counter block */
+ temp_block.block = _mm_xor_si128(temp_block.block, X);
+#ifdef CRYPTO_DEBUG
+ PrintBlock("Encrypted block", temp_block.block);
+#endif
+ /* And copy it out */
+ bcopy(&temp_block, out+total, copy_amt);
+ total += copy_amt;
+ }
+ /*
+ * Allgedly done with it! Except for the tag.
+ */
+#ifdef CRYPTO_DEBUG
+ PrintBlock("Final last block", last_block.block);
+#endif
+ s0.block = AESNI_ENC(s0.block, key, nr);
+ temp_block.block = _mm_xor_si128(s0.block, last_block.block);
+#ifdef CRYPTO_DEBUG
+ printf("Tag length %d; ", tag_length);
+ PrintBlock("Final tag", temp_block.block);
+#endif
+ bcopy(&temp_block, tag, tag_length);
+ return;
+}
+
+/*
+ * 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,
+ aes_block_t s0, size_t nonce_length, aes_block_t *hashp,
+ const unsigned char *key, int nr)
+{
+ size_t total = 0;
+ aes_block_t s_x = s0, hash_block;
+ int counter = 1;
+ const size_t L = sizeof(__m128i) - 1 - nonce_length;
+ __m128i pad_block;
+
+ /*
+ * The starting hash (post AAD, if any).
+ */
+ if (hashp)
+ hash_block = *hashp;
+
+ while (total < nbytes) {
+ aes_block_t temp_block;
+
+ size_t copy_amt = MIN(nbytes - total, sizeof(temp_block));
+ if (copy_amt < sizeof(temp_block)) {
+ temp_block.block = _mm_setzero_si128();
+ }
+ bcopy(in+total, &temp_block, copy_amt);
+
+ /*
+ * temp_block has the current block of input data,
+ * zero-padded if necessary. This is used in computing
+ * both the decrypted data, and the authentication hash.
+ */
+ append_int(counter++, &s_x.block, L+1);
+ /*
+ * The hash is computed based on the decrypted data.
+ */
+ pad_block = AESNI_ENC(s_x.block, key, nr);
+ if (copy_amt < sizeof(temp_block)) {
+ /*
+ * Need to pad out both blocks with 0.
+ */
+ uint8_t *end_of_buffer = (uint8_t*)&pad_block;
+ bzero(&temp_block.bytes[copy_amt],
+ sizeof(temp_block) - copy_amt);
+ bzero(end_of_buffer + copy_amt,
+ sizeof(temp_block) - copy_amt);
+ }
+ temp_block.block = _mm_xor_si128(temp_block.block,
+ pad_block);
+
+ if (out)
+ bcopy(&temp_block, out+total, copy_amt);
+
+ if (hashp)
+ hash_block.block = xor_and_encrypt(hash_block.block,
+ temp_block.block, key, nr);
+ total += copy_amt;
+ }
+ explicit_bzero(&pad_block, sizeof(pad_block));
+
+ if (hashp)
+ *hashp = hash_block;
+ return;
+}
+
+/*
+ * The exposed decryption routine. This is practically a
+ * copy of the encryption routine, except that the order
+ * in which the hash 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;
+ aes_block_t s0, last_block, current_block, s_x, temp_block;
+
+#ifdef CRYPTO_DEBUG
+ printf("%s(%p, %p, %p, %p, %p, %u, %u, %d, %p, %d)\n",
+ __FUNCTION__, in, out, addt, nonce, tag, nbytes, abytes, nlen, key, nr);
+#endif
+ if (nbytes == 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.block = _mm_setzero_si128();
+ current_block = s0;
+
+ last_block.block = cbc_mac_start(addt, abytes, nonce, nlen,
+ key, nr, nbytes, tag_length);
+ /* s0 has flags, nonce, and then 0 */
+ s0.bytes[0] = L-1; /* but the flags byte only has L' */
+ bcopy(nonce, &s0.bytes[1], nlen);
+#ifdef CRYPTO_DEBUG
+ PrintBlock("s0", s0.block);
+#endif
+
+ /*
+ * Now to cycle through the rest of the data.
+ */
+ s_x = s0;
+
+ decrypt_loop(in, NULL, nbytes, s0, nlen, &last_block, key, nr);
+
+ /*
+ * Compare the tag.
+ */
+ temp_block.block = _mm_xor_si128(AESNI_ENC(s0.block, key, nr),
+ last_block.block);
+ if (bcmp(&temp_block, tag, tag_length) != 0) {
+#ifdef CRYPTO_DEBUG
+ PrintBlock("Computed tag", temp_block.block);
+ PrintBlock("Input tag ", *(const __m128i*)tag);
+#endif
+ return 0;
+ }
+
+ /*
+ * Push out the decryption results this time.
+ */
+ decrypt_loop(in, out, nbytes, s0, nlen, NULL, key, nr);
+ return 1;
+}
+
+#ifdef STANDALONE
+/*
+ * Used for testing
+ */
+/*
+ * The hard-coded key expansion for an all-zeroes key.
+ */
+static uint8_t expanded_zero_key[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x62, 0x63, 0x63, 0x63, 0x62, 0x63, 0x63, 0x63, 0x62, 0x63, 0x63, 0x63, 0x62, 0x63, 0x63, 0x63,
+0xaa, 0xfb, 0xfb, 0xfb, 0xaa, 0xfb, 0xfb, 0xfb, 0xaa, 0xfb, 0xfb, 0xfb, 0xaa, 0xfb, 0xfb, 0xfb,
+0x6f, 0x6c, 0x6c, 0xcf, 0x0d, 0x0f, 0x0f, 0xac, 0x6f, 0x6c, 0x6c, 0xcf, 0x0d, 0x0f, 0x0f, 0xac,
+0x7d, 0x8d, 0x8d, 0x6a, 0xd7, 0x76, 0x76, 0x91, 0x7d, 0x8d, 0x8d, 0x6a, 0xd7, 0x76, 0x76, 0x91,
+0x53, 0x54, 0xed, 0xc1, 0x5e, 0x5b, 0xe2, 0x6d, 0x31, 0x37, 0x8e, 0xa2, 0x3c, 0x38, 0x81, 0x0e,
+0x96, 0x8a, 0x81, 0xc1, 0x41, 0xfc, 0xf7, 0x50, 0x3c, 0x71, 0x7a, 0x3a, 0xeb, 0x07, 0x0c, 0xab,
+0x9e, 0xaa, 0x8f, 0x28, 0xc0, 0xf1, 0x6d, 0x45, 0xf1, 0xc6, 0xe3, 0xe7, 0xcd, 0xfe, 0x62, 0xe9,
+0x2b, 0x31, 0x2b, 0xdf, 0x6a, 0xcd, 0xdc, 0x8f, 0x56, 0xbc, 0xa6, 0xb5, 0xbd, 0xbb, 0xaa, 0x1e,
+0x64, 0x06, 0xfd, 0x52, 0xa4, 0xf7, 0x90, 0x17, 0x55, 0x31, 0x73, 0xf0, 0x98, 0xcf, 0x11, 0x19,
+0x6d, 0xbb, 0xa9, 0x0b, 0x07, 0x76, 0x75, 0x84, 0x51, 0xca, 0xd3, 0x31, 0xec, 0x71, 0x79, 0x2f,
+0xe7, 0xb0, 0xe8, 0x9c, 0x43, 0x47, 0x78, 0x8b, 0x16, 0x76, 0x0b, 0x7b, 0x8e, 0xb9, 0x1a, 0x62,
+0x74, 0xed, 0x0b, 0xa1, 0x73, 0x9b, 0x7e, 0x25, 0x22, 0x51, 0xad, 0x14, 0xce, 0x20, 0xd4, 0x3b,
+0x10, 0xf8, 0x0a, 0x17, 0x53, 0xbf, 0x72, 0x9c, 0x45, 0xc9, 0x79, 0xe7, 0xcb, 0x70, 0x63, 0x85,
+};
+
+static uint8_t expanded_zero_key_2[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x62, 0x63, 0x63, 0x63, 0x62, 0x63, 0x63, 0x63, 0x62, 0x63, 0x63, 0x63, 0x62, 0x63, 0x63, 0x63,
+0xaa, 0xfb, 0xfb, 0xfb, 0xaa, 0xfb, 0xfb, 0xfb, 0xaa, 0xfb, 0xfb, 0xfb, 0xaa, 0xfb, 0xfb, 0xfb,
+0x6f, 0x6c, 0x6c, 0xcf, 0x0d, 0x0f, 0x0f, 0xac, 0x6f, 0x6c, 0x6c, 0xcf, 0x0d, 0x0f, 0x0f, 0xac,
+0x7d, 0x8d, 0x8d, 0x6a, 0xd7, 0x76, 0x76, 0x91, 0x7d, 0x8d, 0x8d, 0x6a, 0xd7, 0x76, 0x76, 0x91,
+0x53, 0x54, 0xed, 0xc1, 0x5e, 0x5b, 0xe2, 0x6d, 0x31, 0x37, 0x8e, 0xa2, 0x3c, 0x38, 0x81, 0x0e,
+0x96, 0x8a, 0x81, 0xc1, 0x41, 0xfc, 0xf7, 0x50, 0x3c, 0x71, 0x7a, 0x3a, 0xeb, 0x07, 0x0c, 0xab,
+0x9e, 0xaa, 0x8f, 0x28, 0xc0, 0xf1, 0x6d, 0x45, 0xf1, 0xc6, 0xe3, 0xe7, 0xcd, 0xfe, 0x62, 0xe9,
+0x2b, 0x31, 0x2b, 0xdf, 0x6a, 0xcd, 0xdc, 0x8f, 0x56, 0xbc, 0xa6, 0xb5, 0xbd, 0xbb, 0xaa, 0x1e,
+0x64, 0x06, 0xfd, 0x52, 0xa4, 0xf7, 0x90, 0x17, 0x55, 0x31, 0x73, 0xf0, 0x98, 0xcf, 0x11, 0x19,
+0x6d, 0xbb, 0xa9, 0x0b, 0x07, 0x76, 0x75, 0x84, 0x51, 0xca, 0xd3, 0x31, 0xec, 0x71, 0x79, 0x2f,
+0xe7, 0xb0, 0xe8, 0x9c, 0x43, 0x47, 0x78, 0x8b, 0x16, 0x76, 0x0b, 0x7b, 0x8e, 0xb9, 0x1a, 0x62,
+0x74, 0xed, 0x0b, 0xa1, 0x73, 0x9b, 0x7e, 0x25, 0x22, 0x51, 0xad, 0x14, 0xce, 0x20, 0xd4, 0x3b,
+0x10, 0xf8, 0x0a, 0x17, 0x53, 0xbf, 0x72, 0x9c, 0x45, 0xc9, 0x79, 0xe7, 0xcb, 0x70, 0x63, 0x85,
+};
+static void
+PrintHex(const void *bytes, size_t len)
+{
+ const uint8_t *b = bytes;
+ for (size_t x = 0; x < len; x++)
+ printf("%02x ", b[x]);
+ printf("\n");
+ return;
+}
+
+int
+main(int ac, char **av)
+{
+ uint8_t tag[16];
+ uint8_t nonce[12] = { 0 };
+ unsigned char aad[] = "How now brown cow";
+// unsigned char plain[] = "Four score and seven years ago, our forefathers brought Bill & Ted";
+ unsigned char plain[4] = "abcd";
+ unsigned char crypt[sizeof(plain)];
+ unsigned char decrypted[sizeof(plain)];
+ uint8_t key[256 / 8] = { 0 };
+ int nrounds = 14; // For a 256-bit key, use 14 rounds
+ int rv;
+
+ printf("Plaintext: "); PrintHex(plain, sizeof(plain));
+ printf("aad size = %zx, nonce size = %zx, tag size = %zx\n", sizeof(aad), sizeof(nonce), sizeof(tag));
+ AES_CCM_encrypt(plain, crypt, aad, nonce, tag,
+ sizeof(plain), sizeof(aad), sizeof(nonce),
+ (const unsigned char *)expanded_zero_key, nrounds);
+ printf("Tag: "); PrintHex(tag, sizeof(tag));
+ printf("Crypt: "); PrintHex(crypt, sizeof(crypt));
+
+ rv = AES_CCM_decrypt(crypt, decrypted, aad, nonce, tag,
+ sizeof(plain), sizeof(aad), sizeof(nonce),
+ (const unsigned char *)expanded_zero_key_2, nrounds);
+ printf("%s Decrypted: ", rv == 1 ? "Successfully" : "Unsuccessfully");
+ PrintHex(decrypted, sizeof(decrypted));
+
+ return 0;
+}
+#endif
Index: sys/crypto/aesni/aesni_wrap.c
===================================================================
--- sys/crypto/aesni/aesni_wrap.c
+++ sys/crypto/aesni/aesni_wrap.c
@@ -446,6 +446,7 @@
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:
Index: sys/modules/aesni/Makefile
===================================================================
--- sys/modules/aesni/Makefile
+++ sys/modules/aesni/Makefile
@@ -8,7 +8,7 @@
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.
@@ -18,6 +18,12 @@
-mmmx -msse -msse4 -maes -mpclmul ${.IMPSRC}
${CTFCONVERT_CMD}
+aesni_ccm.o: aesni_ccm.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_wrap.o: aesni_wrap.c
${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc} ${WERROR} ${PROF} \
-mmmx -msse -msse4 -maes ${.IMPSRC}
@@ -35,6 +41,7 @@
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
Index: sys/modules/crypto/Makefile
===================================================================
--- sys/modules/crypto/Makefile
+++ sys/modules/crypto/Makefile
@@ -68,5 +68,7 @@
SRCS += opt_param.h cryptodev_if.h bus_if.h device_if.h
SRCS += opt_ddb.h
+SRCS += ccm-cbc.c
+SRCS += xform_cbc_mac.c
.include <bsd.kmod.mk>
Index: sys/opencrypto/ccm-cbc.h
===================================================================
--- sys/opencrypto/ccm-cbc.h
+++ sys/opencrypto/ccm-cbc.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2014 The FreeBSD Foundation
+ * Copyright (c) 2018, iXsystems Inc.
+ * All rights reserved.
+ *
+ * This software was developed by Sean Eric Fagan, with lots of references
+ * to existing AES-CCM (gmac) code.
+ *
+ * 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$
+ *
+ */
+
+#ifndef _CCM_H
+# define _CCM_H
+
+# include <sys/types.h>
+# include <crypto/rijndael/rijndael.h>
+
+# define CCM_CBC_BLOCK_LEN 16 /* 128 bits */
+# define CCM_CBC_MAX_DIGEST_LEN 16
+# define CCM_CBC_MIN_DIGEST_LEN 4
+
+/*
+ * This is the authentication context structure;
+ * the encryption one is similar.
+ */
+struct aes_cbc_mac_ctx {
+ uint64_t authDataLength, authDataCount;
+ uint64_t cryptDataLength;
+ int tagLength;
+ int blockIndex;
+ uint8_t staging_block[CCM_CBC_BLOCK_LEN];
+ uint8_t block[CCM_CBC_BLOCK_LEN];
+ const uint8_t *aes_key;
+ int keyLength; /* This will be in bits, not bytes! */
+ const uint8_t *nonce;
+ int nonceLength; /* This one is in bytes, not bits! */
+ /* AES state data */
+ int rounds;
+ uint32_t keysched[4*(RIJNDAEL_MAXNR+1)];
+};
+
+void AES_CBC_MAC_Init(struct aes_cbc_mac_ctx *);
+void AES_CBC_MAC_Setkey(struct aes_cbc_mac_ctx *, const uint8_t *, uint16_t);
+void AES_CBC_MAC_Reinit(struct aes_cbc_mac_ctx *, const uint8_t *, uint16_t);
+int AES_CBC_MAC_Update(struct aes_cbc_mac_ctx *, const uint8_t *, uint16_t);
+void AES_CBC_MAC_Final(uint8_t *, struct aes_cbc_mac_ctx *);
+
+#endif /* _CCM_H */
Index: sys/opencrypto/ccm-cbc.c
===================================================================
--- sys/opencrypto/ccm-cbc.c
+++ sys/opencrypto/ccm-cbc.c
@@ -0,0 +1,206 @@
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/param.h>
+#include <sys/endian.h>
+#include <opencrypto/ccm-cbc.h>
+#include <opencrypto/xform_auth.h>
+
+/*
+ * Given two CCM_CBC_BLOCK_LEN blocks, xor
+ * them into dst, and then encrypt dst.
+ */
+static void
+xor_and_encrypt(struct aes_cbc_mac_ctx *ctx,
+ const uint8_t *src, uint8_t *dst)
+{
+ const uint64_t *b1;
+ uint64_t *b2;
+ uint64_t temp_block[CCM_CBC_BLOCK_LEN/sizeof(uint64_t)];
+ b1 = (const uint64_t*)src;
+ b2 = (uint64_t*)dst;
+
+ for (size_t count = 0;
+ count < CCM_CBC_BLOCK_LEN/sizeof(uint64_t);
+ count++) {
+ temp_block[count] = b1[count] ^ b2[count];
+ }
+ rijndaelEncrypt(ctx->keysched, ctx->rounds, (void*)temp_block, dst);
+}
+
+void
+AES_CBC_MAC_Init(struct aes_cbc_mac_ctx *ctx)
+{
+ bzero(ctx, sizeof *ctx);
+ ctx->tagLength = AES_CBC_MAC_HASH_LEN;
+}
+
+void
+AES_CBC_MAC_Setkey(struct aes_cbc_mac_ctx *ctx, const uint8_t *key, uint16_t klen)
+{
+ ctx->rounds = rijndaelKeySetupEnc(ctx->keysched, key, klen * 8);
+ return;
+}
+
+/*
+ * This is called to set the nonce, aka IV.
+ * Before this call, the authDataLength and cryptDataLength fields
+ * MUST have been set. Sadly, there's no way to return an error.
+ *
+ * The CBC-MAC algorithm requires that the first block contain the
+ * nonce, as well as information about the sizes and lengths involved.
+ */
+void
+AES_CBC_MAC_Reinit(struct aes_cbc_mac_ctx *ctx, const uint8_t *nonce, uint16_t nonceLen)
+{
+ uint8_t b0[CCM_CBC_BLOCK_LEN];
+ uint8_t *bp = b0, flags = 0;
+ uint8_t L = 0;
+ uint64_t tmp = ctx->cryptDataLength;
+
+ if (ctx->authDataLength == 0 &&
+ ctx->cryptDataLength == 0) {
+ return;
+ }
+
+ ctx->nonce = nonce;
+ ctx->nonceLength = nonceLen;
+
+ ctx->authDataCount = 0;
+ ctx->blockIndex = 0;
+ explicit_bzero(ctx->staging_block, sizeof(ctx->staging_block));
+
+ /*
+ * Need to determine the L field value.
+ * This is the number of bytes needed to
+ * specify the length of the message; the
+ * length is whatever is left in the 16 bytes
+ * after specifying flags and the nonce.
+ */
+ L = (15 - nonceLen) & 0xff;
+
+ flags = (ctx->authDataLength > 0) * 64 +
+ ((ctx->tagLength-2) / 2) * 8 +
+ L - 1;
+ /*
+ * Now we need to set up the first block,
+ * which has flags, nonce, and the message length.
+ */
+ b0[0] = flags;
+ bcopy(nonce, b0+1, nonceLen);
+ bp = b0 + 1 + nonceLen;
+
+ /* Need to copy L' [aka L-1] bytes of cryptDataLength */
+ for (uint8_t *dst = b0 + sizeof(b0) - 1;
+ dst >= bp;
+ dst--) {
+ *dst = (tmp & 0xff);
+ tmp >>= 8;
+ }
+ /* Now need to encrypt b0 */
+ rijndaelEncrypt(ctx->keysched, ctx->rounds, b0, ctx->block);
+ /* If there is auth data, we need to set up the staging block */
+ if (ctx->authDataLength) {
+ if (ctx->authDataLength < ((1<<16) - (1<<8))) {
+ uint16_t sizeVal = htobe16(ctx->authDataLength);
+ bcopy(&sizeVal, ctx->staging_block, sizeof(sizeVal));
+ ctx->blockIndex = sizeof(sizeVal);
+ } else if (ctx->authDataLength < (1UL<<32)) {
+ uint32_t sizeVal = htobe32(ctx->authDataLength);
+ ctx->staging_block[0] = 0xff;
+ ctx->staging_block[1] = 0xfe;
+ bcopy(&sizeVal, ctx->staging_block+2, sizeof(sizeVal));
+ ctx->blockIndex = 2 + sizeof(sizeVal);
+ } else {
+ uint64_t sizeVal = htobe64(ctx->authDataLength);
+ ctx->staging_block[0] = 0xff;
+ ctx->staging_block[1] = 0xff;
+ bcopy(&sizeVal, ctx->staging_block+2, sizeof(sizeVal));
+ ctx->blockIndex = 2 + sizeof(sizeVal);
+ }
+ }
+ return;
+}
+
+int
+AES_CBC_MAC_Update(struct aes_cbc_mac_ctx *ctx, const uint8_t *data, uint16_t length)
+{
+
+ /*
+ * This will be called in one of two phases:
+ * (1) Applying authentication data, or
+ * (2) Applying the payload data.
+ * Because CBC-MAC puts the authentication data
+ * size before the data, subsequent calls won't
+ * be block-size-aligned. Which complicates things
+ * a fair bit.
+ *
+ * The payload data doesn't have that problem.
+ */
+
+ if (ctx->authDataCount < ctx->authDataLength) {
+ /*
+ * We need to process data as authentication data.
+ * Since we may be out of sync, we may also need
+ * to pad out the staging block.
+ */
+ const uint8_t *ptr = data;
+ while (length) {
+ size_t copy_amt = MIN(length,
+ sizeof(ctx->staging_block) - ctx->blockIndex);
+ bcopy(ptr, ctx->staging_block + ctx->blockIndex, copy_amt);
+ ptr += copy_amt;
+ length -= copy_amt;
+ ctx->authDataCount += copy_amt;
+ ctx->blockIndex += copy_amt;
+ ctx->blockIndex %= sizeof(ctx->staging_block);
+ if (ctx->authDataCount >= ctx->authDataLength)
+ length = 0;
+ if (ctx->blockIndex == 0 ||
+ ctx->authDataCount >= ctx->authDataLength) {
+ /*
+ * We're done with this block, so we
+ * xor staging_block with block, and then
+ * encrypt it.
+ */
+ xor_and_encrypt(ctx, ctx->staging_block, ctx->block);
+ explicit_bzero(ctx->staging_block, sizeof(ctx->staging_block));
+ ctx->blockIndex = 0;
+ }
+ }
+ return (0);
+ }
+ /*
+ * If we're here, then we're encoding payload data.
+ * This is easier, as we just xor&encrypt.
+ */
+ while (length) {
+ const uint8_t *ptr;
+
+ if (length < sizeof(ctx->block)) {
+ explicit_bzero(ctx->staging_block, sizeof(ctx->staging_block));
+ bcopy(data, ctx->staging_block, length);
+ ptr = ctx->staging_block;
+ length = 0;
+ } else {
+ ptr = data;
+ length -= sizeof(ctx->block);
+ }
+ xor_and_encrypt(ctx, ptr, ctx->block);
+ }
+ return (0);
+}
+
+void
+AES_CBC_MAC_Final(uint8_t *buf, struct aes_cbc_mac_ctx *ctx)
+{
+ uint8_t s0[CCM_CBC_BLOCK_LEN];
+
+ explicit_bzero(s0, sizeof(s0));
+ s0[0] = ((15 - ctx->nonceLength) & 0xff) - 1;
+ bcopy(ctx->nonce, s0+1, ctx->nonceLength);
+ rijndaelEncrypt(ctx->keysched, ctx->rounds, s0, s0);
+ for (size_t indx = 0; indx < ctx->tagLength; indx++)
+ buf[indx] = ctx->block[indx] ^ s0[indx];
+ explicit_bzero(s0, sizeof(s0));
+ return;
+}
Index: sys/opencrypto/cryptodev.h
===================================================================
--- sys/opencrypto/cryptodev.h
+++ sys/opencrypto/cryptodev.h
@@ -86,6 +86,7 @@
#define SHA1_KPDK_HASH_LEN 20
#define AES_GMAC_HASH_LEN 16
#define POLY1305_HASH_LEN 16
+#define AES_CBC_MAC_HASH_LEN 16
/* Maximum hash algorithm result length */
#define HASH_MAX_LEN SHA2_512_HASH_LEN /* Keep this updated */
@@ -107,6 +108,9 @@
#define AES_128_GMAC_KEY_LEN 16
#define AES_192_GMAC_KEY_LEN 24
#define AES_256_GMAC_KEY_LEN 32
+#define AES_128_CBC_MAC_KEY_LEN 16
+#define AES_192_CBC_MAC_KEY_LEN 24
+#define AES_256_CBC_MAC_KEY_LEN 32
#define POLY1305_KEY_LEN 32
@@ -129,6 +133,7 @@
#define ARC4_IV_LEN 1
#define AES_GCM_IV_LEN 12
+#define AES_CCM_IV_LEN 12
#define AES_XTS_IV_LEN 8
#define AES_XTS_ALPHA 0x87 /* GF(2^128) generator polynomial */
@@ -199,7 +204,11 @@
#define CRYPTO_SHA2_384 36
#define CRYPTO_SHA2_512 37
#define CRYPTO_POLY1305 38
-#define CRYPTO_ALGORITHM_MAX 38 /* Keep updated - see below */
+#define CRYPTO_AES_128_CCM_CBC_MAC 39 /* auth side */
+#define CRYPTO_AES_192_CCM_CBC_MAC 40 /* auth side */
+#define CRYPTO_AES_256_CCM_CBC_MAC 41 /* auth side */
+#define CRYPTO_AES_CCM_16 42 /* cipher side */
+#define CRYPTO_ALGORITHM_MAX 42 /* Keep updated - see below */
#define CRYPTO_ALGO_VALID(x) ((x) >= CRYPTO_ALGORITHM_MIN && \
(x) <= CRYPTO_ALGORITHM_MAX)
Index: sys/opencrypto/cryptodev.c
===================================================================
--- sys/opencrypto/cryptodev.c
+++ sys/opencrypto/cryptodev.c
@@ -444,6 +444,9 @@
case CRYPTO_CHACHA20:
txform = &enc_xform_chacha20;
break;
+ case CRYPTO_AES_CCM_16:
+ txform = &enc_xform_ccm;
+ break;
default:
CRYPTDEB("invalid cipher");
@@ -488,6 +491,15 @@
thash = &auth_hash_nist_gmac_aes_256;
break;
+ case CRYPTO_AES_128_CCM_CBC_MAC:
+ thash = &auth_hash_ccm_cbc_mac_128;
+ break;
+ case CRYPTO_AES_192_CCM_CBC_MAC:
+ thash = &auth_hash_ccm_cbc_mac_192;
+ break;
+ case CRYPTO_AES_256_CCM_CBC_MAC:
+ thash = &auth_hash_ccm_cbc_mac_256;
+ break;
#ifdef notdef
case CRYPTO_MD5:
thash = &auth_hash_md5;
@@ -1008,7 +1020,8 @@
* cipher text.
*/
crda->crd_skip = 0;
- if (cse->cipher == CRYPTO_AES_NIST_GCM_16)
+ if (cse->cipher == CRYPTO_AES_NIST_GCM_16 ||
+ cse->cipher == CRYPTO_AES_CCM_16)
crda->crd_len = caead->aadlen;
else
crda->crd_len = caead->aadlen + caead->len;
Index: sys/opencrypto/cryptosoft.c
===================================================================
--- sys/opencrypto/cryptosoft.c
+++ sys/opencrypto/cryptosoft.c
@@ -505,6 +505,7 @@
caddr_t buf = (caddr_t)crp->crp_buf;
uint32_t *blkp;
int aadlen, blksz, i, ivlen, len, iskip, oskip, r;
+ int isccm = 0;
ivlen = blksz = iskip = oskip = 0;
@@ -519,6 +520,8 @@
sw = &ses->swcr_algorithms[i];
switch (sw->sw_alg) {
+ case CRYPTO_AES_CCM_16:
+ isccm = 1;
case CRYPTO_AES_NIST_GCM_16:
case CRYPTO_AES_NIST_GMAC:
swe = sw;
@@ -526,6 +529,10 @@
exf = swe->sw_exf;
ivlen = 12;
break;
+ case CRYPTO_AES_128_CCM_CBC_MAC:
+ case CRYPTO_AES_192_CCM_CBC_MAC:
+ case CRYPTO_AES_256_CCM_CBC_MAC:
+ isccm = 1;
case CRYPTO_AES_128_NIST_GMAC:
case CRYPTO_AES_192_NIST_GMAC:
case CRYPTO_AES_256_NIST_GMAC:
@@ -544,7 +551,8 @@
if (crde == NULL || crda == NULL)
return (EINVAL);
- if (crde->crd_alg == CRYPTO_AES_NIST_GCM_16 &&
+ if ((crde->crd_alg == CRYPTO_AES_NIST_GCM_16 ||
+ crde->crd_alg == CRYPTO_AES_CCM_16) &&
(crde->crd_flags & CRD_F_IV_EXPLICIT) == 0)
return (EINVAL);
@@ -575,6 +583,21 @@
}
}
+ if (swa) {
+ switch (swa->sw_alg) {
+ case CRYPTO_AES_128_CCM_CBC_MAC:
+ case CRYPTO_AES_192_CCM_CBC_MAC:
+ case CRYPTO_AES_256_CCM_CBC_MAC:
+ /*
+ * AES CCM-CBC needs to know the length of
+ * both the auth data, and payload data, before
+ * doing the auth computation.
+ */
+ ctx.aes_cbc_mac_ctx.authDataLength = crda->crd_len;
+ ctx.aes_cbc_mac_ctx.cryptDataLength = crde->crd_len;
+ break;
+ }
+ }
/* Supply MAC with IV */
if (axf->Reinit)
axf->Reinit(&ctx, iv, ivlen);
@@ -610,15 +633,20 @@
crypto_copydata(crp->crp_flags, buf, crde->crd_skip + i, len,
blk);
if (crde->crd_flags & CRD_F_ENCRYPT) {
+ if (isccm)
+ axf->Update(&ctx, blk, len);
if (exf->encrypt_multi != NULL)
exf->encrypt_multi(swe->sw_kschedule, blk,
len);
else
exf->encrypt(swe->sw_kschedule, blk);
- axf->Update(&ctx, blk, len);
+ if (!isccm)
+ axf->Update(&ctx, blk, len);
crypto_copyback(crp->crp_flags, buf,
crde->crd_skip + i, len, blk);
} else {
+ if (isccm)
+ exf->decrypt(swe->sw_kschedule, blk);
axf->Update(&ctx, blk, len);
}
}
@@ -649,6 +677,8 @@
r = timingsafe_bcmp(aalg, uaalg, axf->hashsize);
if (r == 0) {
/* tag matches, decrypt data */
+ if (isccm && exf->reinit)
+ exf->reinit(swe->sw_kschedule, iv);
for (i = 0; i < crde->crd_len; i += blksz) {
len = MIN(crde->crd_len - i, blksz);
if (len < blksz)
@@ -797,6 +827,9 @@
case CRYPTO_AES_NIST_GCM_16:
txf = &enc_xform_aes_nist_gcm;
goto enccommon;
+ case CRYPTO_AES_CCM_16:
+ txf = &enc_xform_ccm;
+ goto enccommon;
case CRYPTO_AES_NIST_GMAC:
txf = &enc_xform_aes_nist_gmac;
swd->sw_exf = txf;
@@ -941,6 +974,15 @@
swd->sw_axf = axf;
break;
+ case CRYPTO_AES_128_CCM_CBC_MAC:
+ axf = &auth_hash_ccm_cbc_mac_128;
+ goto auth4common;
+ case CRYPTO_AES_192_CCM_CBC_MAC:
+ axf = &auth_hash_ccm_cbc_mac_192;
+ goto auth4common;
+ case CRYPTO_AES_256_CCM_CBC_MAC:
+ axf = &auth_hash_ccm_cbc_mac_256;
+ goto auth4common;
case CRYPTO_AES_128_NIST_GMAC:
axf = &auth_hash_nist_gmac_aes_128;
goto auth4common;
@@ -1189,11 +1231,15 @@
goto done;
break;
+ case CRYPTO_AES_CCM_16:
case CRYPTO_AES_NIST_GCM_16:
case CRYPTO_AES_NIST_GMAC:
case CRYPTO_AES_128_NIST_GMAC:
case CRYPTO_AES_192_NIST_GMAC:
case CRYPTO_AES_256_NIST_GMAC:
+ case CRYPTO_AES_128_CCM_CBC_MAC:
+ case CRYPTO_AES_192_CCM_CBC_MAC:
+ case CRYPTO_AES_256_CCM_CBC_MAC:
crp->crp_etype = swcr_authenc(crp);
goto done;
@@ -1282,6 +1328,10 @@
REGISTER(CRYPTO_BLAKE2B);
REGISTER(CRYPTO_BLAKE2S);
REGISTER(CRYPTO_CHACHA20);
+ REGISTER(CRYPTO_AES_CCM_16);
+ REGISTER(CRYPTO_AES_128_CCM_CBC_MAC);
+ REGISTER(CRYPTO_AES_192_CCM_CBC_MAC);
+ REGISTER(CRYPTO_AES_256_CCM_CBC_MAC);
REGISTER(CRYPTO_POLY1305);
#undef REGISTER
Index: sys/opencrypto/xform_aes_icm.c
===================================================================
--- sys/opencrypto/xform_aes_icm.c
+++ sys/opencrypto/xform_aes_icm.c
@@ -57,6 +57,7 @@
static void aes_icm_zerokey(u_int8_t **);
static void aes_icm_reinit(caddr_t, u_int8_t *);
static void aes_gcm_reinit(caddr_t, u_int8_t *);
+static void aes_ccm_reinit(caddr_t, u_int8_t *);
/* Encryption instances */
struct enc_xform enc_xform_aes_icm = {
@@ -79,6 +80,16 @@
aes_gcm_reinit,
};
+struct enc_xform enc_xform_ccm = {
+ CRYPTO_AES_CCM_16, "AES-CCM",
+ AES_ICM_BLOCK_LEN, AES_GCM_IV_LEN, AES_MIN_KEY, AES_MAX_KEY,
+ aes_icm_crypt,
+ aes_icm_crypt,
+ aes_icm_setkey,
+ aes_icm_zerokey,
+ aes_ccm_reinit,
+};
+
/*
* Encryption wrapper routines.
*/
@@ -102,6 +113,20 @@
/* GCM starts with 2 as counter 1 is used for final xor of tag. */
bzero(&ctx->ac_block[AESICM_BLOCKSIZE - 4], 4);
ctx->ac_block[AESICM_BLOCKSIZE - 1] = 2;
+}
+
+static void
+aes_ccm_reinit(caddr_t key, u_int8_t *iv)
+{
+ struct aes_icm_ctx *ctx;
+ ctx = (struct aes_icm_ctx*)key;
+
+ /* CCM has flags, then the IV, then the counter, which starts at 1 */
+ bzero(ctx->ac_block, sizeof(ctx->ac_block));
+ /* 3 bytes for length field; this gives a nonce of 12 bytes */
+ ctx->ac_block[0] = (15 - AES_CCM_IV_LEN) - 1;
+ bcopy(iv, ctx->ac_block+1, AES_GCM_IV_LEN);
+ ctx->ac_block[AESICM_BLOCKSIZE - 1] = 1;
}
static void
Index: sys/opencrypto/xform_auth.h
===================================================================
--- sys/opencrypto/xform_auth.h
+++ sys/opencrypto/xform_auth.h
@@ -41,6 +41,7 @@
#include <crypto/sha2/sha512.h>
#include <opencrypto/rmd160.h>
#include <opencrypto/gmac.h>
+#include <opencrypto/ccm-cbc.h>
#include <opencrypto/cryptodev.h>
#include <opencrypto/xform_userland.h>
@@ -84,6 +85,9 @@
extern struct auth_hash auth_hash_blake2b;
extern struct auth_hash auth_hash_blake2s;
extern struct auth_hash auth_hash_poly1305;
+extern struct auth_hash auth_hash_ccm_cbc_mac_128;
+extern struct auth_hash auth_hash_ccm_cbc_mac_192;
+extern struct auth_hash auth_hash_ccm_cbc_mac_256;
union authctx {
MD5_CTX md5ctx;
@@ -93,6 +97,7 @@
SHA384_CTX sha384ctx;
SHA512_CTX sha512ctx;
struct aes_gmac_ctx aes_gmac_ctx;
+ struct aes_cbc_mac_ctx aes_cbc_mac_ctx;
};
#endif /* _CRYPTO_XFORM_AUTH_H_ */
Index: sys/opencrypto/xform_cbc_mac.c
===================================================================
--- sys/opencrypto/xform_cbc_mac.c
+++ sys/opencrypto/xform_cbc_mac.c
@@ -0,0 +1,37 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <opencrypto/ccm-cbc.h>
+#include <opencrypto/xform_auth.h>
+
+/* Authentication instances */
+struct auth_hash auth_hash_ccm_cbc_mac_128 = {
+ CRYPTO_AES_128_CCM_CBC_MAC, "CBC-CCM-AES-128",
+ AES_128_CBC_MAC_KEY_LEN, AES_CBC_MAC_HASH_LEN, sizeof(struct aes_cbc_mac_ctx),
+ CCM_CBC_BLOCK_LEN,
+ (void (*)(void *)) AES_CBC_MAC_Init,
+ (void (*)(void *, const u_int8_t *, u_int16_t)) AES_CBC_MAC_Setkey,
+ (void (*)(void *, const u_int8_t *, u_int16_t)) AES_CBC_MAC_Reinit,
+ (int (*)(void *, const u_int8_t *, u_int16_t)) AES_CBC_MAC_Update,
+ (void (*)(u_int8_t *, void *)) AES_CBC_MAC_Final
+};
+struct auth_hash auth_hash_ccm_cbc_mac_192 = {
+ CRYPTO_AES_192_CCM_CBC_MAC, "CBC-CCM-AES-192",
+ AES_192_CBC_MAC_KEY_LEN, AES_CBC_MAC_HASH_LEN, sizeof(struct aes_cbc_mac_ctx),
+ CCM_CBC_BLOCK_LEN,
+ (void (*)(void *)) AES_CBC_MAC_Init,
+ (void (*)(void *, const u_int8_t *, u_int16_t)) AES_CBC_MAC_Setkey,
+ (void (*)(void *, const u_int8_t *, u_int16_t)) AES_CBC_MAC_Reinit,
+ (int (*)(void *, const u_int8_t *, u_int16_t)) AES_CBC_MAC_Update,
+ (void (*)(u_int8_t *, void *)) AES_CBC_MAC_Final
+};
+struct auth_hash auth_hash_ccm_cbc_mac_256 = {
+ CRYPTO_AES_256_CCM_CBC_MAC, "CBC-CCM-AES-256",
+ AES_256_CBC_MAC_KEY_LEN, AES_CBC_MAC_HASH_LEN, sizeof(struct aes_cbc_mac_ctx),
+ CCM_CBC_BLOCK_LEN,
+ (void (*)(void *)) AES_CBC_MAC_Init,
+ (void (*)(void *, const u_int8_t *, u_int16_t)) AES_CBC_MAC_Setkey,
+ (void (*)(void *, const u_int8_t *, u_int16_t)) AES_CBC_MAC_Reinit,
+ (int (*)(void *, const u_int8_t *, u_int16_t)) AES_CBC_MAC_Update,
+ (void (*)(u_int8_t *, void *)) AES_CBC_MAC_Final
+};
Index: sys/opencrypto/xform_enc.h
===================================================================
--- sys/opencrypto/xform_enc.h
+++ sys/opencrypto/xform_enc.h
@@ -84,6 +84,7 @@
extern struct enc_xform enc_xform_arc4;
extern struct enc_xform enc_xform_camellia;
extern struct enc_xform enc_xform_chacha20;
+extern struct enc_xform enc_xform_ccm;
struct aes_icm_ctx {
u_int32_t ac_ek[4*(RIJNDAEL_MAXNR + 1)];
Index: tools/tools/crypto/cryptocheck.c
===================================================================
--- tools/tools/crypto/cryptocheck.c
+++ tools/tools/crypto/cryptocheck.c
@@ -131,7 +131,7 @@
const char *name;
int cipher;
int mac;
- enum { T_HASH, T_HMAC, T_BLKCIPHER, T_AUTHENC, T_GCM } type;
+ enum { T_HASH, T_HMAC, T_BLKCIPHER, T_AUTHENC, T_GCM, T_CCM } type;
const EVP_CIPHER *(*evp_cipher)(void);
const EVP_MD *(*evp_md)(void);
} algs[] = {
@@ -155,10 +155,12 @@
.evp_md = EVP_sha384 },
{ .name = "sha512hmac", .mac = CRYPTO_SHA2_512_HMAC, .type = T_HMAC,
.evp_md = EVP_sha512 },
+#if 0
{ .name = "blake2b", .mac = CRYPTO_BLAKE2B, .type = T_HASH,
.evp_md = EVP_blake2b512 },
{ .name = "blake2s", .mac = CRYPTO_BLAKE2S, .type = T_HASH,
.evp_md = EVP_blake2s256 },
+#endif
{ .name = "aes-cbc", .cipher = CRYPTO_AES_CBC, .type = T_BLKCIPHER,
.evp_cipher = EVP_aes_128_cbc },
{ .name = "aes-cbc192", .cipher = CRYPTO_AES_CBC, .type = T_BLKCIPHER,
@@ -175,8 +177,10 @@
.evp_cipher = EVP_aes_128_xts },
{ .name = "aes-xts256", .cipher = CRYPTO_AES_XTS, .type = T_BLKCIPHER,
.evp_cipher = EVP_aes_256_xts },
+#if 0
{ .name = "chacha20", .cipher = CRYPTO_CHACHA20, .type = T_BLKCIPHER,
.evp_cipher = EVP_chacha20 },
+#endif
{ .name = "aes-gcm", .cipher = CRYPTO_AES_NIST_GCM_16,
.mac = CRYPTO_AES_128_NIST_GMAC, .type = T_GCM,
.evp_cipher = EVP_aes_128_gcm },
@@ -186,6 +190,15 @@
{ .name = "aes-gcm256", .cipher = CRYPTO_AES_NIST_GCM_16,
.mac = CRYPTO_AES_256_NIST_GMAC, .type = T_GCM,
.evp_cipher = EVP_aes_256_gcm },
+ { .name = "aes-ccm", .cipher = CRYPTO_AES_CCM_16,
+ .mac = CRYPTO_AES_128_CCM_CBC_MAC, .type = T_CCM,
+ .evp_cipher = EVP_aes_128_ccm },
+ { .name = "aes-ccm192", .cipher = CRYPTO_AES_CCM_16,
+ .mac = CRYPTO_AES_192_CCM_CBC_MAC, .type = T_CCM,
+ .evp_cipher = EVP_aes_192_ccm },
+ { .name = "aes-ccm256", .cipher = CRYPTO_AES_CCM_16,
+ .mac = CRYPTO_AES_256_CCM_CBC_MAC, .type = T_CCM,
+ .evp_cipher = EVP_aes_256_ccm },
};
static bool verbose;
@@ -1159,6 +1172,199 @@
}
static void
+openssl_ccm_encrypt(struct alg *alg, const EVP_CIPHER *cipher, const char *key,
+ const char *iv, size_t iv_len, const char *aad, size_t aad_len,
+ const char *input, char *output, size_t size, char *tag)
+{
+ EVP_CIPHER_CTX *ctx;
+ int outl, total;
+
+ ctx = EVP_CIPHER_CTX_new();
+ if (ctx == NULL)
+ errx(1, "OpenSSL %s (%zu) ctx new failed: %s", alg->name,
+ size, ERR_error_string(ERR_get_error(), NULL));
+ if (EVP_EncryptInit_ex(ctx, cipher, NULL, NULL, NULL) != 1)
+ errx(1, "OpenSSL %s (%zu) ctx init failed: %s", alg->name,
+ size, ERR_error_string(ERR_get_error(), NULL));
+ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, iv_len, NULL) != 1)
+ errx(1, "OpenSSL %s (%zu) setting iv length failed: %s", alg->name,
+ size, ERR_error_string(ERR_get_error(), NULL));
+ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, AES_CBC_MAC_HASH_LEN, NULL) != 1)
+ errx(1, "OpenSSL %s (%zu) setting tag length failed: %s", alg->name,
+ size, ERR_error_string(ERR_get_error(), NULL));
+ if (EVP_EncryptInit_ex(ctx, NULL, NULL, (const u_char *)key,
+ (const u_char *)iv) != 1)
+ errx(1, "OpenSSL %s (%zu) ctx init failed: %s", alg->name,
+ size, ERR_error_string(ERR_get_error(), NULL));
+ if (EVP_EncryptUpdate(ctx, NULL, &outl, NULL, size) != 1)
+ errx(1, "OpenSSL %s (%zu) unable to set data length: %s", alg->name,
+ size, ERR_error_string(ERR_get_error(), NULL));
+
+ if (aad != NULL) {
+ if (EVP_EncryptUpdate(ctx, NULL, &outl, (const u_char *)aad,
+ aad_len) != 1)
+ errx(1, "OpenSSL %s (%zu) aad update failed: %s",
+ alg->name, size,
+ ERR_error_string(ERR_get_error(), NULL));
+ }
+ if (EVP_EncryptUpdate(ctx, (u_char *)output, &outl,
+ (const u_char *)input, size) != 1)
+ errx(1, "OpenSSL %s (%zu) encrypt update failed: %s", alg->name,
+ size, ERR_error_string(ERR_get_error(), NULL));
+ total = outl;
+ if (EVP_EncryptFinal_ex(ctx, (u_char *)output + outl, &outl) != 1)
+ errx(1, "OpenSSL %s (%zu) encrypt final failed: %s", alg->name,
+ size, ERR_error_string(ERR_get_error(), NULL));
+ total += outl;
+ if (total != size)
+ errx(1, "OpenSSL %s (%zu) encrypt size mismatch: %d", alg->name,
+ size, total);
+ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, AES_CBC_MAC_HASH_LEN,
+ tag) != 1)
+ errx(1, "OpenSSL %s (%zu) get tag failed: %s", alg->name,
+ size, ERR_error_string(ERR_get_error(), NULL));
+ EVP_CIPHER_CTX_free(ctx);
+}
+
+static bool
+ocf_ccm(struct alg *alg, const char *key, size_t key_len, const char *iv,
+ size_t iv_len, const char *aad, size_t aad_len, const char *input,
+ char *output, size_t size, char *tag, int enc, int *cridp)
+{
+ struct session2_op sop;
+ struct crypt_aead caead;
+ int fd;
+
+ memset(&sop, 0, sizeof(sop));
+ memset(&caead, 0, sizeof(caead));
+ sop.crid = crid;
+ sop.keylen = key_len;
+ sop.key = (char *)key;
+ sop.cipher = alg->cipher;
+ sop.mackeylen = key_len;
+ sop.mackey = (char *)key;
+ sop.mac = alg->mac;
+ fd = crget();
+ if (ioctl(fd, CIOCGSESSION2, &sop) < 0) {
+ warn("cryptodev %s not supported for device %s",
+ alg->name, crfind(crid));
+ close(fd);
+ return (false);
+ }
+
+ caead.ses = sop.ses;
+ caead.op = enc ? COP_ENCRYPT : COP_DECRYPT;
+ caead.len = size;
+ caead.aadlen = aad_len;
+ caead.ivlen = iv_len;
+ caead.src = (char *)input;
+ caead.dst = output;
+ caead.aad = (char *)aad;
+ caead.tag = tag;
+ caead.iv = (char *)iv;
+
+ if (ioctl(fd, CIOCCRYPTAEAD, &caead) < 0) {
+ warn("cryptodev %s (%zu) failed for device %s",
+ alg->name, size, crfind(crid));
+ close(fd);
+ return (false);
+ }
+
+ if (ioctl(fd, CIOCFSESSION, &sop.ses) < 0)
+ warn("ioctl(CIOCFSESSION)");
+
+ close(fd);
+ *cridp = sop.crid;
+ return (true);
+}
+
+static void
+run_ccm_test(struct alg *alg, size_t size)
+{
+ const EVP_CIPHER *cipher;
+ char *aad, *buffer, *cleartext, *ciphertext;
+ char *iv, *key;
+ u_int iv_len, key_len;
+ int crid;
+ char control_tag[AES_CBC_MAC_HASH_LEN], test_tag[AES_CBC_MAC_HASH_LEN];
+
+ cipher = alg->evp_cipher();
+ if (size % EVP_CIPHER_block_size(cipher) != 0) {
+ if (verbose)
+ printf(
+ "%s (%zu): invalid buffer size (block size %d)\n",
+ alg->name, size, EVP_CIPHER_block_size(cipher));
+ return;
+ }
+
+ memset(control_tag, 0x3c, sizeof(control_tag));
+ memset(test_tag, 0x3c, sizeof(test_tag));
+
+ key_len = EVP_CIPHER_key_length(cipher);
+ iv_len = EVP_CIPHER_iv_length(cipher);
+
+ key = alloc_buffer(key_len);
+ iv = generate_iv(iv_len, alg);
+ cleartext = alloc_buffer(size);
+ buffer = malloc(size);
+ ciphertext = malloc(size);
+ if (aad_len != 0)
+ aad = alloc_buffer(aad_len);
+ else
+ aad = NULL;
+
+ /* OpenSSL encrypt */
+ openssl_ccm_encrypt(alg, cipher, key, iv, iv_len, aad, aad_len, cleartext,
+ ciphertext, size, control_tag);
+
+ /* OCF encrypt */
+ if (!ocf_ccm(alg, key, key_len, iv, iv_len, aad, aad_len, cleartext,
+ buffer, size, test_tag, 1, &crid))
+ goto out;
+ if (memcmp(ciphertext, buffer, size) != 0) {
+ printf("%s (%zu) encryption mismatch:\n", alg->name, size);
+ printf("control:\n");
+ hexdump(ciphertext, size, NULL, 0);
+ printf("test (cryptodev device %s):\n", crfind(crid));
+ hexdump(buffer, size, NULL, 0);
+ goto out;
+ }
+ if (memcmp(control_tag, test_tag, sizeof(control_tag)) != 0) {
+ printf("%s (%zu) enc tag mismatch:\n", alg->name, size);
+ printf("control:\n");
+ hexdump(control_tag, sizeof(control_tag), NULL, 0);
+ printf("test (cryptodev device %s):\n", crfind(crid));
+ hexdump(test_tag, sizeof(test_tag), NULL, 0);
+ goto out;
+ }
+
+ /* OCF decrypt */
+ if (!ocf_ccm(alg, key, key_len, iv, iv_len, aad, aad_len, ciphertext,
+ buffer, size, control_tag, 0, &crid))
+ goto out;
+ if (memcmp(cleartext, buffer, size) != 0) {
+ printf("%s (%zu) decryption mismatch:\n", alg->name, size);
+ printf("control:\n");
+ hexdump(cleartext, size, NULL, 0);
+ printf("test (cryptodev device %s):\n", crfind(crid));
+ hexdump(buffer, size, NULL, 0);
+ goto out;
+ }
+
+ if (verbose)
+ printf("%s (%zu) matched (cryptodev device %s)\n",
+ alg->name, size, crfind(crid));
+
+out:
+ free(aad);
+ free(ciphertext);
+ free(buffer);
+ free(cleartext);
+ free(iv);
+ free(key);
+}
+
+static void
run_test(struct alg *alg, size_t size)
{
@@ -1178,6 +1384,9 @@
case T_GCM:
run_gcm_test(alg, size);
break;
+ case T_CCM:
+ run_ccm_test(alg, size);
+ break;
}
}
@@ -1247,7 +1456,8 @@
u_int i;
for (i = 0; i < nitems(algs); i++)
- if (algs[i].type == T_GCM)
+ if (algs[i].type == T_GCM ||
+ algs[i].type == T_CCM)
run_test_sizes(&algs[i], sizes, nsizes);
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Dec 21, 10:32 PM (20 h, 2 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
15547996
Default Alt Text
D17066.diff (59 KB)
Attached To
Mode
D17066: AES CCM-CBC cryptography code
Attached
Detach File
Event Timeline
Log In to Comment