ieee80211_crypto_wep.c revision 170360
1193323Sed/*-
2193323Sed * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
3193323Sed * All rights reserved.
4193323Sed *
5193323Sed * Redistribution and use in source and binary forms, with or without
6193323Sed * modification, are permitted provided that the following conditions
7193323Sed * are met:
8193323Sed * 1. Redistributions of source code must retain the above copyright
9193323Sed *    notice, this list of conditions and the following disclaimer.
10193323Sed * 2. Redistributions in binary form must reproduce the above copyright
11193323Sed *    notice, this list of conditions and the following disclaimer in the
12193323Sed *    documentation and/or other materials provided with the distribution.
13193323Sed *
14198090Srdivacky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15198090Srdivacky * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16193323Sed * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17193323Sed * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18198090Srdivacky * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19193323Sed * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20193323Sed * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21193323Sed * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22193323Sed * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23193323Sed * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24193323Sed */
25193323Sed
26193323Sed#include <sys/cdefs.h>
27193323Sed__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_crypto_wep.c 170360 2007-06-06 04:56:04Z sam $");
28193323Sed
29193323Sed/*
30193323Sed * IEEE 802.11 WEP crypto support.
31193323Sed */
32193323Sed#include <sys/param.h>
33193323Sed#include <sys/systm.h>
34193323Sed#include <sys/mbuf.h>
35218893Sdim#include <sys/malloc.h>
36198090Srdivacky#include <sys/kernel.h>
37198090Srdivacky#include <sys/module.h>
38193323Sed#include <sys/endian.h>
39198090Srdivacky
40193323Sed#include <sys/socket.h>
41193323Sed
42193323Sed#include <net/if.h>
43193323Sed#include <net/if_media.h>
44193323Sed#include <net/ethernet.h>
45193323Sed
46193323Sed#include <net80211/ieee80211_var.h>
47193323Sed
48193323Sedstatic	void *wep_attach(struct ieee80211com *, struct ieee80211_key *);
49193323Sedstatic	void wep_detach(struct ieee80211_key *);
50193323Sedstatic	int wep_setkey(struct ieee80211_key *);
51193323Sedstatic	int wep_encap(struct ieee80211_key *, struct mbuf *, u_int8_t keyid);
52218893Sdimstatic	int wep_decap(struct ieee80211_key *, struct mbuf *, int hdrlen);
53218893Sdimstatic	int wep_enmic(struct ieee80211_key *, struct mbuf *, int);
54193323Sedstatic	int wep_demic(struct ieee80211_key *, struct mbuf *, int);
55193323Sed
56193323Sedstatic const struct ieee80211_cipher wep = {
57193323Sed	.ic_name	= "WEP",
58193323Sed	.ic_cipher	= IEEE80211_CIPHER_WEP,
59218893Sdim	.ic_header	= IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN,
60218893Sdim	.ic_trailer	= IEEE80211_WEP_CRCLEN,
61193323Sed	.ic_miclen	= 0,
62193323Sed	.ic_attach	= wep_attach,
63193323Sed	.ic_detach	= wep_detach,
64198090Srdivacky	.ic_setkey	= wep_setkey,
65198090Srdivacky	.ic_encap	= wep_encap,
66218893Sdim	.ic_decap	= wep_decap,
67218893Sdim	.ic_enmic	= wep_enmic,
68198090Srdivacky	.ic_demic	= wep_demic,
69193323Sed};
70193323Sed
71193323Sedstatic	int wep_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
72218893Sdimstatic	int wep_decrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
73218893Sdim
74193323Sedstruct wep_ctx {
75193323Sed	struct ieee80211com *wc_ic;	/* for diagnostics */
76218893Sdim	u_int32_t	wc_iv;		/* initial vector for crypto */
77193323Sed};
78198090Srdivacky
79193323Sed/* number of references from net80211 layer */
80193323Sedstatic	int nrefs = 0;
81193323Sed
82193323Sedstatic void *
83193323Sedwep_attach(struct ieee80211com *ic, struct ieee80211_key *k)
84193323Sed{
85193323Sed	struct wep_ctx *ctx;
86218893Sdim
87218893Sdim	MALLOC(ctx, struct wep_ctx *, sizeof(struct wep_ctx),
88193323Sed		M_DEVBUF, M_NOWAIT | M_ZERO);
89193323Sed	if (ctx == NULL) {
90193323Sed		ic->ic_stats.is_crypto_nomem++;
91193323Sed		return NULL;
92193724Sed	}
93218893Sdim
94193724Sed	ctx->wc_ic = ic;
95198090Srdivacky	get_random_bytes(&ctx->wc_iv, sizeof(ctx->wc_iv));
96193724Sed	nrefs++;			/* NB: we assume caller locking */
97193724Sed	return ctx;
98218893Sdim}
99194612Sed
100193724Sedstatic void
101194612Sedwep_detach(struct ieee80211_key *k)
102193724Sed{
103193724Sed	struct wep_ctx *ctx = k->wk_private;
104193724Sed
105193724Sed	FREE(ctx, M_DEVBUF);
106198090Srdivacky	KASSERT(nrefs > 0, ("imbalanced attach/detach"));
107193724Sed	nrefs--;			/* NB: we assume caller locking */
108193724Sed}
109193724Sed
110193724Sedstatic int
111193323Sedwep_setkey(struct ieee80211_key *k)
112193323Sed{
113193323Sed	return k->wk_keylen >= 40/NBBY;
114193323Sed}
115193323Sed
116193323Sed/*
117198396Srdivacky * Add privacy headers appropriate for the specified key.
118198396Srdivacky */
119193323Sedstatic int
120193323Sedwep_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid)
121218893Sdim{
122193323Sed	struct wep_ctx *ctx = k->wk_private;
123193323Sed	struct ieee80211com *ic = ctx->wc_ic;
124218893Sdim	u_int32_t iv;
125203954Srdivacky	u_int8_t *ivp;
126193323Sed	int hdrlen;
127193323Sed
128218893Sdim	hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));
129193323Sed
130193323Sed	/*
131193323Sed	 * Copy down 802.11 header and add the IV + KeyID.
132193323Sed	 */
133193323Sed	M_PREPEND(m, wep.ic_header, M_NOWAIT);
134193323Sed	if (m == NULL)
135193323Sed		return 0;
136218893Sdim	ivp = mtod(m, u_int8_t *);
137218893Sdim	ovbcopy(ivp + wep.ic_header, ivp, hdrlen);
138203954Srdivacky	ivp += hdrlen;
139193323Sed
140193323Sed	/*
141193323Sed	 * XXX
142193323Sed	 * IV must not duplicate during the lifetime of the key.
143193323Sed	 * But no mechanism to renew keys is defined in IEEE 802.11
144193323Sed	 * for WEP.  And the IV may be duplicated at other stations
145193323Sed	 * because the session key itself is shared.  So we use a
146193323Sed	 * pseudo random IV for now, though it is not the right way.
147193323Sed	 *
148193323Sed	 * NB: Rather than use a strictly random IV we select a
149193323Sed	 * random one to start and then increment the value for
150193323Sed	 * each frame.  This is an explicit tradeoff between
151193323Sed	 * overhead and security.  Given the basic insecurity of
152193323Sed	 * WEP this seems worthwhile.
153193323Sed	 */
154193323Sed
155193323Sed	/*
156193323Sed	 * Skip 'bad' IVs from Fluhrer/Mantin/Shamir:
157193323Sed	 * (B, 255, N) with 3 <= B < 16 and 0 <= N <= 255
158193323Sed	 */
159193323Sed	iv = ctx->wc_iv;
160193323Sed	if ((iv & 0xff00) == 0xff00) {
161193323Sed		int B = (iv & 0xff0000) >> 16;
162193323Sed		if (3 <= B && B < 16)
163193323Sed			iv += 0x0100;
164193323Sed	}
165193323Sed	ctx->wc_iv = iv + 1;
166193323Sed
167193323Sed	/*
168193323Sed	 * NB: Preserve byte order of IV for packet
169193323Sed	 *     sniffers; it doesn't matter otherwise.
170193323Sed	 */
171193323Sed#if _BYTE_ORDER == _BIG_ENDIAN
172198090Srdivacky	ivp[0] = iv >> 0;
173198090Srdivacky	ivp[1] = iv >> 8;
174193323Sed	ivp[2] = iv >> 16;
175218893Sdim#else
176207618Srdivacky	ivp[2] = iv >> 0;
177207618Srdivacky	ivp[1] = iv >> 8;
178207618Srdivacky	ivp[0] = iv >> 16;
179193323Sed#endif
180193323Sed	ivp[3] = keyid;
181193323Sed
182193323Sed	/*
183193323Sed	 * Finally, do software encrypt if neeed.
184193323Sed	 */
185193323Sed	if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
186193323Sed	    !wep_encrypt(k, m, hdrlen))
187193323Sed		return 0;
188193323Sed
189193323Sed	return 1;
190193323Sed}
191193323Sed
192193323Sed/*
193193323Sed * Add MIC to the frame as needed.
194193323Sed */
195193323Sedstatic int
196193323Sedwep_enmic(struct ieee80211_key *k, struct mbuf *m, int force)
197193323Sed{
198193323Sed
199193323Sed	return 1;
200193323Sed}
201193323Sed
202193323Sed/*
203193323Sed * Validate and strip privacy headers (and trailer) for a
204198090Srdivacky * received frame.  If necessary, decrypt the frame using
205193323Sed * the specified key.
206193323Sed */
207193323Sedstatic int
208193323Sedwep_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
209203954Srdivacky{
210193323Sed	struct wep_ctx *ctx = k->wk_private;
211193323Sed	struct ieee80211_frame *wh;
212193323Sed
213218893Sdim	wh = mtod(m, struct ieee80211_frame *);
214203954Srdivacky
215193323Sed	/*
216193323Sed	 * Check if the device handled the decrypt in hardware.
217193323Sed	 * If so we just strip the header; otherwise we need to
218193323Sed	 * handle the decrypt in software.
219193323Sed	 */
220218893Sdim	if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
221218893Sdim	    !wep_decrypt(k, m, hdrlen)) {
222203954Srdivacky		IEEE80211_DPRINTF(ctx->wc_ic, IEEE80211_MSG_CRYPTO,
223203954Srdivacky		    "[%s] WEP ICV mismatch on decrypt\n",
224193323Sed		    ether_sprintf(wh->i_addr2));
225193323Sed		ctx->wc_ic->ic_stats.is_rx_wepfail++;
226193323Sed		return 0;
227193323Sed	}
228193323Sed
229193323Sed	/*
230193323Sed	 * Copy up 802.11 header and strip crypto bits.
231193323Sed	 */
232193323Sed	ovbcopy(mtod(m, void *), mtod(m, u_int8_t *) + wep.ic_header, hdrlen);
233193323Sed	m_adj(m, wep.ic_header);
234193323Sed	m_adj(m, -wep.ic_trailer);
235193323Sed
236193323Sed	return 1;
237193323Sed}
238193323Sed
239193323Sed/*
240210299Sed * Verify and strip MIC from the frame.
241210299Sed */
242193323Sedstatic int
243193323Sedwep_demic(struct ieee80211_key *k, struct mbuf *skb, int force)
244193323Sed{
245210299Sed	return 1;
246193323Sed}
247210299Sed
248210299Sedstatic const uint32_t crc32_table[256] = {
249193323Sed	0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
250193323Sed	0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
251193323Sed	0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
252193323Sed	0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
253193323Sed	0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
254193323Sed	0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
255193323Sed	0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
256193323Sed	0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
257193323Sed	0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
258193323Sed	0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
259193323Sed	0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
260193323Sed	0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
261193323Sed	0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
262193323Sed	0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
263193323Sed	0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
264218893Sdim	0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
265193323Sed	0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
266193323Sed	0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
267193323Sed	0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
268193323Sed	0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
269193323Sed	0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
270193323Sed	0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
271218893Sdim	0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
272198090Srdivacky	0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
273207618Srdivacky	0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
274207618Srdivacky	0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
275207618Srdivacky	0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
276193323Sed	0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
277193323Sed	0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
278193323Sed	0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
279193323Sed	0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
280193323Sed	0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
281193323Sed	0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
282193323Sed	0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
283193323Sed	0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
284193323Sed	0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
285218893Sdim	0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
286193323Sed	0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
287193323Sed	0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
288193323Sed	0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
289193323Sed	0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
290193323Sed	0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
291193323Sed	0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
292193323Sed	0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
293193323Sed	0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
294193323Sed	0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
295198090Srdivacky	0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
296193323Sed	0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
297198090Srdivacky	0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
298198090Srdivacky	0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
299193323Sed	0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
300193323Sed	0x2d02ef8dL
301193323Sed};
302193323Sed
303193323Sedstatic int
304193323Sedwep_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
305193323Sed{
306193323Sed#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
307193323Sed	struct wep_ctx *ctx = key->wk_private;
308193323Sed	struct mbuf *m = m0;
309193323Sed	u_int8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
310193323Sed	uint8_t icv[IEEE80211_WEP_CRCLEN];
311193323Sed	uint32_t i, j, k, crc;
312193323Sed	size_t buflen, data_len;
313193323Sed	uint8_t S[256];
314193323Sed	uint8_t *pos;
315193323Sed	u_int off, keylen;
316193323Sed
317193323Sed	ctx->wc_ic->ic_stats.is_crypto_wep++;
318193323Sed
319207618Srdivacky	/* NB: this assumes the header was pulled up */
320207618Srdivacky	memcpy(rc4key, mtod(m, u_int8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
321207618Srdivacky	memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen);
322193323Sed
323193323Sed	/* Setup RC4 state */
324218893Sdim	for (i = 0; i < 256; i++)
325193323Sed		S[i] = i;
326193323Sed	j = 0;
327193323Sed	keylen = key->wk_keylen + IEEE80211_WEP_IVLEN;
328193323Sed	for (i = 0; i < 256; i++) {
329193323Sed		j = (j + S[i] + rc4key[i % keylen]) & 0xff;
330193323Sed		S_SWAP(i, j);
331193323Sed	}
332193323Sed
333193323Sed	off = hdrlen + wep.ic_header;
334193323Sed	data_len = m->m_pkthdr.len - off;
335198090Srdivacky
336193323Sed	/* Compute CRC32 over unencrypted data and apply RC4 to data */
337193323Sed	crc = ~0;
338193323Sed	i = j = 0;
339193323Sed	pos = mtod(m, uint8_t *) + off;
340198090Srdivacky	buflen = m->m_len - off;
341198090Srdivacky	for (;;) {
342193323Sed		if (buflen > data_len)
343193323Sed			buflen = data_len;
344193323Sed		data_len -= buflen;
345193323Sed		for (k = 0; k < buflen; k++) {
346193323Sed			crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8);
347193323Sed			i = (i + 1) & 0xff;
348198090Srdivacky			j = (j + S[i]) & 0xff;
349218893Sdim			S_SWAP(i, j);
350193323Sed			*pos++ ^= S[(S[i] + S[j]) & 0xff];
351193323Sed		}
352193323Sed		if (m->m_next == NULL) {
353193323Sed			if (data_len != 0) {		/* out of data */
354193323Sed				IEEE80211_DPRINTF(ctx->wc_ic,
355193323Sed				    IEEE80211_MSG_CRYPTO,
356193323Sed				    "[%s] out of data for WEP (data_len %zu)\n",
357193323Sed				    ether_sprintf(mtod(m0,
358193323Sed					struct ieee80211_frame *)->i_addr2),
359193323Sed				    data_len);
360193323Sed				return 0;
361193323Sed			}
362193323Sed			break;
363193323Sed		}
364193323Sed		m = m->m_next;
365218893Sdim		pos = mtod(m, uint8_t *);
366193323Sed		buflen = m->m_len;
367193323Sed	}
368193323Sed	crc = ~crc;
369193323Sed
370198090Srdivacky	/* Append little-endian CRC32 and encrypt it to produce ICV */
371193323Sed	icv[0] = crc;
372193323Sed	icv[1] = crc >> 8;
373193323Sed	icv[2] = crc >> 16;
374193323Sed	icv[3] = crc >> 24;
375193323Sed	for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) {
376193323Sed		i = (i + 1) & 0xff;
377193323Sed		j = (j + S[i]) & 0xff;
378193323Sed		S_SWAP(i, j);
379193323Sed		icv[k] ^= S[(S[i] + S[j]) & 0xff];
380193323Sed	}
381193323Sed	return m_append(m0, IEEE80211_WEP_CRCLEN, icv);
382193323Sed#undef S_SWAP
383193323Sed}
384193323Sed
385193323Sedstatic int
386193323Sedwep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
387193323Sed{
388207618Srdivacky#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
389207618Srdivacky	struct wep_ctx *ctx = key->wk_private;
390193323Sed	struct mbuf *m = m0;
391193323Sed	u_int8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
392193323Sed	uint8_t icv[IEEE80211_WEP_CRCLEN];
393193323Sed	uint32_t i, j, k, crc;
394203954Srdivacky	size_t buflen, data_len;
395193323Sed	uint8_t S[256];
396193323Sed	uint8_t *pos;
397193323Sed	u_int off, keylen;
398193323Sed
399193323Sed	ctx->wc_ic->ic_stats.is_crypto_wep++;
400193323Sed
401193323Sed	/* NB: this assumes the header was pulled up */
402193323Sed	memcpy(rc4key, mtod(m, u_int8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
403193323Sed	memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen);
404193323Sed
405218893Sdim	/* Setup RC4 state */
406203954Srdivacky	for (i = 0; i < 256; i++)
407193323Sed		S[i] = i;
408193323Sed	j = 0;
409193323Sed	keylen = key->wk_keylen + IEEE80211_WEP_IVLEN;
410193323Sed	for (i = 0; i < 256; i++) {
411218893Sdim		j = (j + S[i] + rc4key[i % keylen]) & 0xff;
412218893Sdim		S_SWAP(i, j);
413203954Srdivacky	}
414203954Srdivacky
415193323Sed	off = hdrlen + wep.ic_header;
416193323Sed	data_len = m->m_pkthdr.len - (off + wep.ic_trailer),
417193323Sed
418193323Sed	/* Compute CRC32 over unencrypted data and apply RC4 to data */
419193323Sed	crc = ~0;
420193323Sed	i = j = 0;
421193323Sed	pos = mtod(m, uint8_t *) + off;
422193323Sed	buflen = m->m_len - off;
423193323Sed	for (;;) {
424193323Sed		if (buflen > data_len)
425193323Sed			buflen = data_len;
426193323Sed		data_len -= buflen;
427193323Sed		for (k = 0; k < buflen; k++) {
428193323Sed			i = (i + 1) & 0xff;
429193323Sed			j = (j + S[i]) & 0xff;
430193323Sed			S_SWAP(i, j);
431193323Sed			*pos ^= S[(S[i] + S[j]) & 0xff];
432193323Sed			crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8);
433193323Sed			pos++;
434193323Sed		}
435193323Sed		m = m->m_next;
436193323Sed		if (m == NULL) {
437193323Sed			if (data_len != 0) {		/* out of data */
438193323Sed				IEEE80211_DPRINTF(ctx->wc_ic,
439193323Sed				    IEEE80211_MSG_CRYPTO,
440193323Sed				    "[%s] out of data for WEP (data_len %zu)\n",
441193323Sed				    ether_sprintf(mtod(m0,
442193323Sed					struct ieee80211_frame *)->i_addr2),
443193323Sed				    data_len);
444193323Sed				return 0;
445193323Sed			}
446193323Sed			break;
447193323Sed		}
448193323Sed		pos = mtod(m, uint8_t *);
449193323Sed		buflen = m->m_len;
450193323Sed	}
451193323Sed	crc = ~crc;
452193323Sed
453193323Sed	/* Encrypt little-endian CRC32 and verify that it matches with
454193323Sed	 * received ICV */
455193323Sed	icv[0] = crc;
456193323Sed	icv[1] = crc >> 8;
457193323Sed	icv[2] = crc >> 16;
458193323Sed	icv[3] = crc >> 24;
459193323Sed	for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) {
460193323Sed		i = (i + 1) & 0xff;
461193323Sed		j = (j + S[i]) & 0xff;
462193323Sed		S_SWAP(i, j);
463193323Sed		/* XXX assumes ICV is contiguous in mbuf */
464193323Sed		if ((icv[k] ^ S[(S[i] + S[j]) & 0xff]) != *pos++) {
465193323Sed			/* ICV mismatch - drop frame */
466193323Sed			return 0;
467193323Sed		}
468193323Sed	}
469193323Sed	return 1;
470193323Sed#undef S_SWAP
471193323Sed}
472193323Sed
473193323Sed/*
474193323Sed * Module glue.
475193323Sed */
476198090Srdivackystatic int
477193323Sedwep_modevent(module_t mod, int type, void *unused)
478193323Sed{
479193323Sed	switch (type) {
480193323Sed	case MOD_LOAD:
481		ieee80211_crypto_register(&wep);
482		return 0;
483	case MOD_UNLOAD:
484	case MOD_QUIESCE:
485		if (nrefs) {
486			printf("wlan_wep: still in use (%u dynamic refs)\n",
487				nrefs);
488			return EBUSY;
489		}
490		if (type == MOD_UNLOAD)
491			ieee80211_crypto_unregister(&wep);
492		return 0;
493	}
494	return EINVAL;
495}
496
497static moduledata_t wep_mod = {
498	"wlan_wep",
499	wep_modevent,
500	0
501};
502DECLARE_MODULE(wlan_wep, wep_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
503MODULE_VERSION(wlan_wep, 1);
504MODULE_DEPEND(wlan_wep, wlan, 1, 1, 1);
505