The branch main has been updated by adrian:

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

commit 6d21920e6d2e03c6ed7360c432b855ca189db305
Author:     Adrian Chadd <adr...@freebsd.org>
AuthorDate: 2025-03-14 23:30:33 +0000
Commit:     Adrian Chadd <adr...@freebsd.org>
CommitDate: 2025-04-08 01:35:21 +0000

    net80211: refactor out the AAD init code shared between GCMP and CCMP
    
    The 802.11 specification specifically calls out that the GCMP AES
    uses the CCMP AES specification, so we can re-use this here.
    
    Changes during refactoring:
    
    * the first block (b0) needs the priority field populated, which was
      being done inline with the CCMP AAD assembly.  So write a new function
      that implements just that.
    
    * Use IEEE80211_IS_QOS_ANY() in the AAD assembly; 802.11-2020
      12.5.3.3.3 (Construct AAD) talks about frames with QoS control field,
      not specifically QoS data frames (ie, not avoiding PS-POLL.)
    
    * Use IEEE80211_IS_QOS_ANY() in the CCM nonce assembly.
      Leave some verbose comments about the definition of "MPDU priority".
    
    * mask the HTC/ORDER bit in the FC if the data frame contains a
      QoS control field.  Again, see 802.11-2020 12.5.3.3.3.
    
    * Add extra comments about missing items and functionality for later
      implementation.
    
    Differential Revision:   https://reviews.freebsd.org/D49367
    Reviewed by:    bz
---
 sys/net80211/ieee80211_crypto.c      |  97 ++++++++++++++++++++++++
 sys/net80211/ieee80211_crypto.h      |   5 ++
 sys/net80211/ieee80211_crypto_ccmp.c | 138 +++++++++++++++++++----------------
 sys/net80211/ieee80211_crypto_gcmp.c |  88 +---------------------
 4 files changed, 181 insertions(+), 147 deletions(-)

diff --git a/sys/net80211/ieee80211_crypto.c b/sys/net80211/ieee80211_crypto.c
index 744d69ce3d1d..84cf1d02e408 100644
--- a/sys/net80211/ieee80211_crypto.c
+++ b/sys/net80211/ieee80211_crypto.c
@@ -900,3 +900,100 @@ ieee80211_crypto_set_deftxkey(struct ieee80211vap *vap, 
ieee80211_keyix kid)
 
        vap->iv_update_deftxkey(vap, kid);
 }
