Path: blob/main/sys/net80211/ieee80211_crypto_ccmp.c
39475 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2002-2008 Sam Leffler, Errno Consulting4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR16* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES17* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.18* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,19* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT20* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,21* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY22* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT23* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF24* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.25*/2627#include <sys/cdefs.h>28/*29* IEEE 802.11i AES-CCMP crypto support.30*31* Part of this module is derived from similar code in the Host32* AP driver. The code is used with the consent of the author and33* it's license is included below.34*/35#include "opt_wlan.h"3637#include <sys/param.h>38#include <sys/systm.h>39#include <sys/mbuf.h>40#include <sys/malloc.h>41#include <sys/kernel.h>42#include <sys/module.h>4344#include <sys/socket.h>4546#include <net/if.h>47#include <net/if_media.h>48#include <net/ethernet.h>4950#include <net80211/ieee80211_var.h>5152#include <crypto/rijndael/rijndael.h>5354#define AES_BLOCK_LEN 165556#define CCMP_128_MIC_LEN 857#define CCMP_256_MIC_LEN 165859struct ccmp_ctx {60struct ieee80211vap *cc_vap; /* for diagnostics+statistics */61struct ieee80211com *cc_ic;62rijndael_ctx cc_aes;63};6465static void *ccmp_attach(struct ieee80211vap *, struct ieee80211_key *);66static void ccmp_detach(struct ieee80211_key *);67static int ccmp_setkey(struct ieee80211_key *);68static void ccmp_setiv(struct ieee80211_key *, uint8_t *);69static int ccmp_encap(struct ieee80211_key *, struct mbuf *);70static int ccmp_decap(struct ieee80211_key *, struct mbuf *, int);71static int ccmp_enmic(struct ieee80211_key *, struct mbuf *, int);72static int ccmp_demic(struct ieee80211_key *, struct mbuf *, int);7374static const struct ieee80211_cipher ccmp = {75.ic_name = "AES-CCM",76.ic_cipher = IEEE80211_CIPHER_AES_CCM,77.ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +78IEEE80211_WEP_EXTIVLEN,79.ic_trailer = CCMP_128_MIC_LEN,80.ic_miclen = 0,81.ic_attach = ccmp_attach,82.ic_detach = ccmp_detach,83.ic_setkey = ccmp_setkey,84.ic_setiv = ccmp_setiv,85.ic_encap = ccmp_encap,86.ic_decap = ccmp_decap,87.ic_enmic = ccmp_enmic,88.ic_demic = ccmp_demic,89};9091static const struct ieee80211_cipher ccmp_256 = {92.ic_name = "AES-CCM-256",93.ic_cipher = IEEE80211_CIPHER_AES_CCM_256,94.ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +95IEEE80211_WEP_EXTIVLEN,96.ic_trailer = CCMP_256_MIC_LEN,97.ic_miclen = 0,98.ic_attach = ccmp_attach,99.ic_detach = ccmp_detach,100.ic_setkey = ccmp_setkey,101.ic_setiv = ccmp_setiv,102.ic_encap = ccmp_encap,103.ic_decap = ccmp_decap,104.ic_enmic = ccmp_enmic,105.ic_demic = ccmp_demic,106};107108static int ccmp_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);109static int ccmp_decrypt(struct ieee80211_key *, u_int64_t pn,110struct mbuf *, int hdrlen);111112/* number of references from net80211 layer */113static int nrefs = 0;114115static void *116ccmp_attach(struct ieee80211vap *vap, struct ieee80211_key *k)117{118struct ccmp_ctx *ctx;119120ctx = (struct ccmp_ctx *) IEEE80211_MALLOC(sizeof(struct ccmp_ctx),121M_80211_CRYPTO, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);122if (ctx == NULL) {123vap->iv_stats.is_crypto_nomem++;124return NULL;125}126ctx->cc_vap = vap;127ctx->cc_ic = vap->iv_ic;128nrefs++; /* NB: we assume caller locking */129return ctx;130}131132static void133ccmp_detach(struct ieee80211_key *k)134{135struct ccmp_ctx *ctx = k->wk_private;136137IEEE80211_FREE(ctx, M_80211_CRYPTO);138KASSERT(nrefs > 0, ("imbalanced attach/detach"));139nrefs--; /* NB: we assume caller locking */140}141142static int143ccmp_get_trailer_len(struct ieee80211_key *k)144{145return (k->wk_cipher->ic_trailer);146}147148static int149ccmp_get_header_len(struct ieee80211_key *k)150{151return (k->wk_cipher->ic_header);152}153154/**155* @brief Return the M parameter to use for CCMP block0 initialisation.156*157* M is defined as the number of bytes in the authentication158* field.159*160* See RFC3610, Section 2 (CCM Mode Specification) for more161* information.162*163* The MIC size is defined in 802.11-2020 12.5.3164* (CTR with CBC-MAC Protocol (CCMP)).165*166* CCM-128 - M=8, MIC is 8 octets.167* CCM-256 - M=16, MIC is 16 octets.168*169* @param key ieee80211_key to calculate M for170* @retval the number of bytes in the authentication field171*/172static int173ccmp_get_ccm_m(struct ieee80211_key *k)174{175if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_AES_CCM)176return (8);177if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_AES_CCM_256)178return (16);179return (8); /* XXX default */180}181182static int183ccmp_setkey(struct ieee80211_key *k)184{185uint32_t keylen;186struct ccmp_ctx *ctx = k->wk_private;187188switch (k->wk_cipher->ic_cipher) {189case IEEE80211_CIPHER_AES_CCM:190keylen = 128;191break;192case IEEE80211_CIPHER_AES_CCM_256:193keylen = 256;194break;195default:196IEEE80211_DPRINTF(ctx->cc_vap, IEEE80211_MSG_CRYPTO,197"%s: Unexpected cipher (%u)",198__func__, k->wk_cipher->ic_cipher);199return (0);200}201202if (k->wk_keylen != (keylen/NBBY)) {203IEEE80211_DPRINTF(ctx->cc_vap, IEEE80211_MSG_CRYPTO,204"%s: Invalid key length %u, expecting %u\n",205__func__, k->wk_keylen, keylen/NBBY);206return 0;207}208if (k->wk_flags & IEEE80211_KEY_SWENCRYPT)209rijndael_set_key(&ctx->cc_aes, k->wk_key, k->wk_keylen*NBBY);210return 1;211}212213static void214ccmp_setiv(struct ieee80211_key *k, uint8_t *ivp)215{216struct ccmp_ctx *ctx = k->wk_private;217struct ieee80211vap *vap = ctx->cc_vap;218uint8_t keyid;219220keyid = ieee80211_crypto_get_keyid(vap, k) << 6;221222k->wk_keytsc++;223ivp[0] = k->wk_keytsc >> 0; /* PN0 */224ivp[1] = k->wk_keytsc >> 8; /* PN1 */225ivp[2] = 0; /* Reserved */226ivp[3] = keyid | IEEE80211_WEP_EXTIV; /* KeyID | ExtID */227ivp[4] = k->wk_keytsc >> 16; /* PN2 */228ivp[5] = k->wk_keytsc >> 24; /* PN3 */229ivp[6] = k->wk_keytsc >> 32; /* PN4 */230ivp[7] = k->wk_keytsc >> 40; /* PN5 */231}232233/*234* Add privacy headers appropriate for the specified key.235*/236static int237ccmp_encap(struct ieee80211_key *k, struct mbuf *m)238{239const struct ieee80211_frame *wh;240struct ccmp_ctx *ctx = k->wk_private;241struct ieee80211com *ic = ctx->cc_ic;242uint8_t *ivp;243int hdrlen;244int is_mgmt;245246hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));247wh = mtod(m, const struct ieee80211_frame *);248is_mgmt = IEEE80211_IS_MGMT(wh);249250/*251* Check to see if we need to insert IV/MIC.252*253* Some offload devices don't require the IV to be inserted254* as part of the hardware encryption.255*/256if (is_mgmt && (k->wk_flags & IEEE80211_KEY_NOIVMGT))257return 1;258if ((! is_mgmt) && (k->wk_flags & IEEE80211_KEY_NOIV))259return 1;260261/*262* Copy down 802.11 header and add the IV, KeyID, and ExtIV.263*/264M_PREPEND(m, ccmp_get_header_len(k), IEEE80211_M_NOWAIT);265if (m == NULL)266return 0;267ivp = mtod(m, uint8_t *);268ovbcopy(ivp + ccmp_get_header_len(k), ivp, hdrlen);269ivp += hdrlen;270271ccmp_setiv(k, ivp);272273/*274* Finally, do software encrypt if needed.275*/276if ((k->wk_flags & IEEE80211_KEY_SWENCRYPT) &&277!ccmp_encrypt(k, m, hdrlen))278return 0;279280return 1;281}282283/*284* Add MIC to the frame as needed.285*/286static int287ccmp_enmic(struct ieee80211_key *k, struct mbuf *m, int force)288{289290return 1;291}292293static __inline uint64_t294READ_6(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5)295{296uint32_t iv32 = (b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24);297uint16_t iv16 = (b4 << 0) | (b5 << 8);298return (((uint64_t)iv16) << 32) | iv32;299}300301/*302* Validate and strip privacy headers (and trailer) for a303* received frame. The specified key should be correct but304* is also verified.305*/306static int307ccmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)308{309const struct ieee80211_rx_stats *rxs;310struct ccmp_ctx *ctx = k->wk_private;311struct ieee80211vap *vap = ctx->cc_vap;312struct ieee80211_frame *wh;313uint8_t *ivp, tid;314uint64_t pn;315bool noreplaycheck;316317rxs = ieee80211_get_rx_params_ptr(m);318319if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP) != 0)320goto finish;321322/*323* Header should have extended IV and sequence number;324* verify the former and validate the latter.325*/326wh = mtod(m, struct ieee80211_frame *);327ivp = mtod(m, uint8_t *) + hdrlen;328if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) {329/*330* No extended IV; discard frame.331*/332IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,333"%s", "missing ExtIV for AES-CCM cipher");334vap->iv_stats.is_rx_ccmpformat++;335return 0;336}337tid = ieee80211_gettid(wh);338pn = READ_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]);339340noreplaycheck = (k->wk_flags & IEEE80211_KEY_NOREPLAY) != 0;341noreplaycheck |= (rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_PN_VALIDATED) != 0;342if (pn <= k->wk_keyrsc[tid] && !noreplaycheck) {343/*344* Replay violation.345*/346ieee80211_notify_replay_failure(vap, wh, k, pn, tid);347vap->iv_stats.is_rx_ccmpreplay++;348return 0;349}350351/*352* Check if the device handled the decrypt in hardware.353* If so we just strip the header; otherwise we need to354* handle the decrypt in software. Note that for the355* latter we leave the header in place for use in the356* decryption work.357*/358if ((k->wk_flags & IEEE80211_KEY_SWDECRYPT) &&359!ccmp_decrypt(k, pn, m, hdrlen))360return 0;361362finish:363/*364* Copy up 802.11 header and strip crypto bits.365*/366if (! ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP))) {367ovbcopy(mtod(m, void *),368mtod(m, uint8_t *) + ccmp_get_header_len(k),369hdrlen);370m_adj(m, ccmp_get_header_len(k));371}372373if ((rxs == NULL) || (rxs->c_pktflags & IEEE80211_RX_F_MIC_STRIP) == 0)374m_adj(m, -ccmp_get_trailer_len(k));375376/*377* Ok to update rsc now.378*/379if ((rxs == NULL) || (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP) == 0) {380/*381* Do not go backwards in the IEEE80211_KEY_NOREPLAY cases382* or in case hardware has checked but frames are arriving383* reordered (e.g., LinuxKPI drivers doing RSS which we are384* not prepared for at all).385*/386if (pn > k->wk_keyrsc[tid])387k->wk_keyrsc[tid] = pn;388}389390return 1;391}392393/*394* Verify and strip MIC from the frame.395*/396static int397ccmp_demic(struct ieee80211_key *k, struct mbuf *m, int force)398{399return 1;400}401402static __inline void403xor_block(uint8_t *b, const uint8_t *a, size_t len)404{405int i;406for (i = 0; i < len; i++)407b[i] ^= a[i];408}409410/**411* @brief Initialise the AES-CCM nonce flag field in the b0 CCMP block.412*413* The B_0 block is defined in RFC 3610 section 2.2 (Authentication).414* b0[0] is the CCM flags field, so the nonce used for B_0 starts at415* b0[1]. Amusingly, b0[1] is also flags, but it's the 802.11 AES-CCM416* nonce flags field, NOT the CCM flags field.417*418* The AES-CCM nonce flags field is defined in 802.11-2020 12.5.3.3.4419* (Construct CCM nonce).420*421* TODO: net80211 currently doesn't support MFP (management frame protection)422* and so bit 4 is never set. This routine and ccmp_init_blocks() will423* need a pointer to the ieee80211_node or a flag that explicitly states424* the frame will be sent w/ MFP encryption / received w/ MFP decryption.425*426* @param wh the 802.11 header to populate427* @param b0 the CCM nonce to update (remembering b0[0] is the CCM428* nonce flags, and b0[1] is the AES-CCM nonce flags).429*/430static void431ieee80211_crypto_ccmp_init_nonce_flags(const struct ieee80211_frame *wh,432char *b0)433{434if (IEEE80211_IS_DSTODS(wh)) {435/*436* 802.11-2020 12.5.33.3.4 (Construct CCM nonce) mentions437* that the low four bits of this byte are the "MPDU priority."438* This is defined in 5.1.1.2 (Determination of UP) and439* 5.1.1.3 (Interpretation of Priority Parameter in MAC440* service primitives).441*442* The former says "The QoS facility supports eight priority443* values, referred to as UPs. The values a UP may take are444* the integer values from 0 to 7 and are identical to the445* 802.11D priority tags."446*447* The latter specifically calls out that "Priority parameter448* and TID subfield values 0 to 7 are interpreted aas UPs for449* the MSDUs" .. and " .. TID subfield values 8 to 15 specify450* TIDs that are TS identifiers (TSIDs)" which are used for451* TSPEC. There's a bunch of extra work to be done with frames452* received in TIDs 8..15 with no TSPEC, "then the MSDU shall453* be sent with priority parameter set to 0."454*455* All QoS frames (not just QoS data) have TID fields and456* thus priorities. However, the code straight up457* copies the 4 bit TID field, rather than a 3 bit MPDU458* priority value. For now, as net80211 doesn't specifically459* support TSPEC negotiation, this likely never gets checked.460* However as part of any future TSPEC work, this will likely461* need to be looked at and checked with interoperability462* with other stacks.463*/464if (IEEE80211_IS_QOS_ANY(wh)) {465const struct ieee80211_qosframe_addr4 *qwh4 =466(const struct ieee80211_qosframe_addr4 *) wh;467b0[1] = qwh4->i_qos[0] & 0x0f; /* prio bits */468} else {469b0[1] = 0;470}471} else {472if (IEEE80211_IS_QOS_ANY(wh)) {473const struct ieee80211_qosframe *qwh =474(const struct ieee80211_qosframe *) wh;475b0[1] = qwh->i_qos[0] & 0x0f; /* prio bits */476} else {477b0[1] = 0;478}479}480/* TODO: populate MFP flag */481}482483/*484* Host AP crypt: host-based CCMP encryption implementation for Host AP driver485*486* Copyright (c) 2003-2004, Jouni Malinen <[email protected]>487*488* This program is free software; you can redistribute it and/or modify489* it under the terms of the GNU General Public License version 2 as490* published by the Free Software Foundation. See README and COPYING for491* more details.492*493* Alternatively, this software may be distributed under the terms of BSD494* license.495*/496497static void498ccmp_init_blocks(rijndael_ctx *ctx, struct ieee80211_frame *wh,499uint32_t m, u_int64_t pn, size_t dlen,500uint8_t b0[AES_BLOCK_LEN], uint8_t aad[2 * AES_BLOCK_LEN],501uint8_t auth[AES_BLOCK_LEN], uint8_t s0[AES_BLOCK_LEN])502{503/*504* Map M parameter to encoding505* RFC3610, Section 2 (CCM Mode Specification)506*/507m = (m - 2) / 2;508509/* CCM Initial Block:510*511* Flag (Include authentication header,512* M=3 or 7 (8 or 16 octet auth field),513* L=1 (2-octet Dlen))514* Adata=1 (one or more auth blocks present)515* Nonce: 0x00 | A2 | PN516* Dlen517*/518b0[0] = 0x40 | 0x01 | (m << 3);519/* Init b0[1] (CCM nonce flags) */520ieee80211_crypto_ccmp_init_nonce_flags(wh, b0);521IEEE80211_ADDR_COPY(b0 + 2, wh->i_addr2);522b0[8] = pn >> 40;523b0[9] = pn >> 32;524b0[10] = pn >> 24;525b0[11] = pn >> 16;526b0[12] = pn >> 8;527b0[13] = pn >> 0;528b0[14] = (dlen >> 8) & 0xff;529b0[15] = dlen & 0xff;530531/* Init AAD */532(void) ieee80211_crypto_init_aad(wh, aad, 2 * AES_BLOCK_LEN);533534/* Start with the first block and AAD */535rijndael_encrypt(ctx, b0, auth);536xor_block(auth, aad, AES_BLOCK_LEN);537rijndael_encrypt(ctx, auth, auth);538xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN);539rijndael_encrypt(ctx, auth, auth);540b0[0] &= 0x07;541b0[14] = b0[15] = 0;542rijndael_encrypt(ctx, b0, s0);543}544545#define CCMP_ENCRYPT(_i, _b, _b0, _pos, _e, _len) do { \546/* Authentication */ \547xor_block(_b, _pos, _len); \548rijndael_encrypt(&ctx->cc_aes, _b, _b); \549/* Encryption, with counter */ \550_b0[14] = (_i >> 8) & 0xff; \551_b0[15] = _i & 0xff; \552rijndael_encrypt(&ctx->cc_aes, _b0, _e); \553xor_block(_pos, _e, _len); \554} while (0)555556static int557ccmp_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)558{559struct ccmp_ctx *ctx = key->wk_private;560struct ieee80211_frame *wh;561struct mbuf *m = m0;562int data_len, i, space;563uint8_t aad[2 * AES_BLOCK_LEN], b0[AES_BLOCK_LEN], b[AES_BLOCK_LEN],564e[AES_BLOCK_LEN], s0[AES_BLOCK_LEN];565uint8_t *pos;566567ctx->cc_vap->iv_stats.is_crypto_ccmp++;568569wh = mtod(m, struct ieee80211_frame *);570data_len = m->m_pkthdr.len - (hdrlen + ccmp_get_header_len(key));571ccmp_init_blocks(&ctx->cc_aes, wh, ccmp_get_ccm_m(key),572key->wk_keytsc, data_len, b0, aad, b, s0);573574i = 1;575pos = mtod(m, uint8_t *) + hdrlen + ccmp_get_header_len(key);576/* NB: assumes header is entirely in first mbuf */577space = m->m_len - (hdrlen + ccmp_get_header_len(key));578for (;;) {579if (space > data_len)580space = data_len;581/*582* Do full blocks.583*/584while (space >= AES_BLOCK_LEN) {585CCMP_ENCRYPT(i, b, b0, pos, e, AES_BLOCK_LEN);586pos += AES_BLOCK_LEN, space -= AES_BLOCK_LEN;587data_len -= AES_BLOCK_LEN;588i++;589}590if (data_len <= 0) /* no more data */591break;592m = m->m_next;593if (m == NULL) { /* last buffer */594if (space != 0) {595/*596* Short last block.597*/598CCMP_ENCRYPT(i, b, b0, pos, e, space);599}600break;601}602if (space != 0) {603uint8_t *pos_next;604int space_next;605int len, dl, sp;606struct mbuf *n;607608/*609* Block straddles one or more mbufs, gather data610* into the block buffer b, apply the cipher, then611* scatter the results back into the mbuf chain.612* The buffer will automatically get space bytes613* of data at offset 0 copied in+out by the614* CCMP_ENCRYPT request so we must take care of615* the remaining data.616*/617n = m;618dl = data_len;619sp = space;620for (;;) {621pos_next = mtod(n, uint8_t *);622len = min(dl, AES_BLOCK_LEN);623space_next = len > sp ? len - sp : 0;624if (n->m_len >= space_next) {625/*626* This mbuf has enough data; just grab627* what we need and stop.628*/629xor_block(b+sp, pos_next, space_next);630break;631}632/*633* This mbuf's contents are insufficient,634* take 'em all and prepare to advance to635* the next mbuf.636*/637xor_block(b+sp, pos_next, n->m_len);638sp += n->m_len, dl -= n->m_len;639n = n->m_next;640if (n == NULL)641break;642}643644CCMP_ENCRYPT(i, b, b0, pos, e, space);645646/* NB: just like above, but scatter data to mbufs */647dl = data_len;648sp = space;649for (;;) {650pos_next = mtod(m, uint8_t *);651len = min(dl, AES_BLOCK_LEN);652space_next = len > sp ? len - sp : 0;653if (m->m_len >= space_next) {654xor_block(pos_next, e+sp, space_next);655break;656}657xor_block(pos_next, e+sp, m->m_len);658sp += m->m_len, dl -= m->m_len;659m = m->m_next;660if (m == NULL)661goto done;662}663/*664* Do bookkeeping. m now points to the last mbuf665* we grabbed data from. We know we consumed a666* full block of data as otherwise we'd have hit667* the end of the mbuf chain, so deduct from data_len.668* Otherwise advance the block number (i) and setup669* pos+space to reflect contents of the new mbuf.670*/671data_len -= AES_BLOCK_LEN;672i++;673pos = pos_next + space_next;674space = m->m_len - space_next;675} else {676/*677* Setup for next buffer.678*/679pos = mtod(m, uint8_t *);680space = m->m_len;681}682}683done:684/* tack on MIC */685xor_block(b, s0, ccmp_get_trailer_len(key));686return m_append(m0, ccmp_get_trailer_len(key), b);687}688#undef CCMP_ENCRYPT689690#define CCMP_DECRYPT(_i, _b, _b0, _pos, _a, _len) do { \691/* Decrypt, with counter */ \692_b0[14] = (_i >> 8) & 0xff; \693_b0[15] = _i & 0xff; \694rijndael_encrypt(&ctx->cc_aes, _b0, _b); \695xor_block(_pos, _b, _len); \696/* Authentication */ \697xor_block(_a, _pos, _len); \698rijndael_encrypt(&ctx->cc_aes, _a, _a); \699} while (0)700701static int702ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, int hdrlen)703{704const struct ieee80211_rx_stats *rxs;705struct ccmp_ctx *ctx = key->wk_private;706struct ieee80211vap *vap = ctx->cc_vap;707struct ieee80211_frame *wh;708uint8_t aad[2 * AES_BLOCK_LEN];709uint8_t b0[AES_BLOCK_LEN], b[AES_BLOCK_LEN], a[AES_BLOCK_LEN];710uint8_t mic[AES_BLOCK_LEN];711size_t data_len;712int i;713uint8_t *pos;714u_int space;715716rxs = ieee80211_get_rx_params_ptr(m);717if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_DECRYPTED) != 0)718return (1);719720ctx->cc_vap->iv_stats.is_crypto_ccmp++;721722wh = mtod(m, struct ieee80211_frame *);723data_len = m->m_pkthdr.len -724(hdrlen + ccmp_get_header_len(key) + ccmp_get_trailer_len(key));725ccmp_init_blocks(&ctx->cc_aes, wh, ccmp_get_ccm_m(key), pn,726data_len, b0, aad, a, b);727m_copydata(m, m->m_pkthdr.len - ccmp_get_trailer_len(key),728ccmp_get_trailer_len(key), mic);729xor_block(mic, b, ccmp_get_trailer_len(key));730731i = 1;732pos = mtod(m, uint8_t *) + hdrlen + ccmp_get_header_len(key);733space = m->m_len - (hdrlen + ccmp_get_header_len(key));734for (;;) {735if (space > data_len)736space = data_len;737while (space >= AES_BLOCK_LEN) {738CCMP_DECRYPT(i, b, b0, pos, a, AES_BLOCK_LEN);739pos += AES_BLOCK_LEN, space -= AES_BLOCK_LEN;740data_len -= AES_BLOCK_LEN;741i++;742}743if (data_len <= 0) /* no more data */744break;745m = m->m_next;746if (m == NULL) { /* last buffer */747if (space != 0) /* short last block */748CCMP_DECRYPT(i, b, b0, pos, a, space);749break;750}751if (space != 0) {752uint8_t *pos_next;753u_int space_next;754u_int len;755756/*757* Block straddles buffers, split references. We758* do not handle splits that require >2 buffers759* since rx'd frames are never badly fragmented760* because drivers typically recv in clusters.761*/762pos_next = mtod(m, uint8_t *);763len = min(data_len, AES_BLOCK_LEN);764space_next = len > space ? len - space : 0;765KASSERT(m->m_len >= space_next,766("not enough data in following buffer, "767"m_len %u need %u\n", m->m_len, space_next));768769xor_block(b+space, pos_next, space_next);770CCMP_DECRYPT(i, b, b0, pos, a, space);771xor_block(pos_next, b+space, space_next);772data_len -= len;773i++;774775pos = pos_next + space_next;776space = m->m_len - space_next;777} else {778/*779* Setup for next buffer.780*/781pos = mtod(m, uint8_t *);782space = m->m_len;783}784}785786/*787* If the MIC was stripped by HW/driver we are done.788*/789if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_MIC_STRIP) != 0)790return (1);791792if (memcmp(mic, a, ccmp_get_trailer_len(key)) != 0) {793IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,794"%s", "AES-CCM decrypt failed; MIC mismatch");795vap->iv_stats.is_rx_ccmpmic++;796return 0;797}798return 1;799}800#undef CCMP_DECRYPT801802/*803* Module glue.804*/805IEEE80211_CRYPTO_MODULE(ccmp, 1);806IEEE80211_CRYPTO_MODULE_ADD(ccmp_256);807808809