diff --git a/sys/net80211/ieee80211_crypto_ccmp.c b/sys/net80211/ieee80211_crypto_ccmp.c --- 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; @@ -63,6 +66,7 @@ static void *ccmp_attach(struct ieee80211vap *, struct ieee80211_key *); static void ccmp_detach(struct ieee80211_key *); static int ccmp_setkey(struct ieee80211_key *); +static int ccmp_256_setkey(struct ieee80211_key *); static void ccmp_setiv(struct ieee80211_key *, uint8_t *); static int ccmp_encap(struct ieee80211_key *, struct mbuf *); static int ccmp_decap(struct ieee80211_key *, struct mbuf *, int); @@ -74,7 +78,7 @@ .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, @@ -86,6 +90,23 @@ .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, + .ic_setkey = ccmp_256_setkey, + .ic_setiv = ccmp_setiv, + .ic_encap = ccmp_encap, + .ic_decap = ccmp_decap, + .ic_enmic = ccmp_enmic, + .ic_demic = ccmp_demic, +}; + static int ccmp_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen); static int ccmp_decrypt(struct ieee80211_key *, u_int64_t pn, struct mbuf *, int hdrlen); @@ -120,6 +141,46 @@ 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) { @@ -136,6 +197,22 @@ return 1; } +static int +ccmp_256_setkey(struct ieee80211_key *k) +{ + struct ccmp_ctx *ctx = k->wk_private; + + if (k->wk_keylen != (256/NBBY)) { + IEEE80211_DPRINTF(ctx->cc_vap, IEEE80211_MSG_CRYPTO, + "%s: Invalid key length %u, expecting %u\n", + __func__, k->wk_keylen, 256/NBBY); + return (0); + } + if (k->wk_flags & IEEE80211_KEY_SWENCRYPT) + rijndael_set_key(&ctx->cc_aes, k->wk_key, k->wk_keylen*NBBY); + return (1); +} + static void ccmp_setiv(struct ieee80211_key *k, uint8_t *ivp) { @@ -187,11 +264,11 @@ /* * 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 +367,14 @@ * 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 +426,28 @@ 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 +469,7 @@ 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 +554,14 @@ 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 +669,8 @@ } 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 +707,17 @@ 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 +776,7 @@ 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 +790,4 @@ * Module glue. */ IEEE80211_CRYPTO_MODULE(ccmp, 1); +IEEE80211_CRYPTO_MODULE_ADD(ccmp_256);