crypto: Support multiple nonce lengths for AES-CCM.

Permit nonces of lengths 7 through 13 in the OCF framework and the
cryptosoft driver.  A helper function (ccm_max_payload_length) can be
used in OCF drivers to reject CCM requests which are too large for the
specified nonce length.

Reviewed by:	sef
Sponsored by:	Chelsio Communications, The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D32111

(cherry picked from commit ae18720d27)
This commit is contained in:
John Baldwin 2021-10-06 14:08:47 -07:00
parent 8581b350dd
commit fd464d2f78
5 changed files with 102 additions and 25 deletions

View File

@ -1,9 +1,13 @@
.\" Copyright (c) 2014 The FreeBSD Foundation
.\" Copyright (c) 2014-2021 The FreeBSD Foundation
.\" All rights reserved.
.\"
.\" This documentation was written by John-Mark Gurney under
.\" the sponsorship of the FreeBSD Foundation and
.\" Portions of this documentation were written by John-Mark Gurney
.\" under the sponsorship of the FreeBSD Foundation and
.\" Rubicon Communications, LLC (Netgate).
.\"
.\" Portions of this documentation were written by Ararat River
.\" Consulting, LLC under sponsorship of the FreeBSD Foundation.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
@ -27,7 +31,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd March 3, 2021
.Dd October 6, 2021
.Dt CRYPTO 7
.Os
.Sh NAME
@ -153,13 +157,15 @@ This nonce must be provided in
via the
.Dv CRYPTO_F_IV_SEPARATE
flag.
Some AEAD algorithms support multiple nonce sizes.
The first size listed is the default nonce size.
.Pp
The following AEAD algorithms are supported:
.Bl -column "CRYPTO_AES_NIST_GCM_16" "Nonce" "16, 24, 32" "Tag"
.Bl -column "CRYPTO_AES_NIST_GCM_16" "12, 7-13" "16, 24, 32" "Tag"
.It Sy Name Ta Sy Nonce Ta Sy Key Sizes Ta Sy Tag Ta Sy Description
.It Dv CRYPTO_AES_NIST_GCM_16 Ta 12 Ta 16, 24, 32 Ta 16 Ta
AES Galois/Counter Mode
.It Dv CRYPTO_AES_CCM_16 Ta 12 Ta 16, 24, 32 Ta 16 Ta
.It Dv CRYPTO_AES_CCM_16 Ta 12, 7-13 Ta 16, 24, 32 Ta 16 Ta
AES Counter with CBC-MAC
.It Dv CRYPTO_CHACHA20_POLY1305 Ta 12 Ta 32 Ta 16 Ta
ChaCha20-Poly1305

View File

@ -1,5 +1,9 @@
/*-
* Copyright (c) 2002-2006 Sam Leffler. All rights reserved.
* Copyright (c) 2021 The FreeBSD Foundation
*
* Portions of this software were developed by Ararat River
* Consulting, LLC under sponsorship of the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -743,6 +747,24 @@ alg_is_aead(int alg)
return (alg_type(alg) == ALG_AEAD);
}
static bool
ccm_tag_length_valid(int len)
{
/* RFC 3610 */
switch (len) {
case 4:
case 6:
case 8:
case 10:
case 12:
case 14:
case 16:
return (true);
default:
return (false);
}
}
#define SUPPORTED_SES (CSP_F_SEPARATE_OUTPUT | CSP_F_SEPARATE_AAD | CSP_F_ESN)
/* Various sanity checks on crypto session parameters. */
@ -800,8 +822,21 @@ check_csp(const struct crypto_session_params *csp)
return (false);
/* IV is optional for digests (e.g. GMAC). */
if (csp->csp_ivlen >= EALG_MAX_BLOCK_LEN)
return (false);
switch (csp->csp_auth_alg) {
case CRYPTO_AES_CCM_CBC_MAC:
if (csp->csp_ivlen < 7 || csp->csp_ivlen > 13)
return (false);
break;
case CRYPTO_AES_NIST_GMAC:
if (csp->csp_ivlen != AES_GCM_IV_LEN)
return (false);
break;
default:
if (csp->csp_ivlen != 0)
return (false);
break;
}
if (!alg_is_digest(csp->csp_auth_alg))
return (false);
@ -820,6 +855,10 @@ check_csp(const struct crypto_session_params *csp)
axf = crypto_auth_hash(csp);
if (axf == NULL || csp->csp_auth_mlen > axf->hashsize)
return (false);
if (csp->csp_auth_alg == CRYPTO_AES_CCM_CBC_MAC &&
!ccm_tag_length_valid(csp->csp_auth_mlen))
return (false);
}
break;
case CSP_MODE_AEAD:
@ -833,13 +872,16 @@ check_csp(const struct crypto_session_params *csp)
if (csp->csp_auth_alg != 0 || csp->csp_auth_klen != 0)
return (false);
/*
* XXX: Would be nice to have a better way to get this
* value.
*/
switch (csp->csp_cipher_alg) {
case CRYPTO_AES_NIST_GCM_16:
case CRYPTO_AES_CCM_16:
if (csp->csp_auth_mlen != 0 &&
!ccm_tag_length_valid(csp->csp_auth_mlen))
return (false);
if (csp->csp_ivlen < 7 || csp->csp_ivlen > 13)
return (false);
break;
case CRYPTO_AES_NIST_GCM_16:
case CRYPTO_CHACHA20_POLY1305:
if (csp->csp_auth_mlen > 16)
return (false);

View File

@ -753,5 +753,34 @@ crypto_read_iv(struct cryptop *crp, void *iv)
crypto_copydata(crp, crp->crp_iv_start, csp->csp_ivlen, iv);
}
static __inline size_t
ccm_max_payload_length(const struct crypto_session_params *csp)
{
/* RFC 3160 */
const u_int L = 15 - csp->csp_ivlen;
switch (L) {
case 2:
return (0xffff);
case 3:
return (0xffffff);
#ifdef __LP64__
case 4:
return (0xffffffff);
case 5:
return (0xffffffffff);
case 6:
return (0xffffffffffff);
case 7:
return (0xffffffffffffff);
default:
return (0xffffffffffffffff);
#else
default:
return (0xffffffff);
#endif
}
}
#endif /* _KERNEL */
#endif /* _CRYPTO_CRYPTO_H_ */

