xref: /dflybsd-src/sys/netproto/802_11/wlan_ccmp/ieee80211_crypto_ccmp.c (revision bff82488b6f45c2f067e4c552e649b1d3e07cd7c)
132176cfdSRui Paulo /*-
232176cfdSRui Paulo  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
3841ab66cSSepherosa Ziehau  * All rights reserved.
4841ab66cSSepherosa Ziehau  *
5841ab66cSSepherosa Ziehau  * Redistribution and use in source and binary forms, with or without
6841ab66cSSepherosa Ziehau  * modification, are permitted provided that the following conditions
7841ab66cSSepherosa Ziehau  * are met:
8841ab66cSSepherosa Ziehau  * 1. Redistributions of source code must retain the above copyright
9841ab66cSSepherosa Ziehau  *    notice, this list of conditions and the following disclaimer.
10841ab66cSSepherosa Ziehau  * 2. Redistributions in binary form must reproduce the above copyright
11841ab66cSSepherosa Ziehau  *    notice, this list of conditions and the following disclaimer in the
12841ab66cSSepherosa Ziehau  *    documentation and/or other materials provided with the distribution.
13841ab66cSSepherosa Ziehau  *
14841ab66cSSepherosa Ziehau  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15841ab66cSSepherosa Ziehau  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16841ab66cSSepherosa Ziehau  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17841ab66cSSepherosa Ziehau  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18841ab66cSSepherosa Ziehau  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19841ab66cSSepherosa Ziehau  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20841ab66cSSepherosa Ziehau  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21841ab66cSSepherosa Ziehau  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22841ab66cSSepherosa Ziehau  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23841ab66cSSepherosa Ziehau  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24841ab66cSSepherosa Ziehau  */
25841ab66cSSepherosa Ziehau 
26085ff963SMatthew Dillon #include <sys/cdefs.h>
27085ff963SMatthew Dillon __FBSDID("$FreeBSD$");
28085ff963SMatthew Dillon 
29841ab66cSSepherosa Ziehau /*
30841ab66cSSepherosa Ziehau  * IEEE 802.11i AES-CCMP crypto support.
31841ab66cSSepherosa Ziehau  *
32841ab66cSSepherosa Ziehau  * Part of this module is derived from similar code in the Host
33841ab66cSSepherosa Ziehau  * AP driver. The code is used with the consent of the author and
34841ab66cSSepherosa Ziehau  * it's license is included below.
35841ab66cSSepherosa Ziehau  */
3632176cfdSRui Paulo #include "opt_wlan.h"
3732176cfdSRui Paulo 
38841ab66cSSepherosa Ziehau #include <sys/param.h>
39841ab66cSSepherosa Ziehau #include <sys/systm.h>
40841ab66cSSepherosa Ziehau #include <sys/mbuf.h>
41841ab66cSSepherosa Ziehau #include <sys/malloc.h>
42841ab66cSSepherosa Ziehau #include <sys/kernel.h>
43841ab66cSSepherosa Ziehau #include <sys/module.h>
44841ab66cSSepherosa Ziehau 
45841ab66cSSepherosa Ziehau #include <sys/socket.h>
46841ab66cSSepherosa Ziehau 
47841ab66cSSepherosa Ziehau #include <net/if.h>
48*bff82488SAaron LI #include <net/if_var.h>
49841ab66cSSepherosa Ziehau #include <net/if_media.h>
50841ab66cSSepherosa Ziehau #include <net/ethernet.h>
51841ab66cSSepherosa Ziehau 
52841ab66cSSepherosa Ziehau #include <netproto/802_11/ieee80211_var.h>
53841ab66cSSepherosa Ziehau 
5442ee1e6bSSascha Wildner #include <crypto/rijndael/rijndael.h>
55841ab66cSSepherosa Ziehau 
56841ab66cSSepherosa Ziehau #define AES_BLOCK_LEN 16
57841ab66cSSepherosa Ziehau 
58841ab66cSSepherosa Ziehau struct ccmp_ctx {
5932176cfdSRui Paulo 	struct ieee80211vap *cc_vap;	/* for diagnostics+statistics */
6032176cfdSRui Paulo 	struct ieee80211com *cc_ic;
61841ab66cSSepherosa Ziehau 	rijndael_ctx	     cc_aes;
62841ab66cSSepherosa Ziehau };
63841ab66cSSepherosa Ziehau 
6432176cfdSRui Paulo static	void *ccmp_attach(struct ieee80211vap *, struct ieee80211_key *);
65841ab66cSSepherosa Ziehau static	void ccmp_detach(struct ieee80211_key *);
66841ab66cSSepherosa Ziehau static	int ccmp_setkey(struct ieee80211_key *);
674f655ef5SMatthew Dillon static	void ccmp_setiv(struct ieee80211_key *, uint8_t *);
684f655ef5SMatthew Dillon static	int ccmp_encap(struct ieee80211_key *, struct mbuf *);
69841ab66cSSepherosa Ziehau static	int ccmp_decap(struct ieee80211_key *, struct mbuf *, int);
70841ab66cSSepherosa Ziehau static	int ccmp_enmic(struct ieee80211_key *, struct mbuf *, int);
71841ab66cSSepherosa Ziehau static	int ccmp_demic(struct ieee80211_key *, struct mbuf *, int);
72841ab66cSSepherosa Ziehau 
73841ab66cSSepherosa Ziehau static const struct ieee80211_cipher ccmp = {
74841ab66cSSepherosa Ziehau 	.ic_name	= "AES-CCM",
75841ab66cSSepherosa Ziehau 	.ic_cipher	= IEEE80211_CIPHER_AES_CCM,
76841ab66cSSepherosa Ziehau 	.ic_header	= IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +
77841ab66cSSepherosa Ziehau 			  IEEE80211_WEP_EXTIVLEN,
78841ab66cSSepherosa Ziehau 	.ic_trailer	= IEEE80211_WEP_MICLEN,
79841ab66cSSepherosa Ziehau 	.ic_miclen	= 0,
80841ab66cSSepherosa Ziehau 	.ic_attach	= ccmp_attach,
81841ab66cSSepherosa Ziehau 	.ic_detach	= ccmp_detach,
82841ab66cSSepherosa Ziehau 	.ic_setkey	= ccmp_setkey,
834f655ef5SMatthew Dillon 	.ic_setiv	= ccmp_setiv,
84841ab66cSSepherosa Ziehau 	.ic_encap	= ccmp_encap,
85841ab66cSSepherosa Ziehau 	.ic_decap	= ccmp_decap,
86841ab66cSSepherosa Ziehau 	.ic_enmic	= ccmp_enmic,
87841ab66cSSepherosa Ziehau 	.ic_demic	= ccmp_demic,
88841ab66cSSepherosa Ziehau };
89841ab66cSSepherosa Ziehau 
90841ab66cSSepherosa Ziehau static	int ccmp_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
9132176cfdSRui Paulo static	int ccmp_decrypt(struct ieee80211_key *, u_int64_t pn,
92841ab66cSSepherosa Ziehau 		struct mbuf *, int hdrlen);
93841ab66cSSepherosa Ziehau 
94841ab66cSSepherosa Ziehau /* number of references from net80211 layer */
95841ab66cSSepherosa Ziehau static	int nrefs = 0;
96841ab66cSSepherosa Ziehau 
97841ab66cSSepherosa Ziehau static void *
ccmp_attach(struct ieee80211vap * vap,struct ieee80211_key * k)9832176cfdSRui Paulo ccmp_attach(struct ieee80211vap *vap, struct ieee80211_key *k)
99841ab66cSSepherosa Ziehau {
100841ab66cSSepherosa Ziehau 	struct ccmp_ctx *ctx;
101841ab66cSSepherosa Ziehau 
1024f655ef5SMatthew Dillon #if defined(__DragonFly__)
10332176cfdSRui Paulo 	ctx = (struct ccmp_ctx *) kmalloc(sizeof(struct ccmp_ctx),
104c567b546SJoe Talbott 		M_80211_CRYPTO, M_INTWAIT | M_ZERO);
1054f655ef5SMatthew Dillon #else
1064f655ef5SMatthew Dillon 	ctx = (struct ccmp_ctx *) IEEE80211_MALLOC(sizeof(struct ccmp_ctx),
1074f655ef5SMatthew Dillon 		M_80211_CRYPTO, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
1084f655ef5SMatthew Dillon #endif
109841ab66cSSepherosa Ziehau 	if (ctx == NULL) {
11032176cfdSRui Paulo 		vap->iv_stats.is_crypto_nomem++;
111841ab66cSSepherosa Ziehau 		return NULL;
112841ab66cSSepherosa Ziehau 	}
11332176cfdSRui Paulo 	ctx->cc_vap = vap;
11432176cfdSRui Paulo 	ctx->cc_ic = vap->iv_ic;
115841ab66cSSepherosa Ziehau 	nrefs++;			/* NB: we assume caller locking */
116841ab66cSSepherosa Ziehau 	return ctx;
117841ab66cSSepherosa Ziehau }
118841ab66cSSepherosa Ziehau 
119841ab66cSSepherosa Ziehau static void
ccmp_detach(struct ieee80211_key * k)120841ab66cSSepherosa Ziehau ccmp_detach(struct ieee80211_key *k)
121841ab66cSSepherosa Ziehau {
122841ab66cSSepherosa Ziehau 	struct ccmp_ctx *ctx = k->wk_private;
123841ab66cSSepherosa Ziehau 
1244f655ef5SMatthew Dillon 	IEEE80211_FREE(ctx, M_80211_CRYPTO);
125841ab66cSSepherosa Ziehau 	KASSERT(nrefs > 0, ("imbalanced attach/detach"));
126841ab66cSSepherosa Ziehau 	nrefs--;			/* NB: we assume caller locking */
127841ab66cSSepherosa Ziehau }
128841ab66cSSepherosa Ziehau 
129841ab66cSSepherosa Ziehau static int
ccmp_setkey(struct ieee80211_key * k)130841ab66cSSepherosa Ziehau ccmp_setkey(struct ieee80211_key *k)
131841ab66cSSepherosa Ziehau {
132841ab66cSSepherosa Ziehau 	struct ccmp_ctx *ctx = k->wk_private;
133841ab66cSSepherosa Ziehau 
134841ab66cSSepherosa Ziehau 	if (k->wk_keylen != (128/NBBY)) {
13532176cfdSRui Paulo 		IEEE80211_DPRINTF(ctx->cc_vap, IEEE80211_MSG_CRYPTO,
136841ab66cSSepherosa Ziehau 			"%s: Invalid key length %u, expecting %u\n",
137841ab66cSSepherosa Ziehau 			__func__, k->wk_keylen, 128/NBBY);
138841ab66cSSepherosa Ziehau 		return 0;
139841ab66cSSepherosa Ziehau 	}
14032176cfdSRui Paulo 	if (k->wk_flags & IEEE80211_KEY_SWENCRYPT)
14142ee1e6bSSascha Wildner 		rijndael_set_key(&ctx->cc_aes, k->wk_key, k->wk_keylen*NBBY);
142841ab66cSSepherosa Ziehau 	return 1;
143841ab66cSSepherosa Ziehau }
144841ab66cSSepherosa Ziehau 
1454f655ef5SMatthew Dillon static void
ccmp_setiv(struct ieee80211_key * k,uint8_t * ivp)1464f655ef5SMatthew Dillon ccmp_setiv(struct ieee80211_key *k, uint8_t *ivp)
1474f655ef5SMatthew Dillon {
1484f655ef5SMatthew Dillon 	struct ccmp_ctx *ctx = k->wk_private;
1494f655ef5SMatthew Dillon 	struct ieee80211vap *vap = ctx->cc_vap;
1504f655ef5SMatthew Dillon 	uint8_t keyid;
1514f655ef5SMatthew Dillon 
1524f655ef5SMatthew Dillon 	keyid = ieee80211_crypto_get_keyid(vap, k) << 6;
1534f655ef5SMatthew Dillon 
1544f655ef5SMatthew Dillon 	k->wk_keytsc++;
1554f655ef5SMatthew Dillon 	ivp[0] = k->wk_keytsc >> 0;		/* PN0 */
1564f655ef5SMatthew Dillon 	ivp[1] = k->wk_keytsc >> 8;		/* PN1 */
1574f655ef5SMatthew Dillon 	ivp[2] = 0;				/* Reserved */
1584f655ef5SMatthew Dillon 	ivp[3] = keyid | IEEE80211_WEP_EXTIV;	/* KeyID | ExtID */
1594f655ef5SMatthew Dillon 	ivp[4] = k->wk_keytsc >> 16;		/* PN2 */
1604f655ef5SMatthew Dillon 	ivp[5] = k->wk_keytsc >> 24;		/* PN3 */
1614f655ef5SMatthew Dillon 	ivp[6] = k->wk_keytsc >> 32;		/* PN4 */
1624f655ef5SMatthew Dillon 	ivp[7] = k->wk_keytsc >> 40;		/* PN5 */
1634f655ef5SMatthew Dillon }
1644f655ef5SMatthew Dillon 
165841ab66cSSepherosa Ziehau /*
166841ab66cSSepherosa Ziehau  * Add privacy headers appropriate for the specified key.
167841ab66cSSepherosa Ziehau  */
168841ab66cSSepherosa Ziehau static int
ccmp_encap(struct ieee80211_key * k,struct mbuf * m)1694f655ef5SMatthew Dillon ccmp_encap(struct ieee80211_key *k, struct mbuf *m)
170841ab66cSSepherosa Ziehau {
171841ab66cSSepherosa Ziehau 	struct ccmp_ctx *ctx = k->wk_private;
172841ab66cSSepherosa Ziehau 	struct ieee80211com *ic = ctx->cc_ic;
173841ab66cSSepherosa Ziehau 	uint8_t *ivp;
174841ab66cSSepherosa Ziehau 	int hdrlen;
175841ab66cSSepherosa Ziehau 
176841ab66cSSepherosa Ziehau 	hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));
177841ab66cSSepherosa Ziehau 
178841ab66cSSepherosa Ziehau 	/*
179841ab66cSSepherosa Ziehau 	 * Copy down 802.11 header and add the IV, KeyID, and ExtIV.
180841ab66cSSepherosa Ziehau 	 */
181b5523eacSSascha Wildner 	M_PREPEND(m, ccmp.ic_header, M_NOWAIT);
182841ab66cSSepherosa Ziehau 	if (m == NULL)
183841ab66cSSepherosa Ziehau 		return 0;
184841ab66cSSepherosa Ziehau 	ivp = mtod(m, uint8_t *);
185afd2da4dSMatthew Dillon 	bcopy(ivp + ccmp.ic_header, ivp, hdrlen);
186841ab66cSSepherosa Ziehau 	ivp += hdrlen;
187841ab66cSSepherosa Ziehau 
1884f655ef5SMatthew Dillon 	ccmp_setiv(k, ivp);
189841ab66cSSepherosa Ziehau 
190841ab66cSSepherosa Ziehau 	/*
1914f655ef5SMatthew Dillon 	 * Finally, do software encrypt if needed.
192841ab66cSSepherosa Ziehau 	 */
19332176cfdSRui Paulo 	if ((k->wk_flags & IEEE80211_KEY_SWENCRYPT) &&
194841ab66cSSepherosa Ziehau 	    !ccmp_encrypt(k, m, hdrlen))
195841ab66cSSepherosa Ziehau 		return 0;
196841ab66cSSepherosa Ziehau 
197841ab66cSSepherosa Ziehau 	return 1;
198841ab66cSSepherosa Ziehau }
199841ab66cSSepherosa Ziehau 
200841ab66cSSepherosa Ziehau /*
201841ab66cSSepherosa Ziehau  * Add MIC to the frame as needed.
202841ab66cSSepherosa Ziehau  */
203841ab66cSSepherosa Ziehau static int
ccmp_enmic(struct ieee80211_key * k,struct mbuf * m,int force)204841ab66cSSepherosa Ziehau ccmp_enmic(struct ieee80211_key *k, struct mbuf *m, int force)
205841ab66cSSepherosa Ziehau {
20632176cfdSRui Paulo 
207841ab66cSSepherosa Ziehau 	return 1;
208841ab66cSSepherosa Ziehau }
209841ab66cSSepherosa Ziehau 
210841ab66cSSepherosa Ziehau static __inline uint64_t
READ_6(uint8_t b0,uint8_t b1,uint8_t b2,uint8_t b3,uint8_t b4,uint8_t b5)211841ab66cSSepherosa Ziehau READ_6(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5)
212841ab66cSSepherosa Ziehau {
213841ab66cSSepherosa Ziehau 	uint32_t iv32 = (b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24);
214841ab66cSSepherosa Ziehau 	uint16_t iv16 = (b4 << 0) | (b5 << 8);
215841ab66cSSepherosa Ziehau 	return (((uint64_t)iv16) << 32) | iv32;
216841ab66cSSepherosa Ziehau }
217841ab66cSSepherosa Ziehau 
218841ab66cSSepherosa Ziehau /*
219841ab66cSSepherosa Ziehau  * Validate and strip privacy headers (and trailer) for a
220841ab66cSSepherosa Ziehau  * received frame. The specified key should be correct but
221841ab66cSSepherosa Ziehau  * is also verified.
222841ab66cSSepherosa Ziehau  */
223841ab66cSSepherosa Ziehau static int
ccmp_decap(struct ieee80211_key * k,struct mbuf * m,int hdrlen)224841ab66cSSepherosa Ziehau ccmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
225841ab66cSSepherosa Ziehau {
226841ab66cSSepherosa Ziehau 	struct ccmp_ctx *ctx = k->wk_private;
22732176cfdSRui Paulo 	struct ieee80211vap *vap = ctx->cc_vap;
228841ab66cSSepherosa Ziehau 	struct ieee80211_frame *wh;
22932176cfdSRui Paulo 	uint8_t *ivp, tid;
230841ab66cSSepherosa Ziehau 	uint64_t pn;
231841ab66cSSepherosa Ziehau 
232841ab66cSSepherosa Ziehau 	/*
233841ab66cSSepherosa Ziehau 	 * Header should have extended IV and sequence number;
234841ab66cSSepherosa Ziehau 	 * verify the former and validate the latter.
235841ab66cSSepherosa Ziehau 	 */
236841ab66cSSepherosa Ziehau 	wh = mtod(m, struct ieee80211_frame *);
237841ab66cSSepherosa Ziehau 	ivp = mtod(m, uint8_t *) + hdrlen;
238841ab66cSSepherosa Ziehau 	if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) {
239841ab66cSSepherosa Ziehau 		/*
240841ab66cSSepherosa Ziehau 		 * No extended IV; discard frame.
241841ab66cSSepherosa Ziehau 		 */
24232176cfdSRui Paulo 		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
24332176cfdSRui Paulo 			"%s", "missing ExtIV for AES-CCM cipher");
24432176cfdSRui Paulo 		vap->iv_stats.is_rx_ccmpformat++;
245841ab66cSSepherosa Ziehau 		return 0;
246841ab66cSSepherosa Ziehau 	}
24732176cfdSRui Paulo 	tid = ieee80211_gettid(wh);
248841ab66cSSepherosa Ziehau 	pn = READ_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]);
249085ff963SMatthew Dillon 	if (pn <= k->wk_keyrsc[tid] &&
250085ff963SMatthew Dillon 	    (k->wk_flags & IEEE80211_KEY_NOREPLAY) == 0) {
251841ab66cSSepherosa Ziehau 		/*
252841ab66cSSepherosa Ziehau 		 * Replay violation.
253841ab66cSSepherosa Ziehau 		 */
25432176cfdSRui Paulo 		ieee80211_notify_replay_failure(vap, wh, k, pn, tid);
25532176cfdSRui Paulo 		vap->iv_stats.is_rx_ccmpreplay++;
256841ab66cSSepherosa Ziehau 		return 0;
257841ab66cSSepherosa Ziehau 	}
258841ab66cSSepherosa Ziehau 
259841ab66cSSepherosa Ziehau 	/*
260841ab66cSSepherosa Ziehau 	 * Check if the device handled the decrypt in hardware.
261841ab66cSSepherosa Ziehau 	 * If so we just strip the header; otherwise we need to
262841ab66cSSepherosa Ziehau 	 * handle the decrypt in software.  Note that for the
263841ab66cSSepherosa Ziehau 	 * latter we leave the header in place for use in the
264841ab66cSSepherosa Ziehau 	 * decryption work.
265841ab66cSSepherosa Ziehau 	 */
26632176cfdSRui Paulo 	if ((k->wk_flags & IEEE80211_KEY_SWDECRYPT) &&
267841ab66cSSepherosa Ziehau 	    !ccmp_decrypt(k, pn, m, hdrlen))
268841ab66cSSepherosa Ziehau 		return 0;
269841ab66cSSepherosa Ziehau 
270841ab66cSSepherosa Ziehau 	/*
271841ab66cSSepherosa Ziehau 	 * Copy up 802.11 header and strip crypto bits.
272841ab66cSSepherosa Ziehau 	 */
273afd2da4dSMatthew Dillon 	bcopy(mtod(m, void *), mtod(m, uint8_t *) + ccmp.ic_header, hdrlen);
274841ab66cSSepherosa Ziehau 	m_adj(m, ccmp.ic_header);
275841ab66cSSepherosa Ziehau 	m_adj(m, -ccmp.ic_trailer);
276841ab66cSSepherosa Ziehau 
277841ab66cSSepherosa Ziehau 	/*
278841ab66cSSepherosa Ziehau 	 * Ok to update rsc now.
279841ab66cSSepherosa Ziehau 	 */
28032176cfdSRui Paulo 	k->wk_keyrsc[tid] = pn;
281841ab66cSSepherosa Ziehau 
282841ab66cSSepherosa Ziehau 	return 1;
283841ab66cSSepherosa Ziehau }
284841ab66cSSepherosa Ziehau 
285841ab66cSSepherosa Ziehau /*
286841ab66cSSepherosa Ziehau  * Verify and strip MIC from the frame.
287841ab66cSSepherosa Ziehau  */
288841ab66cSSepherosa Ziehau static int
ccmp_demic(struct ieee80211_key * k,struct mbuf * m,int force)289841ab66cSSepherosa Ziehau ccmp_demic(struct ieee80211_key *k, struct mbuf *m, int force)
290841ab66cSSepherosa Ziehau {
291841ab66cSSepherosa Ziehau 	return 1;
292841ab66cSSepherosa Ziehau }
293841ab66cSSepherosa Ziehau 
294841ab66cSSepherosa Ziehau static __inline void
xor_block(uint8_t * b,const uint8_t * a,size_t len)295841ab66cSSepherosa Ziehau xor_block(uint8_t *b, const uint8_t *a, size_t len)
296841ab66cSSepherosa Ziehau {
297841ab66cSSepherosa Ziehau 	int i;
298841ab66cSSepherosa Ziehau 	for (i = 0; i < len; i++)
299841ab66cSSepherosa Ziehau 		b[i] ^= a[i];
300841ab66cSSepherosa Ziehau }
301841ab66cSSepherosa Ziehau 
302841ab66cSSepherosa Ziehau /*
303841ab66cSSepherosa Ziehau  * Host AP crypt: host-based CCMP encryption implementation for Host AP driver
304841ab66cSSepherosa Ziehau  *
305841ab66cSSepherosa Ziehau  * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
306841ab66cSSepherosa Ziehau  *
307841ab66cSSepherosa Ziehau  * This program is free software; you can redistribute it and/or modify
308841ab66cSSepherosa Ziehau  * it under the terms of the GNU General Public License version 2 as
309841ab66cSSepherosa Ziehau  * published by the Free Software Foundation. See README and COPYING for
310841ab66cSSepherosa Ziehau  * more details.
311841ab66cSSepherosa Ziehau  *
312841ab66cSSepherosa Ziehau  * Alternatively, this software may be distributed under the terms of BSD
313841ab66cSSepherosa Ziehau  * license.
314841ab66cSSepherosa Ziehau  */
315841ab66cSSepherosa Ziehau 
316841ab66cSSepherosa Ziehau static void
ccmp_init_blocks(rijndael_ctx * ctx,struct ieee80211_frame * wh,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])317841ab66cSSepherosa Ziehau ccmp_init_blocks(rijndael_ctx *ctx, struct ieee80211_frame *wh,
31832176cfdSRui Paulo 	u_int64_t pn, size_t dlen,
319841ab66cSSepherosa Ziehau 	uint8_t b0[AES_BLOCK_LEN], uint8_t aad[2 * AES_BLOCK_LEN],
320841ab66cSSepherosa Ziehau 	uint8_t auth[AES_BLOCK_LEN], uint8_t s0[AES_BLOCK_LEN])
321841ab66cSSepherosa Ziehau {
322841ab66cSSepherosa Ziehau #define	IS_QOS_DATA(wh)	IEEE80211_QOS_HAS_SEQ(wh)
323841ab66cSSepherosa Ziehau 
324841ab66cSSepherosa Ziehau 	/* CCM Initial Block:
325841ab66cSSepherosa Ziehau 	 * Flag (Include authentication header, M=3 (8-octet MIC),
326841ab66cSSepherosa Ziehau 	 *       L=1 (2-octet Dlen))
327841ab66cSSepherosa Ziehau 	 * Nonce: 0x00 | A2 | PN
328841ab66cSSepherosa Ziehau 	 * Dlen */
329841ab66cSSepherosa Ziehau 	b0[0] = 0x59;
330841ab66cSSepherosa Ziehau 	/* NB: b0[1] set below */
331841ab66cSSepherosa Ziehau 	IEEE80211_ADDR_COPY(b0 + 2, wh->i_addr2);
332841ab66cSSepherosa Ziehau 	b0[8] = pn >> 40;
333841ab66cSSepherosa Ziehau 	b0[9] = pn >> 32;
334841ab66cSSepherosa Ziehau 	b0[10] = pn >> 24;
335841ab66cSSepherosa Ziehau 	b0[11] = pn >> 16;
336841ab66cSSepherosa Ziehau 	b0[12] = pn >> 8;
337841ab66cSSepherosa Ziehau 	b0[13] = pn >> 0;
338841ab66cSSepherosa Ziehau 	b0[14] = (dlen >> 8) & 0xff;
339841ab66cSSepherosa Ziehau 	b0[15] = dlen & 0xff;
340841ab66cSSepherosa Ziehau 
341841ab66cSSepherosa Ziehau 	/* AAD:
342841ab66cSSepherosa Ziehau 	 * FC with bits 4..6 and 11..13 masked to zero; 14 is always one
343841ab66cSSepherosa Ziehau 	 * A1 | A2 | A3
344841ab66cSSepherosa Ziehau 	 * SC with bits 4..15 (seq#) masked to zero
345841ab66cSSepherosa Ziehau 	 * A4 (if present)
346841ab66cSSepherosa Ziehau 	 * QC (if present)
347841ab66cSSepherosa Ziehau 	 */
348841ab66cSSepherosa Ziehau 	aad[0] = 0;	/* AAD length >> 8 */
349841ab66cSSepherosa Ziehau 	/* NB: aad[1] set below */
350841ab66cSSepherosa Ziehau 	aad[2] = wh->i_fc[0] & 0x8f;	/* XXX magic #s */
351841ab66cSSepherosa Ziehau 	aad[3] = wh->i_fc[1] & 0xc7;	/* XXX magic #s */
352841ab66cSSepherosa Ziehau 	/* NB: we know 3 addresses are contiguous */
353841ab66cSSepherosa Ziehau 	memcpy(aad + 4, wh->i_addr1, 3 * IEEE80211_ADDR_LEN);
354841ab66cSSepherosa Ziehau 	aad[22] = wh->i_seq[0] & IEEE80211_SEQ_FRAG_MASK;
355841ab66cSSepherosa Ziehau 	aad[23] = 0; /* all bits masked */
356841ab66cSSepherosa Ziehau 	/*
357841ab66cSSepherosa Ziehau 	 * Construct variable-length portion of AAD based
358841ab66cSSepherosa Ziehau 	 * on whether this is a 4-address frame/QOS frame.
359841ab66cSSepherosa Ziehau 	 * We always zero-pad to 32 bytes before running it
360841ab66cSSepherosa Ziehau 	 * through the cipher.
361841ab66cSSepherosa Ziehau 	 *
362841ab66cSSepherosa Ziehau 	 * We also fill in the priority bits of the CCM
363841ab66cSSepherosa Ziehau 	 * initial block as we know whether or not we have
364841ab66cSSepherosa Ziehau 	 * a QOS frame.
365841ab66cSSepherosa Ziehau 	 */
36632176cfdSRui Paulo 	if (IEEE80211_IS_DSTODS(wh)) {
367841ab66cSSepherosa Ziehau 		IEEE80211_ADDR_COPY(aad + 24,
368841ab66cSSepherosa Ziehau 			((struct ieee80211_frame_addr4 *)wh)->i_addr4);
369841ab66cSSepherosa Ziehau 		if (IS_QOS_DATA(wh)) {
370841ab66cSSepherosa Ziehau 			struct ieee80211_qosframe_addr4 *qwh4 =
371841ab66cSSepherosa Ziehau 				(struct ieee80211_qosframe_addr4 *) wh;
372841ab66cSSepherosa Ziehau 			aad[30] = qwh4->i_qos[0] & 0x0f;/* just priority bits */
373841ab66cSSepherosa Ziehau 			aad[31] = 0;
374841ab66cSSepherosa Ziehau 			b0[1] = aad[30];
375841ab66cSSepherosa Ziehau 			aad[1] = 22 + IEEE80211_ADDR_LEN + 2;
376841ab66cSSepherosa Ziehau 		} else {
377841ab66cSSepherosa Ziehau 			*(uint16_t *)&aad[30] = 0;
378841ab66cSSepherosa Ziehau 			b0[1] = 0;
379841ab66cSSepherosa Ziehau 			aad[1] = 22 + IEEE80211_ADDR_LEN;
380841ab66cSSepherosa Ziehau 		}
381841ab66cSSepherosa Ziehau 	} else {
382841ab66cSSepherosa Ziehau 		if (IS_QOS_DATA(wh)) {
383841ab66cSSepherosa Ziehau 			struct ieee80211_qosframe *qwh =
384841ab66cSSepherosa Ziehau 				(struct ieee80211_qosframe*) wh;
385841ab66cSSepherosa Ziehau 			aad[24] = qwh->i_qos[0] & 0x0f;	/* just priority bits */
386841ab66cSSepherosa Ziehau 			aad[25] = 0;
387841ab66cSSepherosa Ziehau 			b0[1] = aad[24];
388841ab66cSSepherosa Ziehau 			aad[1] = 22 + 2;
389841ab66cSSepherosa Ziehau 		} else {
390841ab66cSSepherosa Ziehau 			*(uint16_t *)&aad[24] = 0;
391841ab66cSSepherosa Ziehau 			b0[1] = 0;
392841ab66cSSepherosa Ziehau 			aad[1] = 22;
393841ab66cSSepherosa Ziehau 		}
394841ab66cSSepherosa Ziehau 		*(uint16_t *)&aad[26] = 0;
395841ab66cSSepherosa Ziehau 		*(uint32_t *)&aad[28] = 0;
396841ab66cSSepherosa Ziehau 	}
397841ab66cSSepherosa Ziehau 
398841ab66cSSepherosa Ziehau 	/* Start with the first block and AAD */
399841ab66cSSepherosa Ziehau 	rijndael_encrypt(ctx, b0, auth);
400841ab66cSSepherosa Ziehau 	xor_block(auth, aad, AES_BLOCK_LEN);
401841ab66cSSepherosa Ziehau 	rijndael_encrypt(ctx, auth, auth);
402841ab66cSSepherosa Ziehau 	xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN);
403841ab66cSSepherosa Ziehau 	rijndael_encrypt(ctx, auth, auth);
404841ab66cSSepherosa Ziehau 	b0[0] &= 0x07;
405841ab66cSSepherosa Ziehau 	b0[14] = b0[15] = 0;
406841ab66cSSepherosa Ziehau 	rijndael_encrypt(ctx, b0, s0);
407841ab66cSSepherosa Ziehau #undef	IS_QOS_DATA
408841ab66cSSepherosa Ziehau }
409841ab66cSSepherosa Ziehau 
410841ab66cSSepherosa Ziehau #define	CCMP_ENCRYPT(_i, _b, _b0, _pos, _e, _len) do {	\
411841ab66cSSepherosa Ziehau 	/* Authentication */				\
412841ab66cSSepherosa Ziehau 	xor_block(_b, _pos, _len);			\
413841ab66cSSepherosa Ziehau 	rijndael_encrypt(&ctx->cc_aes, _b, _b);		\
414841ab66cSSepherosa Ziehau 	/* Encryption, with counter */			\
415841ab66cSSepherosa Ziehau 	_b0[14] = (_i >> 8) & 0xff;			\
416841ab66cSSepherosa Ziehau 	_b0[15] = _i & 0xff;				\
417841ab66cSSepherosa Ziehau 	rijndael_encrypt(&ctx->cc_aes, _b0, _e);	\
418841ab66cSSepherosa Ziehau 	xor_block(_pos, _e, _len);			\
419841ab66cSSepherosa Ziehau } while (0)
420841ab66cSSepherosa Ziehau 
421841ab66cSSepherosa Ziehau static int
ccmp_encrypt(struct ieee80211_key * key,struct mbuf * m0,int hdrlen)422841ab66cSSepherosa Ziehau ccmp_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
423841ab66cSSepherosa Ziehau {
424841ab66cSSepherosa Ziehau 	struct ccmp_ctx *ctx = key->wk_private;
425841ab66cSSepherosa Ziehau 	struct ieee80211_frame *wh;
426841ab66cSSepherosa Ziehau 	struct mbuf *m = m0;
427841ab66cSSepherosa Ziehau 	int data_len, i, space;
428841ab66cSSepherosa Ziehau 	uint8_t aad[2 * AES_BLOCK_LEN], b0[AES_BLOCK_LEN], b[AES_BLOCK_LEN],
429841ab66cSSepherosa Ziehau 		e[AES_BLOCK_LEN], s0[AES_BLOCK_LEN];
430841ab66cSSepherosa Ziehau 	uint8_t *pos;
431841ab66cSSepherosa Ziehau 
43232176cfdSRui Paulo 	ctx->cc_vap->iv_stats.is_crypto_ccmp++;
433841ab66cSSepherosa Ziehau 
434841ab66cSSepherosa Ziehau 	wh = mtod(m, struct ieee80211_frame *);
435841ab66cSSepherosa Ziehau 	data_len = m->m_pkthdr.len - (hdrlen + ccmp.ic_header);
436841ab66cSSepherosa Ziehau 	ccmp_init_blocks(&ctx->cc_aes, wh, key->wk_keytsc,
437841ab66cSSepherosa Ziehau 		data_len, b0, aad, b, s0);
438841ab66cSSepherosa Ziehau 
439841ab66cSSepherosa Ziehau 	i = 1;
440841ab66cSSepherosa Ziehau 	pos = mtod(m, uint8_t *) + hdrlen + ccmp.ic_header;
441841ab66cSSepherosa Ziehau 	/* NB: assumes header is entirely in first mbuf */
442841ab66cSSepherosa Ziehau 	space = m->m_len - (hdrlen + ccmp.ic_header);
443841ab66cSSepherosa Ziehau 	for (;;) {
444841ab66cSSepherosa Ziehau 		if (space > data_len)
445841ab66cSSepherosa Ziehau 			space = data_len;
446841ab66cSSepherosa Ziehau 		/*
447841ab66cSSepherosa Ziehau 		 * Do full blocks.
448841ab66cSSepherosa Ziehau 		 */
449841ab66cSSepherosa Ziehau 		while (space >= AES_BLOCK_LEN) {
450841ab66cSSepherosa Ziehau 			CCMP_ENCRYPT(i, b, b0, pos, e, AES_BLOCK_LEN);
451841ab66cSSepherosa Ziehau 			pos += AES_BLOCK_LEN, space -= AES_BLOCK_LEN;
452841ab66cSSepherosa Ziehau 			data_len -= AES_BLOCK_LEN;
453841ab66cSSepherosa Ziehau 			i++;
454841ab66cSSepherosa Ziehau 		}
455841ab66cSSepherosa Ziehau 		if (data_len <= 0)		/* no more data */
456841ab66cSSepherosa Ziehau 			break;
457841ab66cSSepherosa Ziehau 		m = m->m_next;
458841ab66cSSepherosa Ziehau 		if (m == NULL) {		/* last buffer */
459841ab66cSSepherosa Ziehau 			if (space != 0) {
460841ab66cSSepherosa Ziehau 				/*
461841ab66cSSepherosa Ziehau 				 * Short last block.
462841ab66cSSepherosa Ziehau 				 */
463841ab66cSSepherosa Ziehau 				CCMP_ENCRYPT(i, b, b0, pos, e, space);
464841ab66cSSepherosa Ziehau 			}
465841ab66cSSepherosa Ziehau 			break;
466841ab66cSSepherosa Ziehau 		}
467841ab66cSSepherosa Ziehau 		if (space != 0) {
468841ab66cSSepherosa Ziehau 			uint8_t *pos_next;
469841ab66cSSepherosa Ziehau 			int space_next;
470841ab66cSSepherosa Ziehau 			int len, dl, sp;
471841ab66cSSepherosa Ziehau 			struct mbuf *n;
472841ab66cSSepherosa Ziehau 
473841ab66cSSepherosa Ziehau 			/*
474841ab66cSSepherosa Ziehau 			 * Block straddles one or more mbufs, gather data
475841ab66cSSepherosa Ziehau 			 * into the block buffer b, apply the cipher, then
476841ab66cSSepherosa Ziehau 			 * scatter the results back into the mbuf chain.
477841ab66cSSepherosa Ziehau 			 * The buffer will automatically get space bytes
478841ab66cSSepherosa Ziehau 			 * of data at offset 0 copied in+out by the
479841ab66cSSepherosa Ziehau 			 * CCMP_ENCRYPT request so we must take care of
480841ab66cSSepherosa Ziehau 			 * the remaining data.
481841ab66cSSepherosa Ziehau 			 */
482841ab66cSSepherosa Ziehau 			n = m;
483841ab66cSSepherosa Ziehau 			dl = data_len;
484841ab66cSSepherosa Ziehau 			sp = space;
485841ab66cSSepherosa Ziehau 			for (;;) {
486841ab66cSSepherosa Ziehau 				pos_next = mtod(n, uint8_t *);
487841ab66cSSepherosa Ziehau 				len = min(dl, AES_BLOCK_LEN);
488841ab66cSSepherosa Ziehau 				space_next = len > sp ? len - sp : 0;
489841ab66cSSepherosa Ziehau 				if (n->m_len >= space_next) {
490841ab66cSSepherosa Ziehau 					/*
491841ab66cSSepherosa Ziehau 					 * This mbuf has enough data; just grab
492841ab66cSSepherosa Ziehau 					 * what we need and stop.
493841ab66cSSepherosa Ziehau 					 */
494841ab66cSSepherosa Ziehau 					xor_block(b+sp, pos_next, space_next);
495841ab66cSSepherosa Ziehau 					break;
496841ab66cSSepherosa Ziehau 				}
497841ab66cSSepherosa Ziehau 				/*
498841ab66cSSepherosa Ziehau 				 * This mbuf's contents are insufficient,
499841ab66cSSepherosa Ziehau 				 * take 'em all and prepare to advance to
500841ab66cSSepherosa Ziehau 				 * the next mbuf.
501841ab66cSSepherosa Ziehau 				 */
502841ab66cSSepherosa Ziehau 				xor_block(b+sp, pos_next, n->m_len);
503841ab66cSSepherosa Ziehau 				sp += n->m_len, dl -= n->m_len;
504841ab66cSSepherosa Ziehau 				n = n->m_next;
505841ab66cSSepherosa Ziehau 				if (n == NULL)
506841ab66cSSepherosa Ziehau 					break;
507841ab66cSSepherosa Ziehau 			}
508841ab66cSSepherosa Ziehau 
509841ab66cSSepherosa Ziehau 			CCMP_ENCRYPT(i, b, b0, pos, e, space);
510841ab66cSSepherosa Ziehau 
511841ab66cSSepherosa Ziehau 			/* NB: just like above, but scatter data to mbufs */
512841ab66cSSepherosa Ziehau 			dl = data_len;
513841ab66cSSepherosa Ziehau 			sp = space;
514841ab66cSSepherosa Ziehau 			for (;;) {
515841ab66cSSepherosa Ziehau 				pos_next = mtod(m, uint8_t *);
516841ab66cSSepherosa Ziehau 				len = min(dl, AES_BLOCK_LEN);
517841ab66cSSepherosa Ziehau 				space_next = len > sp ? len - sp : 0;
518841ab66cSSepherosa Ziehau 				if (m->m_len >= space_next) {
519841ab66cSSepherosa Ziehau 					xor_block(pos_next, e+sp, space_next);
520841ab66cSSepherosa Ziehau 					break;
521841ab66cSSepherosa Ziehau 				}
522841ab66cSSepherosa Ziehau 				xor_block(pos_next, e+sp, m->m_len);
523841ab66cSSepherosa Ziehau 				sp += m->m_len, dl -= m->m_len;
524841ab66cSSepherosa Ziehau 				m = m->m_next;
525841ab66cSSepherosa Ziehau 				if (m == NULL)
526841ab66cSSepherosa Ziehau 					goto done;
527841ab66cSSepherosa Ziehau 			}
528841ab66cSSepherosa Ziehau 			/*
529841ab66cSSepherosa Ziehau 			 * Do bookkeeping.  m now points to the last mbuf
530841ab66cSSepherosa Ziehau 			 * we grabbed data from.  We know we consumed a
531841ab66cSSepherosa Ziehau 			 * full block of data as otherwise we'd have hit
532841ab66cSSepherosa Ziehau 			 * the end of the mbuf chain, so deduct from data_len.
533841ab66cSSepherosa Ziehau 			 * Otherwise advance the block number (i) and setup
534841ab66cSSepherosa Ziehau 			 * pos+space to reflect contents of the new mbuf.
535841ab66cSSepherosa Ziehau 			 */
536841ab66cSSepherosa Ziehau 			data_len -= AES_BLOCK_LEN;
537841ab66cSSepherosa Ziehau 			i++;
538841ab66cSSepherosa Ziehau 			pos = pos_next + space_next;
539841ab66cSSepherosa Ziehau 			space = m->m_len - space_next;
540841ab66cSSepherosa Ziehau 		} else {
541841ab66cSSepherosa Ziehau 			/*
542841ab66cSSepherosa Ziehau 			 * Setup for next buffer.
543841ab66cSSepherosa Ziehau 			 */
544841ab66cSSepherosa Ziehau 			pos = mtod(m, uint8_t *);
545841ab66cSSepherosa Ziehau 			space = m->m_len;
546841ab66cSSepherosa Ziehau 		}
547841ab66cSSepherosa Ziehau 	}
548841ab66cSSepherosa Ziehau done:
549841ab66cSSepherosa Ziehau 	/* tack on MIC */
550841ab66cSSepherosa Ziehau 	xor_block(b, s0, ccmp.ic_trailer);
55132176cfdSRui Paulo 	return m_append(m0, ccmp.ic_trailer, b);
552841ab66cSSepherosa Ziehau }
553841ab66cSSepherosa Ziehau #undef CCMP_ENCRYPT
554841ab66cSSepherosa Ziehau 
555841ab66cSSepherosa Ziehau #define	CCMP_DECRYPT(_i, _b, _b0, _pos, _a, _len) do {	\
556841ab66cSSepherosa Ziehau 	/* Decrypt, with counter */			\
557841ab66cSSepherosa Ziehau 	_b0[14] = (_i >> 8) & 0xff;			\
558841ab66cSSepherosa Ziehau 	_b0[15] = _i & 0xff;				\
559841ab66cSSepherosa Ziehau 	rijndael_encrypt(&ctx->cc_aes, _b0, _b);	\
560841ab66cSSepherosa Ziehau 	xor_block(_pos, _b, _len);			\
561841ab66cSSepherosa Ziehau 	/* Authentication */				\
562841ab66cSSepherosa Ziehau 	xor_block(_a, _pos, _len);			\
563841ab66cSSepherosa Ziehau 	rijndael_encrypt(&ctx->cc_aes, _a, _a);		\
564841ab66cSSepherosa Ziehau } while (0)
565841ab66cSSepherosa Ziehau 
566841ab66cSSepherosa Ziehau static int
ccmp_decrypt(struct ieee80211_key * key,u_int64_t pn,struct mbuf * m,int hdrlen)56732176cfdSRui Paulo ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, int hdrlen)
568841ab66cSSepherosa Ziehau {
569841ab66cSSepherosa Ziehau 	struct ccmp_ctx *ctx = key->wk_private;
57032176cfdSRui Paulo 	struct ieee80211vap *vap = ctx->cc_vap;
571841ab66cSSepherosa Ziehau 	struct ieee80211_frame *wh;
572841ab66cSSepherosa Ziehau 	uint8_t aad[2 * AES_BLOCK_LEN];
573841ab66cSSepherosa Ziehau 	uint8_t b0[AES_BLOCK_LEN], b[AES_BLOCK_LEN], a[AES_BLOCK_LEN];
574841ab66cSSepherosa Ziehau 	uint8_t mic[AES_BLOCK_LEN];
575841ab66cSSepherosa Ziehau 	size_t data_len;
576841ab66cSSepherosa Ziehau 	int i;
577841ab66cSSepherosa Ziehau 	uint8_t *pos;
578841ab66cSSepherosa Ziehau 	u_int space;
579841ab66cSSepherosa Ziehau 
58032176cfdSRui Paulo 	ctx->cc_vap->iv_stats.is_crypto_ccmp++;
581841ab66cSSepherosa Ziehau 
582841ab66cSSepherosa Ziehau 	wh = mtod(m, struct ieee80211_frame *);
58332176cfdSRui Paulo 	data_len = m->m_pkthdr.len - (hdrlen + ccmp.ic_header + ccmp.ic_trailer);
584841ab66cSSepherosa Ziehau 	ccmp_init_blocks(&ctx->cc_aes, wh, pn, data_len, b0, aad, a, b);
58532176cfdSRui Paulo 	m_copydata(m, m->m_pkthdr.len - ccmp.ic_trailer, ccmp.ic_trailer, mic);
586841ab66cSSepherosa Ziehau 	xor_block(mic, b, ccmp.ic_trailer);
587841ab66cSSepherosa Ziehau 
588841ab66cSSepherosa Ziehau 	i = 1;
589841ab66cSSepherosa Ziehau 	pos = mtod(m, uint8_t *) + hdrlen + ccmp.ic_header;
590841ab66cSSepherosa Ziehau 	space = m->m_len - (hdrlen + ccmp.ic_header);
591841ab66cSSepherosa Ziehau 	for (;;) {
592841ab66cSSepherosa Ziehau 		if (space > data_len)
593841ab66cSSepherosa Ziehau 			space = data_len;
594841ab66cSSepherosa Ziehau 		while (space >= AES_BLOCK_LEN) {
595841ab66cSSepherosa Ziehau 			CCMP_DECRYPT(i, b, b0, pos, a, AES_BLOCK_LEN);
596841ab66cSSepherosa Ziehau 			pos += AES_BLOCK_LEN, space -= AES_BLOCK_LEN;
597841ab66cSSepherosa Ziehau 			data_len -= AES_BLOCK_LEN;
598841ab66cSSepherosa Ziehau 			i++;
599841ab66cSSepherosa Ziehau 		}
600841ab66cSSepherosa Ziehau 		if (data_len <= 0)		/* no more data */
601841ab66cSSepherosa Ziehau 			break;
602841ab66cSSepherosa Ziehau 		m = m->m_next;
603841ab66cSSepherosa Ziehau 		if (m == NULL) {		/* last buffer */
604841ab66cSSepherosa Ziehau 			if (space != 0)		/* short last block */
605841ab66cSSepherosa Ziehau 				CCMP_DECRYPT(i, b, b0, pos, a, space);
606841ab66cSSepherosa Ziehau 			break;
607841ab66cSSepherosa Ziehau 		}
608841ab66cSSepherosa Ziehau 		if (space != 0) {
609841ab66cSSepherosa Ziehau 			uint8_t *pos_next;
610841ab66cSSepherosa Ziehau 			u_int space_next;
611841ab66cSSepherosa Ziehau 			u_int len;
612841ab66cSSepherosa Ziehau 
613841ab66cSSepherosa Ziehau 			/*
614841ab66cSSepherosa Ziehau 			 * Block straddles buffers, split references.  We
615841ab66cSSepherosa Ziehau 			 * do not handle splits that require >2 buffers
616841ab66cSSepherosa Ziehau 			 * since rx'd frames are never badly fragmented
617841ab66cSSepherosa Ziehau 			 * because drivers typically recv in clusters.
618841ab66cSSepherosa Ziehau 			 */
619841ab66cSSepherosa Ziehau 			pos_next = mtod(m, uint8_t *);
620841ab66cSSepherosa Ziehau 			len = min(data_len, AES_BLOCK_LEN);
621841ab66cSSepherosa Ziehau 			space_next = len > space ? len - space : 0;
622841ab66cSSepherosa Ziehau 			KASSERT(m->m_len >= space_next,
623841ab66cSSepherosa Ziehau 				("not enough data in following buffer, "
624085ff963SMatthew Dillon 				"m_len %u need %u\n", m->m_len, space_next));
625841ab66cSSepherosa Ziehau 
626841ab66cSSepherosa Ziehau 			xor_block(b+space, pos_next, space_next);
627841ab66cSSepherosa Ziehau 			CCMP_DECRYPT(i, b, b0, pos, a, space);
628841ab66cSSepherosa Ziehau 			xor_block(pos_next, b+space, space_next);
629841ab66cSSepherosa Ziehau 			data_len -= len;
630841ab66cSSepherosa Ziehau 			i++;
631841ab66cSSepherosa Ziehau 
632841ab66cSSepherosa Ziehau 			pos = pos_next + space_next;
633841ab66cSSepherosa Ziehau 			space = m->m_len - space_next;
634841ab66cSSepherosa Ziehau 		} else {
635841ab66cSSepherosa Ziehau 			/*
636841ab66cSSepherosa Ziehau 			 * Setup for next buffer.
637841ab66cSSepherosa Ziehau 			 */
638841ab66cSSepherosa Ziehau 			pos = mtod(m, uint8_t *);
639841ab66cSSepherosa Ziehau 			space = m->m_len;
640841ab66cSSepherosa Ziehau 		}
641841ab66cSSepherosa Ziehau 	}
642841ab66cSSepherosa Ziehau 	if (memcmp(mic, a, ccmp.ic_trailer) != 0) {
64332176cfdSRui Paulo 		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
64432176cfdSRui Paulo 		    "%s", "AES-CCM decrypt failed; MIC mismatch");
64532176cfdSRui Paulo 		vap->iv_stats.is_rx_ccmpmic++;
646841ab66cSSepherosa Ziehau 		return 0;
647841ab66cSSepherosa Ziehau 	}
648841ab66cSSepherosa Ziehau 	return 1;
649841ab66cSSepherosa Ziehau }
650841ab66cSSepherosa Ziehau #undef CCMP_DECRYPT
651841ab66cSSepherosa Ziehau 
652841ab66cSSepherosa Ziehau /*
653841ab66cSSepherosa Ziehau  * Module glue.
654841ab66cSSepherosa Ziehau  */
65532176cfdSRui Paulo IEEE80211_CRYPTO_MODULE(ccmp, 1);
656