+
+/**
+ * @brief Calculate the AAD required for this frame for AES-GCM/AES-CCM.
+ *
+ * The contents are described in 802.11-2020 12.5.3.3.3 (Construct AAD)
+ * under AES-CCM and are shared with AES-GCM as covered in 12.5.5.3.3
+ * (Construct AAD) (AES-GCM).
+ *
+ * NOTE: the first two bytes are a 16 bit big-endian length, which are used
+ * by AES-CCM as part of the Adata field (RFC 3610, section 2.2
+ * (Authentication)) to indicate the length of the Adata field itself.
+ * Since this is small and fits in 0xfeff bytes, the length field
+ * uses the two byte big endian option.
+ *
+ * AES-GCM doesn't require the length at the beginning and will need to
+ * skip it.
+ *
+ * TODO: net80211 currently doesn't support negotiating SPP (Signaling
+ * and Payload Protected A-MSDUs) and thus bit 7 of the QoS control field
+ * is always masked.
+ *
+ * TODO: net80211 currently doesn't support DMG (802.11ad) so bit 7
+ * (A-MSDU present) and bit 8 (A-MSDU type) are always masked.
+ *
+ * @param wh   802.11 frame to calculate the AAD over
+ * @param aad  AAD (additional authentication data) buffer
+ * @param len  The AAD buffer length in bytes.
+ * @returns    The number of AAD payload bytes (ignoring the first two
+ *             bytes, which are the AAD payload length in big-endian).
+ */
+uint16_t
+ieee80211_crypto_init_aad(const struct ieee80211_frame *wh, uint8_t *aad,
+    int len)
+{
+       uint16_t aad_len;
+
+       memset(aad, 0, len);
+
+       /*
+        * AAD for PV0 MPDUs:
+        *
+        * FC with bits 4..6 and 11..13 masked to zero; 14 is always one
+        * A1 | A2 | A3
+        * SC with bits 4..15 (seq#) masked to zero
+        * A4 (if present)
+        * QC (if present)
+        */
+       aad[0] = 0;     /* AAD length >> 8 */
+       /* NB: aad[1] set below */
+       aad[2] = wh->i_fc[0] & 0x8f;    /* see above for bitfields */
+       aad[3] = wh->i_fc[1] & 0xc7;    /* see above for bitfields */
+       /* mask aad[3] b7 if frame is data frame w/ QoS control field */
+       if (IEEE80211_IS_QOS_ANY(wh))
+               aad[3] &= 0x7f;
+
+       /* NB: we know 3 addresses are contiguous */
+       memcpy(aad + 4, wh->i_addr1, 3 * IEEE80211_ADDR_LEN);
+       aad[22] = wh->i_seq[0] & IEEE80211_SEQ_FRAG_MASK;
+       aad[23] = 0; /* all bits masked */
+       /*
+        * Construct variable-length portion of AAD based
+        * on whether this is a 4-address frame/QOS frame.
+        * We always zero-pad to 32 bytes before running it
+        * through the cipher.
+        */
+       if (IEEE80211_IS_DSTODS(wh)) {
+               IEEE80211_ADDR_COPY(aad + 24,
+                       ((const struct ieee80211_frame_addr4 *)wh)->i_addr4);
+               if (IEEE80211_IS_QOS_ANY(wh)) {
+                       const struct ieee80211_qosframe_addr4 *qwh4 =
+                               (const struct ieee80211_qosframe_addr4 *) wh;
+                       /* TODO: SPP A-MSDU / A-MSDU present bit */
+                       aad[30] = qwh4->i_qos[0] & 0x0f;/* just priority bits */
+                       aad[31] = 0;
+                       aad_len = aad[1] = 22 + IEEE80211_ADDR_LEN + 2;
+               } else {
+                       *(uint16_t *)&aad[30] = 0;
+                       aad_len = aad[1] = 22 + IEEE80211_ADDR_LEN;
+               }
+       } else {
+               if (IEEE80211_IS_QOS_ANY(wh)) {
+                       const struct ieee80211_qosframe *qwh =
+                               (const struct ieee80211_qosframe*) wh;
+                       /* TODO: SPP A-MSDU / A-MSDU present bit */
+                       aad[24] = qwh->i_qos[0] & 0x0f; /* just priority bits */
+                       aad[25] = 0;
+                       aad_len = aad[1] = 22 + 2;
+               } else {
+                       *(uint16_t *)&aad[24] = 0;
+                       aad_len = aad[1] = 22;
+               }
+               *(uint16_t *)&aad[26] = 0;
+               *(uint32_t *)&aad[28] = 0;
+       }
+
+       return (aad_len);
+}
diff --git a/sys/net80211/ieee80211_crypto.h b/sys/net80211/ieee80211_crypto.h
index fa0d3fc3272a..89b8b4f9daa4 100644
--- a/sys/net80211/ieee80211_crypto.h
+++ b/sys/net80211/ieee80211_crypto.h
@@ -295,5 +295,10 @@ void       ieee80211_notify_replay_failure(struct 
ieee80211vap *,
                uint64_t rsc, int tid);
 void   ieee80211_notify_michael_failure(struct ieee80211vap *,
                const struct ieee80211_frame *, ieee80211_keyix keyix);
+
+/* AAD assembly for CCMP/GCMP. */
+uint16_t       ieee80211_crypto_init_aad(const struct ieee80211_frame *,
+               uint8_t *, int);
+
 #endif /* defined(__KERNEL__) || defined(_KERNEL) */
 #endif /* _NET80211_IEEE80211_CRYPTO_H_ */
diff --git a/sys/net80211/ieee80211_crypto_ccmp.c 
b/sys/net80211/ieee80211_crypto_ccmp.c
index 70265335fe70..01bc50f885f2 100644
--- a/sys/net80211/ieee80211_crypto_ccmp.c
+++ b/sys/net80211/ieee80211_crypto_ccmp.c
@@ -408,6 +408,79 @@ xor_block(uint8_t *b, const uint8_t *a, size_t len)
                b[i] ^= a[i];
 }
 
