git: eac4d55dba47 - main - net80211: Refactor CCMP-128 support; add CCMP-256 support
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 08 Apr 2025 01:35:27 UTC
The branch main has been updated by adrian: URL: https://cgit.FreeBSD.org/src/commit/?id=eac4d55dba47e3a6c45db96d32e2f66d71565543 commit eac4d55dba47e3a6c45db96d32e2f66d71565543 Author: Adrian Chadd <adrian@FreeBSD.org> AuthorDate: 2025-03-05 03:41:18 +0000 Commit: Adrian Chadd <adrian@FreeBSD.org> CommitDate: 2025-04-08 01:35:21 +0000 net80211: Refactor CCMP-128 support; add CCMP-256 support Refactor CCMP-128 support and add CCMP-256 support. This has been verified against openwrt on an ath9k device with CCMP-256 + WPA-PSK-SHA256. (And whilst I was at it, I also tested GCMP-256.) Differential Revision: https://reviews.freebsd.org/D49238 Reviewed by: thj --- sys/net80211/ieee80211_crypto_ccmp.c | 145 ++++++++++++++++++++++++++++------- 1 file changed, 118 insertions(+), 27 deletions(-) diff --git a/sys/net80211/ieee80211_crypto_ccmp.c b/sys/net80211/ieee80211_crypto_ccmp.c index 87730679c47f..70265335fe70 100644 --- a/sys/net80211/ieee80211_crypto_ccmp.c +++ b/sys/net80211/ieee80211_crypto_ccmp.c @@ -54,6 +54,9 @@ #define AES_BLOCK_LEN 16 +#define CCMP_128_MIC_LEN 8 +#define CCMP_256_MIC_LEN 16 + struct ccmp_ctx { struct ieee80211vap *cc_vap; /* for diagnostics+statistics */ struct ieee80211com *cc_ic; @@ -74,7 +77,24 @@ static const struct ieee80211_cipher ccmp = { .ic_cipher = IEEE80211_CIPHER_AES_CCM, .ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_EXTIVLEN, - .ic_trailer = IEEE80211_WEP_MICLEN, + .ic_trailer = CCMP_128_MIC_LEN, + .ic_miclen = 0, + .ic_attach = ccmp_attach, + .ic_detach = ccmp_detach, + .ic_setkey = ccmp_setkey, + .ic_setiv = ccmp_setiv, + .ic_encap = ccmp_encap, + .ic_decap = ccmp_decap, + .ic_enmic = ccmp_enmic, + .ic_demic = ccmp_demic, +}; + +static const struct ieee80211_cipher ccmp_256 = { + .ic_name = "AES-CCM-256", + .ic_cipher = IEEE80211_CIPHER_AES_CCM_256, + .ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + + IEEE80211_WEP_EXTIVLEN, + .ic_trailer = CCMP_256_MIC_LEN, .ic_miclen = 0, .ic_attach = ccmp_attach, .ic_detach = ccmp_detach, @@ -120,15 +140,70 @@ ccmp_detach(struct ieee80211_key *k) nrefs--; /* NB: we assume caller locking */ } +static int +ccmp_get_trailer_len(struct ieee80211_key *k) +{ + return (k->wk_cipher->ic_trailer); +} + +static int +ccmp_get_header_len(struct ieee80211_key *k) +{ + return (k->wk_cipher->ic_header); +} + +/** + * @brief Return the M parameter to use for CCMP block0 initialisation. + * + * M is defined as the number of bytes in the authentication + * field. + * + * See RFC3610, Section 2 (CCM Mode Specification) for more + * information. + * + * The MIC size is defined in 802.11-2020 12.5.3 + * (CTR with CBC-MAC Protocol (CCMP)). + * + * CCM-128 - M=8, MIC is 8 octets. + * CCM-256 - M=16, MIC is 16 octets. + * + * @param key ieee80211_key to calculate M for + * @retval the number of bytes in the authentication field + */ +static int +ccmp_get_ccm_m(struct ieee80211_key *k) +{ + if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_AES_CCM) + return (8); + if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_AES_CCM_256) + return (16); + return (8); /* XXX default */ +} + static int ccmp_setkey(struct ieee80211_key *k) { + uint32_t keylen; struct ccmp_ctx *ctx = k->wk_private; - if (k->wk_keylen != (128/NBBY)) { + switch (k->wk_cipher->ic_cipher) { + case IEEE80211_CIPHER_AES_CCM: + keylen = 128; + break; + case IEEE80211_CIPHER_AES_CCM_256: + keylen = 256; + break; + default: + IEEE80211_DPRINTF(ctx->cc_vap, IEEE80211_MSG_CRYPTO, + "%s: Unexpected cipher (%u)", + __func__, k->wk_cipher->ic_cipher); + return (0); + } + + if (k->wk_keylen != (keylen/NBBY)) { IEEE80211_DPRINTF(ctx->cc_vap, IEEE80211_MSG_CRYPTO, "%s: Invalid key length %u, expecting %u\n", - __func__, k->wk_keylen, 128/NBBY); + __func__, k->wk_keylen, keylen/NBBY); return 0; } if (k->wk_flags & IEEE80211_KEY_SWENCRYPT) @@ -187,11 +262,11 @@ ccmp_encap(struct ieee80211_key *k, struct mbuf *m) /* * Copy down 802.11 header and add the IV, KeyID, and ExtIV. */ - M_PREPEND(m, ccmp.ic_header, IEEE80211_M_NOWAIT); + M_PREPEND(m, ccmp_get_header_len(k), IEEE80211_M_NOWAIT); if (m == NULL) return 0; ivp = mtod(m, uint8_t *); - ovbcopy(ivp + ccmp.ic_header, ivp, hdrlen); + ovbcopy(ivp + ccmp_get_header_len(k), ivp, hdrlen); ivp += hdrlen; ccmp_setiv(k, ivp); @@ -290,13 +365,14 @@ finish: * Copy up 802.11 header and strip crypto bits. */ if (! ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP))) { - ovbcopy(mtod(m, void *), mtod(m, uint8_t *) + ccmp.ic_header, + ovbcopy(mtod(m, void *), + mtod(m, uint8_t *) + ccmp_get_header_len(k), hdrlen); - m_adj(m, ccmp.ic_header); + m_adj(m, ccmp_get_header_len(k)); } if ((rxs == NULL) || (rxs->c_pktflags & IEEE80211_RX_F_MIC_STRIP) == 0) - m_adj(m, -ccmp.ic_trailer); + m_adj(m, -ccmp_get_trailer_len(k)); /* * Ok to update rsc now. @@ -348,18 +424,28 @@ xor_block(uint8_t *b, const uint8_t *a, size_t len) static void ccmp_init_blocks(rijndael_ctx *ctx, struct ieee80211_frame *wh, - u_int64_t pn, size_t dlen, + uint32_t m, u_int64_t pn, size_t dlen, 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) + */ + m = (m - 2) / 2; + /* CCM Initial Block: - * Flag (Include authentication header, M=3 (8-octet MIC), - * L=1 (2-octet Dlen)) + * + * Flag (Include authentication header, + * M=3 or 7 (8 or 16 octet auth field), + * L=1 (2-octet Dlen)) + * Adata=1 (one or more auth blocks present) * Nonce: 0x00 | A2 | PN - * Dlen */ - b0[0] = 0x59; + * Dlen + */ + b0[0] = 0x40 | 0x01 | (m << 3); /* NB: b0[1] set below */ IEEE80211_ADDR_COPY(b0 + 2, wh->i_addr2); b0[8] = pn >> 40; @@ -381,6 +467,7 @@ ccmp_init_blocks(rijndael_ctx *ctx, struct ieee80211_frame *wh, 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); @@ -465,14 +552,14 @@ ccmp_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) ctx->cc_vap->iv_stats.is_crypto_ccmp++; wh = mtod(m, struct ieee80211_frame *); - data_len = m->m_pkthdr.len - (hdrlen + ccmp.ic_header); - ccmp_init_blocks(&ctx->cc_aes, wh, key->wk_keytsc, - data_len, b0, aad, b, s0); + data_len = m->m_pkthdr.len - (hdrlen + ccmp_get_header_len(key)); + ccmp_init_blocks(&ctx->cc_aes, wh, ccmp_get_ccm_m(key), + key->wk_keytsc, data_len, b0, aad, b, s0); i = 1; - pos = mtod(m, uint8_t *) + hdrlen + ccmp.ic_header; + pos = mtod(m, uint8_t *) + hdrlen + ccmp_get_header_len(key); /* NB: assumes header is entirely in first mbuf */ - space = m->m_len - (hdrlen + ccmp.ic_header); + space = m->m_len - (hdrlen + ccmp_get_header_len(key)); for (;;) { if (space > data_len) space = data_len; @@ -580,8 +667,8 @@ ccmp_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) } done: /* tack on MIC */ - xor_block(b, s0, ccmp.ic_trailer); - return m_append(m0, ccmp.ic_trailer, b); + xor_block(b, s0, ccmp_get_trailer_len(key)); + return m_append(m0, ccmp_get_trailer_len(key), b); } #undef CCMP_ENCRYPT @@ -618,14 +705,17 @@ ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, int hdrlen ctx->cc_vap->iv_stats.is_crypto_ccmp++; wh = mtod(m, struct ieee80211_frame *); - data_len = m->m_pkthdr.len - (hdrlen + ccmp.ic_header + ccmp.ic_trailer); - ccmp_init_blocks(&ctx->cc_aes, wh, pn, data_len, b0, aad, a, b); - m_copydata(m, m->m_pkthdr.len - ccmp.ic_trailer, ccmp.ic_trailer, mic); - xor_block(mic, b, ccmp.ic_trailer); + data_len = m->m_pkthdr.len - + (hdrlen + ccmp_get_header_len(key) + ccmp_get_trailer_len(key)); + ccmp_init_blocks(&ctx->cc_aes, wh, ccmp_get_ccm_m(key), pn, + data_len, b0, aad, a, b); + m_copydata(m, m->m_pkthdr.len - ccmp_get_trailer_len(key), + ccmp_get_trailer_len(key), mic); + xor_block(mic, b, ccmp_get_trailer_len(key)); i = 1; - pos = mtod(m, uint8_t *) + hdrlen + ccmp.ic_header; - space = m->m_len - (hdrlen + ccmp.ic_header); + pos = mtod(m, uint8_t *) + hdrlen + ccmp_get_header_len(key); + space = m->m_len - (hdrlen + ccmp_get_header_len(key)); for (;;) { if (space > data_len) space = data_len; @@ -684,7 +774,7 @@ ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, int hdrlen if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_MIC_STRIP) != 0) return (1); - if (memcmp(mic, a, ccmp.ic_trailer) != 0) { + if (memcmp(mic, a, ccmp_get_trailer_len(key)) != 0) { IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, "%s", "AES-CCM decrypt failed; MIC mismatch"); vap->iv_stats.is_rx_ccmpmic++; @@ -698,3 +788,4 @@ ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, int hdrlen * Module glue. */ IEEE80211_CRYPTO_MODULE(ccmp, 1); +IEEE80211_CRYPTO_MODULE_ADD(ccmp_256);