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 @@ -1,902 +1,999 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include /* * IEEE 802.11 generic crypto support. */ #include "opt_wlan.h" #include #include #include #include #include #include #include #include /* XXX ETHER_HDR_LEN */ #include MALLOC_DEFINE(M_80211_CRYPTO, "80211crypto", "802.11 crypto state"); static int _ieee80211_crypto_delkey(struct ieee80211vap *, struct ieee80211_key *); /* * Table of registered cipher modules. */ static const struct ieee80211_cipher *ciphers[IEEE80211_CIPHER_MAX]; /* * Default "null" key management routines. */ static int null_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *k, ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix) { if (!ieee80211_is_key_global(vap, k)) { /* * Not in the global key table, the driver should handle this * by allocating a slot in the h/w key table/cache. In * lieu of that return key slot 0 for any unicast key * request. We disallow the request if this is a group key. * This default policy does the right thing for legacy hardware * with a 4 key table. It also handles devices that pass * packets through untouched when marked with the WEP bit * and key index 0. */ if (k->wk_flags & IEEE80211_KEY_GROUP) return 0; *keyix = 0; /* NB: use key index 0 for ucast key */ } else { *keyix = ieee80211_crypto_get_key_wepidx(vap, k); } *rxkeyix = IEEE80211_KEYIX_NONE; /* XXX maybe *keyix? */ return 1; } static int null_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) { return 1; } static int null_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k) { return 1; } static void null_key_update(struct ieee80211vap *vap) {} /* * Write-arounds for common operations. */ static __inline void cipher_detach(struct ieee80211_key *key) { key->wk_cipher->ic_detach(key); } static __inline void * cipher_attach(struct ieee80211vap *vap, struct ieee80211_key *key) { return key->wk_cipher->ic_attach(vap, key); } /* * Wrappers for driver key management methods. */ static __inline int dev_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *key, ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix) { return vap->iv_key_alloc(vap, key, keyix, rxkeyix); } static __inline int dev_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *key) { return vap->iv_key_delete(vap, key); } static __inline int dev_key_set(struct ieee80211vap *vap, const struct ieee80211_key *key) { return vap->iv_key_set(vap, key); } /* * Setup crypto support for a device/shared instance. */ void ieee80211_crypto_attach(struct ieee80211com *ic) { /* NB: we assume everything is pre-zero'd */ ciphers[IEEE80211_CIPHER_NONE] = &ieee80211_cipher_none; /* * Default set of net80211 supported ciphers. * * These are the default set that all drivers are expected to * support, either/or in hardware and software. * * Drivers can add their own support to this and the * hardware cipher list (ic_cryptocaps.) */ ic->ic_sw_cryptocaps = IEEE80211_CRYPTO_WEP | IEEE80211_CRYPTO_TKIP | IEEE80211_CRYPTO_AES_CCM; /* * Default set of key management types supported by net80211. * * These are supported by software net80211 and announced/ * driven by hostapd + wpa_supplicant. * * Drivers doing full supplicant offload must not set * anything here. * * Note that IEEE80211_C_WPA1 and IEEE80211_C_WPA2 are the * "old" style way of drivers announcing key management * capabilities. There are many, many more key management * suites in 802.11-2016 (see 9.4.2.25.3 - AKM suites.) * For now they still need to be set - these flags are checked * when assembling a beacon to reserve space for the WPA * vendor IE (WPA 1) and RSN IE (WPA 2). */ ic->ic_sw_keymgmtcaps = 0; } /* * Teardown crypto support. */ void ieee80211_crypto_detach(struct ieee80211com *ic) { } /* * Set the supported ciphers for software encryption. */ void ieee80211_crypto_set_supported_software_ciphers(struct ieee80211com *ic, uint32_t cipher_set) { ic->ic_sw_cryptocaps = cipher_set; } /* * Set the supported ciphers for hardware encryption. */ void ieee80211_crypto_set_supported_hardware_ciphers(struct ieee80211com *ic, uint32_t cipher_set) { ic->ic_cryptocaps = cipher_set; } /* * Set the supported software key management by the driver. * * These are the key management suites that are supported via * the driver via hostapd/wpa_supplicant. * * Key management which is completely offloaded (ie, the supplicant * runs in hardware/firmware) must not be set here. */ void ieee80211_crypto_set_supported_driver_keymgmt(struct ieee80211com *ic, uint32_t keymgmt_set) { ic->ic_sw_keymgmtcaps = keymgmt_set; } /* * Setup crypto support for a vap. */ void ieee80211_crypto_vattach(struct ieee80211vap *vap) { int i; /* NB: we assume everything is pre-zero'd */ vap->iv_max_keyix = IEEE80211_WEP_NKID; vap->iv_def_txkey = IEEE80211_KEYIX_NONE; for (i = 0; i < IEEE80211_WEP_NKID; i++) ieee80211_crypto_resetkey(vap, &vap->iv_nw_keys[i], IEEE80211_KEYIX_NONE); /* * Initialize the driver key support routines to noop entries. * This is useful especially for the cipher test modules. */ vap->iv_key_alloc = null_key_alloc; vap->iv_key_set = null_key_set; vap->iv_key_delete = null_key_delete; vap->iv_key_update_begin = null_key_update; vap->iv_key_update_end = null_key_update; } /* * Teardown crypto support for a vap. */ void ieee80211_crypto_vdetach(struct ieee80211vap *vap) { ieee80211_crypto_delglobalkeys(vap); } /* * Register a crypto cipher module. */ void ieee80211_crypto_register(const struct ieee80211_cipher *cip) { if (cip->ic_cipher >= IEEE80211_CIPHER_MAX) { printf("%s: cipher %s has an invalid cipher index %u\n", __func__, cip->ic_name, cip->ic_cipher); return; } if (ciphers[cip->ic_cipher] != NULL && ciphers[cip->ic_cipher] != cip) { printf("%s: cipher %s registered with a different template\n", __func__, cip->ic_name); return; } ciphers[cip->ic_cipher] = cip; } /* * Unregister a crypto cipher module. */ void ieee80211_crypto_unregister(const struct ieee80211_cipher *cip) { if (cip->ic_cipher >= IEEE80211_CIPHER_MAX) { printf("%s: cipher %s has an invalid cipher index %u\n", __func__, cip->ic_name, cip->ic_cipher); return; } if (ciphers[cip->ic_cipher] != NULL && ciphers[cip->ic_cipher] != cip) { printf("%s: cipher %s registered with a different template\n", __func__, cip->ic_name); return; } /* NB: don't complain about not being registered */ /* XXX disallow if references */ ciphers[cip->ic_cipher] = NULL; } int ieee80211_crypto_available(u_int cipher) { return cipher < IEEE80211_CIPHER_MAX && ciphers[cipher] != NULL; } /* XXX well-known names! */ static const char *cipher_modnames[IEEE80211_CIPHER_MAX] = { [IEEE80211_CIPHER_WEP] = "wlan_wep", [IEEE80211_CIPHER_TKIP] = "wlan_tkip", [IEEE80211_CIPHER_AES_OCB] = "wlan_aes_ocb", [IEEE80211_CIPHER_AES_CCM] = "wlan_ccmp", [IEEE80211_CIPHER_TKIPMIC] = "#4", /* NB: reserved */ [IEEE80211_CIPHER_CKIP] = "wlan_ckip", [IEEE80211_CIPHER_NONE] = "wlan_none", [IEEE80211_CIPHER_AES_CCM_256] = "wlan_ccmp", [IEEE80211_CIPHER_BIP_CMAC_128] = "wlan_bip_cmac", [IEEE80211_CIPHER_BIP_CMAC_256] = "wlan_bip_cmac", [IEEE80211_CIPHER_BIP_GMAC_128] = "wlan_bip_gmac", [IEEE80211_CIPHER_BIP_GMAC_256] = "wlan_bip_gmac", [IEEE80211_CIPHER_AES_GCM_128] = "wlan_gcmp", [IEEE80211_CIPHER_AES_GCM_256] = "wlan_gcmp", }; /* NB: there must be no overlap between user-supplied and device-owned flags */ CTASSERT((IEEE80211_KEY_COMMON & IEEE80211_KEY_DEVICE) == 0); /* * Establish a relationship between the specified key and cipher * and, if necessary, allocate a hardware index from the driver. * Note that when a fixed key index is required it must be specified. * * This must be the first call applied to a key; all the other key * routines assume wk_cipher is setup. * * Locking must be handled by the caller using: * ieee80211_key_update_begin(vap); * ieee80211_key_update_end(vap); */ int ieee80211_crypto_newkey(struct ieee80211vap *vap, int cipher, int flags, struct ieee80211_key *key) { struct ieee80211com *ic = vap->iv_ic; const struct ieee80211_cipher *cip; ieee80211_keyix keyix, rxkeyix; void *keyctx; int oflags; IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO, "%s: cipher %u flags 0x%x keyix %u\n", __func__, cipher, flags, key->wk_keyix); /* * Validate cipher and set reference to cipher routines. */ if (cipher >= IEEE80211_CIPHER_MAX) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO, "%s: invalid cipher %u\n", __func__, cipher); vap->iv_stats.is_crypto_badcipher++; return 0; } cip = ciphers[cipher]; if (cip == NULL) { /* * Auto-load cipher module if we have a well-known name * for it. It might be better to use string names rather * than numbers and craft a module name based on the cipher * name; e.g. wlan_cipher_. */ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO, "%s: unregistered cipher %u, load module %s\n", __func__, cipher, cipher_modnames[cipher]); ieee80211_load_module(cipher_modnames[cipher]); /* * If cipher module loaded it should immediately * call ieee80211_crypto_register which will fill * in the entry in the ciphers array. */ cip = ciphers[cipher]; if (cip == NULL) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO, "%s: unable to load cipher %u, module %s\n", __func__, cipher, cipher_modnames[cipher]); vap->iv_stats.is_crypto_nocipher++; return 0; } } oflags = key->wk_flags; flags &= IEEE80211_KEY_COMMON; /* NB: preserve device attributes */ flags |= (oflags & IEEE80211_KEY_DEVICE); /* * If the hardware does not support the cipher then * fallback to a host-based implementation. */ if ((ic->ic_cryptocaps & (1<ic_name); flags |= IEEE80211_KEY_SWCRYPT; } /* * Hardware TKIP with software MIC is an important * combination; we handle it by flagging each key, * the cipher modules honor it. */ if (cipher == IEEE80211_CIPHER_TKIP && (ic->ic_cryptocaps & IEEE80211_CRYPTO_TKIPMIC) == 0) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO, "%s: no h/w support for TKIP MIC, falling back to s/w\n", __func__); flags |= IEEE80211_KEY_SWMIC; } /* * Bind cipher to key instance. Note we do this * after checking the device capabilities so the * cipher module can optimize space usage based on * whether or not it needs to do the cipher work. */ if (key->wk_cipher != cip || key->wk_flags != flags) { /* * Fillin the flags so cipher modules can see s/w * crypto requirements and potentially allocate * different state and/or attach different method * pointers. */ key->wk_flags = flags; keyctx = cip->ic_attach(vap, key); if (keyctx == NULL) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO, "%s: unable to attach cipher %s\n", __func__, cip->ic_name); key->wk_flags = oflags; /* restore old flags */ vap->iv_stats.is_crypto_attachfail++; return 0; } cipher_detach(key); key->wk_cipher = cip; /* XXX refcnt? */ key->wk_private = keyctx; } /* * Ask the driver for a key index if we don't have one. * Note that entries in the global key table always have * an index; this means it's safe to call this routine * for these entries just to setup the reference to the * cipher template. Note also that when using software * crypto we also call the driver to give us a key index. */ if ((key->wk_flags & IEEE80211_KEY_DEVKEY) == 0) { if (!dev_key_alloc(vap, key, &keyix, &rxkeyix)) { /* * Unable to setup driver state. */ vap->iv_stats.is_crypto_keyfail++; IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO, "%s: unable to setup cipher %s\n", __func__, cip->ic_name); return 0; } if (key->wk_flags != flags) { /* * Driver overrode flags we setup; typically because * resources were unavailable to handle _this_ key. * Re-attach the cipher context to allow cipher * modules to handle differing requirements. */ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO, "%s: driver override for cipher %s, flags " "%b -> %b\n", __func__, cip->ic_name, oflags, IEEE80211_KEY_BITS, key->wk_flags, IEEE80211_KEY_BITS); keyctx = cip->ic_attach(vap, key); if (keyctx == NULL) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO, "%s: unable to attach cipher %s with " "flags %b\n", __func__, cip->ic_name, key->wk_flags, IEEE80211_KEY_BITS); key->wk_flags = oflags; /* restore old flags */ vap->iv_stats.is_crypto_attachfail++; return 0; } cipher_detach(key); key->wk_cipher = cip; /* XXX refcnt? */ key->wk_private = keyctx; } key->wk_keyix = keyix; key->wk_rxkeyix = rxkeyix; key->wk_flags |= IEEE80211_KEY_DEVKEY; } return 1; } /* * Remove the key (no locking, for internal use). */ static int _ieee80211_crypto_delkey(struct ieee80211vap *vap, struct ieee80211_key *key) { KASSERT(key->wk_cipher != NULL, ("No cipher!")); IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO, "%s: %s keyix %u flags %b rsc %ju tsc %ju len %u\n", __func__, key->wk_cipher->ic_name, key->wk_keyix, key->wk_flags, IEEE80211_KEY_BITS, key->wk_keyrsc[IEEE80211_NONQOS_TID], key->wk_keytsc, key->wk_keylen); if (key->wk_flags & IEEE80211_KEY_DEVKEY) { /* * Remove hardware entry. */ /* XXX key cache */ if (!dev_key_delete(vap, key)) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO, "%s: driver did not delete key index %u\n", __func__, key->wk_keyix); vap->iv_stats.is_crypto_delkey++; /* XXX recovery? */ } } cipher_detach(key); memset(key, 0, sizeof(*key)); ieee80211_crypto_resetkey(vap, key, IEEE80211_KEYIX_NONE); return 1; } /* * Remove the specified key. */ int ieee80211_crypto_delkey(struct ieee80211vap *vap, struct ieee80211_key *key) { int status; ieee80211_key_update_begin(vap); status = _ieee80211_crypto_delkey(vap, key); ieee80211_key_update_end(vap); return status; } /* * Clear the global key table. */ void ieee80211_crypto_delglobalkeys(struct ieee80211vap *vap) { int i; ieee80211_key_update_begin(vap); for (i = 0; i < IEEE80211_WEP_NKID; i++) (void) _ieee80211_crypto_delkey(vap, &vap->iv_nw_keys[i]); ieee80211_key_update_end(vap); } /* * Set the contents of the specified key. * * Locking must be handled by the caller using: * ieee80211_key_update_begin(vap); * ieee80211_key_update_end(vap); */ int ieee80211_crypto_setkey(struct ieee80211vap *vap, struct ieee80211_key *key) { const struct ieee80211_cipher *cip = key->wk_cipher; KASSERT(cip != NULL, ("No cipher!")); IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO, "%s: %s keyix %u flags %b mac %s rsc %ju tsc %ju len %u\n", __func__, cip->ic_name, key->wk_keyix, key->wk_flags, IEEE80211_KEY_BITS, ether_sprintf(key->wk_macaddr), key->wk_keyrsc[IEEE80211_NONQOS_TID], key->wk_keytsc, key->wk_keylen); if ((key->wk_flags & IEEE80211_KEY_DEVKEY) == 0) { /* XXX nothing allocated, should not happen */ IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO, "%s: no device key setup done; should not happen!\n", __func__); vap->iv_stats.is_crypto_setkey_nokey++; return 0; } /* * Give cipher a chance to validate key contents. * XXX should happen before modifying state. */ if (!cip->ic_setkey(key)) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO, "%s: cipher %s rejected key index %u len %u flags %b\n", __func__, cip->ic_name, key->wk_keyix, key->wk_keylen, key->wk_flags, IEEE80211_KEY_BITS); vap->iv_stats.is_crypto_setkey_cipher++; return 0; } return dev_key_set(vap, key); } /* * Return index if the key is a WEP key (0..3); -1 otherwise. * * This is different to "get_keyid" which defaults to returning * 0 for unicast keys; it assumes that it won't be used for WEP. */ int ieee80211_crypto_get_key_wepidx(const struct ieee80211vap *vap, const struct ieee80211_key *k) { if (ieee80211_is_key_global(vap, k)) { return (k - vap->iv_nw_keys); } return (-1); } /* * Note: only supports a single unicast key (0). */ uint8_t ieee80211_crypto_get_keyid(struct ieee80211vap *vap, struct ieee80211_key *k) { if (ieee80211_is_key_global(vap, k)) { return (k - vap->iv_nw_keys); } return (0); } struct ieee80211_key * ieee80211_crypto_get_txkey(struct ieee80211_node *ni, struct mbuf *m) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_frame *wh; /* * Multicast traffic always uses the multicast key. * * Historically we would fall back to the default * transmit key if there was no unicast key. This * behaviour was documented up to IEEE Std 802.11-2016, * 12.9.2.2 Per-MSDU/Per-A-MSDU Tx pseudocode, in the * 'else' case but is no longer in later versions of * the standard. Additionally falling back to the * group key for unicast was a security risk. */ wh = mtod(m, struct ieee80211_frame *); if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { if (vap->iv_def_txkey == IEEE80211_KEYIX_NONE) { IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr1, "no default transmit key (%s) deftxkey %u", __func__, vap->iv_def_txkey); vap->iv_stats.is_tx_nodefkey++; return NULL; } return &vap->iv_nw_keys[vap->iv_def_txkey]; } if (IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)) return NULL; return &ni->ni_ucastkey; } /* * Add privacy headers appropriate for the specified key. */ struct ieee80211_key * ieee80211_crypto_encap(struct ieee80211_node *ni, struct mbuf *m) { struct ieee80211_key *k; const struct ieee80211_cipher *cip; if ((k = ieee80211_crypto_get_txkey(ni, m)) != NULL) { cip = k->wk_cipher; return (cip->ic_encap(k, m) ? k : NULL); } return NULL; } /* * Validate and strip privacy headers (and trailer) for a * received frame that has the WEP/Privacy bit set. */ int ieee80211_crypto_decap(struct ieee80211_node *ni, struct mbuf *m, int hdrlen, struct ieee80211_key **key) { #define IEEE80211_WEP_HDRLEN (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN) #define IEEE80211_WEP_MINLEN \ (sizeof(struct ieee80211_frame) + \ IEEE80211_WEP_HDRLEN + IEEE80211_WEP_CRCLEN) struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_key *k; struct ieee80211_frame *wh; const struct ieee80211_rx_stats *rxs; const struct ieee80211_cipher *cip; uint8_t keyid; /* * Check for hardware decryption and IV stripping. * If the IV is stripped then we definitely can't find a key. * Set the key to NULL but return true; upper layers * will need to handle a NULL key for a successful * decrypt. */ rxs = ieee80211_get_rx_params_ptr(m); if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_DECRYPTED)) { if (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP) { /* * Hardware decrypted, IV stripped. * We can't find a key with a stripped IV. * Return successful. */ *key = NULL; return (1); } } /* NB: this minimum size data frame could be bigger */ if (m->m_pkthdr.len < IEEE80211_WEP_MINLEN) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY, "%s: WEP data frame too short, len %u\n", __func__, m->m_pkthdr.len); vap->iv_stats.is_rx_tooshort++; /* XXX need unique stat? */ *key = NULL; return (0); } /* * Locate the key. If unicast and there is no unicast * key then we fall back to the key id in the header. * This assumes unicast keys are only configured when * the key id in the header is meaningless (typically 0). */ wh = mtod(m, struct ieee80211_frame *); m_copydata(m, hdrlen + IEEE80211_WEP_IVLEN, sizeof(keyid), &keyid); if (IEEE80211_IS_MULTICAST(wh->i_addr1) || IEEE80211_KEY_UNDEFINED(&ni->ni_ucastkey)) k = &vap->iv_nw_keys[keyid >> 6]; else k = &ni->ni_ucastkey; /* * Ensure crypto header is contiguous and long enough for all * decap work. */ cip = k->wk_cipher; if (m->m_len < hdrlen + cip->ic_header) { IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, "frame is too short (%d < %u) for crypto decap", cip->ic_name, m->m_len, hdrlen + cip->ic_header); vap->iv_stats.is_rx_tooshort++; *key = NULL; return (0); } /* * Attempt decryption. * * If we fail then don't return the key - return NULL * and an error. */ if (cip->ic_decap(k, m, hdrlen)) { /* success */ *key = k; return (1); } /* Failure */ *key = NULL; return (0); #undef IEEE80211_WEP_MINLEN #undef IEEE80211_WEP_HDRLEN } /** * @brief Check and remove any post-defragmentation MIC from an MSDU. * * This is called after defragmentation. Crypto types that implement * a MIC/ICV check per MSDU will not implement this function. * * As an example, TKIP decapsulation covers both MIC/ICV checks per * MPDU (the "WEP" ICV) and then a Michael MIC verification on the * defragmented MSDU. Please see 802.11-2020 12.5.2.1.3 (TKIP decapsulation) * for more information. * * @param vap the current VAP * @param k the current key * @param m the mbuf representing the MSDU * @param f set to 1 to force a MSDU MIC check, even if HW decrypted * @returns 0 if error / MIC check failed, 1 if OK */ int ieee80211_crypto_demic(struct ieee80211vap *vap, struct ieee80211_key *k, struct mbuf *m, int force) { const struct ieee80211_cipher *cip; const struct ieee80211_rx_stats *rxs; struct ieee80211_frame *wh; rxs = ieee80211_get_rx_params_ptr(m); wh = mtod(m, struct ieee80211_frame *); /* * Handle demic / mic errors from hardware-decrypted offload devices. */ if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_DECRYPTED)) { if ((rxs->c_pktflags & IEEE80211_RX_F_FAIL_MMIC) != 0) { /* * Hardware has said MMIC failed. We don't care about * whether it was stripped or not. * * Eventually - teach the demic methods in crypto * modules to handle a NULL key and not to dereference * it. */ ieee80211_notify_michael_failure(vap, wh, IEEE80211_KEYIX_NONE); return (0); } if ((rxs->c_pktflags & (IEEE80211_RX_F_MIC_STRIP|IEEE80211_RX_F_MMIC_STRIP)) != 0) { /* * Hardware has decrypted and not indicated a * MIC failure and has stripped the MIC. * We may not have a key, so for now just * return OK. */ return (1); } } /* * If we don't have a key at this point then we don't * have to demic anything. */ if (k == NULL) return (1); cip = k->wk_cipher; return (cip->ic_miclen > 0 ? cip->ic_demic(k, m, force) : 1); } static void load_ucastkey(void *arg, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_key *k; if (vap->iv_state != IEEE80211_S_RUN) return; k = &ni->ni_ucastkey; if (k->wk_flags & IEEE80211_KEY_DEVKEY) dev_key_set(vap, k); } /* * Re-load all keys known to the 802.11 layer that may * have hardware state backing them. This is used by * drivers on resume to push keys down into the device. */ void ieee80211_crypto_reload_keys(struct ieee80211com *ic) { struct ieee80211vap *vap; int i; /* * Keys in the global key table of each vap. */ /* NB: used only during resume so don't lock for now */ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { if (vap->iv_state != IEEE80211_S_RUN) continue; for (i = 0; i < IEEE80211_WEP_NKID; i++) { const struct ieee80211_key *k = &vap->iv_nw_keys[i]; if (k->wk_flags & IEEE80211_KEY_DEVKEY) dev_key_set(vap, k); } } /* * Unicast keys. */ ieee80211_iterate_nodes(&ic->ic_sta, load_ucastkey, NULL); } /* * Set the default key index for WEP, or KEYIX_NONE for no default TX key. * * This should be done as part of a key update block (iv_key_update_begin / * iv_key_update_end.) */ void ieee80211_crypto_set_deftxkey(struct ieee80211vap *vap, ieee80211_keyix kid) { /* XXX TODO: assert we're in a key update block */ 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 @@ -1,299 +1,304 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _NET80211_IEEE80211_CRYPTO_H_ #define _NET80211_IEEE80211_CRYPTO_H_ /* * 802.11 protocol crypto-related definitions. */ #define IEEE80211_KEYBUF_SIZE 16 #define IEEE80211_MICBUF_SIZE (8+8) /* space for both tx+rx keys */ /* * Old WEP-style key. Deprecated. */ struct ieee80211_wepkey { u_int wk_len; /* key length in bytes */ uint8_t wk_key[IEEE80211_KEYBUF_SIZE]; }; struct ieee80211_rsnparms { uint8_t rsn_mcastcipher; /* mcast/group cipher */ uint8_t rsn_mcastkeylen; /* mcast key length */ uint8_t rsn_ucastcipher; /* selected unicast cipher */ uint8_t rsn_ucastkeylen; /* unicast key length */ uint8_t rsn_keymgmt; /* selected key mgmt algo */ uint16_t rsn_caps; /* capabilities */ }; struct ieee80211_cipher; /* * Crypto key state. There is sufficient room for all supported * ciphers (see below). The underlying ciphers are handled * separately through loadable cipher modules that register with * the generic crypto support. A key has a reference to an instance * of the cipher; any per-key state is hung off wk_private by the * cipher when it is attached. Ciphers are automatically called * to detach and cleanup any such state when the key is deleted. * * The generic crypto support handles encap/decap of cipher-related * frame contents for both hardware- and software-based implementations. * A key requiring software crypto support is automatically flagged and * the cipher is expected to honor this and do the necessary work. * Ciphers such as TKIP may also support mixed hardware/software * encrypt/decrypt and MIC processing. */ typedef uint16_t ieee80211_keyix; /* h/w key index */ struct ieee80211_key { uint8_t wk_keylen; /* key length in bytes */ uint8_t wk_pad; /* .. some drivers use this. Fix that. */ uint8_t wk_pad1[2]; uint32_t wk_flags; #define IEEE80211_KEY_XMIT 0x00000001 /* key used for xmit */ #define IEEE80211_KEY_RECV 0x00000002 /* key used for recv */ #define IEEE80211_KEY_GROUP 0x00000004 /* key used for WPA group operation */ #define IEEE80211_KEY_NOREPLAY 0x00000008 /* ignore replay failures */ #define IEEE80211_KEY_SWENCRYPT 0x00000010 /* host-based encrypt */ #define IEEE80211_KEY_SWDECRYPT 0x00000020 /* host-based decrypt */ #define IEEE80211_KEY_SWENMIC 0x00000040 /* host-based enmic */ #define IEEE80211_KEY_SWDEMIC 0x00000080 /* host-based demic */ #define IEEE80211_KEY_DEVKEY 0x00000100 /* device key request completed */ #define IEEE80211_KEY_CIPHER0 0x00001000 /* cipher-specific action 0 */ #define IEEE80211_KEY_CIPHER1 0x00002000 /* cipher-specific action 1 */ #define IEEE80211_KEY_NOIV 0x00004000 /* don't insert IV/MIC for !mgmt */ #define IEEE80211_KEY_NOIVMGT 0x00008000 /* don't insert IV/MIC for mgmt */ #define IEEE80211_KEY_NOMIC 0x00010000 /* don't insert MIC for !mgmt */ #define IEEE80211_KEY_NOMICMGT 0x00020000 /* don't insert MIC for mgmt */ ieee80211_keyix wk_keyix; /* h/w key index */ ieee80211_keyix wk_rxkeyix; /* optional h/w rx key index */ uint8_t wk_key[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE]; #define wk_txmic wk_key+IEEE80211_KEYBUF_SIZE+0 /* XXX can't () right */ #define wk_rxmic wk_key+IEEE80211_KEYBUF_SIZE+8 /* XXX can't () right */ /* key receive sequence counter */ uint64_t wk_keyrsc[IEEE80211_TID_SIZE]; uint64_t wk_keytsc; /* key transmit sequence counter */ const struct ieee80211_cipher *wk_cipher; void *wk_private; /* private cipher state */ uint8_t wk_macaddr[IEEE80211_ADDR_LEN]; }; #define IEEE80211_KEY_COMMON /* common flags passed in by apps */\ (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV | IEEE80211_KEY_GROUP | \ IEEE80211_KEY_NOREPLAY) #define IEEE80211_KEY_SWCRYPT \ (IEEE80211_KEY_SWENCRYPT | IEEE80211_KEY_SWDECRYPT) #define IEEE80211_KEY_SWMIC (IEEE80211_KEY_SWENMIC | IEEE80211_KEY_SWDEMIC) #define IEEE80211_KEY_DEVICE /* flags owned by device driver */\ (IEEE80211_KEY_DEVKEY|IEEE80211_KEY_CIPHER0|IEEE80211_KEY_CIPHER1| \ IEEE80211_KEY_SWCRYPT|IEEE80211_KEY_SWMIC|IEEE80211_KEY_NOIV | \ IEEE80211_KEY_NOIVMGT|IEEE80211_KEY_NOMIC|IEEE80211_KEY_NOMICMGT) #define IEEE80211_KEY_BITS \ "\20\1XMIT\2RECV\3GROUP\4NOREPLAY\5SWENCRYPT\6SWDECRYPT\7SWENMIC\10SWDEMIC" \ "\11DEVKEY\12CIPHER0\13CIPHER1\14NOIV\15NOIVMGT\16NOMIC\17NOMICMGT" #define IEEE80211_KEYIX_NONE ((ieee80211_keyix) -1) /* * NB: these values are ordered carefully; there are lots of * of implications in any reordering. Beware that 4 is used * only to indicate h/w TKIP MIC support in driver capabilities; * there is no separate cipher support (it's rolled into the * TKIP cipher support). */ #define IEEE80211_CIPHER_WEP 0 #define IEEE80211_CIPHER_TKIP 1 #define IEEE80211_CIPHER_AES_OCB 2 #define IEEE80211_CIPHER_AES_CCM 3 #define IEEE80211_CIPHER_TKIPMIC 4 /* TKIP MIC capability */ #define IEEE80211_CIPHER_CKIP 5 #define IEEE80211_CIPHER_NONE 6 /* pseudo value */ #define IEEE80211_CIPHER_AES_CCM_256 7 #define IEEE80211_CIPHER_BIP_CMAC_128 8 #define IEEE80211_CIPHER_BIP_CMAC_256 9 #define IEEE80211_CIPHER_BIP_GMAC_128 10 #define IEEE80211_CIPHER_BIP_GMAC_256 11 #define IEEE80211_CIPHER_AES_GCM_128 12 #define IEEE80211_CIPHER_AES_GCM_256 13 #define IEEE80211_CIPHER_LAST 13 #define IEEE80211_CIPHER_MAX (IEEE80211_CIPHER_LAST+1) /* capability bits in ic_cryptocaps/iv_cryptocaps */ #define IEEE80211_CRYPTO_WEP (1<wk_cipher == &ieee80211_cipher_none) void ieee80211_crypto_register(const struct ieee80211_cipher *); void ieee80211_crypto_unregister(const struct ieee80211_cipher *); int ieee80211_crypto_available(u_int cipher); int ieee80211_crypto_get_key_wepidx(const struct ieee80211vap *, const struct ieee80211_key *k); uint8_t ieee80211_crypto_get_keyid(struct ieee80211vap *vap, struct ieee80211_key *k); struct ieee80211_key *ieee80211_crypto_get_txkey(struct ieee80211_node *, struct mbuf *); struct ieee80211_key *ieee80211_crypto_encap(struct ieee80211_node *, struct mbuf *); int ieee80211_crypto_decap(struct ieee80211_node *, struct mbuf *, int, struct ieee80211_key **); int ieee80211_crypto_demic(struct ieee80211vap *vap, struct ieee80211_key *k, struct mbuf *, int); /** * @brief Add any pre-fragmentation MIC to an MSDU. * * This is called before 802.11 fragmentation. Crypto types that implement * a MIC/ICV check per MSDU will not implement this function. * * As an example, TKIP implements a Michael MIC check over the entire * unencrypted MSDU before fragmenting it into MPDUs and passing each * MPDU to be separately encrypted with their own MIC/ICV. * * Please see 802.11-2020 12.5.2.1.2 (TKIP cryptographic encapsulation) * for more information. * * @param vap the current VAP * @param k the current key * @param m the mbuf representing the MSDU * @param f set to 1 to force a MSDU MIC check, even if HW encrypted * @returns 0 if error / MIC encap failed, 1 if OK */ static __inline int ieee80211_crypto_enmic(struct ieee80211vap *vap, struct ieee80211_key *k, struct mbuf *m, int force) { const struct ieee80211_cipher *cip = k->wk_cipher; return (cip->ic_miclen > 0 ? cip->ic_enmic(k, m, force) : 1); } /* * Reset key state to an unused state. The crypto * key allocation mechanism insures other state (e.g. * key data) is properly setup before a key is used. */ static __inline void ieee80211_crypto_resetkey(struct ieee80211vap *vap, struct ieee80211_key *k, ieee80211_keyix ix) { k->wk_cipher = &ieee80211_cipher_none; k->wk_private = k->wk_cipher->ic_attach(vap, k); k->wk_keyix = k->wk_rxkeyix = ix; k->wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV; } /* * Crypt-related notification methods. */ void ieee80211_notify_replay_failure(struct ieee80211vap *, const struct ieee80211_frame *, const struct ieee80211_key *, 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 @@ -1,791 +1,807 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include /* * IEEE 802.11i AES-CCMP crypto support. * * Part of this module is derived from similar code in the Host * AP driver. The code is used with the consent of the author and * it's license is included below. */ #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #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; rijndael_ctx cc_aes; }; static void *ccmp_attach(struct ieee80211vap *, struct ieee80211_key *); static void ccmp_detach(struct ieee80211_key *); static int ccmp_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); static int ccmp_enmic(struct ieee80211_key *, struct mbuf *, int); static int ccmp_demic(struct ieee80211_key *, struct mbuf *, int); static const struct ieee80211_cipher ccmp = { .ic_name = "AES-CCM", .ic_cipher = IEEE80211_CIPHER_AES_CCM, .ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_EXTIVLEN, .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, .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 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); /* number of references from net80211 layer */ static int nrefs = 0; static void * ccmp_attach(struct ieee80211vap *vap, struct ieee80211_key *k) { struct ccmp_ctx *ctx; ctx = (struct ccmp_ctx *) IEEE80211_MALLOC(sizeof(struct ccmp_ctx), M_80211_CRYPTO, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); if (ctx == NULL) { vap->iv_stats.is_crypto_nomem++; return NULL; } ctx->cc_vap = vap; ctx->cc_ic = vap->iv_ic; nrefs++; /* NB: we assume caller locking */ return ctx; } static void ccmp_detach(struct ieee80211_key *k) { struct ccmp_ctx *ctx = k->wk_private; IEEE80211_FREE(ctx, M_80211_CRYPTO); KASSERT(nrefs > 0, ("imbalanced attach/detach")); 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; 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, keylen/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) { struct ccmp_ctx *ctx = k->wk_private; struct ieee80211vap *vap = ctx->cc_vap; uint8_t keyid; keyid = ieee80211_crypto_get_keyid(vap, k) << 6; k->wk_keytsc++; ivp[0] = k->wk_keytsc >> 0; /* PN0 */ ivp[1] = k->wk_keytsc >> 8; /* PN1 */ ivp[2] = 0; /* Reserved */ ivp[3] = keyid | IEEE80211_WEP_EXTIV; /* KeyID | ExtID */ ivp[4] = k->wk_keytsc >> 16; /* PN2 */ ivp[5] = k->wk_keytsc >> 24; /* PN3 */ ivp[6] = k->wk_keytsc >> 32; /* PN4 */ ivp[7] = k->wk_keytsc >> 40; /* PN5 */ } /* * Add privacy headers appropriate for the specified key. */ static int ccmp_encap(struct ieee80211_key *k, struct mbuf *m) { const struct ieee80211_frame *wh; struct ccmp_ctx *ctx = k->wk_private; struct ieee80211com *ic = ctx->cc_ic; uint8_t *ivp; int hdrlen; int is_mgmt; hdrlen = ieee80211_hdrspace(ic, mtod(m, void *)); wh = mtod(m, const struct ieee80211_frame *); is_mgmt = IEEE80211_IS_MGMT(wh); /* * Check to see if we need to insert IV/MIC. * * Some offload devices don't require the IV to be inserted * as part of the hardware encryption. */ if (is_mgmt && (k->wk_flags & IEEE80211_KEY_NOIVMGT)) return 1; if ((! is_mgmt) && (k->wk_flags & IEEE80211_KEY_NOIV)) return 1; /* * Copy down 802.11 header and add the IV, KeyID, and ExtIV. */ M_PREPEND(m, ccmp_get_header_len(k), IEEE80211_M_NOWAIT); if (m == NULL) return 0; ivp = mtod(m, uint8_t *); ovbcopy(ivp + ccmp_get_header_len(k), ivp, hdrlen); ivp += hdrlen; ccmp_setiv(k, ivp); /* * Finally, do software encrypt if needed. */ if ((k->wk_flags & IEEE80211_KEY_SWENCRYPT) && !ccmp_encrypt(k, m, hdrlen)) return 0; return 1; } /* * Add MIC to the frame as needed. */ static int ccmp_enmic(struct ieee80211_key *k, struct mbuf *m, int force) { return 1; } static __inline uint64_t READ_6(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5) { uint32_t iv32 = (b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24); uint16_t iv16 = (b4 << 0) | (b5 << 8); return (((uint64_t)iv16) << 32) | iv32; } /* * Validate and strip privacy headers (and trailer) for a * received frame. The specified key should be correct but * is also verified. */ static int ccmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen) { const struct ieee80211_rx_stats *rxs; struct ccmp_ctx *ctx = k->wk_private; struct ieee80211vap *vap = ctx->cc_vap; struct ieee80211_frame *wh; uint8_t *ivp, tid; uint64_t pn; bool noreplaycheck; rxs = ieee80211_get_rx_params_ptr(m); if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP) != 0) goto finish; /* * Header should have extended IV and sequence number; * verify the former and validate the latter. */ wh = mtod(m, struct ieee80211_frame *); ivp = mtod(m, uint8_t *) + hdrlen; if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) { /* * No extended IV; discard frame. */ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, "%s", "missing ExtIV for AES-CCM cipher"); vap->iv_stats.is_rx_ccmpformat++; return 0; } tid = ieee80211_gettid(wh); pn = READ_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]); noreplaycheck = (k->wk_flags & IEEE80211_KEY_NOREPLAY) != 0; noreplaycheck |= (rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_PN_VALIDATED) != 0; if (pn <= k->wk_keyrsc[tid] && !noreplaycheck) { /* * Replay violation. */ ieee80211_notify_replay_failure(vap, wh, k, pn, tid); vap->iv_stats.is_rx_ccmpreplay++; return 0; } /* * Check if the device handled the decrypt in hardware. * If so we just strip the header; otherwise we need to * handle the decrypt in software. Note that for the * latter we leave the header in place for use in the * decryption work. */ if ((k->wk_flags & IEEE80211_KEY_SWDECRYPT) && !ccmp_decrypt(k, pn, m, hdrlen)) return 0; 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_get_header_len(k), hdrlen); m_adj(m, ccmp_get_header_len(k)); } if ((rxs == NULL) || (rxs->c_pktflags & IEEE80211_RX_F_MIC_STRIP) == 0) m_adj(m, -ccmp_get_trailer_len(k)); /* * Ok to update rsc now. */ if ((rxs == NULL) || (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP) == 0) { /* * Do not go backwards in the IEEE80211_KEY_NOREPLAY cases * or in case hardware has checked but frames are arriving * reordered (e.g., LinuxKPI drivers doing RSS which we are * not prepared for at all). */ if (pn > k->wk_keyrsc[tid]) k->wk_keyrsc[tid] = pn; } return 1; } /* * Verify and strip MIC from the frame. */ static int ccmp_demic(struct ieee80211_key *k, struct mbuf *m, int force) { return 1; } static __inline void xor_block(uint8_t *b, const uint8_t *a, size_t len) { int i; for (i = 0; i < len; i++) 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 * * Copyright (c) 2003-2004, Jouni Malinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. See README and COPYING for * more details. * * Alternatively, this software may be distributed under the terms of BSD * license. */ static void ccmp_init_blocks(rijndael_ctx *ctx, struct ieee80211_frame *wh, 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 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] = 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; b0[10] = pn >> 24; b0[11] = pn >> 16; b0[12] = pn >> 8; b0[13] = pn >> 0; 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); xor_block(auth, aad, AES_BLOCK_LEN); rijndael_encrypt(ctx, auth, auth); xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN); rijndael_encrypt(ctx, auth, auth); 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 { \ /* Authentication */ \ xor_block(_b, _pos, _len); \ rijndael_encrypt(&ctx->cc_aes, _b, _b); \ /* Encryption, with counter */ \ _b0[14] = (_i >> 8) & 0xff; \ _b0[15] = _i & 0xff; \ rijndael_encrypt(&ctx->cc_aes, _b0, _e); \ xor_block(_pos, _e, _len); \ } while (0) static int ccmp_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) { struct ccmp_ctx *ctx = key->wk_private; struct ieee80211_frame *wh; struct mbuf *m = m0; int data_len, i, space; uint8_t aad[2 * AES_BLOCK_LEN], b0[AES_BLOCK_LEN], b[AES_BLOCK_LEN], e[AES_BLOCK_LEN], s0[AES_BLOCK_LEN]; uint8_t *pos; ctx->cc_vap->iv_stats.is_crypto_ccmp++; wh = mtod(m, struct ieee80211_frame *); 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_get_header_len(key); /* NB: assumes header is entirely in first mbuf */ space = m->m_len - (hdrlen + ccmp_get_header_len(key)); for (;;) { if (space > data_len) space = data_len; /* * Do full blocks. */ while (space >= AES_BLOCK_LEN) { CCMP_ENCRYPT(i, b, b0, pos, e, AES_BLOCK_LEN); pos += AES_BLOCK_LEN, space -= AES_BLOCK_LEN; data_len -= AES_BLOCK_LEN; i++; } if (data_len <= 0) /* no more data */ break; m = m->m_next; if (m == NULL) { /* last buffer */ if (space != 0) { /* * Short last block. */ CCMP_ENCRYPT(i, b, b0, pos, e, space); } break; } if (space != 0) { uint8_t *pos_next; int space_next; int len, dl, sp; struct mbuf *n; /* * Block straddles one or more mbufs, gather data * into the block buffer b, apply the cipher, then * scatter the results back into the mbuf chain. * The buffer will automatically get space bytes * of data at offset 0 copied in+out by the * CCMP_ENCRYPT request so we must take care of * the remaining data. */ n = m; dl = data_len; sp = space; for (;;) { pos_next = mtod(n, uint8_t *); len = min(dl, AES_BLOCK_LEN); space_next = len > sp ? len - sp : 0; if (n->m_len >= space_next) { /* * This mbuf has enough data; just grab * what we need and stop. */ xor_block(b+sp, pos_next, space_next); break; } /* * This mbuf's contents are insufficient, * take 'em all and prepare to advance to * the next mbuf. */ xor_block(b+sp, pos_next, n->m_len); sp += n->m_len, dl -= n->m_len; n = n->m_next; if (n == NULL) break; } CCMP_ENCRYPT(i, b, b0, pos, e, space); /* NB: just like above, but scatter data to mbufs */ dl = data_len; sp = space; for (;;) { pos_next = mtod(m, uint8_t *); len = min(dl, AES_BLOCK_LEN); space_next = len > sp ? len - sp : 0; if (m->m_len >= space_next) { xor_block(pos_next, e+sp, space_next); break; } xor_block(pos_next, e+sp, m->m_len); sp += m->m_len, dl -= m->m_len; m = m->m_next; if (m == NULL) goto done; } /* * Do bookkeeping. m now points to the last mbuf * we grabbed data from. We know we consumed a * full block of data as otherwise we'd have hit * the end of the mbuf chain, so deduct from data_len. * Otherwise advance the block number (i) and setup * pos+space to reflect contents of the new mbuf. */ data_len -= AES_BLOCK_LEN; i++; pos = pos_next + space_next; space = m->m_len - space_next; } else { /* * Setup for next buffer. */ pos = mtod(m, uint8_t *); space = m->m_len; } } done: /* tack on MIC */ xor_block(b, s0, ccmp_get_trailer_len(key)); return m_append(m0, ccmp_get_trailer_len(key), b); } #undef CCMP_ENCRYPT #define CCMP_DECRYPT(_i, _b, _b0, _pos, _a, _len) do { \ /* Decrypt, with counter */ \ _b0[14] = (_i >> 8) & 0xff; \ _b0[15] = _i & 0xff; \ rijndael_encrypt(&ctx->cc_aes, _b0, _b); \ xor_block(_pos, _b, _len); \ /* Authentication */ \ xor_block(_a, _pos, _len); \ rijndael_encrypt(&ctx->cc_aes, _a, _a); \ } while (0) static int ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, int hdrlen) { const struct ieee80211_rx_stats *rxs; struct ccmp_ctx *ctx = key->wk_private; struct ieee80211vap *vap = ctx->cc_vap; struct ieee80211_frame *wh; uint8_t aad[2 * AES_BLOCK_LEN]; uint8_t b0[AES_BLOCK_LEN], b[AES_BLOCK_LEN], a[AES_BLOCK_LEN]; uint8_t mic[AES_BLOCK_LEN]; size_t data_len; int i; uint8_t *pos; u_int space; rxs = ieee80211_get_rx_params_ptr(m); if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_DECRYPTED) != 0) return (1); ctx->cc_vap->iv_stats.is_crypto_ccmp++; wh = mtod(m, struct ieee80211_frame *); 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_get_header_len(key); space = m->m_len - (hdrlen + ccmp_get_header_len(key)); for (;;) { if (space > data_len) space = data_len; while (space >= AES_BLOCK_LEN) { CCMP_DECRYPT(i, b, b0, pos, a, AES_BLOCK_LEN); pos += AES_BLOCK_LEN, space -= AES_BLOCK_LEN; data_len -= AES_BLOCK_LEN; i++; } if (data_len <= 0) /* no more data */ break; m = m->m_next; if (m == NULL) { /* last buffer */ if (space != 0) /* short last block */ CCMP_DECRYPT(i, b, b0, pos, a, space); break; } if (space != 0) { uint8_t *pos_next; u_int space_next; u_int len; /* * Block straddles buffers, split references. We * do not handle splits that require >2 buffers * since rx'd frames are never badly fragmented * because drivers typically recv in clusters. */ pos_next = mtod(m, uint8_t *); len = min(data_len, AES_BLOCK_LEN); space_next = len > space ? len - space : 0; KASSERT(m->m_len >= space_next, ("not enough data in following buffer, " "m_len %u need %u\n", m->m_len, space_next)); xor_block(b+space, pos_next, space_next); CCMP_DECRYPT(i, b, b0, pos, a, space); xor_block(pos_next, b+space, space_next); data_len -= len; i++; pos = pos_next + space_next; space = m->m_len - space_next; } else { /* * Setup for next buffer. */ pos = mtod(m, uint8_t *); space = m->m_len; } } /* * If the MIC was stripped by HW/driver we are done. */ if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_MIC_STRIP) != 0) return (1); 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++; return 0; } return 1; } #undef CCMP_DECRYPT /* * Module glue. */ IEEE80211_CRYPTO_MODULE(ccmp, 1); IEEE80211_CRYPTO_MODULE_ADD(ccmp_256); 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 @@ -1,682 +1,598 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * All rights reserved. * Copyright (c) 2025 Adrian Chadd . * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * IEEE 802.11 AES-GCMP crypto support. * * The AES-GCM crypto routines in sys/net80211/ieee80211_crypto_gcm.[ch] * are derived from similar code in hostapd 2.11 (src/crypto/aes-gcm.c). * The code is used with the consent of the author and its licence is * included in the above source files. */ #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define AES_BLOCK_LEN 16 /* * Note: GCMP_MIC_LEN defined in ieee80211_crypto_gcm.h, as it is also * used by the AES-GCM routines for sizing the S and T hashes which are * used by GCMP as the MIC. */ #define GCMP_PN_LEN 6 #define GCMP_IV_LEN 12 struct gcmp_ctx { struct ieee80211vap *cc_vap; /* for diagnostics+statistics */ struct ieee80211com *cc_ic; rijndael_ctx cc_aes; }; static void *gcmp_attach(struct ieee80211vap *, struct ieee80211_key *); static void gcmp_detach(struct ieee80211_key *); static int gcmp_setkey(struct ieee80211_key *); static void gcmp_setiv(struct ieee80211_key *, uint8_t *); static int gcmp_encap(struct ieee80211_key *, struct mbuf *); static int gcmp_decap(struct ieee80211_key *, struct mbuf *, int); static int gcmp_enmic(struct ieee80211_key *, struct mbuf *, int); static int gcmp_demic(struct ieee80211_key *, struct mbuf *, int); static const struct ieee80211_cipher gcmp = { .ic_name = "AES-GCMP", .ic_cipher = IEEE80211_CIPHER_AES_GCM_128, .ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_EXTIVLEN, .ic_trailer = GCMP_MIC_LEN, .ic_miclen = 0, .ic_attach = gcmp_attach, .ic_detach = gcmp_detach, .ic_setkey = gcmp_setkey, .ic_setiv = gcmp_setiv, .ic_encap = gcmp_encap, .ic_decap = gcmp_decap, .ic_enmic = gcmp_enmic, .ic_demic = gcmp_demic, }; static const struct ieee80211_cipher gcmp_256 = { .ic_name = "AES-GCMP-256", .ic_cipher = IEEE80211_CIPHER_AES_GCM_256, .ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_EXTIVLEN, .ic_trailer = GCMP_MIC_LEN, .ic_miclen = 0, .ic_attach = gcmp_attach, .ic_detach = gcmp_detach, .ic_setkey = gcmp_setkey, .ic_setiv = gcmp_setiv, .ic_encap = gcmp_encap, .ic_decap = gcmp_decap, .ic_enmic = gcmp_enmic, .ic_demic = gcmp_demic, }; static int gcmp_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen); static int gcmp_decrypt(struct ieee80211_key *, u_int64_t pn, struct mbuf *, int hdrlen); /* number of references from net80211 layer */ static int nrefs = 0; static void * gcmp_attach(struct ieee80211vap *vap, struct ieee80211_key *k) { struct gcmp_ctx *ctx; ctx = (struct gcmp_ctx *) IEEE80211_MALLOC(sizeof(struct gcmp_ctx), M_80211_CRYPTO, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); if (ctx == NULL) { vap->iv_stats.is_crypto_nomem++; return (NULL); } ctx->cc_vap = vap; ctx->cc_ic = vap->iv_ic; nrefs++; /* NB: we assume caller locking */ return (ctx); } static void gcmp_detach(struct ieee80211_key *k) { struct gcmp_ctx *ctx = k->wk_private; IEEE80211_FREE(ctx, M_80211_CRYPTO); KASSERT(nrefs > 0, ("imbalanced attach/detach")); nrefs--; /* NB: we assume caller locking */ } static int gcmp_get_trailer_len(struct ieee80211_key *k) { return (k->wk_cipher->ic_trailer); } static int gcmp_get_header_len(struct ieee80211_key *k) { return (k->wk_cipher->ic_header); } static int gcmp_setkey(struct ieee80211_key *k) { uint32_t keylen; struct gcmp_ctx *ctx = k->wk_private; switch (k->wk_cipher->ic_cipher) { case IEEE80211_CIPHER_AES_GCM_128: keylen = 128; break; case IEEE80211_CIPHER_AES_GCM_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, keylen/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 gcmp_setiv(struct ieee80211_key *k, uint8_t *ivp) { struct gcmp_ctx *ctx = k->wk_private; struct ieee80211vap *vap = ctx->cc_vap; uint8_t keyid; keyid = ieee80211_crypto_get_keyid(vap, k) << 6; k->wk_keytsc++; ivp[0] = k->wk_keytsc >> 0; /* PN0 */ ivp[1] = k->wk_keytsc >> 8; /* PN1 */ ivp[2] = 0; /* Reserved */ ivp[3] = keyid | IEEE80211_WEP_EXTIV; /* KeyID | ExtID */ ivp[4] = k->wk_keytsc >> 16; /* PN2 */ ivp[5] = k->wk_keytsc >> 24; /* PN3 */ ivp[6] = k->wk_keytsc >> 32; /* PN4 */ ivp[7] = k->wk_keytsc >> 40; /* PN5 */ } /* * Add privacy headers appropriate for the specified key. */ static int gcmp_encap(struct ieee80211_key *k, struct mbuf *m) { const struct ieee80211_frame *wh; struct gcmp_ctx *ctx = k->wk_private; struct ieee80211com *ic = ctx->cc_ic; uint8_t *ivp; int hdrlen; int is_mgmt; hdrlen = ieee80211_hdrspace(ic, mtod(m, void *)); wh = mtod(m, const struct ieee80211_frame *); is_mgmt = IEEE80211_IS_MGMT(wh); /* * Check to see if we need to insert IV/MIC. * * Some offload devices don't require the IV to be inserted * as part of the hardware encryption. */ if (is_mgmt && (k->wk_flags & IEEE80211_KEY_NOIVMGT)) return (1); if (!is_mgmt && (k->wk_flags & IEEE80211_KEY_NOIV)) return (1); /* * Copy down 802.11 header and add the IV, KeyID, and ExtIV. */ M_PREPEND(m, gcmp_get_header_len(k), IEEE80211_M_NOWAIT); if (m == NULL) return (0); ivp = mtod(m, uint8_t *); ovbcopy(ivp + gcmp_get_header_len(k), ivp, hdrlen); ivp += hdrlen; gcmp_setiv(k, ivp); /* * Finally, do software encrypt if needed. */ if ((k->wk_flags & IEEE80211_KEY_SWENCRYPT) && !gcmp_encrypt(k, m, hdrlen)) return (0); return (1); } /* * Add MIC to the frame as needed. */ static int gcmp_enmic(struct ieee80211_key *k, struct mbuf *m, int force) { return (1); } static __inline uint64_t READ_6(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5) { uint32_t iv32 = (b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24); uint16_t iv16 = (b4 << 0) | (b5 << 8); return ((((uint64_t)iv16) << 32) | iv32); } /* * Validate and strip privacy headers (and trailer) for a * received frame. The specified key should be correct but * is also verified. */ static int gcmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen) { const struct ieee80211_rx_stats *rxs; struct gcmp_ctx *ctx = k->wk_private; struct ieee80211vap *vap = ctx->cc_vap; struct ieee80211_frame *wh; uint8_t *ivp, tid; uint64_t pn; bool noreplaycheck; rxs = ieee80211_get_rx_params_ptr(m); if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP) != 0) goto finish; /* * Header should have extended IV and sequence number; * verify the former and validate the latter. */ wh = mtod(m, struct ieee80211_frame *); ivp = mtod(m, uint8_t *) + hdrlen; if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) { /* * No extended IV; discard frame. */ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, "%s", "missing ExtIV for AES-GCM cipher"); vap->iv_stats.is_rx_gcmpformat++; return (0); } tid = ieee80211_gettid(wh); pn = READ_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]); noreplaycheck = (k->wk_flags & IEEE80211_KEY_NOREPLAY) != 0; noreplaycheck |= (rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_PN_VALIDATED) != 0; if (pn <= k->wk_keyrsc[tid] && !noreplaycheck) { /* * Replay violation. */ ieee80211_notify_replay_failure(vap, wh, k, pn, tid); vap->iv_stats.is_rx_gcmpreplay++; return (0); } /* * Check if the device handled the decrypt in hardware. * If so we just strip the header; otherwise we need to * handle the decrypt in software. Note that for the * latter we leave the header in place for use in the * decryption work. */ if ((k->wk_flags & IEEE80211_KEY_SWDECRYPT) && !gcmp_decrypt(k, pn, m, hdrlen)) return (0); finish: /* * Copy up 802.11 header and strip crypto bits. */ if ((rxs == NULL) || (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP) == 0) { ovbcopy(mtod(m, void *), mtod(m, uint8_t *) + gcmp_get_header_len(k), hdrlen); m_adj(m, gcmp_get_header_len(k)); } if ((rxs == NULL) || (rxs->c_pktflags & IEEE80211_RX_F_MIC_STRIP) == 0) m_adj(m, -gcmp_get_trailer_len(k)); /* * Ok to update rsc now. */ if ((rxs == NULL) || (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP) == 0) { /* * Do not go backwards in the IEEE80211_KEY_NOREPLAY cases * or in case hardware has checked but frames are arriving * reordered (e.g., LinuxKPI drivers doing RSS which we are * not prepared for at all). */ if (pn > k->wk_keyrsc[tid]) k->wk_keyrsc[tid] = pn; } return (1); } /* * Verify and strip MIC from the frame. */ static int 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. */ static int gcmp_init_iv(uint8_t *iv, const struct ieee80211_frame *wh, u_int64_t pn) { uint8_t j_pn[GCMP_PN_LEN]; /* Construct the pn buffer */ j_pn[0] = pn >> 40; j_pn[1] = pn >> 32; j_pn[2] = pn >> 24; j_pn[3] = pn >> 16; j_pn[4] = pn >> 8; j_pn[5] = pn >> 0; memcpy(iv, wh->i_addr2, IEEE80211_ADDR_LEN); memcpy(iv + IEEE80211_ADDR_LEN, j_pn, GCMP_PN_LEN); return (GCMP_IV_LEN); /* 96 bits */ } /* * @brief Encrypt an mbuf. * * This uses a temporary memory buffer to encrypt; the * current AES-GCM code expects things in a contiguous buffer * and this avoids the need of breaking out the GCTR and * GHASH routines into using mbuf iterators. * * @param key ieee80211_key to use * @param mbuf 802.11 frame to encrypt * @param hdrlen the length of the 802.11 header, including any padding * @returns 0 if error, > 0 if OK. */ static int gcmp_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) { struct gcmp_ctx *ctx = key->wk_private; struct ieee80211_frame *wh; struct mbuf *m = m0; int data_len, aad_len, iv_len, ret; uint8_t aad[GCM_AAD_LEN]; uint8_t T[GCMP_MIC_LEN]; uint8_t iv[GCMP_IV_LEN]; uint8_t *p_pktbuf = NULL; uint8_t *c_pktbuf = NULL; wh = mtod(m, struct ieee80211_frame *); data_len = m->m_pkthdr.len - (hdrlen + gcmp_get_header_len(key)); ctx->cc_vap->iv_stats.is_crypto_gcmp++; p_pktbuf = IEEE80211_MALLOC(data_len, M_TEMP, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); if (p_pktbuf == NULL) { IEEE80211_NOTE_MAC(ctx->cc_vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, "%s", "AES-GCM encrypt failed; couldn't allocate buffer"); ctx->cc_vap->iv_stats.is_crypto_gcmp_nomem++; return (0); } c_pktbuf = IEEE80211_MALLOC(data_len, M_TEMP, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); if (c_pktbuf == NULL) { IEEE80211_NOTE_MAC(ctx->cc_vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, "%s", "AES-GCM encrypt failed; couldn't allocate buffer"); ctx->cc_vap->iv_stats.is_crypto_gcmp_nomem++; IEEE80211_FREE(p_pktbuf, M_TEMP); return (0); } /* 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 */ iv_len = gcmp_init_iv(iv, wh, key->wk_keytsc); /* Copy mbuf data part into plaintext pktbuf */ m_copydata(m0, hdrlen + gcmp_get_header_len(key), data_len, p_pktbuf); /* Run encrypt */ /* * Note: aad + 2 to skip over the 2 byte length populated * at the beginning, since it's based on the AAD code in CCMP. */ ieee80211_crypto_aes_gcm_ae(&ctx->cc_aes, iv, iv_len, p_pktbuf, data_len, aad + 2, aad_len, c_pktbuf, T); /* Copy data back over mbuf */ m_copyback(m0, hdrlen + gcmp_get_header_len(key), data_len, c_pktbuf); /* Append MIC */ ret = m_append(m0, gcmp_get_trailer_len(key), T); if (ret == 0) { IEEE80211_NOTE_MAC(ctx->cc_vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, "%s", "AES-GCM encrypt failed; couldn't append T"); ctx->cc_vap->iv_stats.is_crypto_gcmp_nospc++; } IEEE80211_FREE(p_pktbuf, M_TEMP); IEEE80211_FREE(c_pktbuf, M_TEMP); return (ret); } /* * @brief Decrypt an mbuf. * * This uses a temporary memory buffer to decrypt; the * current AES-GCM code expects things in a contiguous buffer * and this avoids the need of breaking out the GCTR and * GHASH routines into using mbuf iterators. * * @param key ieee80211_key to use * @param mbuf 802.11 frame to decrypt * @param hdrlen the length of the 802.11 header, including any padding * @returns 0 if error, > 0 if OK. */ static int gcmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, int hdrlen) { const struct ieee80211_rx_stats *rxs; struct gcmp_ctx *ctx = key->wk_private; struct ieee80211_frame *wh; int data_len, aad_len, iv_len, ret; uint8_t aad[GCM_AAD_LEN]; uint8_t T[GCMP_MIC_LEN]; uint8_t iv[GCMP_IV_LEN]; uint8_t *p_pktbuf = NULL; uint8_t *c_pktbuf = NULL; rxs = ieee80211_get_rx_params_ptr(m); if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_DECRYPTED) != 0) return (1); wh = mtod(m, struct ieee80211_frame *); /* Data length doesn't include the MIC at the end */ data_len = m->m_pkthdr.len - (hdrlen + gcmp_get_header_len(key) + GCMP_MIC_LEN); ctx->cc_vap->iv_stats.is_crypto_gcmp++; p_pktbuf = IEEE80211_MALLOC(data_len, M_TEMP, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); if (p_pktbuf == NULL) { ctx->cc_vap->iv_stats.is_crypto_gcmp_nomem++; return (0); } c_pktbuf = IEEE80211_MALLOC(data_len, M_TEMP, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); if (c_pktbuf == NULL) { ctx->cc_vap->iv_stats.is_crypto_gcmp_nomem++; IEEE80211_FREE(p_pktbuf, M_TEMP); return (0); } /* 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); /* Copy mbuf into ciphertext pktbuf */ m_copydata(m, hdrlen + gcmp_get_header_len(key), data_len, c_pktbuf); /* Copy the MIC into the tag buffer */ m_copydata(m, hdrlen + gcmp_get_header_len(key) + data_len, GCMP_MIC_LEN, T); /* Run decrypt */ /* * Note: aad + 2 to skip over the 2 byte length populated * at the beginning, since it's based on the AAD code in CCMP. */ ret = ieee80211_crypto_aes_gcm_ad(&ctx->cc_aes, iv, iv_len, c_pktbuf, data_len, aad + 2, aad_len, T, p_pktbuf); /* If the MIC was stripped by HW/driver we are done. */ if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_MIC_STRIP) != 0) goto skip_ok; if (ret != 0) { /* Decrypt failure */ ctx->cc_vap->iv_stats.is_rx_gcmpmic++; IEEE80211_NOTE_MAC(ctx->cc_vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, "%s", "AES-GCM decrypt failed; MIC mismatch"); IEEE80211_FREE(p_pktbuf, M_TEMP); IEEE80211_FREE(c_pktbuf, M_TEMP); return (0); } skip_ok: /* Copy data back over mbuf */ m_copyback(m, hdrlen + gcmp_get_header_len(key), data_len, p_pktbuf); IEEE80211_FREE(p_pktbuf, M_TEMP); IEEE80211_FREE(c_pktbuf, M_TEMP); return (1); } /* * Module glue. */ IEEE80211_CRYPTO_MODULE(gcmp, 1); IEEE80211_CRYPTO_MODULE_ADD(gcmp_256);