Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -4826,6 +4826,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 @@ -170,6 +170,11 @@ cddl/dev/dtrace/x86/instr_size.c optional dtrace_fbt | dtraceall compile-with "${DTRACE_C}" crypto/aesni/aeskeys_amd64.S optional aesni crypto/aesni/aesni.c optional aesni +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_ghash.o optional aesni \ dependency "$S/crypto/aesni/aesni_ghash.c" \ compile-with "${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc} ${WERROR} ${NO_WCAST_QUAL} ${PROF} -mmmx -msse -msse4 -maes -mpclmul ${.IMPSRC}" \ Index: sys/conf/files.i386 =================================================================== --- sys/conf/files.i386 +++ sys/conf/files.i386 @@ -122,6 +122,11 @@ no-implicit-rule crypto/aesni/aeskeys_i386.S optional aesni crypto/aesni/aesni.c optional aesni +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_ghash.o optional aesni \ dependency "$S/crypto/aesni/aesni_ghash.c" \ compile-with "${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc} ${WERROR} ${NO_WCAST_QUAL} ${PROF} -mmmx -msse -msse4 -maes -mpclmul ${.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 @@ -55,6 +55,7 @@ #include #include +#include #include #include @@ -131,9 +132,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-CCM,AES-GCM,AES-ICM,AES-XTS,SHA1,SHA256"); else if (has_aes) - device_set_desc(dev, "AES-CBC,AES-XTS,AES-GCM,AES-ICM"); + device_set_desc(dev, "AES-CBC,AES-XTS,AES-GCM,AES-ICM,AES-CCM"); else device_set_desc(dev, "SHA1,SHA256"); @@ -193,6 +194,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 +231,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 +248,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,14 +271,29 @@ } encini = cri; break; + case CRYPTO_AES_128_CCM_CBC_MAC: + case CRYPTO_AES_192_CCM_CBC_MAC: + case CRYPTO_AES_256_CCM_CBC_MAC: + if (authini != NULL) { + CRYPTDEB("authini already set"); + return (EINVAL); + } + cbc_hash = true; + authini = cri; + break; case CRYPTO_AES_128_NIST_GMAC: case CRYPTO_AES_192_NIST_GMAC: case CRYPTO_AES_256_NIST_GMAC: + if (authini != NULL) { + CRYPTDEB("authini already set"); + return (EINVAL); + } /* * nothing to do here, maybe in the future cache some * values for GHASH */ gcm_hash = true; + authini = cri; break; case CRYPTO_SHA1: case CRYPTO_SHA1_HMAC: @@ -295,8 +323,15 @@ * 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; @@ -338,6 +373,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 +389,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: @@ -647,6 +686,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); @@ -700,8 +740,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; @@ -711,7 +752,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; @@ -737,6 +779,7 @@ ivlen = 8; break; case CRYPTO_AES_NIST_GCM_16: + case CRYPTO_AES_CCM_16: ivlen = 12; /* should support arbitarily larger */ break; } @@ -805,9 +848,26 @@ error = EBADMSG; } break; + case CRYPTO_AES_CCM_16: + if (encflag) { + bzero(tag, sizeof tag); + 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, CCM_CBC_MAX_DIGEST_LEN, tag); + } else { + crypto_copydata(crp->crp_flags, crp->crp_buf, + authcrd->crd_inject, CCM_CBC_MAX_DIGEST_LEN, tag); + 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 =================================================================== --- /dev/null +++ sys/crypto/aesni/aesni_ccm.c @@ -0,0 +1,593 @@ +/*- + * 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; + + CRYPTDEB("%s(%p, %p, %p, %p, %p, %u, %u, %d, %p, %d)\n", + __func__, in, out, addt, nonce, tag, nbytes, abytes, nlen, key, nr); + + 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; + + CRYPTDEB("%s(%p, %p, %p, %p, %p, %u, %u, %d, %p, %d)\n", + __func__, in, out, addt, nonce, tag, nbytes, abytes, nlen, key, nr); + + 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,12 +8,16 @@ SRCS+= aeskeys_${MACHINE_CPUARCH}.S SRCS+= device_if.h bus_if.h opt_bus.h cryptodev_if.h -OBJS+= aesni_ghash.o aesni_wrap.o +OBJS+= aesni_ghash.o aesni_wrap.o aesni_ccm.o OBJS+= intel_sha1.o intel_sha256.o # Remove -nostdinc so we can get the intrinsics. aesni_ghash.o: aesni_ghash.c - # XXX - gcc won't understand -mpclmul + ${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc} ${WERROR} ${PROF} \ + -mmmx -msse -msse4 -maes -mpclmul ${.IMPSRC} + ${CTFCONVERT_CMD} + +aesni_ccm.o: aesni_ccm.c ${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc} ${WERROR} ${PROF} \ -mmmx -msse -msse4 -maes -mpclmul ${.IMPSRC} ${CTFCONVERT_CMD} @@ -35,6 +39,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 =================================================================== --- /dev/null +++ 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 =================================================================== --- /dev/null +++ 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_CCM_16 39 /* cipher side */ +#define CRYPTO_AES_128_CCM_CBC_MAC 41 /* auth side */ +#define CRYPTO_AES_192_CCM_CBC_MAC 42 /* auth side */ +#define CRYPTO_AES_256_CCM_CBC_MAC 43 /* auth side */ +#define CRYPTO_ALGORITHM_MAX 43 /* Keep updated - see below */ #define CRYPTO_ALGO_VALID(x) ((x) >= CRYPTO_ALGORITHM_MIN && \ (x) <= CRYPTO_ALGORITHM_MAX) @@ -371,7 +380,7 @@ #ifdef _KERNEL -#if 0 +#ifdef CRYPTO_DEBUG #define CRYPTDEB(s, ...) do { \ printf("%s:%d: " s "\n", __FILE__, __LINE__, ## __VA_ARGS__); \ } while (0) 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 @@ -506,6 +506,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; @@ -520,6 +521,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; @@ -527,6 +530,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: @@ -545,7 +552,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); @@ -576,6 +584,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); @@ -611,15 +634,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); } } @@ -650,6 +678,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) @@ -799,6 +829,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; @@ -943,6 +976,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; @@ -1042,6 +1084,7 @@ case CRYPTO_CAMELLIA_CBC: case CRYPTO_NULL_CBC: case CRYPTO_CHACHA20: + case CRYPTO_AES_CCM_16: txf = swd->sw_exf; if (swd->sw_kschedule) @@ -1056,6 +1099,9 @@ case CRYPTO_SHA2_512_HMAC: case CRYPTO_RIPEMD160_HMAC: case CRYPTO_NULL_HMAC: + case CRYPTO_AES_128_CCM_CBC_MAC: + case CRYPTO_AES_192_CCM_CBC_MAC: + case CRYPTO_AES_256_CCM_CBC_MAC: axf = swd->sw_axf; if (swd->sw_ictx) { @@ -1193,11 +1239,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; @@ -1288,6 +1338,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_CCM_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. */ @@ -104,6 +115,20 @@ 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 aes_icm_crypt(caddr_t key, u_int8_t *data) { Index: sys/opencrypto/xform_auth.h =================================================================== --- sys/opencrypto/xform_auth.h +++ sys/opencrypto/xform_auth.h @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -85,6 +86,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; @@ -95,6 +99,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 =================================================================== --- /dev/null +++ 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 @@ -55,7 +55,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * - * $FreeBSD$ + * $FreeBSD: head/tools/tools/crypto/cryptocheck.c 331726 2018-03-29 04:14:37Z cem $ */ /* @@ -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[] = { @@ -186,6 +186,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; @@ -1158,6 +1167,199 @@ free(key); } +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 +1380,9 @@ case T_GCM: run_gcm_test(alg, size); break; + case T_CCM: + run_ccm_test(alg, size); + break; } } @@ -1247,7 +1452,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); }