git: fd464d2f78f3 - stable/13 - crypto: Support multiple nonce lengths for AES-CCM.
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 21 Oct 2021 22:04:16 UTC
The branch stable/13 has been updated by jhb:
URL: https://cgit.FreeBSD.org/src/commit/?id=fd464d2f78f3b3ff6cf8197b95a952c2db29ec5c
commit fd464d2f78f3b3ff6cf8197b95a952c2db29ec5c
Author: John Baldwin <jhb@FreeBSD.org>
AuthorDate: 2021-10-06 21:08:47 +0000
Commit: John Baldwin <jhb@FreeBSD.org>
CommitDate: 2021-10-21 21:07:36 +0000
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 ae18720d2792287c9ec658404f1a3173014d4979)
---
share/man/man7/crypto.7 | 18 +++++++++-----
sys/opencrypto/crypto.c | 56 ++++++++++++++++++++++++++++++++++++------
sys/opencrypto/cryptodev.h | 29 ++++++++++++++++++++++
sys/opencrypto/cryptosoft.c | 17 +++++++------
sys/opencrypto/xform_aes_icm.c | 7 +++---
5 files changed, 102 insertions(+), 25 deletions(-)
diff --git a/share/man/man7/crypto.7 b/share/man/man7/crypto.7
index 6e5bd83621aa..d75daa62adcb 100644
--- a/share/man/man7/crypto.7
+++ b/share/man/man7/crypto.7
@@ -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
diff --git a/sys/opencrypto/crypto.c b/sys/opencrypto/crypto.c
index bcde910728e1..a3a42827d51b 100644
--- a/sys/opencrypto/crypto.c
+++ b/sys/opencrypto/crypto.c
@@ -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);
diff --git a/sys/opencrypto/cryptodev.h b/sys/opencrypto/cryptodev.h
index 046f67de418e..1aa11c34d8ba 100644
--- a/sys/opencrypto/cryptodev.h
+++ b/sys/opencrypto/cryptodev.h
@@ -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_ */
diff --git a/sys/opencrypto/cryptosoft.c b/sys/opencrypto/cryptosoft.c
index 77df37420bf5..c86ff86613db 100644
--- a/sys/opencrypto/cryptosoft.c
+++ b/sys/opencrypto/cryptosoft.c
@@ -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);
diff --git a/sys/opencrypto/xform_aes_icm.c b/sys/opencrypto/xform_aes_icm.c
index 45da8267ca7d..4126bd755e0c 100644
--- a/sys/opencrypto/xform_aes_icm.c
+++ b/sys/opencrypto/xform_aes_icm.c
@@ -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;
}