+/**
+ * @brief Initialise the AES-CCM nonce flag field in the b0 CCMP block.
+ *
+ * The B_0 block is defined in RFC 3610 section 2.2 (Authentication).
+ * b0[0] is the CCM flags field, so the nonce used for B_0 starts at
+ * b0[1].  Amusingly, b0[1] is also flags, but it's the 802.11 AES-CCM
+ * nonce flags field, NOT the CCM flags field.
+ *
+ * The AES-CCM nonce flags field is defined in 802.11-2020 12.5.3.3.4
+ * (Construct CCM nonce).
+ *
+ * TODO: net80211 currently doesn't support MFP (management frame protection)
+ * and so bit 4 is never set.  This routine and ccmp_init_blocks() will
+ * need a pointer to the ieee80211_node or a flag that explicitly states
+ * the frame will be sent w/ MFP encryption / received w/ MFP decryption.
+ *
+ * @param wh   the 802.11 header to populate
+ * @param b0   the CCM nonce to update (remembering b0[0] is the CCM
+ *             nonce flags, and b0[1] is the AES-CCM nonce flags).
+ */
+static void
+ieee80211_crypto_ccmp_init_nonce_flags(const struct ieee80211_frame *wh,
+    char *b0)
+{
+       if (IEEE80211_IS_DSTODS(wh)) {
+               /*
+                * 802.11-2020 12.5.33.3.4 (Construct CCM nonce) mentions
+                * that the low four bits of this byte are the "MPDU priority."
+                * This is defined in 5.1.1.2 (Determination of UP) and
+                * 5.1.1.3 (Interpretation of Priority Parameter in MAC
+                * service primitives).
+                *
+                * The former says "The QoS facility supports eight priority
+                * values, referred to as UPs. The values a UP may take are
+                * the integer values from 0 to 7 and are identical to the
+                * 802.11D priority tags."
+                *
+                * The latter specifically calls out that "Priority parameter
+                * and TID subfield values 0 to 7 are interpreted aas UPs for
+                * the MSDUs" .. and " .. TID subfield values 8 to 15 specify
+                * TIDs that are TS identifiers (TSIDs)" which are used for
+                * TSPEC.  There's a bunch of extra work to be done with frames
+                * received in TIDs 8..15 with no TSPEC, "then the MSDU shall
+                * be sent with priority parameter set to 0."
+                *
+                * All QoS frames (not just QoS data) have TID fields and
+                * thus priorities.  However, the code straight up
+                * copies the 4 bit TID field, rather than a 3 bit MPDU
+                * priority value.  For now, as net80211 doesn't specifically
+                * support TSPEC negotiation, this likely never gets checked.
+                * However as part of any future TSPEC work, this will likely
+                * need to be looked at and checked with interoperability
+                * with other stacks.
+                */
+               if (IEEE80211_IS_QOS_ANY(wh)) {
+                       const struct ieee80211_qosframe_addr4 *qwh4 =
+                           (const struct ieee80211_qosframe_addr4 *) wh;
+                       b0[1] = qwh4->i_qos[0] & 0x0f;  /* prio bits */
+               } else {
+                       b0[1] = 0;
+               }
+       } else {
+               if (IEEE80211_IS_QOS_ANY(wh)) {
+                       const struct ieee80211_qosframe *qwh =
+                           (const struct ieee80211_qosframe *) wh;
+                       b0[1] = qwh->i_qos[0] & 0x0f;   /* prio bits */
+               } else {
+                       b0[1] = 0;
+               }
+       }
+       /* TODO: populate MFP flag */
+}
+
 /*
  * Host AP crypt: host-based CCMP encryption implementation for Host AP driver
  *
@@ -428,8 +501,6 @@ ccmp_init_blocks(rijndael_ctx *ctx, struct ieee80211_frame 
*wh,
        uint8_t b0[AES_BLOCK_LEN], uint8_t aad[2 * AES_BLOCK_LEN],
        uint8_t auth[AES_BLOCK_LEN], uint8_t s0[AES_BLOCK_LEN])
 {
-#define        IS_QOS_DATA(wh) IEEE80211_QOS_HAS_SEQ(wh)
-
        /*
         * Map M parameter to encoding
         * RFC3610, Section 2 (CCM Mode Specification)
@@ -446,7 +517,8 @@ ccmp_init_blocks(rijndael_ctx *ctx, struct ieee80211_frame 
*wh,
         * Dlen
         */
        b0[0] = 0x40 | 0x01 | (m << 3);
-       /* NB: b0[1] set below */
+       /* Init b0[1] (CCM nonce flags) */
+       ieee80211_crypto_ccmp_init_nonce_flags(wh, b0);
        IEEE80211_ADDR_COPY(b0 + 2, wh->i_addr2);
        b0[8] = pn >> 40;
        b0[9] = pn >> 32;
