Page MenuHomeFreeBSD

D17066.diff
No OneTemporary

D17066.diff

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(&current_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

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)

Event Timeline