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 + * 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 is in the range + * of 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 +#include +#include + +#ifdef _KERNEL +#include +#include +#include +#include +#define AESNI_ENC(d, k, nr) aesni_enc(nr-1, (const __m128i*)k, d) +#else +#include +#include +#include +#include +#include +#endif + +#include +#include +#include + +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 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 +# include + +# 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 +#include +#include +#include +#include +#include + +/* + * 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 #include #include +#include #include #include @@ -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 +__FBSDID("$FreeBSD$"); + +#include +#include + +/* 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); }