ieee80211_crypto_wep.c revision 153353
1211809Sjchandra/*-
2198160Srrs * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
3198160Srrs * All rights reserved.
4198160Srrs *
5198160Srrs * Redistribution and use in source and binary forms, with or without
6198160Srrs * modification, are permitted provided that the following conditions
7198160Srrs * are met:
8198160Srrs * 1. Redistributions of source code must retain the above copyright
9198160Srrs *    notice, this list of conditions and the following disclaimer.
10198160Srrs * 2. Redistributions in binary form must reproduce the above copyright
11198160Srrs *    notice, this list of conditions and the following disclaimer in the
12198160Srrs *    documentation and/or other materials provided with the distribution.
13198160Srrs * 3. The name of the author may not be used to endorse or promote products
14198160Srrs *    derived from this software without specific prior written permission.
15198160Srrs *
16198160Srrs * Alternatively, this software may be distributed under the terms of the
17198160Srrs * GNU General Public License ("GPL") version 2 as published by the Free
18198160Srrs * Software Foundation.
19198160Srrs *
20198160Srrs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21198160Srrs * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22198160Srrs * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23198160Srrs * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24198160Srrs * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25198160Srrs * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26198160Srrs * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27198160Srrs * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28198160Srrs * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29198160Srrs * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30203112Srrs */
31203112Srrs
32198160Srrs#include <sys/cdefs.h>
33198160Srrs__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_crypto_wep.c 153353 2005-12-12 19:07:48Z sam $");
34198160Srrs
35198160Srrs/*
36198160Srrs * IEEE 802.11 WEP crypto support.
37198160Srrs */
38198160Srrs#include <sys/param.h>
39198160Srrs#include <sys/systm.h>
40280013Sian#include <sys/mbuf.h>
41198160Srrs#include <sys/malloc.h>
42208165Srrs#include <sys/kernel.h>
43208165Srrs#include <sys/module.h>
44208165Srrs#include <sys/endian.h>
45208165Srrs
46208165Srrs#include <sys/socket.h>
47208165Srrs
48208165Srrs#include <net/if.h>
49208165Srrs#include <net/if_media.h>
50208165Srrs#include <net/ethernet.h>
51198160Srrs
52198160Srrs#include <net80211/ieee80211_var.h>
53208165Srrs
54198160Srrsstatic	void *wep_attach(struct ieee80211com *, struct ieee80211_key *);
55198160Srrsstatic	void wep_detach(struct ieee80211_key *);
56198160Srrsstatic	int wep_setkey(struct ieee80211_key *);
57198160Srrsstatic	int wep_encap(struct ieee80211_key *, struct mbuf *, u_int8_t keyid);
58198607Srrsstatic	int wep_decap(struct ieee80211_key *, struct mbuf *, int hdrlen);
59198607Srrsstatic	int wep_enmic(struct ieee80211_key *, struct mbuf *, int);
60198607Srrsstatic	int wep_demic(struct ieee80211_key *, struct mbuf *, int);
61198607Srrs
62198160Srrsstatic const struct ieee80211_cipher wep = {
63198160Srrs	.ic_name	= "WEP",
64198160Srrs	.ic_cipher	= IEEE80211_CIPHER_WEP,
65198160Srrs	.ic_header	= IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN,
66198160Srrs	.ic_trailer	= IEEE80211_WEP_CRCLEN,
67198160Srrs	.ic_miclen	= 0,
68198160Srrs	.ic_attach	= wep_attach,
69198160Srrs	.ic_detach	= wep_detach,
70198160Srrs	.ic_setkey	= wep_setkey,
71198160Srrs	.ic_encap	= wep_encap,
72198160Srrs	.ic_decap	= wep_decap,
73198160Srrs	.ic_enmic	= wep_enmic,
74198160Srrs	.ic_demic	= wep_demic,
75198160Srrs};
76213377Sjchandra
77213377Sjchandrastatic	int wep_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
78213377Sjchandrastatic	int wep_decrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
79213377Sjchandra
80213377Sjchandrastruct wep_ctx {
81213377Sjchandra	struct ieee80211com *wc_ic;	/* for diagnostics */
82213377Sjchandra	u_int32_t	wc_iv;		/* initial vector for crypto */
83213377Sjchandra};
84213377Sjchandra
85213377Sjchandra/* number of references from net80211 layer */
86213377Sjchandrastatic	int nrefs = 0;
87213377Sjchandra
88213377Sjchandrastatic void *
89213377Sjchandrawep_attach(struct ieee80211com *ic, struct ieee80211_key *k)
90213377Sjchandra{
91213377Sjchandra	struct wep_ctx *ctx;
92213377Sjchandra
93198160Srrs	MALLOC(ctx, struct wep_ctx *, sizeof(struct wep_ctx),
94213377Sjchandra		M_DEVBUF, M_NOWAIT | M_ZERO);
95213377Sjchandra	if (ctx == NULL) {
96213443Sjchandra		ic->ic_stats.is_crypto_nomem++;
97213377Sjchandra		return NULL;
98213443Sjchandra	}
99213377Sjchandra
100213377Sjchandra	ctx->wc_ic = ic;
101213377Sjchandra	get_random_bytes(&ctx->wc_iv, sizeof(ctx->wc_iv));
102218909Sbrucec	nrefs++;			/* NB: we assume caller locking */
103213377Sjchandra	return ctx;
104213377Sjchandra}
105213377Sjchandra
106213377Sjchandrastatic void
107213377Sjchandrawep_detach(struct ieee80211_key *k)
108213377Sjchandra{
109213377Sjchandra	struct wep_ctx *ctx = k->wk_private;
110213377Sjchandra
111213377Sjchandra	FREE(ctx, M_DEVBUF);
112213377Sjchandra	KASSERT(nrefs > 0, ("imbalanced attach/detach"));
113213377Sjchandra	nrefs--;			/* NB: we assume caller locking */
114213377Sjchandra}
115213377Sjchandra
116213377Sjchandrastatic int
117213377Sjchandrawep_setkey(struct ieee80211_key *k)
118198625Srrs{
119198625Srrs	return k->wk_keylen >= 40/NBBY;
120198160Srrs}
121198160Srrs
122198160Srrs/*
123213377Sjchandra * Add privacy headers appropriate for the specified key.
124198160Srrs */
125198160Srrsstatic int
126208165Srrswep_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid)
127213377Sjchandra{
128208165Srrs	struct wep_ctx *ctx = k->wk_private;
129198160Srrs	struct ieee80211com *ic = ctx->wc_ic;
130198160Srrs	u_int32_t iv;
131198160Srrs	u_int8_t *ivp;
132213377Sjchandra	int hdrlen;
133198625Srrs
134198625Srrs	hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));
135213377Sjchandra
136213377Sjchandra	/*
137198160Srrs	 * Copy down 802.11 header and add the IV + KeyID.
138198625Srrs	 */
139198625Srrs	M_PREPEND(m, wep.ic_header, M_NOWAIT);
140198625Srrs	if (m == NULL)
141198625Srrs		return 0;
142198625Srrs	ivp = mtod(m, u_int8_t *);
143198625Srrs	ovbcopy(ivp + wep.ic_header, ivp, hdrlen);
144198625Srrs	ivp += hdrlen;
145198625Srrs
146198160Srrs	/*
147213377Sjchandra	 * XXX
148213377Sjchandra	 * IV must not duplicate during the lifetime of the key.
149213377Sjchandra	 * But no mechanism to renew keys is defined in IEEE 802.11
150213377Sjchandra	 * for WEP.  And the IV may be duplicated at other stations
151218909Sbrucec	 * because the session key itself is shared.  So we use a
152213443Sjchandra	 * pseudo random IV for now, though it is not the right way.
153213377Sjchandra	 *
154213377Sjchandra	 * NB: Rather than use a strictly random IV we select a
155213377Sjchandra	 * random one to start and then increment the value for
156213377Sjchandra	 * each frame.  This is an explicit tradeoff between
157213377Sjchandra	 * overhead and security.  Given the basic insecurity of
158213377Sjchandra	 * WEP this seems worthwhile.
159213377Sjchandra	 */
160213377Sjchandra
161213377Sjchandra	/*
162213377Sjchandra	 * Skip 'bad' IVs from Fluhrer/Mantin/Shamir:
163213377Sjchandra	 * (B, 255, N) with 3 <= B < 16 and 0 <= N <= 255
164198160Srrs	 */
165198160Srrs	iv = ctx->wc_iv;
166198160Srrs	if ((iv & 0xff00) == 0xff00) {
167198160Srrs		int B = (iv & 0xff0000) >> 16;
168198160Srrs		if (3 <= B && B < 16)
169198160Srrs			iv += 0x0100;
170212321Sjchandra	}
171198160Srrs	ctx->wc_iv = iv + 1;
172198160Srrs
173213377Sjchandra	/*
174213377Sjchandra	 * NB: Preserve byte order of IV for packet
175213377Sjchandra	 *     sniffers; it doesn't matter otherwise.
176198625Srrs	 */
177198625Srrs#if _BYTE_ORDER == _BIG_ENDIAN
178198160Srrs	ivp[0] = iv >> 0;
179213377Sjchandra	ivp[1] = iv >> 8;
180213377Sjchandra	ivp[2] = iv >> 16;
181213377Sjchandra#else
182213377Sjchandra	ivp[2] = iv >> 0;
183213377Sjchandra	ivp[1] = iv >> 8;
184198160Srrs	ivp[0] = iv >> 16;
185198160Srrs#endif
186212790Sjchandra	ivp[3] = keyid;
187212790Sjchandra
188212790Sjchandra	/*
189212790Sjchandra	 * Finally, do software encrypt if neeed.
190212790Sjchandra	 */
191212790Sjchandra	if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
192198160Srrs	    !wep_encrypt(k, m, hdrlen))
193198625Srrs		return 0;
194213377Sjchandra
195198160Srrs	return 1;
196213377Sjchandra}
197198625Srrs
198212790Sjchandra/*
199213377Sjchandra * Add MIC to the frame as needed.
200213377Sjchandra */
201198160Srrsstatic int
202212790Sjchandrawep_enmic(struct ieee80211_key *k, struct mbuf *m, int force)
203212321Sjchandra{
204198625Srrs
205213377Sjchandra	return 1;
206198160Srrs}
207198625Srrs
208213377Sjchandra/*
209198625Srrs * Validate and strip privacy headers (and trailer) for a
210198160Srrs * received frame.  If necessary, decrypt the frame using
211213377Sjchandra * the specified key.
212213377Sjchandra */
213198625Srrsstatic int
214198625Srrswep_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
215213377Sjchandra{
216213377Sjchandra	struct wep_ctx *ctx = k->wk_private;
217213377Sjchandra	struct ieee80211_frame *wh;
218198625Srrs
219212790Sjchandra	wh = mtod(m, struct ieee80211_frame *);
220213377Sjchandra
221213377Sjchandra	/*
222213377Sjchandra	 * Check if the device handled the decrypt in hardware.
223213377Sjchandra	 * If so we just strip the header; otherwise we need to
224213377Sjchandra	 * handle the decrypt in software.
225213377Sjchandra	 */
226198625Srrs	if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
227212321Sjchandra	    !wep_decrypt(k, m, hdrlen)) {
228213377Sjchandra		IEEE80211_DPRINTF(ctx->wc_ic, IEEE80211_MSG_CRYPTO,
229213377Sjchandra		    "[%s] WEP ICV mismatch on decrypt\n",
230212321Sjchandra		    ether_sprintf(wh->i_addr2));
231198625Srrs		ctx->wc_ic->ic_stats.is_rx_wepfail++;
232212790Sjchandra		return 0;
233212790Sjchandra	}
234198160Srrs
235198160Srrs	/*
236212790Sjchandra	 * Copy up 802.11 header and strip crypto bits.
237212790Sjchandra	 */
238212321Sjchandra	ovbcopy(mtod(m, void *), mtod(m, u_int8_t *) + wep.ic_header, hdrlen);
239212790Sjchandra	m_adj(m, wep.ic_header);
240198160Srrs	m_adj(m, -wep.ic_trailer);
241198160Srrs
242213377Sjchandra	return 1;
243213377Sjchandra}
244213377Sjchandra
245213377Sjchandra/*
246213377Sjchandra * Verify and strip MIC from the frame.
247213377Sjchandra */
248213377Sjchandrastatic int
249213377Sjchandrawep_demic(struct ieee80211_key *k, struct mbuf *skb, int force)
250198160Srrs{
251212321Sjchandra	return 1;
252213377Sjchandra}
253213377Sjchandra
254198160Srrsstatic const uint32_t crc32_table[256] = {
255213377Sjchandra	0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
256213377Sjchandra	0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
257213377Sjchandra	0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
258198160Srrs	0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
259213377Sjchandra	0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
260213377Sjchandra	0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
261213377Sjchandra	0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
262213377Sjchandra	0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
263213377Sjchandra	0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
264213377Sjchandra	0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
265213443Sjchandra	0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
266213377Sjchandra	0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
267213443Sjchandra	0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
268213377Sjchandra	0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
269213443Sjchandra	0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
270213377Sjchandra	0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
271213377Sjchandra	0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
272213377Sjchandra	0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
273212321Sjchandra	0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
274213377Sjchandra	0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
275213377Sjchandra	0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
276213443Sjchandra	0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
277213377Sjchandra	0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
278198160Srrs	0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
279212321Sjchandra	0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
280198160Srrs	0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
281198160Srrs	0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
282213443Sjchandra	0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
283213443Sjchandra	0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
284213443Sjchandra	0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
285213443Sjchandra	0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
286213443Sjchandra	0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
287213443Sjchandra	0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
288213443Sjchandra	0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
289208165Srrs	0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
290208165Srrs	0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
291208165Srrs	0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
292213377Sjchandra	0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
293213377Sjchandra	0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
294213377Sjchandra	0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
295213377Sjchandra	0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
296198160Srrs	0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
297213377Sjchandra	0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
298213377Sjchandra	0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
299213443Sjchandra	0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
300213377Sjchandra	0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
301213377Sjchandra	0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
302213377Sjchandra	0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
303213443Sjchandra	0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
304213377Sjchandra	0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
305213377Sjchandra	0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
306213377Sjchandra	0x2d02ef8dL
307213377Sjchandra};
308213377Sjchandra
309213377Sjchandrastatic int
310213377Sjchandrawep_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
311213377Sjchandra{
312213377Sjchandra#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
313213443Sjchandra	struct wep_ctx *ctx = key->wk_private;
314213443Sjchandra	struct mbuf *m = m0;
315213377Sjchandra	u_int8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
316213377Sjchandra	uint8_t icv[IEEE80211_WEP_CRCLEN];
317213377Sjchandra	uint32_t i, j, k, crc;
318213377Sjchandra	size_t buflen, data_len;
319213377Sjchandra	uint8_t S[256];
320208165Srrs	uint8_t *pos;
321208165Srrs	u_int off, keylen;
322213443Sjchandra
323208165Srrs	ctx->wc_ic->ic_stats.is_crypto_wep++;
324208165Srrs
325213443Sjchandra	/* NB: this assumes the header was pulled up */
326213443Sjchandra	memcpy(rc4key, mtod(m, u_int8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
327208165Srrs	memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen);
328213377Sjchandra
329208165Srrs	/* Setup RC4 state */
330208165Srrs	for (i = 0; i < 256; i++)
331208165Srrs		S[i] = i;
332208165Srrs	j = 0;
333208165Srrs	keylen = key->wk_keylen + IEEE80211_WEP_IVLEN;
334213377Sjchandra	for (i = 0; i < 256; i++) {
335213377Sjchandra		j = (j + S[i] + rc4key[i % keylen]) & 0xff;
336213377Sjchandra		S_SWAP(i, j);
337213377Sjchandra	}
338208165Srrs
339213377Sjchandra	off = hdrlen + wep.ic_header;
340213377Sjchandra	data_len = m->m_pkthdr.len - off;
341213377Sjchandra
342213377Sjchandra	/* Compute CRC32 over unencrypted data and apply RC4 to data */
343213377Sjchandra	crc = ~0;
344213377Sjchandra	i = j = 0;
345213377Sjchandra	pos = mtod(m, uint8_t *) + off;
346208165Srrs	buflen = m->m_len - off;
347208165Srrs	for (;;) {
348208165Srrs		if (buflen > data_len)
349213377Sjchandra			buflen = data_len;
350208165Srrs		data_len -= buflen;
351208165Srrs		for (k = 0; k < buflen; k++) {
352213377Sjchandra			crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8);
353213443Sjchandra			i = (i + 1) & 0xff;
354213377Sjchandra			j = (j + S[i]) & 0xff;
355213377Sjchandra			S_SWAP(i, j);
356213377Sjchandra			*pos++ ^= S[(S[i] + S[j]) & 0xff];
357213377Sjchandra		}
358213377Sjchandra		if (m->m_next == NULL) {
359213377Sjchandra			if (data_len != 0) {		/* out of data */
360213377Sjchandra				IEEE80211_DPRINTF(ctx->wc_ic,
361213377Sjchandra				    IEEE80211_MSG_CRYPTO,
362213377Sjchandra				    "[%s] out of data for WEP (data_len %zu)\n",
363213377Sjchandra				    ether_sprintf(mtod(m0,
364213377Sjchandra					struct ieee80211_frame *)->i_addr2),
365213443Sjchandra				    data_len);
366213377Sjchandra				return 0;
367213377Sjchandra			}
368213377Sjchandra			break;
369213377Sjchandra		}
370213377Sjchandra		m = m->m_next;
371213377Sjchandra		pos = mtod(m, uint8_t *);
372213377Sjchandra		buflen = m->m_len;
373213377Sjchandra	}
374213377Sjchandra	crc = ~crc;
375208165Srrs
376213377Sjchandra	/* Append little-endian CRC32 and encrypt it to produce ICV */
377208165Srrs	icv[0] = crc;
378213377Sjchandra	icv[1] = crc >> 8;
379213377Sjchandra	icv[2] = crc >> 16;
380213377Sjchandra	icv[3] = crc >> 24;
381213377Sjchandra	for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) {
382208165Srrs		i = (i + 1) & 0xff;
383208165Srrs		j = (j + S[i]) & 0xff;
384208165Srrs		S_SWAP(i, j);
385208165Srrs		icv[k] ^= S[(S[i] + S[j]) & 0xff];
386213377Sjchandra	}
387208165Srrs	return m_append(m0, IEEE80211_WEP_CRCLEN, icv);
388213377Sjchandra#undef S_SWAP
389208165Srrs}
390213377Sjchandra
391213377Sjchandrastatic int
392208165Srrswep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
393213377Sjchandra{
394213377Sjchandra#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
395213377Sjchandra	struct wep_ctx *ctx = key->wk_private;
396213377Sjchandra	struct mbuf *m = m0;
397213377Sjchandra	u_int8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
398213377Sjchandra	uint8_t icv[IEEE80211_WEP_CRCLEN];
399213377Sjchandra	uint32_t i, j, k, crc;
400213474Sjchandra	size_t buflen, data_len;
401213377Sjchandra	uint8_t S[256];
402213377Sjchandra	uint8_t *pos;
403208165Srrs	u_int off, keylen;
404213377Sjchandra
405213377Sjchandra	ctx->wc_ic->ic_stats.is_crypto_wep++;
406208165Srrs
407208165Srrs	/* NB: this assumes the header was pulled up */
408208165Srrs	memcpy(rc4key, mtod(m, u_int8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
409208165Srrs	memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen);
410208165Srrs
411210845Sjchandra	/* Setup RC4 state */
412208165Srrs	for (i = 0; i < 256; i++)
413208165Srrs		S[i] = i;
414198625Srrs	j = 0;
415213377Sjchandra	keylen = key->wk_keylen + IEEE80211_WEP_IVLEN;
416213377Sjchandra	for (i = 0; i < 256; i++) {
417198160Srrs		j = (j + S[i] + rc4key[i % keylen]) & 0xff;
418213377Sjchandra		S_SWAP(i, j);
419213377Sjchandra	}
420213377Sjchandra
421198160Srrs	off = hdrlen + wep.ic_header;
422213377Sjchandra	data_len = m->m_pkthdr.len - (off + wep.ic_trailer),
423213377Sjchandra
424198160Srrs	/* Compute CRC32 over unencrypted data and apply RC4 to data */
425213377Sjchandra	crc = ~0;
426213377Sjchandra	i = j = 0;
427213377Sjchandra	pos = mtod(m, uint8_t *) + off;
428213377Sjchandra	buflen = m->m_len - off;
429213377Sjchandra	for (;;) {
430213377Sjchandra		if (buflen > data_len)
431213377Sjchandra			buflen = data_len;
432213377Sjchandra		data_len -= buflen;
433198625Srrs		for (k = 0; k < buflen; k++) {
434198160Srrs			i = (i + 1) & 0xff;
435213377Sjchandra			j = (j + S[i]) & 0xff;
436213377Sjchandra			S_SWAP(i, j);
437213377Sjchandra			*pos ^= S[(S[i] + S[j]) & 0xff];
438213377Sjchandra			crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8);
439203112Srrs			pos++;
440217072Sjhb		}
441198160Srrs		m = m->m_next;
442213377Sjchandra		if (m == NULL) {
443198160Srrs			if (data_len != 0) {		/* out of data */
444198160Srrs				IEEE80211_DPRINTF(ctx->wc_ic,
445213443Sjchandra				    IEEE80211_MSG_CRYPTO,
446213443Sjchandra				    "[%s] out of data for WEP (data_len %zu)\n",
447213443Sjchandra				    ether_sprintf(mtod(m0,
448208165Srrs					struct ieee80211_frame *)->i_addr2),
449208165Srrs				    data_len);
450208165Srrs				return 0;
451213377Sjchandra			}
452208165Srrs			break;
453213377Sjchandra		}
454213377Sjchandra		pos = mtod(m, uint8_t *);
455213377Sjchandra		buflen = m->m_len;
456213377Sjchandra	}
457213377Sjchandra	crc = ~crc;
458213377Sjchandra
459213377Sjchandra	/* Encrypt little-endian CRC32 and verify that it matches with
460208369Sjchandra	 * received ICV */
461198160Srrs	icv[0] = crc;
462208165Srrs	icv[1] = crc >> 8;
463213443Sjchandra	icv[2] = crc >> 16;
464213443Sjchandra	icv[3] = crc >> 24;
465213443Sjchandra	for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) {
466213443Sjchandra		i = (i + 1) & 0xff;
467213443Sjchandra		j = (j + S[i]) & 0xff;
468213443Sjchandra		S_SWAP(i, j);
469213443Sjchandra		/* XXX assumes ICV is contiguous in mbuf */
470213443Sjchandra		if ((icv[k] ^ S[(S[i] + S[j]) & 0xff]) != *pos++) {
471213443Sjchandra			/* ICV mismatch - drop frame */
472280013Sian			return 0;
473280013Sian		}
474213443Sjchandra	}
475280013Sian	return 1;
476280013Sian#undef S_SWAP
477213443Sjchandra}
478213443Sjchandra
479213443Sjchandra/*
480213443Sjchandra * Module glue.
481280013Sian */
482213443Sjchandrastatic int
483213443Sjchandrawep_modevent(module_t mod, int type, void *unused)
484213443Sjchandra{
485213443Sjchandra	switch (type) {
486280013Sian	case MOD_LOAD:
487280013Sian		ieee80211_crypto_register(&wep);
488213443Sjchandra		return 0;
489213443Sjchandra	case MOD_UNLOAD:
490213443Sjchandra	case MOD_QUIESCE:
491213443Sjchandra		if (nrefs) {
492213443Sjchandra			printf("wlan_wep: still in use (%u dynamic refs)\n",
493				nrefs);
494			return EBUSY;
495		}
496		if (type == MOD_UNLOAD)
497			ieee80211_crypto_unregister(&wep);
498		return 0;
499	}
500	return EINVAL;
501}
502
503static moduledata_t wep_mod = {
504	"wlan_wep",
505	wep_modevent,
506	0
507};
508DECLARE_MODULE(wlan_wep, wep_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
509MODULE_VERSION(wlan_wep, 1);
510MODULE_DEPEND(wlan_wep, wlan, 1, 1, 1);
511