Path: blob/main/sys/net80211/ieee80211_crypto_gcmp.c
39475 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2002-2008 Sam Leffler, Errno Consulting4* All rights reserved.5* Copyright (c) 2025 Adrian Chadd <[email protected]>.6*7* Redistribution and use in source and binary forms, with or without8* modification, are permitted provided that the following conditions9* are met:10* 1. Redistributions of source code must retain the above copyright11* notice, this list of conditions and the following disclaimer.12* 2. Redistributions in binary form must reproduce the above copyright13* notice, this list of conditions and the following disclaimer in the14* documentation and/or other materials provided with the distribution.15*16* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR17* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES18* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.19* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,20* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT21* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,22* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY23* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT24* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF25* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.26*/2728/*29* IEEE 802.11 AES-GCMP crypto support.30*31* The AES-GCM crypto routines in sys/net80211/ieee80211_crypto_gcm.[ch]32* are derived from similar code in hostapd 2.11 (src/crypto/aes-gcm.c).33* The code is used with the consent of the author and its licence is34* included in the above source files.35*/36#include "opt_wlan.h"3738#include <sys/param.h>39#include <sys/systm.h>40#include <sys/mbuf.h>41#include <sys/malloc.h>42#include <sys/kernel.h>43#include <sys/module.h>4445#include <sys/socket.h>4647#include <net/if.h>48#include <net/if_media.h>49#include <net/ethernet.h>5051#include <net80211/ieee80211_var.h>52#include <net80211/ieee80211_crypto_gcm.h>5354#include <crypto/rijndael/rijndael.h>5556#define AES_BLOCK_LEN 165758/*59* Note: GCMP_MIC_LEN defined in ieee80211_crypto_gcm.h, as it is also60* used by the AES-GCM routines for sizing the S and T hashes which are61* used by GCMP as the MIC.62*/63#define GCMP_PN_LEN 664#define GCMP_IV_LEN 126566struct gcmp_ctx {67struct ieee80211vap *cc_vap; /* for diagnostics+statistics */68struct ieee80211com *cc_ic;69rijndael_ctx cc_aes;70};7172static void *gcmp_attach(struct ieee80211vap *, struct ieee80211_key *);73static void gcmp_detach(struct ieee80211_key *);74static int gcmp_setkey(struct ieee80211_key *);75static void gcmp_setiv(struct ieee80211_key *, uint8_t *);76static int gcmp_encap(struct ieee80211_key *, struct mbuf *);77static int gcmp_decap(struct ieee80211_key *, struct mbuf *, int);78static int gcmp_enmic(struct ieee80211_key *, struct mbuf *, int);79static int gcmp_demic(struct ieee80211_key *, struct mbuf *, int);8081static const struct ieee80211_cipher gcmp = {82.ic_name = "AES-GCMP",83.ic_cipher = IEEE80211_CIPHER_AES_GCM_128,84.ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +85IEEE80211_WEP_EXTIVLEN,86.ic_trailer = GCMP_MIC_LEN,87.ic_miclen = 0,88.ic_attach = gcmp_attach,89.ic_detach = gcmp_detach,90.ic_setkey = gcmp_setkey,91.ic_setiv = gcmp_setiv,92.ic_encap = gcmp_encap,93.ic_decap = gcmp_decap,94.ic_enmic = gcmp_enmic,95.ic_demic = gcmp_demic,96};9798static const struct ieee80211_cipher gcmp_256 = {99.ic_name = "AES-GCMP-256",100.ic_cipher = IEEE80211_CIPHER_AES_GCM_256,101.ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +102IEEE80211_WEP_EXTIVLEN,103.ic_trailer = GCMP_MIC_LEN,104.ic_miclen = 0,105.ic_attach = gcmp_attach,106.ic_detach = gcmp_detach,107.ic_setkey = gcmp_setkey,108.ic_setiv = gcmp_setiv,109.ic_encap = gcmp_encap,110.ic_decap = gcmp_decap,111.ic_enmic = gcmp_enmic,112.ic_demic = gcmp_demic,113};114115116static int gcmp_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);117static int gcmp_decrypt(struct ieee80211_key *, u_int64_t pn,118struct mbuf *, int hdrlen);119120/* number of references from net80211 layer */121static int nrefs = 0;122123static void *124gcmp_attach(struct ieee80211vap *vap, struct ieee80211_key *k)125{126struct gcmp_ctx *ctx;127128ctx = (struct gcmp_ctx *) IEEE80211_MALLOC(sizeof(struct gcmp_ctx),129M_80211_CRYPTO, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);130if (ctx == NULL) {131vap->iv_stats.is_crypto_nomem++;132return (NULL);133}134ctx->cc_vap = vap;135ctx->cc_ic = vap->iv_ic;136nrefs++; /* NB: we assume caller locking */137return (ctx);138}139140static void141gcmp_detach(struct ieee80211_key *k)142{143struct gcmp_ctx *ctx = k->wk_private;144145IEEE80211_FREE(ctx, M_80211_CRYPTO);146KASSERT(nrefs > 0, ("imbalanced attach/detach"));147nrefs--; /* NB: we assume caller locking */148}149150static int151gcmp_get_trailer_len(struct ieee80211_key *k)152{153return (k->wk_cipher->ic_trailer);154}155156static int157gcmp_get_header_len(struct ieee80211_key *k)158{159return (k->wk_cipher->ic_header);160}161162static int163gcmp_setkey(struct ieee80211_key *k)164{165uint32_t keylen;166167struct gcmp_ctx *ctx = k->wk_private;168169switch (k->wk_cipher->ic_cipher) {170case IEEE80211_CIPHER_AES_GCM_128:171keylen = 128;172break;173case IEEE80211_CIPHER_AES_GCM_256:174keylen = 256;175break;176default:177IEEE80211_DPRINTF(ctx->cc_vap, IEEE80211_MSG_CRYPTO,178"%s: Unexpected cipher (%u)",179__func__, k->wk_cipher->ic_cipher);180return (0);181}182183if (k->wk_keylen != (keylen/NBBY)) {184IEEE80211_DPRINTF(ctx->cc_vap, IEEE80211_MSG_CRYPTO,185"%s: Invalid key length %u, expecting %u\n",186__func__, k->wk_keylen, keylen/NBBY);187return (0);188}189if (k->wk_flags & IEEE80211_KEY_SWENCRYPT)190rijndael_set_key(&ctx->cc_aes, k->wk_key, k->wk_keylen*NBBY);191return (1);192}193194static void195gcmp_setiv(struct ieee80211_key *k, uint8_t *ivp)196{197struct gcmp_ctx *ctx = k->wk_private;198struct ieee80211vap *vap = ctx->cc_vap;199uint8_t keyid;200201keyid = ieee80211_crypto_get_keyid(vap, k) << 6;202203k->wk_keytsc++;204ivp[0] = k->wk_keytsc >> 0; /* PN0 */205ivp[1] = k->wk_keytsc >> 8; /* PN1 */206ivp[2] = 0; /* Reserved */207ivp[3] = keyid | IEEE80211_WEP_EXTIV; /* KeyID | ExtID */208ivp[4] = k->wk_keytsc >> 16; /* PN2 */209ivp[5] = k->wk_keytsc >> 24; /* PN3 */210ivp[6] = k->wk_keytsc >> 32; /* PN4 */211ivp[7] = k->wk_keytsc >> 40; /* PN5 */212}213214/*215* Add privacy headers appropriate for the specified key.216*/217static int218gcmp_encap(struct ieee80211_key *k, struct mbuf *m)219{220const struct ieee80211_frame *wh;221struct gcmp_ctx *ctx = k->wk_private;222struct ieee80211com *ic = ctx->cc_ic;223uint8_t *ivp;224int hdrlen;225int is_mgmt;226227hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));228wh = mtod(m, const struct ieee80211_frame *);229is_mgmt = IEEE80211_IS_MGMT(wh);230231/*232* Check to see if we need to insert IV/MIC.233*234* Some offload devices don't require the IV to be inserted235* as part of the hardware encryption.236*/237if (is_mgmt && (k->wk_flags & IEEE80211_KEY_NOIVMGT))238return (1);239if (!is_mgmt && (k->wk_flags & IEEE80211_KEY_NOIV))240return (1);241242/*243* Copy down 802.11 header and add the IV, KeyID, and ExtIV.244*/245M_PREPEND(m, gcmp_get_header_len(k), IEEE80211_M_NOWAIT);246if (m == NULL)247return (0);248ivp = mtod(m, uint8_t *);249ovbcopy(ivp + gcmp_get_header_len(k), ivp, hdrlen);250ivp += hdrlen;251252gcmp_setiv(k, ivp);253254/*255* Finally, do software encrypt if needed.256*/257if ((k->wk_flags & IEEE80211_KEY_SWENCRYPT) &&258!gcmp_encrypt(k, m, hdrlen))259return (0);260261return (1);262}263264/*265* Add MIC to the frame as needed.266*/267static int268gcmp_enmic(struct ieee80211_key *k, struct mbuf *m, int force)269{270return (1);271}272273static __inline uint64_t274READ_6(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5)275{276uint32_t iv32 = (b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24);277uint16_t iv16 = (b4 << 0) | (b5 << 8);278return ((((uint64_t)iv16) << 32) | iv32);279}280281/*282* Validate and strip privacy headers (and trailer) for a283* received frame. The specified key should be correct but284* is also verified.285*/286static int287gcmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)288{289const struct ieee80211_rx_stats *rxs;290struct gcmp_ctx *ctx = k->wk_private;291struct ieee80211vap *vap = ctx->cc_vap;292struct ieee80211_frame *wh;293uint8_t *ivp, tid;294uint64_t pn;295bool noreplaycheck;296297rxs = ieee80211_get_rx_params_ptr(m);298299if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP) != 0)300goto finish;301302/*303* Header should have extended IV and sequence number;304* verify the former and validate the latter.305*/306wh = mtod(m, struct ieee80211_frame *);307ivp = mtod(m, uint8_t *) + hdrlen;308if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) {309/*310* No extended IV; discard frame.311*/312IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,313"%s", "missing ExtIV for AES-GCM cipher");314vap->iv_stats.is_rx_gcmpformat++;315return (0);316}317tid = ieee80211_gettid(wh);318pn = READ_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]);319320noreplaycheck = (k->wk_flags & IEEE80211_KEY_NOREPLAY) != 0;321noreplaycheck |= (rxs != NULL) &&322(rxs->c_pktflags & IEEE80211_RX_F_PN_VALIDATED) != 0;323if (pn <= k->wk_keyrsc[tid] && !noreplaycheck) {324/*325* Replay violation.326*/327ieee80211_notify_replay_failure(vap, wh, k, pn, tid);328vap->iv_stats.is_rx_gcmpreplay++;329return (0);330}331332/*333* Check if the device handled the decrypt in hardware.334* If so we just strip the header; otherwise we need to335* handle the decrypt in software. Note that for the336* latter we leave the header in place for use in the337* decryption work.338*/339if ((k->wk_flags & IEEE80211_KEY_SWDECRYPT) &&340!gcmp_decrypt(k, pn, m, hdrlen))341return (0);342343finish:344/*345* Copy up 802.11 header and strip crypto bits.346*/347if ((rxs == NULL) || (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP) == 0) {348ovbcopy(mtod(m, void *), mtod(m, uint8_t *) +349gcmp_get_header_len(k), hdrlen);350m_adj(m, gcmp_get_header_len(k));351}352353if ((rxs == NULL) || (rxs->c_pktflags & IEEE80211_RX_F_MIC_STRIP) == 0)354m_adj(m, -gcmp_get_trailer_len(k));355356/*357* Ok to update rsc now.358*/359if ((rxs == NULL) || (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP) == 0) {360/*361* Do not go backwards in the IEEE80211_KEY_NOREPLAY cases362* or in case hardware has checked but frames are arriving363* reordered (e.g., LinuxKPI drivers doing RSS which we are364* not prepared for at all).365*/366if (pn > k->wk_keyrsc[tid])367k->wk_keyrsc[tid] = pn;368}369370return (1);371}372373/*374* Verify and strip MIC from the frame.375*/376static int377gcmp_demic(struct ieee80211_key *k, struct mbuf *m, int force)378{379return (1);380}381382/*383* Populate the 12 byte / 96 bit IV buffer.384*/385static int386gcmp_init_iv(uint8_t *iv, const struct ieee80211_frame *wh, u_int64_t pn)387{388uint8_t j_pn[GCMP_PN_LEN];389390/* Construct the pn buffer */391j_pn[0] = pn >> 40;392j_pn[1] = pn >> 32;393j_pn[2] = pn >> 24;394j_pn[3] = pn >> 16;395j_pn[4] = pn >> 8;396j_pn[5] = pn >> 0;397398memcpy(iv, wh->i_addr2, IEEE80211_ADDR_LEN);399memcpy(iv + IEEE80211_ADDR_LEN, j_pn, GCMP_PN_LEN);400401return (GCMP_IV_LEN); /* 96 bits */402}403404/*405* @brief Encrypt an mbuf.406*407* This uses a temporary memory buffer to encrypt; the408* current AES-GCM code expects things in a contiguous buffer409* and this avoids the need of breaking out the GCTR and410* GHASH routines into using mbuf iterators.411*412* @param key ieee80211_key to use413* @param mbuf 802.11 frame to encrypt414* @param hdrlen the length of the 802.11 header, including any padding415* @returns 0 if error, > 0 if OK.416*/417static int418gcmp_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)419{420struct gcmp_ctx *ctx = key->wk_private;421struct ieee80211_frame *wh;422struct mbuf *m = m0;423int data_len, aad_len, iv_len, ret;424uint8_t aad[GCM_AAD_LEN];425uint8_t T[GCMP_MIC_LEN];426uint8_t iv[GCMP_IV_LEN];427uint8_t *p_pktbuf = NULL;428uint8_t *c_pktbuf = NULL;429430wh = mtod(m, struct ieee80211_frame *);431data_len = m->m_pkthdr.len - (hdrlen + gcmp_get_header_len(key));432433ctx->cc_vap->iv_stats.is_crypto_gcmp++;434435p_pktbuf = IEEE80211_MALLOC(data_len, M_TEMP,436IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);437if (p_pktbuf == NULL) {438IEEE80211_NOTE_MAC(ctx->cc_vap, IEEE80211_MSG_CRYPTO,439wh->i_addr2, "%s",440"AES-GCM encrypt failed; couldn't allocate buffer");441ctx->cc_vap->iv_stats.is_crypto_gcmp_nomem++;442return (0);443}444c_pktbuf = IEEE80211_MALLOC(data_len, M_TEMP,445IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);446if (c_pktbuf == NULL) {447IEEE80211_NOTE_MAC(ctx->cc_vap, IEEE80211_MSG_CRYPTO,448wh->i_addr2, "%s",449"AES-GCM encrypt failed; couldn't allocate buffer");450ctx->cc_vap->iv_stats.is_crypto_gcmp_nomem++;451IEEE80211_FREE(p_pktbuf, M_TEMP);452return (0);453}454455/* Initialise AAD */456aad_len = ieee80211_crypto_init_aad(wh, aad, GCM_AAD_LEN);457458/* Initialise local Nonce to work on */459/* TODO: rename iv stuff here to nonce */460iv_len = gcmp_init_iv(iv, wh, key->wk_keytsc);461462/* Copy mbuf data part into plaintext pktbuf */463m_copydata(m0, hdrlen + gcmp_get_header_len(key), data_len,464p_pktbuf);465466/* Run encrypt */467/*468* Note: aad + 2 to skip over the 2 byte length populated469* at the beginning, since it's based on the AAD code in CCMP.470*/471ieee80211_crypto_aes_gcm_ae(&ctx->cc_aes, iv, iv_len,472p_pktbuf, data_len, aad + 2, aad_len, c_pktbuf, T);473474/* Copy data back over mbuf */475m_copyback(m0, hdrlen + gcmp_get_header_len(key), data_len,476c_pktbuf);477478/* Append MIC */479ret = m_append(m0, gcmp_get_trailer_len(key), T);480if (ret == 0) {481IEEE80211_NOTE_MAC(ctx->cc_vap, IEEE80211_MSG_CRYPTO,482wh->i_addr2, "%s",483"AES-GCM encrypt failed; couldn't append T");484ctx->cc_vap->iv_stats.is_crypto_gcmp_nospc++;485}486487IEEE80211_FREE(p_pktbuf, M_TEMP);488IEEE80211_FREE(c_pktbuf, M_TEMP);489490return (ret);491}492493/*494* @brief Decrypt an mbuf.495*496* This uses a temporary memory buffer to decrypt; the497* current AES-GCM code expects things in a contiguous buffer498* and this avoids the need of breaking out the GCTR and499* GHASH routines into using mbuf iterators.500*501* @param key ieee80211_key to use502* @param mbuf 802.11 frame to decrypt503* @param hdrlen the length of the 802.11 header, including any padding504* @returns 0 if error, > 0 if OK.505*/506static int507gcmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m,508int hdrlen)509{510const struct ieee80211_rx_stats *rxs;511struct gcmp_ctx *ctx = key->wk_private;512struct ieee80211_frame *wh;513int data_len, aad_len, iv_len, ret;514uint8_t aad[GCM_AAD_LEN];515uint8_t T[GCMP_MIC_LEN];516uint8_t iv[GCMP_IV_LEN];517uint8_t *p_pktbuf = NULL;518uint8_t *c_pktbuf = NULL;519520rxs = ieee80211_get_rx_params_ptr(m);521if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_DECRYPTED) != 0)522return (1);523524wh = mtod(m, struct ieee80211_frame *);525526/* Data length doesn't include the MIC at the end */527data_len = m->m_pkthdr.len -528(hdrlen + gcmp_get_header_len(key) + GCMP_MIC_LEN);529530ctx->cc_vap->iv_stats.is_crypto_gcmp++;531532p_pktbuf = IEEE80211_MALLOC(data_len, M_TEMP,533IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);534if (p_pktbuf == NULL) {535ctx->cc_vap->iv_stats.is_crypto_gcmp_nomem++;536return (0);537}538c_pktbuf = IEEE80211_MALLOC(data_len, M_TEMP,539IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);540if (c_pktbuf == NULL) {541ctx->cc_vap->iv_stats.is_crypto_gcmp_nomem++;542IEEE80211_FREE(p_pktbuf, M_TEMP);543return (0);544}545546/* Initialise AAD */547aad_len = ieee80211_crypto_init_aad(wh, aad, GCM_AAD_LEN);548549/* Initialise local IV copy to work on */550iv_len = gcmp_init_iv(iv, wh, pn);551552/* Copy mbuf into ciphertext pktbuf */553m_copydata(m, hdrlen + gcmp_get_header_len(key), data_len,554c_pktbuf);555556/* Copy the MIC into the tag buffer */557m_copydata(m, hdrlen + gcmp_get_header_len(key) + data_len,558GCMP_MIC_LEN, T);559560/* Run decrypt */561/*562* Note: aad + 2 to skip over the 2 byte length populated563* at the beginning, since it's based on the AAD code in CCMP.564*/565ret = ieee80211_crypto_aes_gcm_ad(&ctx->cc_aes, iv, iv_len,566c_pktbuf, data_len, aad + 2, aad_len, T, p_pktbuf);567568/* If the MIC was stripped by HW/driver we are done. */569if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_MIC_STRIP) != 0)570goto skip_ok;571572if (ret != 0) {573/* Decrypt failure */574ctx->cc_vap->iv_stats.is_rx_gcmpmic++;575IEEE80211_NOTE_MAC(ctx->cc_vap, IEEE80211_MSG_CRYPTO,576wh->i_addr2, "%s", "AES-GCM decrypt failed; MIC mismatch");577IEEE80211_FREE(p_pktbuf, M_TEMP);578IEEE80211_FREE(c_pktbuf, M_TEMP);579return (0);580}581582skip_ok:583/* Copy data back over mbuf */584m_copyback(m, hdrlen + gcmp_get_header_len(key), data_len,585p_pktbuf);586587IEEE80211_FREE(p_pktbuf, M_TEMP);588IEEE80211_FREE(c_pktbuf, M_TEMP);589590return (1);591}592593/*594* Module glue.595*/596IEEE80211_CRYPTO_MODULE(gcmp, 1);597IEEE80211_CRYPTO_MODULE_ADD(gcmp_256);598599600