ieee80211_crypto_wep.c revision 139508
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 139508 2004-12-31 20:51:41Z 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;
123139508Ssam	struct ieee80211com *ic = ctx->wc_ic;
124138568Ssam	u_int32_t iv;
125138568Ssam	u_int8_t *ivp;
126138568Ssam	int hdrlen;
127138568Ssam
128139508Ssam	hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));
129138568Ssam
130138568Ssam	/*
131138568Ssam	 * Copy down 802.11 header and add the IV + KeyID.
132138568Ssam	 */
133138568Ssam	M_PREPEND(m, wep.ic_header, M_NOWAIT);
134138568Ssam	if (m == NULL)
135138568Ssam		return 0;
136138568Ssam	ivp = mtod(m, u_int8_t *);
137138568Ssam	ovbcopy(ivp + wep.ic_header, ivp, hdrlen);
138138568Ssam	ivp += hdrlen;
139138568Ssam
140138568Ssam	/*
141138568Ssam	 * XXX
142138568Ssam	 * IV must not duplicate during the lifetime of the key.
143138568Ssam	 * But no mechanism to renew keys is defined in IEEE 802.11
144138568Ssam	 * for WEP.  And the IV may be duplicated at other stations
145138568Ssam	 * because the session key itself is shared.  So we use a
146138568Ssam	 * pseudo random IV for now, though it is not the right way.
147138568Ssam	 *
148138568Ssam	 * NB: Rather than use a strictly random IV we select a
149138568Ssam	 * random one to start and then increment the value for
150138568Ssam	 * each frame.  This is an explicit tradeoff between
151138568Ssam	 * overhead and security.  Given the basic insecurity of
152138568Ssam	 * WEP this seems worthwhile.
153138568Ssam	 */
154138568Ssam
155138568Ssam	/*
156138568Ssam	 * Skip 'bad' IVs from Fluhrer/Mantin/Shamir:
157138568Ssam	 * (B, 255, N) with 3 <= B < 16 and 0 <= N <= 255
158138568Ssam	 */
159138568Ssam	iv = ctx->wc_iv;
160138568Ssam	if ((iv & 0xff00) == 0xff00) {
161138568Ssam		int B = (iv & 0xff0000) >> 16;
162138568Ssam		if (3 <= B && B < 16)
163138568Ssam			iv += 0x0100;
164138568Ssam	}
165138568Ssam	ctx->wc_iv = iv + 1;
166138568Ssam
167138568Ssam	/*
168138568Ssam	 * NB: Preserve byte order of IV for packet
169138568Ssam	 *     sniffers; it doesn't matter otherwise.
170138568Ssam	 */
171138568Ssam#if _BYTE_ORDER == _BIG_ENDIAN
172138568Ssam	ivp[0] = iv >> 0;
173138568Ssam	ivp[1] = iv >> 8;
174138568Ssam	ivp[2] = iv >> 16;
175138568Ssam#else
176138568Ssam	ivp[2] = iv >> 0;
177138568Ssam	ivp[1] = iv >> 8;
178138568Ssam	ivp[0] = iv >> 16;
179138568Ssam#endif
180138568Ssam	ivp[3] = keyid;
181138568Ssam
182138568Ssam	/*
183138568Ssam	 * Finally, do software encrypt if neeed.
184138568Ssam	 */
185138568Ssam	if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
186138568Ssam	    !wep_encrypt(k, m, hdrlen))
187138568Ssam		return 0;
188138568Ssam
189138568Ssam	return 1;
190138568Ssam}
191138568Ssam
192138568Ssam/*
193138568Ssam * Add MIC to the frame as needed.
194138568Ssam */
195138568Ssamstatic int
196138568Ssamwep_enmic(struct ieee80211_key *k, struct mbuf *m)
197138568Ssam{
198138568Ssam
199138568Ssam	return 1;
200138568Ssam}
201138568Ssam
202138568Ssam/*
203138568Ssam * Validate and strip privacy headers (and trailer) for a
204138568Ssam * received frame.  If necessary, decrypt the frame using
205138568Ssam * the specified key.
206138568Ssam */
207138568Ssamstatic int
208138568Ssamwep_decap(struct ieee80211_key *k, struct mbuf *m)
209138568Ssam{
210138568Ssam	struct wep_ctx *ctx = k->wk_private;
211138568Ssam	struct ieee80211_frame *wh;
212138568Ssam	int hdrlen;
213138568Ssam
214138568Ssam	wh = mtod(m, struct ieee80211_frame *);
215138568Ssam	hdrlen = ieee80211_hdrsize(wh);
216138568Ssam
217138568Ssam	/*
218138568Ssam	 * Check if the device handled the decrypt in hardware.
219138568Ssam	 * If so we just strip the header; otherwise we need to
220138568Ssam	 * handle the decrypt in software.
221138568Ssam	 */
222138568Ssam	if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
223138568Ssam	    !wep_decrypt(k, m, hdrlen)) {
224138568Ssam		IEEE80211_DPRINTF(ctx->wc_ic, IEEE80211_MSG_CRYPTO,
225138568Ssam		    "[%s] WEP ICV mismatch on decrypt\n",
226138568Ssam		    ether_sprintf(wh->i_addr2));
227138568Ssam		ctx->wc_ic->ic_stats.is_rx_wepfail++;
228138568Ssam		return 0;
229138568Ssam	}
230138568Ssam
231138568Ssam	/*
232138568Ssam	 * Copy up 802.11 header and strip crypto bits.
233138568Ssam	 */
234138568Ssam	ovbcopy(mtod(m, void *), mtod(m, u_int8_t *) + wep.ic_header, hdrlen);
235138568Ssam	m_adj(m, wep.ic_header);
236138568Ssam	m_adj(m, -wep.ic_trailer);
237138568Ssam
238138568Ssam	return 1;
239138568Ssam}
240138568Ssam
241138568Ssam/*
242138568Ssam * Verify and strip MIC from the frame.
243138568Ssam */
244138568Ssamstatic int
245138568Ssamwep_demic(struct ieee80211_key *k, struct mbuf *skb)
246138568Ssam{
247138568Ssam	return 1;
248138568Ssam}
249138568Ssam
250138568Ssamstatic const uint32_t crc32_table[256] = {
251138568Ssam	0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
252138568Ssam	0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
253138568Ssam	0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
254138568Ssam	0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
255138568Ssam	0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
256138568Ssam	0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
257138568Ssam	0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
258138568Ssam	0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
259138568Ssam	0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
260138568Ssam	0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
261138568Ssam	0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
262138568Ssam	0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
263138568Ssam	0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
264138568Ssam	0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
265138568Ssam	0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
266138568Ssam	0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
267138568Ssam	0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
268138568Ssam	0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
269138568Ssam	0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
270138568Ssam	0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
271138568Ssam	0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
272138568Ssam	0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
273138568Ssam	0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
274138568Ssam	0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
275138568Ssam	0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
276138568Ssam	0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
277138568Ssam	0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
278138568Ssam	0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
279138568Ssam	0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
280138568Ssam	0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
281138568Ssam	0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
282138568Ssam	0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
283138568Ssam	0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
284138568Ssam	0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
285138568Ssam	0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
286138568Ssam	0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
287138568Ssam	0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
288138568Ssam	0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
289138568Ssam	0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
290138568Ssam	0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
291138568Ssam	0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
292138568Ssam	0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
293138568Ssam	0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
294138568Ssam	0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
295138568Ssam	0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
296138568Ssam	0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
297138568Ssam	0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
298138568Ssam	0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
299138568Ssam	0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
300138568Ssam	0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
301138568Ssam	0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
302138568Ssam	0x2d02ef8dL
303138568Ssam};
304138568Ssam
305138568Ssamstatic int
306138568Ssamwep_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
307138568Ssam{
308138568Ssam#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
309138568Ssam	struct wep_ctx *ctx = key->wk_private;
310138568Ssam	struct mbuf *m = m0;
311138568Ssam	u_int8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
312138568Ssam	uint8_t icv[IEEE80211_WEP_CRCLEN];
313138568Ssam	uint32_t i, j, k, crc;
314138568Ssam	size_t buflen, data_len;
315138568Ssam	uint8_t S[256];
316138568Ssam	uint8_t *pos;
317138568Ssam	u_int off, keylen;
318138568Ssam
319138568Ssam	ctx->wc_ic->ic_stats.is_crypto_wep++;
320138568Ssam
321138568Ssam	/* NB: this assumes the header was pulled up */
322138568Ssam	memcpy(rc4key, mtod(m, u_int8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
323138568Ssam	memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen);
324138568Ssam
325138568Ssam	/* Setup RC4 state */
326138568Ssam	for (i = 0; i < 256; i++)
327138568Ssam		S[i] = i;
328138568Ssam	j = 0;
329138568Ssam	keylen = key->wk_keylen + IEEE80211_WEP_IVLEN;
330138568Ssam	for (i = 0; i < 256; i++) {
331138568Ssam		j = (j + S[i] + rc4key[i % keylen]) & 0xff;
332138568Ssam		S_SWAP(i, j);
333138568Ssam	}
334138568Ssam
335138568Ssam	off = hdrlen + wep.ic_header;
336138568Ssam	data_len = m->m_pkthdr.len - off;
337138568Ssam
338138568Ssam	/* Compute CRC32 over unencrypted data and apply RC4 to data */
339138568Ssam	crc = ~0;
340138568Ssam	i = j = 0;
341138568Ssam	pos = mtod(m, uint8_t *) + off;
342138568Ssam	buflen = m->m_len - off;
343138568Ssam	for (;;) {
344138568Ssam		if (buflen > data_len)
345138568Ssam			buflen = data_len;
346138568Ssam		data_len -= buflen;
347138568Ssam		for (k = 0; k < buflen; k++) {
348138568Ssam			crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8);
349138568Ssam			i = (i + 1) & 0xff;
350138568Ssam			j = (j + S[i]) & 0xff;
351138568Ssam			S_SWAP(i, j);
352138568Ssam			*pos++ ^= S[(S[i] + S[j]) & 0xff];
353138568Ssam		}
354138568Ssam		if (m->m_next == NULL) {
355138568Ssam			if (data_len != 0) {		/* out of data */
356138568Ssam				IEEE80211_DPRINTF(ctx->wc_ic,
357138568Ssam				    IEEE80211_MSG_CRYPTO,
358138609Ssam				    "[%s] out of data for WEP (data_len %zu)\n",
359138568Ssam				    ether_sprintf(mtod(m0,
360138568Ssam					struct ieee80211_frame *)->i_addr2),
361138568Ssam				    data_len);
362138568Ssam				return 0;
363138568Ssam			}
364138568Ssam			break;
365138568Ssam		}
366138568Ssam		m = m->m_next;
367138568Ssam		pos = mtod(m, uint8_t *);
368138568Ssam		buflen = m->m_len;
369138568Ssam	}
370138568Ssam	crc = ~crc;
371138568Ssam
372138568Ssam	/* Append little-endian CRC32 and encrypt it to produce ICV */
373138568Ssam	icv[0] = crc;
374138568Ssam	icv[1] = crc >> 8;
375138568Ssam	icv[2] = crc >> 16;
376138568Ssam	icv[3] = crc >> 24;
377138568Ssam	for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) {
378138568Ssam		i = (i + 1) & 0xff;
379138568Ssam		j = (j + S[i]) & 0xff;
380138568Ssam		S_SWAP(i, j);
381138568Ssam		icv[k] ^= S[(S[i] + S[j]) & 0xff];
382138568Ssam	}
383138568Ssam	return m_append(m0, IEEE80211_WEP_CRCLEN, icv);
384138568Ssam#undef S_SWAP
385138568Ssam}
386138568Ssam
387138568Ssamstatic int
388138568Ssamwep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
389138568Ssam{
390138568Ssam#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
391138568Ssam	struct wep_ctx *ctx = key->wk_private;
392138568Ssam	struct mbuf *m = m0;
393138568Ssam	u_int8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
394138568Ssam	uint8_t icv[IEEE80211_WEP_CRCLEN];
395138568Ssam	uint32_t i, j, k, crc;
396138568Ssam	size_t buflen, data_len;
397138568Ssam	uint8_t S[256];
398138568Ssam	uint8_t *pos;
399138568Ssam	u_int off, keylen;
400138568Ssam
401138568Ssam	ctx->wc_ic->ic_stats.is_crypto_wep++;
402138568Ssam
403138568Ssam	/* NB: this assumes the header was pulled up */
404138568Ssam	memcpy(rc4key, mtod(m, u_int8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
405138568Ssam	memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen);
406138568Ssam
407138568Ssam	/* Setup RC4 state */
408138568Ssam	for (i = 0; i < 256; i++)
409138568Ssam		S[i] = i;
410138568Ssam	j = 0;
411138568Ssam	keylen = key->wk_keylen + IEEE80211_WEP_IVLEN;
412138568Ssam	for (i = 0; i < 256; i++) {
413138568Ssam		j = (j + S[i] + rc4key[i % keylen]) & 0xff;
414138568Ssam		S_SWAP(i, j);
415138568Ssam	}
416138568Ssam
417138568Ssam	off = hdrlen + wep.ic_header;
418138568Ssam	data_len = m->m_pkthdr.len - (off + wep.ic_trailer),
419138568Ssam
420138568Ssam	/* Compute CRC32 over unencrypted data and apply RC4 to data */
421138568Ssam	crc = ~0;
422138568Ssam	i = j = 0;
423138568Ssam	pos = mtod(m, uint8_t *) + off;
424138568Ssam	buflen = m->m_len - off;
425138568Ssam	for (;;) {
426138568Ssam		if (buflen > data_len)
427138568Ssam			buflen = data_len;
428138568Ssam		data_len -= buflen;
429138568Ssam		for (k = 0; k < buflen; k++) {
430138568Ssam			i = (i + 1) & 0xff;
431138568Ssam			j = (j + S[i]) & 0xff;
432138568Ssam			S_SWAP(i, j);
433138568Ssam			*pos ^= S[(S[i] + S[j]) & 0xff];
434138568Ssam			crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8);
435138568Ssam			pos++;
436138568Ssam		}
437138568Ssam		m = m->m_next;
438138568Ssam		if (m == NULL) {
439138568Ssam			if (data_len != 0) {		/* out of data */
440138568Ssam				IEEE80211_DPRINTF(ctx->wc_ic,
441138568Ssam				    IEEE80211_MSG_CRYPTO,
442138609Ssam				    "[%s] out of data for WEP (data_len %zu)\n",
443138568Ssam				    ether_sprintf(mtod(m0,
444138568Ssam					struct ieee80211_frame *)->i_addr2),
445138568Ssam				    data_len);
446138568Ssam				return 0;
447138568Ssam			}
448138568Ssam			break;
449138568Ssam		}
450138568Ssam		pos = mtod(m, uint8_t *);
451138568Ssam		buflen = m->m_len;
452138568Ssam	}
453138568Ssam	crc = ~crc;
454138568Ssam
455138568Ssam	/* Encrypt little-endian CRC32 and verify that it matches with
456138568Ssam	 * received ICV */
457138568Ssam	icv[0] = crc;
458138568Ssam	icv[1] = crc >> 8;
459138568Ssam	icv[2] = crc >> 16;
460138568Ssam	icv[3] = crc >> 24;
461138568Ssam	for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) {
462138568Ssam		i = (i + 1) & 0xff;
463138568Ssam		j = (j + S[i]) & 0xff;
464138568Ssam		S_SWAP(i, j);
465138568Ssam		/* XXX assumes ICV is contiguous in mbuf */
466138568Ssam		if ((icv[k] ^ S[(S[i] + S[j]) & 0xff]) != *pos++) {
467138568Ssam			/* ICV mismatch - drop frame */
468138568Ssam			return 0;
469138568Ssam		}
470138568Ssam	}
471138568Ssam	return 1;
472138568Ssam#undef S_SWAP
473138568Ssam}
474138568Ssam
475138568Ssam/*
476138568Ssam * Module glue.
477138568Ssam */
478138568Ssamstatic int
479138568Ssamwep_modevent(module_t mod, int type, void *unused)
480138568Ssam{
481138568Ssam	switch (type) {
482138568Ssam	case MOD_LOAD:
483138568Ssam		ieee80211_crypto_register(&wep);
484138568Ssam		return 0;
485138568Ssam	case MOD_UNLOAD:
486138568Ssam		ieee80211_crypto_unregister(&wep);
487138568Ssam		return 0;
488138568Ssam	}
489138568Ssam	return EINVAL;
490138568Ssam}
491138568Ssam
492138568Ssamstatic moduledata_t wep_mod = {
493138568Ssam	"wlan_wep",
494138568Ssam	wep_modevent,
495138568Ssam	0
496138568Ssam};
497138568SsamDECLARE_MODULE(wlan_wep, wep_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
498138568SsamMODULE_VERSION(wlan_wep, 1);
499138568SsamMODULE_DEPEND(wlan_wep, wlan, 1, 1, 1);
500