@@ -457,63 +529,8 @@ ccmp_init_blocks(rijndael_ctx *ctx, struct ieee80211_frame 
*wh,
        b0[14] = (dlen >> 8) & 0xff;
        b0[15] = dlen & 0xff;
 
-       /* AAD:
-        * FC with bits 4..6 and 11..13 masked to zero; 14 is always one
-        * A1 | A2 | A3
-        * SC with bits 4..15 (seq#) masked to zero
-        * A4 (if present)
-        * QC (if present)
-        */
-       aad[0] = 0;     /* AAD length >> 8 */
-       /* NB: aad[1] set below */
-       aad[2] = wh->i_fc[0] & 0x8f;    /* XXX magic #s */
-       /* TODO: 802.11-2016 12.5.3.3.3 - QoS control field mask */
-       aad[3] = wh->i_fc[1] & 0xc7;    /* XXX magic #s */
-       /* NB: we know 3 addresses are contiguous */
-       memcpy(aad + 4, wh->i_addr1, 3 * IEEE80211_ADDR_LEN);
-       aad[22] = wh->i_seq[0] & IEEE80211_SEQ_FRAG_MASK;
-       aad[23] = 0; /* all bits masked */
-       /*
-        * Construct variable-length portion of AAD based
-        * on whether this is a 4-address frame/QOS frame.
-        * We always zero-pad to 32 bytes before running it
-        * through the cipher.
-        *
-        * We also fill in the priority bits of the CCM
-        * initial block as we know whether or not we have
-        * a QOS frame.
-        */
-       if (IEEE80211_IS_DSTODS(wh)) {
-               IEEE80211_ADDR_COPY(aad + 24,
-                       ((struct ieee80211_frame_addr4 *)wh)->i_addr4);
-               if (IS_QOS_DATA(wh)) {
-                       struct ieee80211_qosframe_addr4 *qwh4 =
-                               (struct ieee80211_qosframe_addr4 *) wh;
-                       aad[30] = qwh4->i_qos[0] & 0x0f;/* just priority bits */
-                       aad[31] = 0;
-                       b0[1] = aad[30];
-                       aad[1] = 22 + IEEE80211_ADDR_LEN + 2;
-               } else {
-                       *(uint16_t *)&aad[30] = 0;
-                       b0[1] = 0;
-                       aad[1] = 22 + IEEE80211_ADDR_LEN;
-               }
-       } else {
-               if (IS_QOS_DATA(wh)) {
-                       struct ieee80211_qosframe *qwh =
-                               (struct ieee80211_qosframe*) wh;
-                       aad[24] = qwh->i_qos[0] & 0x0f; /* just priority bits */
-                       aad[25] = 0;
-                       b0[1] = aad[24];
-                       aad[1] = 22 + 2;
-               } else {
-                       *(uint16_t *)&aad[24] = 0;
-                       b0[1] = 0;
-                       aad[1] = 22;
-               }
-               *(uint16_t *)&aad[26] = 0;
-               *(uint32_t *)&aad[28] = 0;
-       }
+       /* Init AAD */
+       (void) ieee80211_crypto_init_aad(wh, aad, 2 * AES_BLOCK_LEN);
 
        /* Start with the first block and AAD */
        rijndael_encrypt(ctx, b0, auth);
@@ -524,7 +541,6 @@ ccmp_init_blocks(rijndael_ctx *ctx, struct ieee80211_frame 
*wh,
        b0[0] &= 0x07;
        b0[14] = b0[15] = 0;
        rijndael_encrypt(ctx, b0, s0);
-#undef IS_QOS_DATA
 }
 
 #define        CCMP_ENCRYPT(_i, _b, _b0, _pos, _e, _len) do {  \
diff --git a/sys/net80211/ieee80211_crypto_gcmp.c 
b/sys/net80211/ieee80211_crypto_gcmp.c
index 290111235b4e..28e1cc5dab46 100644
--- a/sys/net80211/ieee80211_crypto_gcmp.c
+++ b/sys/net80211/ieee80211_crypto_gcmp.c
@@ -380,90 +380,6 @@ gcmp_demic(struct ieee80211_key *k, struct mbuf *m, int 
force)
        return (1);
 }
 
