ieee80211_crypto_wep.c revision 186302
1317019Sdim/*-
2317019Sdim * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
3353358Sdim * All rights reserved.
4353358Sdim *
5353358Sdim * Redistribution and use in source and binary forms, with or without
6317019Sdim * modification, are permitted provided that the following conditions
7317019Sdim * are met:
8317019Sdim * 1. Redistributions of source code must retain the above copyright
9317019Sdim *    notice, this list of conditions and the following disclaimer.
10317019Sdim * 2. Redistributions in binary form must reproduce the above copyright
11317019Sdim *    notice, this list of conditions and the following disclaimer in the
12317019Sdim *    documentation and/or other materials provided with the distribution.
13344779Sdim *
14317019Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15317019Sdim * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16344779Sdim * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17317019Sdim * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18317019Sdim * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19317019Sdim * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20317019Sdim * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21317019Sdim * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22317019Sdim * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23317019Sdim * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24317019Sdim */
25317019Sdim
26317019Sdim#include <sys/cdefs.h>
27317019Sdim__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_crypto_wep.c 186302 2008-12-18 23:00:09Z sam $");
28317019Sdim
29317019Sdim/*
30317019Sdim * IEEE 802.11 WEP crypto support.
31317019Sdim */
32317019Sdim#include "opt_wlan.h"
33317019Sdim
34317019Sdim#include <sys/param.h>
35317019Sdim#include <sys/systm.h>
36317019Sdim#include <sys/mbuf.h>
37317019Sdim#include <sys/malloc.h>
38317019Sdim#include <sys/kernel.h>
39317019Sdim#include <sys/module.h>
40317019Sdim#include <sys/endian.h>
41317019Sdim
42317019Sdim#include <sys/socket.h>
43317019Sdim
44317019Sdim#include <net/if.h>
45317019Sdim#include <net/if_media.h>
46317019Sdim#include <net/ethernet.h>
47317019Sdim
48317019Sdim#include <net80211/ieee80211_var.h>
49317019Sdim
50317019Sdimstatic	void *wep_attach(struct ieee80211vap *, struct ieee80211_key *);
51317019Sdimstatic	void wep_detach(struct ieee80211_key *);
52317019Sdimstatic	int wep_setkey(struct ieee80211_key *);
53317019Sdimstatic	int wep_encap(struct ieee80211_key *, struct mbuf *, uint8_t keyid);
54317019Sdimstatic	int wep_decap(struct ieee80211_key *, struct mbuf *, int hdrlen);
55317019Sdimstatic	int wep_enmic(struct ieee80211_key *, struct mbuf *, int);
56317019Sdimstatic	int wep_demic(struct ieee80211_key *, struct mbuf *, int);
57317019Sdim
58317019Sdimstatic const struct ieee80211_cipher wep = {
59317019Sdim	.ic_name	= "WEP",
60317019Sdim	.ic_cipher	= IEEE80211_CIPHER_WEP,
61317019Sdim	.ic_header	= IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN,
62317019Sdim	.ic_trailer	= IEEE80211_WEP_CRCLEN,
63317019Sdim	.ic_miclen	= 0,
64317019Sdim	.ic_attach	= wep_attach,
65317019Sdim	.ic_detach	= wep_detach,
66317019Sdim	.ic_setkey	= wep_setkey,
67317019Sdim	.ic_encap	= wep_encap,
68317019Sdim	.ic_decap	= wep_decap,
69317019Sdim	.ic_enmic	= wep_enmic,
70317019Sdim	.ic_demic	= wep_demic,
71317019Sdim};
72317019Sdim
73317019Sdimstatic	int wep_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
74317019Sdimstatic	int wep_decrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
75317019Sdim
76317019Sdimstruct wep_ctx {
77317019Sdim	struct ieee80211vap *wc_vap;	/* for diagnostics+statistics */
78317019Sdim	struct ieee80211com *wc_ic;
79317019Sdim	uint32_t	wc_iv;		/* initial vector for crypto */
80317019Sdim};
81317019Sdim
82317019Sdim/* number of references from net80211 layer */
83317019Sdimstatic	int nrefs = 0;
84317019Sdim
85317019Sdimstatic void *
86317019Sdimwep_attach(struct ieee80211vap *vap, struct ieee80211_key *k)
87317019Sdim{
88317019Sdim	struct wep_ctx *ctx;
89317019Sdim
90317019Sdim	ctx = (struct wep_ctx *) malloc(sizeof(struct wep_ctx),
91317019Sdim		M_80211_CRYPTO, M_NOWAIT | M_ZERO);
92317019Sdim	if (ctx == NULL) {
93317019Sdim		vap->iv_stats.is_crypto_nomem++;
94317019Sdim		return NULL;
95317019Sdim	}
96317019Sdim
97317019Sdim	ctx->wc_vap = vap;
98317019Sdim	ctx->wc_ic = vap->iv_ic;
99317019Sdim	get_random_bytes(&ctx->wc_iv, sizeof(ctx->wc_iv));
100317019Sdim	nrefs++;			/* NB: we assume caller locking */
101317019Sdim	return ctx;
102317019Sdim}
103317019Sdim
104317019Sdimstatic void
105317019Sdimwep_detach(struct ieee80211_key *k)
106317019Sdim{
107317019Sdim	struct wep_ctx *ctx = k->wk_private;
108317019Sdim
109317019Sdim	free(ctx, M_80211_CRYPTO);
110317019Sdim	KASSERT(nrefs > 0, ("imbalanced attach/detach"));
111317019Sdim	nrefs--;			/* NB: we assume caller locking */
112317019Sdim}
113317019Sdim
114317019Sdimstatic int
115317019Sdimwep_setkey(struct ieee80211_key *k)
116317019Sdim{
117317019Sdim	return k->wk_keylen >= 40/NBBY;
118317019Sdim}
119317019Sdim
120317019Sdim/*
121317019Sdim * Add privacy headers appropriate for the specified key.
122317019Sdim */
123317019Sdimstatic int
124317019Sdimwep_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid)
125317019Sdim{
126317019Sdim	struct wep_ctx *ctx = k->wk_private;
127317019Sdim	struct ieee80211com *ic = ctx->wc_ic;
128317019Sdim	uint32_t iv;
129317019Sdim	uint8_t *ivp;
130317019Sdim	int hdrlen;
131317019Sdim
132317019Sdim	hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));
133317019Sdim
134317019Sdim	/*
135317019Sdim	 * Copy down 802.11 header and add the IV + KeyID.
136317019Sdim	 */
137317019Sdim	M_PREPEND(m, wep.ic_header, M_NOWAIT);
138317019Sdim	if (m == NULL)
139317019Sdim		return 0;
140317019Sdim	ivp = mtod(m, uint8_t *);
141317019Sdim	ovbcopy(ivp + wep.ic_header, ivp, hdrlen);
142317019Sdim	ivp += hdrlen;
143317019Sdim
144317019Sdim	/*
145317019Sdim	 * XXX
146317019Sdim	 * IV must not duplicate during the lifetime of the key.
147317019Sdim	 * But no mechanism to renew keys is defined in IEEE 802.11
148317019Sdim	 * for WEP.  And the IV may be duplicated at other stations
149317019Sdim	 * because the session key itself is shared.  So we use a
150317019Sdim	 * pseudo random IV for now, though it is not the right way.
151317019Sdim	 *
152317019Sdim	 * NB: Rather than use a strictly random IV we select a
153317019Sdim	 * random one to start and then increment the value for
154317019Sdim	 * each frame.  This is an explicit tradeoff between
155317019Sdim	 * overhead and security.  Given the basic insecurity of
156327952Sdim	 * WEP this seems worthwhile.
157327952Sdim	 */
158327952Sdim
159317019Sdim	/*
160353358Sdim	 * Skip 'bad' IVs from Fluhrer/Mantin/Shamir:
161353358Sdim	 * (B, 255, N) with 3 <= B < 16 and 0 <= N <= 255
162317019Sdim	 */
163317019Sdim	iv = ctx->wc_iv;
164317019Sdim	if ((iv & 0xff00) == 0xff00) {
165317019Sdim		int B = (iv & 0xff0000) >> 16;
166317019Sdim		if (3 <= B && B < 16)
167317019Sdim			iv += 0x0100;
168317019Sdim	}
169317019Sdim	ctx->wc_iv = iv + 1;
170360784Sdim
171360784Sdim	/*
172360784Sdim	 * NB: Preserve byte order of IV for packet
173360784Sdim	 *     sniffers; it doesn't matter otherwise.
174317019Sdim	 */
175317019Sdim#if _BYTE_ORDER == _BIG_ENDIAN
176317019Sdim	ivp[0] = iv >> 0;
177317019Sdim	ivp[1] = iv >> 8;
178317019Sdim	ivp[2] = iv >> 16;
179317019Sdim#else
180317019Sdim	ivp[2] = iv >> 0;
181327952Sdim	ivp[1] = iv >> 8;
182327952Sdim	ivp[0] = iv >> 16;
183327952Sdim#endif
184327952Sdim	ivp[3] = keyid;
185327952Sdim
186327952Sdim	/*
187327952Sdim	 * Finally, do software encrypt if neeed.
188327952Sdim	 */
189327952Sdim	if ((k->wk_flags & IEEE80211_KEY_SWENCRYPT) &&
190327952Sdim	    !wep_encrypt(k, m, hdrlen))
191327952Sdim		return 0;
192327952Sdim
193327952Sdim	return 1;
194327952Sdim}
195327952Sdim
196327952Sdim/*
197317019Sdim * Add MIC to the frame as needed.
198344779Sdim */
199344779Sdimstatic int
200344779Sdimwep_enmic(struct ieee80211_key *k, struct mbuf *m, int force)
201317019Sdim{
202317019Sdim
203317019Sdim	return 1;
204317019Sdim}
205317019Sdim
206317019Sdim/*
207317019Sdim * Validate and strip privacy headers (and trailer) for a
208317019Sdim * received frame.  If necessary, decrypt the frame using
209317019Sdim * the specified key.
210317019Sdim */
211317019Sdimstatic int
212317019Sdimwep_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
213317019Sdim{
214317019Sdim	struct wep_ctx *ctx = k->wk_private;
215317019Sdim	struct ieee80211vap *vap = ctx->wc_vap;
216317019Sdim	struct ieee80211_frame *wh;
217317019Sdim
218317019Sdim	wh = mtod(m, struct ieee80211_frame *);
219317019Sdim
220317019Sdim	/*
221317019Sdim	 * Check if the device handled the decrypt in hardware.
222317019Sdim	 * If so we just strip the header; otherwise we need to
223317019Sdim	 * handle the decrypt in software.
224317019Sdim	 */
225317019Sdim	if ((k->wk_flags & IEEE80211_KEY_SWDECRYPT) &&
226317019Sdim	    !wep_decrypt(k, m, hdrlen)) {
227317019Sdim		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
228317019Sdim		    "%s", "WEP ICV mismatch on decrypt");
229317019Sdim		vap->iv_stats.is_rx_wepfail++;
230317019Sdim		return 0;
231317019Sdim	}
232317019Sdim
233317019Sdim	/*
234317019Sdim	 * Copy up 802.11 header and strip crypto bits.
235317019Sdim	 */
236317019Sdim	ovbcopy(mtod(m, void *), mtod(m, uint8_t *) + wep.ic_header, hdrlen);
237317019Sdim	m_adj(m, wep.ic_header);
238317019Sdim	m_adj(m, -wep.ic_trailer);
239317019Sdim
240317019Sdim	return 1;
241317019Sdim}
242322740Sdim
243317019Sdim/*
244317019Sdim * Verify and strip MIC from the frame.
245317019Sdim */
246317019Sdimstatic int
247317019Sdimwep_demic(struct ieee80211_key *k, struct mbuf *skb, int force)
248317019Sdim{
249317019Sdim	return 1;
250317019Sdim}
251317019Sdim
252317019Sdimstatic const uint32_t crc32_table[256] = {
253317019Sdim	0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
254317019Sdim	0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
255317019Sdim	0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
256327952Sdim	0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
257327952Sdim	0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
258327952Sdim	0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
259317019Sdim	0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
260317019Sdim	0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
261344779Sdim	0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
262344779Sdim	0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
263344779Sdim	0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
264344779Sdim	0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
265360784Sdim	0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
266360784Sdim	0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
267360784Sdim	0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
268317019Sdim	0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
269317019Sdim	0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
270317019Sdim	0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
271317019Sdim	0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
272317019Sdim	0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
273317019Sdim	0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
274317019Sdim	0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
275317019Sdim	0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
276317019Sdim	0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
277317019Sdim	0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
278317019Sdim	0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
279317019Sdim	0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
280317019Sdim	0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
281317019Sdim	0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
282317019Sdim	0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
283317019Sdim	0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
284317019Sdim	0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
285317019Sdim	0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
286327952Sdim	0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
287317019Sdim	0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
288327952Sdim	0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
289327952Sdim	0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
290327952Sdim	0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
291327952Sdim	0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
292317019Sdim	0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
293317019Sdim	0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
294327952Sdim	0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
295317019Sdim	0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
296317019Sdim	0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
297317019Sdim	0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
298317019Sdim	0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
299344779Sdim	0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
300344779Sdim	0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
301344779Sdim	0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
302317019Sdim	0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
303317019Sdim	0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
304317019Sdim	0x2d02ef8dL
305317019Sdim};
306317019Sdim
307317019Sdimstatic int
308317019Sdimwep_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
309317019Sdim{
310317019Sdim#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
311317019Sdim	struct wep_ctx *ctx = key->wk_private;
312317019Sdim	struct ieee80211vap *vap = ctx->wc_vap;
313317019Sdim	struct mbuf *m = m0;
314317019Sdim	uint8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
315317019Sdim	uint8_t icv[IEEE80211_WEP_CRCLEN];
316317019Sdim	uint32_t i, j, k, crc;
317317019Sdim	size_t buflen, data_len;
318317019Sdim	uint8_t S[256];
319317019Sdim	uint8_t *pos;
320317019Sdim	u_int off, keylen;
321360784Sdim
322360784Sdim	vap->iv_stats.is_crypto_wep++;
323360784Sdim
324317019Sdim	/* NB: this assumes the header was pulled up */
325317019Sdim	memcpy(rc4key, mtod(m, uint8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
326317019Sdim	memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen);
327317019Sdim
328317019Sdim	/* Setup RC4 state */
329317019Sdim	for (i = 0; i < 256; i++)
330317019Sdim		S[i] = i;
331317019Sdim	j = 0;
332317019Sdim	keylen = key->wk_keylen + IEEE80211_WEP_IVLEN;
333317019Sdim	for (i = 0; i < 256; i++) {
334317019Sdim		j = (j + S[i] + rc4key[i % keylen]) & 0xff;
335317019Sdim		S_SWAP(i, j);
336317019Sdim	}
337317019Sdim
338317019Sdim	off = hdrlen + wep.ic_header;
339317019Sdim	data_len = m->m_pkthdr.len - off;
340317019Sdim
341317019Sdim	/* Compute CRC32 over unencrypted data and apply RC4 to data */
342327952Sdim	crc = ~0;
343327952Sdim	i = j = 0;
344317019Sdim	pos = mtod(m, uint8_t *) + off;
345317019Sdim	buflen = m->m_len - off;
346317019Sdim	for (;;) {
347327952Sdim		if (buflen > data_len)
348317019Sdim			buflen = data_len;
349317019Sdim		data_len -= buflen;
350317019Sdim		for (k = 0; k < buflen; k++) {
351317019Sdim			crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8);
352317019Sdim			i = (i + 1) & 0xff;
353317019Sdim			j = (j + S[i]) & 0xff;
354327952Sdim			S_SWAP(i, j);
355317019Sdim			*pos++ ^= S[(S[i] + S[j]) & 0xff];
356327952Sdim		}
357327952Sdim		if (m->m_next == NULL) {
358317019Sdim			if (data_len != 0) {		/* out of data */
359317019Sdim				IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO,
360317019Sdim				    ether_sprintf(mtod(m0,
361317019Sdim					struct ieee80211_frame *)->i_addr2),
362327952Sdim				    "out of data for WEP (data_len %zu)",
363327952Sdim				    data_len);
364317019Sdim				/* XXX stat */
365317019Sdim				return 0;
366317019Sdim			}
367317019Sdim			break;
368327952Sdim		}
369327952Sdim		m = m->m_next;
370317019Sdim		pos = mtod(m, uint8_t *);
371317019Sdim		buflen = m->m_len;
372317019Sdim	}
373317019Sdim	crc = ~crc;
374317019Sdim
375317019Sdim	/* Append little-endian CRC32 and encrypt it to produce ICV */
376317019Sdim	icv[0] = crc;
377317019Sdim	icv[1] = crc >> 8;
378317019Sdim	icv[2] = crc >> 16;
379327952Sdim	icv[3] = crc >> 24;
380317019Sdim	for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) {
381317019Sdim		i = (i + 1) & 0xff;
382317019Sdim		j = (j + S[i]) & 0xff;
383317019Sdim		S_SWAP(i, j);
384327952Sdim		icv[k] ^= S[(S[i] + S[j]) & 0xff];
385317019Sdim	}
386317019Sdim	return m_append(m0, IEEE80211_WEP_CRCLEN, icv);
387317019Sdim#undef S_SWAP
388317019Sdim}
389327952Sdim
390317019Sdimstatic int
391317019Sdimwep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
392317019Sdim{
393317019Sdim#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
394327952Sdim	struct wep_ctx *ctx = key->wk_private;
395317019Sdim	struct ieee80211vap *vap = ctx->wc_vap;
396317019Sdim	struct mbuf *m = m0;
397317019Sdim	uint8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
398317019Sdim	uint8_t icv[IEEE80211_WEP_CRCLEN];
399327952Sdim	uint32_t i, j, k, crc;
400317019Sdim	size_t buflen, data_len;
401317019Sdim	uint8_t S[256];
402317019Sdim	uint8_t *pos;
403317019Sdim	u_int off, keylen;
404327952Sdim
405317019Sdim	vap->iv_stats.is_crypto_wep++;
406317019Sdim
407317019Sdim	/* NB: this assumes the header was pulled up */
408317019Sdim	memcpy(rc4key, mtod(m, uint8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
409317019Sdim	memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen);
410317019Sdim
411317019Sdim	/* Setup RC4 state */
412317019Sdim	for (i = 0; i < 256; i++)
413317019Sdim		S[i] = i;
414317019Sdim	j = 0;
415317019Sdim	keylen = key->wk_keylen + IEEE80211_WEP_IVLEN;
416317019Sdim	for (i = 0; i < 256; i++) {
417317019Sdim		j = (j + S[i] + rc4key[i % keylen]) & 0xff;
418317019Sdim		S_SWAP(i, j);
419317019Sdim	}
420317019Sdim
421317019Sdim	off = hdrlen + wep.ic_header;
422317019Sdim	data_len = m->m_pkthdr.len - (off + wep.ic_trailer),
423317019Sdim
424317019Sdim	/* Compute CRC32 over unencrypted data and apply RC4 to data */
425317019Sdim	crc = ~0;
426317019Sdim	i = j = 0;
427317019Sdim	pos = mtod(m, uint8_t *) + off;
428317019Sdim	buflen = m->m_len - off;
429317019Sdim	for (;;) {
430320572Sdim		if (buflen > data_len)
431320572Sdim			buflen = data_len;
432320572Sdim		data_len -= buflen;
433320572Sdim		for (k = 0; k < buflen; k++) {
434320572Sdim			i = (i + 1) & 0xff;
435320572Sdim			j = (j + S[i]) & 0xff;
436320970Sdim			S_SWAP(i, j);
437320970Sdim			*pos ^= S[(S[i] + S[j]) & 0xff];
438320572Sdim			crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8);
439317019Sdim			pos++;
440344779Sdim		}
441317019Sdim		m = m->m_next;
442317019Sdim		if (m == NULL) {
443327952Sdim			if (data_len != 0) {		/* out of data */
444327952Sdim				IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO,
445317019Sdim				    mtod(m0, struct ieee80211_frame *)->i_addr2,
446317019Sdim				    "out of data for WEP (data_len %zu)",
447317019Sdim				    data_len);
448317019Sdim				return 0;
449317019Sdim			}
450317019Sdim			break;
451317019Sdim		}
452317019Sdim		pos = mtod(m, uint8_t *);
453317019Sdim		buflen = m->m_len;
454317019Sdim	}
455317019Sdim	crc = ~crc;
456317019Sdim
457317019Sdim	/* Encrypt little-endian CRC32 and verify that it matches with
458317019Sdim	 * received ICV */
459317019Sdim	icv[0] = crc;
460317019Sdim	icv[1] = crc >> 8;
461317019Sdim	icv[2] = crc >> 16;
462317019Sdim	icv[3] = crc >> 24;
463317019Sdim	for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) {
464317019Sdim		i = (i + 1) & 0xff;
465317019Sdim		j = (j + S[i]) & 0xff;
466317019Sdim		S_SWAP(i, j);
467317019Sdim		/* XXX assumes ICV is contiguous in mbuf */
468317019Sdim		if ((icv[k] ^ S[(S[i] + S[j]) & 0xff]) != *pos++) {
469317019Sdim			/* ICV mismatch - drop frame */
470317019Sdim			return 0;
471317019Sdim		}
472317019Sdim	}
473317019Sdim	return 1;
474317019Sdim#undef S_SWAP
475317019Sdim}
476317019Sdim
477317019Sdim/*
478317019Sdim * Module glue.
479317019Sdim */
480317019SdimIEEE80211_CRYPTO_MODULE(wep, 1);
481317019Sdim