The branch stable/13 has been updated by jhb:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=fd464d2f78f3b3ff6cf8197b95a952c2db29ec5c

commit fd464d2f78f3b3ff6cf8197b95a952c2db29ec5c
Author:     John Baldwin <[email protected]>
AuthorDate: 2021-10-06 21:08:47 +0000
Commit:     John Baldwin <[email protected]>
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;
 }
 

Reply via email to