-/**
- * @brief Calculate the AAD required for this frame for AES-GCM.
- *
- * Note: This code was first copied over from ieee80211_crypto_ccmp.c, so
- * it has some CCMP-isms.
- *
- * NOTE: the first two bytes are a 16 bit big-endian length, which are used
- * by AES-CCM.  AES-GCM doesn't require the length at the beginning.
- *
- * @param wh   802.11 frame to calculate the AAD over
- * @param aad  AAD buffer, GCM_AAD_LEN bytes
- * @param The AAD length in bytes.
- */
-static int
-gcmp_init_aad(const struct ieee80211_frame *wh, uint8_t *aad)
-{
-       int aad_len;
-
-       memset(aad, 0, GCM_AAD_LEN);
-
-#define        IS_QOS_DATA(wh) IEEE80211_QOS_HAS_SEQ(wh)
-       /* AAD:
-        * FC with bits 4..6 and 11..13 masked to zero; 14 is always one
-        * A1 | A2 | A3
-        * SC with bits 4..15 (seq#) masked to zero
-        * A4 (if present)
-        * QC (if present)
-        */
-       aad[0] = 0;     /* AAD length >> 8 */
-       /* NB: aad[1] set below */
-
-       /*
-        * TODO: go back over this in 802.11-2020 and triple check
-        * the AAD assembly with regards to packet flags.
-        */
-
-       aad[2] = wh->i_fc[0] & 0x8f;    /* XXX magic #s */
-       /*
-        * TODO: 12.5.3.3.3 - bit 14 should always be set; bit 15 masked to 0
-        * if QoS control field, unmasked otherwise
-        */
-       aad[3] = wh->i_fc[1] & 0xc7;    /* XXX magic #s */
-       /* NB: we know 3 addresses are contiguous */
-       memcpy(aad + 4, wh->i_addr1, 3 * IEEE80211_ADDR_LEN);
-       aad[22] = wh->i_seq[0] & IEEE80211_SEQ_FRAG_MASK;
-       aad[23] = 0; /* all bits masked */
-       /*
-        * Construct variable-length portion of AAD based
-        * on whether this is a 4-address frame/QOS frame.
-        * We always zero-pad to 32 bytes before running it
-        * through the cipher.
-        */
-       if (IEEE80211_IS_DSTODS(wh)) {
-               IEEE80211_ADDR_COPY(aad + 24,
-                       ((const struct ieee80211_frame_addr4 *)wh)->i_addr4);
-               if (IS_QOS_DATA(wh)) {
-                       const struct ieee80211_qosframe_addr4 *qwh4 =
-                               (const struct ieee80211_qosframe_addr4 *) wh;
-                       aad[30] = qwh4->i_qos[0] & 0x0f;/* just priority bits */
-                       aad[31] = 0;
-                       aad_len = aad[1] = 22 + IEEE80211_ADDR_LEN + 2;
-               } else {
-                       *(uint16_t *)&aad[30] = 0;
-                       aad_len = aad[1] = 22 + IEEE80211_ADDR_LEN;
-               }
-       } else {
-               if (IS_QOS_DATA(wh)) {
-                       const struct ieee80211_qosframe *qwh =
-                               (const struct ieee80211_qosframe*) wh;
-                       aad[24] = qwh->i_qos[0] & 0x0f; /* just priority bits */
-                       aad[25] = 0;
-                       aad_len = aad[1] = 22 + 2;
-               } else {
-                       *(uint16_t *)&aad[24] = 0;
-                       aad_len = aad[1] = 22;
-               }
-               *(uint16_t *)&aad[26] = 0;
-               *(uint32_t *)&aad[28] = 0;
-       }
-#undef IS_QOS_DATA
-
-       return (aad_len);
-}
-
 /*
  * Populate the 12 byte / 96 bit IV buffer.
  */
@@ -538,7 +454,7 @@ gcmp_encrypt(struct ieee80211_key *key, struct mbuf *m0, 
int hdrlen)
        }
 
        /* Initialise AAD */
-       aad_len = gcmp_init_aad(wh, aad);
+       aad_len = ieee80211_crypto_init_aad(wh, aad, GCM_AAD_LEN);
 
        /* Initialise local Nonce to work on */
        /* TODO: rename iv stuff here to nonce */
@@ -629,7 +545,7 @@ gcmp_decrypt(struct ieee80211_key *key, u_int64_t pn, 
struct mbuf *m,
        }
 
        /* Initialise AAD */
-       aad_len = gcmp_init_aad(wh, aad);
+       aad_len = ieee80211_crypto_init_aad(wh, aad, GCM_AAD_LEN);
 
        /* Initialise local IV copy to work on */
        iv_len = gcmp_init_iv(iv, wh, pn);

Reply via email to