View File

@ -642,17 +642,19 @@ swcr_ccm_cbc_mac(struct swcr_session *ses, struct cryptop *crp)
u_char tag[AES_CBC_MAC_HASH_LEN];
u_char iv[AES_BLOCK_LEN];
union authctx ctx;
const struct crypto_session_params *csp;
struct swcr_auth *swa;
struct auth_hash *axf;
int error, ivlen;
csp = crypto_get_params(crp->crp_session);
swa = &ses->swcr_auth;
axf = swa->sw_axf;
bcopy(swa->sw_ictx, &ctx, axf->ctxsize);
/* Initialize the IV */
ivlen = AES_CCM_IV_LEN;
ivlen = csp->csp_ivlen;
crypto_read_iv(crp, iv);
/*
@ -694,6 +696,7 @@ swcr_ccm_cbc_mac(struct swcr_session *ses, struct cryptop *crp)
static int
swcr_ccm(struct swcr_session *ses, struct cryptop *crp)
{
const struct crypto_session_params *csp;
uint32_t blkbuf[howmany(AES_BLOCK_LEN, sizeof(uint32_t))];
u_char *blk = (u_char *)blkbuf;
u_char tag[AES_CBC_MAC_HASH_LEN];
@ -708,6 +711,7 @@ swcr_ccm(struct swcr_session *ses, struct cryptop *crp)
size_t len;
int blksz, error, ivlen, r, resid;
csp = crypto_get_params(crp->crp_session);
swa = &ses->swcr_auth;
axf = swa->sw_axf;
@ -721,10 +725,13 @@ swcr_ccm(struct swcr_session *ses, struct cryptop *crp)
KASSERT(axf->blocksize == exf->native_blocksize,
("%s: blocksize mismatch", __func__));
if (crp->crp_payload_length > ccm_max_payload_length(csp))
return (EMSGSIZE);
if ((crp->crp_flags & CRYPTO_F_IV_SEPARATE) == 0)
return (EINVAL);
ivlen = AES_CCM_IV_LEN;
ivlen = csp->csp_ivlen;
/*
* AES CCM-CBC-MAC needs to know the length of both the auth
@ -1130,7 +1137,6 @@ swcr_setup_cipher(struct swcr_session *ses,
swe = &ses->swcr_encdec;
txf = crypto_cipher(csp);
MPASS(txf->ivsize == csp->csp_ivlen);
if (txf->ctxsize != 0) {
swe->sw_kschedule = malloc(txf->ctxsize, M_CRYPTO_DATA,
M_NOWAIT);
@ -1282,9 +1288,6 @@ swcr_setup_ccm(struct swcr_session *ses,
struct swcr_auth *swa;
struct auth_hash *axf;
if (csp->csp_ivlen != AES_CCM_IV_LEN)
return (EINVAL);
/* First, setup the auth side. */
swa = &ses->swcr_auth;
switch (csp->csp_cipher_klen * 8) {
@ -1392,8 +1395,6 @@ swcr_auth_supported(const struct crypto_session_params *csp)
}
if (csp->csp_auth_key == NULL)
return (false);
if (csp->csp_ivlen != AES_CCM_IV_LEN)
return (false);
break;
}
return (true);

View File

@ -144,15 +144,14 @@ aes_ccm_reinit(void *key, const uint8_t *iv, size_t ivlen)
{
struct aes_icm_ctx *ctx;
KASSERT(ivlen == AES_CCM_IV_LEN,
KASSERT(ivlen >= 7 && ivlen <= 13,
("%s: invalid IV length", __func__));
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_CCM_IV_LEN);
ctx->ac_block[0] = (15 - ivlen) - 1;
bcopy(iv, ctx->ac_block + 1, ivlen);
ctx->ac_block[AESICM_BLOCKSIZE - 1] = 1;
}