ieee80211_crypto_wep.c revision 138568
1138568Ssam/*-
2138568Ssam * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
3138568Ssam * All rights reserved.
4138568Ssam *
5138568Ssam * Redistribution and use in source and binary forms, with or without
6138568Ssam * modification, are permitted provided that the following conditions
7138568Ssam * are met:
8138568Ssam * 1. Redistributions of source code must retain the above copyright
9138568Ssam *    notice, this list of conditions and the following disclaimer.
10138568Ssam * 2. Redistributions in binary form must reproduce the above copyright
11138568Ssam *    notice, this list of conditions and the following disclaimer in the
12138568Ssam *    documentation and/or other materials provided with the distribution.
13138568Ssam * 3. The name of the author may not be used to endorse or promote products
14138568Ssam *    derived from this software without specific prior written permission.
15138568Ssam *
16138568Ssam * Alternatively, this software may be distributed under the terms of the
17138568Ssam * GNU General Public License ("GPL") version 2 as published by the Free
18138568Ssam * Software Foundation.
19138568Ssam *
20138568Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21138568Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22138568Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23138568Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24138568Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25138568Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26138568Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27138568Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28138568Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29138568Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30138568Ssam */
31138568Ssam
32138568Ssam#include <sys/cdefs.h>
33138568Ssam__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_crypto_wep.c 138568 2004-12-08 17:26:47Z sam $");
34138568Ssam
35138568Ssam/*
36138568Ssam * IEEE 802.11 WEP crypto support.
37138568Ssam */
38138568Ssam#include <sys/param.h>
39138568Ssam#include <sys/systm.h>
40138568Ssam#include <sys/mbuf.h>
41138568Ssam#include <sys/malloc.h>
42138568Ssam#include <sys/kernel.h>
43138568Ssam#include <sys/module.h>
44138568Ssam#include <sys/endian.h>
45138568Ssam
46138568Ssam#include <sys/socket.h>
47138568Ssam
48138568Ssam#include <net/if.h>
49138568Ssam#include <net/if_media.h>
50138568Ssam#include <net/ethernet.h>
51138568Ssam
52138568Ssam#include <net80211/ieee80211_var.h>
53138568Ssam
54138568Ssamstatic	void *wep_attach(struct ieee80211com *, struct ieee80211_key *);
55138568Ssamstatic	void wep_detach(struct ieee80211_key *);
56138568Ssamstatic	int wep_setkey(struct ieee80211_key *);
57138568Ssamstatic	int wep_encap(struct ieee80211_key *, struct mbuf *, u_int8_t keyid);
58138568Ssamstatic	int wep_decap(struct ieee80211_key *, struct mbuf *);
59138568Ssamstatic	int wep_enmic(struct ieee80211_key *, struct mbuf *);
60138568Ssamstatic	int wep_demic(struct ieee80211_key *, struct mbuf *);
61138568Ssam
62138568Ssamstatic const struct ieee80211_cipher wep = {
63138568Ssam	.ic_name	= "WEP",
64138568Ssam	.ic_cipher	= IEEE80211_CIPHER_WEP,
65138568Ssam	.ic_header	= IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN,
66138568Ssam	.ic_trailer	= IEEE80211_WEP_CRCLEN,
67138568Ssam	.ic_miclen	= 0,
68138568Ssam	.ic_attach	= wep_attach,
69138568Ssam	.ic_detach	= wep_detach,
70138568Ssam	.ic_setkey	= wep_setkey,
71138568Ssam	.ic_encap	= wep_encap,
72138568Ssam	.ic_decap	= wep_decap,
73138568Ssam	.ic_enmic	= wep_enmic,
74138568Ssam	.ic_demic	= wep_demic,
75138568Ssam};
76138568Ssam
77138568Ssamstatic	int wep_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
78138568Ssamstatic	int wep_decrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
79138568Ssam
80138568Ssamstruct wep_ctx {
81138568Ssam	struct ieee80211com *wc_ic;	/* for diagnostics */
82138568Ssam	u_int32_t	wc_iv;		/* initial vector for crypto */
83138568Ssam};
84138568Ssam
85138568Ssamstatic void *
86138568Ssamwep_attach(struct ieee80211com *ic, struct ieee80211_key *k)
87138568Ssam{
88138568Ssam	struct wep_ctx *ctx;
89138568Ssam
90138568Ssam	MALLOC(ctx, struct wep_ctx *, sizeof(struct wep_ctx),
91138568Ssam		M_DEVBUF, M_NOWAIT | M_ZERO);
92138568Ssam	if (ctx == NULL) {
93138568Ssam		ic->ic_stats.is_crypto_nomem++;
94138568Ssam		return NULL;
95138568Ssam	}
96138568Ssam
97138568Ssam	ctx->wc_ic = ic;
98138568Ssam	get_random_bytes(&ctx->wc_iv, sizeof(ctx->wc_iv));
99138568Ssam	return ctx;
100138568Ssam}
101138568Ssam
102138568Ssamstatic void
103138568Ssamwep_detach(struct ieee80211_key *k)
104138568Ssam{
105138568Ssam	struct wep_ctx *ctx = k->wk_private;
106138568Ssam
107138568Ssam	FREE(ctx, M_DEVBUF);
108138568Ssam}
109138568Ssam
110138568Ssamstatic int
111138568Ssamwep_setkey(struct ieee80211_key *k)
112138568Ssam{
113138568Ssam	return k->wk_keylen >= 40/NBBY;
114138568Ssam}
115138568Ssam
116138568Ssam/*
117138568Ssam * Add privacy headers appropriate for the specified key.
118138568Ssam */
119138568Ssamstatic int
120138568Ssamwep_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid)
121138568Ssam{
122138568Ssam	struct wep_ctx *ctx = k->wk_private;
123138568Ssam	u_int32_t iv;
124138568Ssam	u_int8_t *ivp;
125138568Ssam	int hdrlen;
126138568Ssam
127138568Ssam	hdrlen = ieee80211_hdrsize(mtod(m, void *));
128138568Ssam
129138568Ssam	/*
130138568Ssam	 * Copy down 802.11 header and add the IV + KeyID.
131138568Ssam	 */
132138568Ssam	M_PREPEND(m, wep.ic_header, M_NOWAIT);
133138568Ssam	if (m == NULL)
134138568Ssam		return 0;
135138568Ssam	ivp = mtod(m, u_int8_t *);
136138568Ssam	ovbcopy(ivp + wep.ic_header, ivp, hdrlen);
137138568Ssam	ivp += hdrlen;
138138568Ssam
139138568Ssam	/*
140138568Ssam	 * XXX
141138568Ssam	 * IV must not duplicate during the lifetime of the key.
142138568Ssam	 * But no mechanism to renew keys is defined in IEEE 802.11
143138568Ssam	 * for WEP.  And the IV may be duplicated at other stations
144138568Ssam	 * because the session key itself is shared.  So we use a
145138568Ssam	 * pseudo random IV for now, though it is not the right way.
146138568Ssam	 *
147138568Ssam	 * NB: Rather than use a strictly random IV we select a
148138568Ssam	 * random one to start and then increment the value for
149138568Ssam	 * each frame.  This is an explicit tradeoff between
150138568Ssam	 * overhead and security.  Given the basic insecurity of
151138568Ssam	 * WEP this seems worthwhile.
152138568Ssam	 */
153138568Ssam
154138568Ssam	/*
155138568Ssam	 * Skip 'bad' IVs from Fluhrer/Mantin/Shamir:
156138568Ssam	 * (B, 255, N) with 3 <= B < 16 and 0 <= N <= 255
157138568Ssam	 */
158138568Ssam	iv = ctx->wc_iv;
159138568Ssam	if ((iv & 0xff00) == 0xff00) {
160138568Ssam		int B = (iv & 0xff0000) >> 16;
161138568Ssam		if (3 <= B && B < 16)
162138568Ssam			iv += 0x0100;
163138568Ssam	}
164138568Ssam	ctx->wc_iv = iv + 1;
165138568Ssam
166138568Ssam	/*
167138568Ssam	 * NB: Preserve byte order of IV for packet
168138568Ssam	 *     sniffers; it doesn't matter otherwise.
169138568Ssam	 */
170138568Ssam#if _BYTE_ORDER == _BIG_ENDIAN
171138568Ssam	ivp[0] = iv >> 0;
172138568Ssam	ivp[1] = iv >> 8;
173138568Ssam	ivp[2] = iv >> 16;
174138568Ssam#else
175138568Ssam	ivp[2] = iv >> 0;
176138568Ssam	ivp[1] = iv >> 8;
177138568Ssam	ivp[0] = iv >> 16;
178138568Ssam#endif
179138568Ssam	ivp[3] = keyid;
180138568Ssam
181138568Ssam	/*
182138568Ssam	 * Finally, do software encrypt if neeed.
183138568Ssam	 */
184138568Ssam	if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
185138568Ssam	    !wep_encrypt(k, m, hdrlen))
186138568Ssam		return 0;
187138568Ssam
188138568Ssam	return 1;
189138568Ssam}
190138568Ssam
191138568Ssam/*
192138568Ssam * Add MIC to the frame as needed.
193138568Ssam */
194138568Ssamstatic int
195138568Ssamwep_enmic(struct ieee80211_key *k, struct mbuf *m)
196138568Ssam{
197138568Ssam
198138568Ssam	return 1;
199138568Ssam}
200138568Ssam
201138568Ssam/*
202138568Ssam * Validate and strip privacy headers (and trailer) for a
203138568Ssam * received frame.  If necessary, decrypt the frame using
204138568Ssam * the specified key.
205138568Ssam */
206138568Ssamstatic int
207138568Ssamwep_decap(struct ieee80211_key *k, struct mbuf *m)
208138568Ssam{
209138568Ssam	struct wep_ctx *ctx = k->wk_private;
210138568Ssam	struct ieee80211_frame *wh;
211138568Ssam	int hdrlen;
212138568Ssam
213138568Ssam	wh = mtod(m, struct ieee80211_frame *);
214138568Ssam	hdrlen = ieee80211_hdrsize(wh);
215138568Ssam
216138568Ssam	/*
217138568Ssam	 * Check if the device handled the decrypt in hardware.
218138568Ssam	 * If so we just strip the header; otherwise we need to
219138568Ssam	 * handle the decrypt in software.
220138568Ssam	 */
221138568Ssam	if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
222138568Ssam	    !wep_decrypt(k, m, hdrlen)) {
223138568Ssam		IEEE80211_DPRINTF(ctx->wc_ic, IEEE80211_MSG_CRYPTO,
224138568Ssam		    "[%s] WEP ICV mismatch on decrypt\n",
225138568Ssam		    ether_sprintf(wh->i_addr2));
226138568Ssam		ctx->wc_ic->ic_stats.is_rx_wepfail++;
227138568Ssam		return 0;
228138568Ssam	}
229138568Ssam
230138568Ssam	/*
231138568Ssam	 * Copy up 802.11 header and strip crypto bits.
232138568Ssam	 */
233138568Ssam	ovbcopy(mtod(m, void *), mtod(m, u_int8_t *) + wep.ic_header, hdrlen);
234138568Ssam	m_adj(m, wep.ic_header);
235138568Ssam	m_adj(m, -wep.ic_trailer);
236138568Ssam
237138568Ssam	return 1;
238138568Ssam}
239138568Ssam
240138568Ssam/*
241138568Ssam * Verify and strip MIC from the frame.
242138568Ssam */
243138568Ssamstatic int
244138568Ssamwep_demic(struct ieee80211_key *k, struct mbuf *skb)
245138568Ssam{
246138568Ssam	return 1;
247138568Ssam}
248138568Ssam
249138568Ssamstatic const uint32_t crc32_table[256] = {
250138568Ssam	0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
251138568Ssam	0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
252138568Ssam	0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
253138568Ssam	0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
254138568Ssam	0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
255138568Ssam	0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
256138568Ssam	0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
257138568Ssam	0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
258138568Ssam	0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
259138568Ssam	0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
260138568Ssam	0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
261138568Ssam	0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
262138568Ssam	0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
263138568Ssam	0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
264138568Ssam	0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
265138568Ssam	0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
266138568Ssam	0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
267138568Ssam	0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
268138568Ssam	0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
269138568Ssam	0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
270138568Ssam	0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
271138568Ssam	0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
272138568Ssam	0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
273138568Ssam	0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
274138568Ssam	0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
275138568Ssam	0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
276138568Ssam	0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
277138568Ssam	0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
278138568Ssam	0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
279138568Ssam	0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
280138568Ssam	0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
281138568Ssam	0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
282138568Ssam	0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
283138568Ssam	0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
284138568Ssam	0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
285138568Ssam	0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
286138568Ssam	0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
287138568Ssam	0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
288138568Ssam	0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
289138568Ssam	0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
290138568Ssam	0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
291138568Ssam	0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
292138568Ssam	0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
293138568Ssam	0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
294138568Ssam	0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
295138568Ssam	0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
296138568Ssam	0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
297138568Ssam	0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
298138568Ssam	0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
299138568Ssam	0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
300138568Ssam	0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
301138568Ssam	0x2d02ef8dL
302138568Ssam};
303138568Ssam
304138568Ssamstatic int
305138568Ssamwep_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
306138568Ssam{
307138568Ssam#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
308138568Ssam	struct wep_ctx *ctx = key->wk_private;
309138568Ssam	struct mbuf *m = m0;
310138568Ssam	u_int8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
311138568Ssam	uint8_t icv[IEEE80211_WEP_CRCLEN];
312138568Ssam	uint32_t i, j, k, crc;
313138568Ssam	size_t buflen, data_len;
314138568Ssam	uint8_t S[256];
315138568Ssam	uint8_t *pos;
316138568Ssam	u_int off, keylen;
317138568Ssam
318138568Ssam	ctx->wc_ic->ic_stats.is_crypto_wep++;
319138568Ssam
320138568Ssam	/* NB: this assumes the header was pulled up */
321138568Ssam	memcpy(rc4key, mtod(m, u_int8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
322138568Ssam	memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen);
323138568Ssam
324138568Ssam	/* Setup RC4 state */
325138568Ssam	for (i = 0; i < 256; i++)
326138568Ssam		S[i] = i;
327138568Ssam	j = 0;
328138568Ssam	keylen = key->wk_keylen + IEEE80211_WEP_IVLEN;
329138568Ssam	for (i = 0; i < 256; i++) {
330138568Ssam		j = (j + S[i] + rc4key[i % keylen]) & 0xff;
331138568Ssam		S_SWAP(i, j);
332138568Ssam	}
333138568Ssam
334138568Ssam	off = hdrlen + wep.ic_header;
335138568Ssam	data_len = m->m_pkthdr.len - off;
336138568Ssam
337138568Ssam	/* Compute CRC32 over unencrypted data and apply RC4 to data */
338138568Ssam	crc = ~0;
339138568Ssam	i = j = 0;
340138568Ssam	pos = mtod(m, uint8_t *) + off;
341138568Ssam	buflen = m->m_len - off;
342138568Ssam	for (;;) {
343138568Ssam		if (buflen > data_len)
344138568Ssam			buflen = data_len;
345138568Ssam		data_len -= buflen;
346138568Ssam		for (k = 0; k < buflen; k++) {
347138568Ssam			crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8);
348138568Ssam			i = (i + 1) & 0xff;
349138568Ssam			j = (j + S[i]) & 0xff;
350138568Ssam			S_SWAP(i, j);
351138568Ssam			*pos++ ^= S[(S[i] + S[j]) & 0xff];
352138568Ssam		}
353138568Ssam		if (m->m_next == NULL) {
354138568Ssam			if (data_len != 0) {		/* out of data */
355138568Ssam				IEEE80211_DPRINTF(ctx->wc_ic,
356138568Ssam				    IEEE80211_MSG_CRYPTO,
357138568Ssam				    "[%s] out of data for WEP (data_len %u)\n",
358138568Ssam				    ether_sprintf(mtod(m0,
359138568Ssam					struct ieee80211_frame *)->i_addr2),
360138568Ssam				    data_len);
361138568Ssam				return 0;
362138568Ssam			}
363138568Ssam			break;
364138568Ssam		}
365138568Ssam		m = m->m_next;
366138568Ssam		pos = mtod(m, uint8_t *);
367138568Ssam		buflen = m->m_len;
368138568Ssam	}
369138568Ssam	crc = ~crc;
370138568Ssam
371138568Ssam	/* Append little-endian CRC32 and encrypt it to produce ICV */
372138568Ssam	icv[0] = crc;
373138568Ssam	icv[1] = crc >> 8;
374138568Ssam	icv[2] = crc >> 16;
375138568Ssam	icv[3] = crc >> 24;
376138568Ssam	for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) {
377138568Ssam		i = (i + 1) & 0xff;
378138568Ssam		j = (j + S[i]) & 0xff;
379138568Ssam		S_SWAP(i, j);
380138568Ssam		icv[k] ^= S[(S[i] + S[j]) & 0xff];
381138568Ssam	}
382138568Ssam	return m_append(m0, IEEE80211_WEP_CRCLEN, icv);
383138568Ssam#undef S_SWAP
384138568Ssam}
385138568Ssam
386138568Ssamstatic int
387138568Ssamwep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
388138568Ssam{
389138568Ssam#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
390138568Ssam	struct wep_ctx *ctx = key->wk_private;
391138568Ssam	struct mbuf *m = m0;
392138568Ssam	u_int8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
393138568Ssam	uint8_t icv[IEEE80211_WEP_CRCLEN];
394138568Ssam	uint32_t i, j, k, crc;
395138568Ssam	size_t buflen, data_len;
396138568Ssam	uint8_t S[256];
397138568Ssam	uint8_t *pos;
398138568Ssam	u_int off, keylen;
399138568Ssam
400138568Ssam	ctx->wc_ic->ic_stats.is_crypto_wep++;
401138568Ssam
402138568Ssam	/* NB: this assumes the header was pulled up */
403138568Ssam	memcpy(rc4key, mtod(m, u_int8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
404138568Ssam	memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen);
405138568Ssam
406138568Ssam	/* Setup RC4 state */
407138568Ssam	for (i = 0; i < 256; i++)
408138568Ssam		S[i] = i;
409138568Ssam	j = 0;
410138568Ssam	keylen = key->wk_keylen + IEEE80211_WEP_IVLEN;
411138568Ssam	for (i = 0; i < 256; i++) {
412138568Ssam		j = (j + S[i] + rc4key[i % keylen]) & 0xff;
413138568Ssam		S_SWAP(i, j);
414138568Ssam	}
415138568Ssam
416138568Ssam	off = hdrlen + wep.ic_header;
417138568Ssam	data_len = m->m_pkthdr.len - (off + wep.ic_trailer),
418138568Ssam
419138568Ssam	/* Compute CRC32 over unencrypted data and apply RC4 to data */
420138568Ssam	crc = ~0;
421138568Ssam	i = j = 0;
422138568Ssam	pos = mtod(m, uint8_t *) + off;
423138568Ssam	buflen = m->m_len - off;
424138568Ssam	for (;;) {
425138568Ssam		if (buflen > data_len)
426138568Ssam			buflen = data_len;
427138568Ssam		data_len -= buflen;
428138568Ssam		for (k = 0; k < buflen; k++) {
429138568Ssam			i = (i + 1) & 0xff;
430138568Ssam			j = (j + S[i]) & 0xff;
431138568Ssam			S_SWAP(i, j);
432138568Ssam			*pos ^= S[(S[i] + S[j]) & 0xff];
433138568Ssam			crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8);
434138568Ssam			pos++;
435138568Ssam		}
436138568Ssam		m = m->m_next;
437138568Ssam		if (m == NULL) {
438138568Ssam			if (data_len != 0) {		/* out of data */
439138568Ssam				IEEE80211_DPRINTF(ctx->wc_ic,
440138568Ssam				    IEEE80211_MSG_CRYPTO,
441138568Ssam				    "[%s] out of data for WEP (data_len %u)\n",
442138568Ssam				    ether_sprintf(mtod(m0,
443138568Ssam					struct ieee80211_frame *)->i_addr2),
444138568Ssam				    data_len);
445138568Ssam				return 0;
446138568Ssam			}
447138568Ssam			break;
448138568Ssam		}
449138568Ssam		pos = mtod(m, uint8_t *);
450138568Ssam		buflen = m->m_len;
451138568Ssam	}
452138568Ssam	crc = ~crc;
453138568Ssam
454138568Ssam	/* Encrypt little-endian CRC32 and verify that it matches with
455138568Ssam	 * received ICV */
456138568Ssam	icv[0] = crc;
457138568Ssam	icv[1] = crc >> 8;
458138568Ssam	icv[2] = crc >> 16;
459138568Ssam	icv[3] = crc >> 24;
460138568Ssam	for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) {
461138568Ssam		i = (i + 1) & 0xff;
462138568Ssam		j = (j + S[i]) & 0xff;
463138568Ssam		S_SWAP(i, j);
464138568Ssam		/* XXX assumes ICV is contiguous in mbuf */
465138568Ssam		if ((icv[k] ^ S[(S[i] + S[j]) & 0xff]) != *pos++) {
466138568Ssam			/* ICV mismatch - drop frame */
467138568Ssam			return 0;
468138568Ssam		}
469138568Ssam	}
470138568Ssam	return 1;
471138568Ssam#undef S_SWAP
472138568Ssam}
473138568Ssam
474138568Ssam/*
475138568Ssam * Module glue.
476138568Ssam */
477138568Ssamstatic int
478138568Ssamwep_modevent(module_t mod, int type, void *unused)
479138568Ssam{
480138568Ssam	switch (type) {
481138568Ssam	case MOD_LOAD:
482138568Ssam		ieee80211_crypto_register(&wep);
483138568Ssam		return 0;
484138568Ssam	case MOD_UNLOAD:
485138568Ssam		ieee80211_crypto_unregister(&wep);
486138568Ssam		return 0;
487138568Ssam	}
488138568Ssam	return EINVAL;
489138568Ssam}
490138568Ssam
491138568Ssamstatic moduledata_t wep_mod = {
492138568Ssam	"wlan_wep",
493138568Ssam	wep_modevent,
494138568Ssam	0
495138568Ssam};
496138568SsamDECLARE_MODULE(wlan_wep, wep_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
497138568SsamMODULE_VERSION(wlan_wep, 1);
498138568SsamMODULE_DEPEND(wlan_wep, wlan, 1, 1, 1);
499138568SsamMODULE_DEPEND(wlan_wep, rc4, 1, 1, 1);
500