Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -4830,6 +4830,8 @@ opencrypto/rmd160.c optional crypto | ipsec | ipsec_support opencrypto/skipjack.c optional crypto | ipsec | ipsec_support opencrypto/xform.c optional crypto | ipsec | ipsec_support +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/modules/crypto/Makefile =================================================================== --- sys/modules/crypto/Makefile +++ sys/modules/crypto/Makefile @@ -46,5 +46,7 @@ SRCS += chacha-sw.c 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 @@ -80,6 +80,8 @@ #define MD5_KPDK_HASH_LEN 16 #define SHA1_KPDK_HASH_LEN 20 #define AES_GMAC_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 */ @@ -99,6 +101,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 /* Encryption algorithm block sizes */ #define NULL_BLOCK_LEN 4 /* IPsec to maintain alignment */ @@ -182,7 +187,11 @@ #define CRYPTO_BLAKE2B 29 /* Blake2b hash */ #define CRYPTO_BLAKE2S 30 /* Blake2s hash */ #define CRYPTO_CHACHA20 31 /* Chacha20 stream cipher */ -#define CRYPTO_ALGORITHM_MAX 31 /* Keep updated - see below */ +#define CRYPTO_AES_CCM_16 32 /* cipher side */ +#define CRYPTO_AES_128_CCM_CBC_MAC 33 /* auth side */ +#define CRYPTO_AES_192_CCM_CBC_MAC 34 /* auth side */ +#define CRYPTO_AES_256_CCM_CBC_MAC 35 /* auth side */ +#define CRYPTO_ALGORITHM_MAX 35 /* 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 @@ -441,6 +441,9 @@ case CRYPTO_AES_NIST_GCM_16: txform = &enc_xform_aes_nist_gcm; break; + case CRYPTO_AES_CCM_16: + txform = &enc_xform_ccm; + break; case CRYPTO_CHACHA20: txform = &enc_xform_chacha20; break; @@ -482,6 +485,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; @@ -984,12 +996,12 @@ } /* - * For GCM, crd_len covers only the AAD. For other ciphers + * For GCM and CCM, crd_len covers only the AAD. For other ciphers * chained with an HMAC, crd_len covers both the AAD and the * 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 @@ -487,6 +487,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; @@ -499,6 +500,8 @@ return (EINVAL); switch (sw->sw_alg) { + case CRYPTO_AES_CCM_16: + isccm = 1; case CRYPTO_AES_NIST_GCM_16: case CRYPTO_AES_NIST_GMAC: swe = sw; @@ -506,6 +509,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: @@ -524,7 +531,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); @@ -555,6 +563,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); @@ -590,15 +613,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); } } @@ -629,6 +657,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) @@ -821,6 +851,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; @@ -947,6 +980,15 @@ break; #endif + 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; @@ -1216,11 +1258,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; @@ -1305,6 +1351,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); #undef REGISTER return 0; 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. */ @@ -105,6 +116,19 @@ } 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 */ + explicit_bzero(ctx->ac_block, sizeof(ctx->ac_block)); + ctx->ac_block[0] = (15 - AES_GCM_IV_LEN) - 1; /* 3 bytes for length field; this gives a nonce of 12 bytes */ + 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) { struct aes_icm_ctx *ctx; 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 @@ -77,6 +78,9 @@ extern struct auth_hash auth_hash_nist_gmac_aes_256; extern struct auth_hash auth_hash_blake2b; extern struct auth_hash auth_hash_blake2s; +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; @@ -86,6 +90,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_HMAC, T_BLKCIPHER, T_AUTHENC, T_GCM } type; + enum { T_HMAC, T_BLKCIPHER, T_AUTHENC, T_GCM, T_CCM } type; const EVP_CIPHER *(*evp_cipher)(void); const EVP_MD *(*evp_md)(void); } algs[] = { @@ -143,10 +143,12 @@ .evp_md = EVP_sha384 }, { .name = "sha512", .mac = CRYPTO_SHA2_512_HMAC, .type = T_HMAC, .evp_md = EVP_sha512 }, +#if 0 { .name = "blake2b", .mac = CRYPTO_BLAKE2B, .type = T_HMAC, .evp_md = EVP_blake2b512 }, { .name = "blake2s", .mac = CRYPTO_BLAKE2S, .type = T_HMAC, .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, @@ -163,8 +165,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 }, @@ -174,6 +178,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; @@ -1025,6 +1038,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) { @@ -1041,6 +1247,9 @@ case T_GCM: run_gcm_test(alg, size); break; + case T_CCM: + run_ccm_test(alg, size); + break; } } @@ -1100,7 +1309,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); }