xref: /freebsd/sys/net80211/ieee80211_crypto_wep.c (revision 3fc36ee018bb836bd1796067cf4ef8683f166ebc)
1 /*-
2  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28 
29 /*
30  * IEEE 802.11 WEP crypto support.
31  */
32 #include "opt_wlan.h"
33 
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/mbuf.h>
37 #include <sys/malloc.h>
38 #include <sys/kernel.h>
39 #include <sys/module.h>
40 #include <sys/endian.h>
41 
42 #include <sys/socket.h>
43 
44 #include <net/if.h>
45 #include <net/if_media.h>
46 #include <net/ethernet.h>
47 
48 #include <net80211/ieee80211_var.h>
49 
50 static	void *wep_attach(struct ieee80211vap *, struct ieee80211_key *);
51 static	void wep_detach(struct ieee80211_key *);
52 static	int wep_setkey(struct ieee80211_key *);
53 static	void wep_setiv(struct ieee80211_key *, uint8_t *);
54 static	int wep_encap(struct ieee80211_key *, struct mbuf *);
55 static	int wep_decap(struct ieee80211_key *, struct mbuf *, int);
56 static	int wep_enmic(struct ieee80211_key *, struct mbuf *, int);
57 static	int wep_demic(struct ieee80211_key *, struct mbuf *, int);
58 
59 static const struct ieee80211_cipher wep = {
60 	.ic_name	= "WEP",
61 	.ic_cipher	= IEEE80211_CIPHER_WEP,
62 	.ic_header	= IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN,
63 	.ic_trailer	= IEEE80211_WEP_CRCLEN,
64 	.ic_miclen	= 0,
65 	.ic_attach	= wep_attach,
66 	.ic_detach	= wep_detach,
67 	.ic_setkey	= wep_setkey,
68 	.ic_setiv	= wep_setiv,
69 	.ic_encap	= wep_encap,
70 	.ic_decap	= wep_decap,
71 	.ic_enmic	= wep_enmic,
72 	.ic_demic	= wep_demic,
73 };
74 
75 static	int wep_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
76 static	int wep_decrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
77 
78 struct wep_ctx {
79 	struct ieee80211vap *wc_vap;	/* for diagnostics+statistics */
80 	struct ieee80211com *wc_ic;
81 	uint32_t	wc_iv;		/* initial vector for crypto */
82 };
83 
84 /* number of references from net80211 layer */
85 static	int nrefs = 0;
86 
87 static void *
88 wep_attach(struct ieee80211vap *vap, struct ieee80211_key *k)
89 {
90 	struct wep_ctx *ctx;
91 
92 	ctx = (struct wep_ctx *) IEEE80211_MALLOC(sizeof(struct wep_ctx),
93 		M_80211_CRYPTO, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
94 	if (ctx == NULL) {
95 		vap->iv_stats.is_crypto_nomem++;
96 		return NULL;
97 	}
98 
99 	ctx->wc_vap = vap;
100 	ctx->wc_ic = vap->iv_ic;
101 	get_random_bytes(&ctx->wc_iv, sizeof(ctx->wc_iv));
102 	nrefs++;			/* NB: we assume caller locking */
103 	return ctx;
104 }
105 
106 static void
107 wep_detach(struct ieee80211_key *k)
108 {
109 	struct wep_ctx *ctx = k->wk_private;
110 
111 	IEEE80211_FREE(ctx, M_80211_CRYPTO);
112 	KASSERT(nrefs > 0, ("imbalanced attach/detach"));
113 	nrefs--;			/* NB: we assume caller locking */
114 }
115 
116 static int
117 wep_setkey(struct ieee80211_key *k)
118 {
119 	return k->wk_keylen >= 40/NBBY;
120 }
121 
122 static void
123 wep_setiv(struct ieee80211_key *k, uint8_t *ivp)
124 {
125 	struct wep_ctx *ctx = k->wk_private;
126 	struct ieee80211vap *vap = ctx->wc_vap;
127 	uint32_t iv;
128 	uint8_t keyid;
129 
130 	keyid = ieee80211_crypto_get_keyid(vap, k) << 6;
131 
132 	/*
133 	 * XXX
134 	 * IV must not duplicate during the lifetime of the key.
135 	 * But no mechanism to renew keys is defined in IEEE 802.11
136 	 * for WEP.  And the IV may be duplicated at other stations
137 	 * because the session key itself is shared.  So we use a
138 	 * pseudo random IV for now, though it is not the right way.
139 	 *
140 	 * NB: Rather than use a strictly random IV we select a
141 	 * random one to start and then increment the value for
142 	 * each frame.  This is an explicit tradeoff between
143 	 * overhead and security.  Given the basic insecurity of
144 	 * WEP this seems worthwhile.
145 	 */
146 
147 	/*
148 	 * Skip 'bad' IVs from Fluhrer/Mantin/Shamir:
149 	 * (B, 255, N) with 3 <= B < 16 and 0 <= N <= 255
150 	 */
151 	iv = ctx->wc_iv;
152 	if ((iv & 0xff00) == 0xff00) {
153 		int B = (iv & 0xff0000) >> 16;
154 		if (3 <= B && B < 16)
155 			iv += 0x0100;
156 	}
157 	ctx->wc_iv = iv + 1;
158 
159 	/*
160 	 * NB: Preserve byte order of IV for packet
161 	 *     sniffers; it doesn't matter otherwise.
162 	 */
163 #if _BYTE_ORDER == _BIG_ENDIAN
164 	ivp[0] = iv >> 0;
165 	ivp[1] = iv >> 8;
166 	ivp[2] = iv >> 16;
167 #else
168 	ivp[2] = iv >> 0;
169 	ivp[1] = iv >> 8;
170 	ivp[0] = iv >> 16;
171 #endif
172 	ivp[3] = keyid;
173 }
174 
175 /*
176  * Add privacy headers appropriate for the specified key.
177  */
178 static int
179 wep_encap(struct ieee80211_key *k, struct mbuf *m)
180 {
181 	struct wep_ctx *ctx = k->wk_private;
182 	struct ieee80211com *ic = ctx->wc_ic;
183 	uint8_t *ivp;
184 	int hdrlen;
185 
186 	hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));
187 
188 	/*
189 	 * Copy down 802.11 header and add the IV + KeyID.
190 	 */
191 	M_PREPEND(m, wep.ic_header, M_NOWAIT);
192 	if (m == NULL)
193 		return 0;
194 	ivp = mtod(m, uint8_t *);
195 	ovbcopy(ivp + wep.ic_header, ivp, hdrlen);
196 	ivp += hdrlen;
197 
198 	wep_setiv(k, ivp);
199 
200 	/*
201 	 * Finally, do software encrypt if needed.
202 	 */
203 	if ((k->wk_flags & IEEE80211_KEY_SWENCRYPT) &&
204 	    !wep_encrypt(k, m, hdrlen))
205 		return 0;
206 
207 	return 1;
208 }
209 
210 /*
211  * Add MIC to the frame as needed.
212  */
213 static int
214 wep_enmic(struct ieee80211_key *k, struct mbuf *m, int force)
215 {
216 
217 	return 1;
218 }
219 
220 /*
221  * Validate and strip privacy headers (and trailer) for a
222  * received frame.  If necessary, decrypt the frame using
223  * the specified key.
224  */
225 static int
226 wep_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
227 {
228 	struct wep_ctx *ctx = k->wk_private;
229 	struct ieee80211vap *vap = ctx->wc_vap;
230 	struct ieee80211_frame *wh;
231 
232 	wh = mtod(m, struct ieee80211_frame *);
233 
234 	/*
235 	 * Check if the device handled the decrypt in hardware.
236 	 * If so we just strip the header; otherwise we need to
237 	 * handle the decrypt in software.
238 	 */
239 	if ((k->wk_flags & IEEE80211_KEY_SWDECRYPT) &&
240 	    !wep_decrypt(k, m, hdrlen)) {
241 		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2,
242 		    "%s", "WEP ICV mismatch on decrypt");
243 		vap->iv_stats.is_rx_wepfail++;
244 		return 0;
245 	}
246 
247 	/*
248 	 * Copy up 802.11 header and strip crypto bits.
249 	 */
250 	ovbcopy(mtod(m, void *), mtod(m, uint8_t *) + wep.ic_header, hdrlen);
251 	m_adj(m, wep.ic_header);
252 	m_adj(m, -wep.ic_trailer);
253 
254 	return 1;
255 }
256 
257 /*
258  * Verify and strip MIC from the frame.
259  */
260 static int
261 wep_demic(struct ieee80211_key *k, struct mbuf *skb, int force)
262 {
263 	return 1;
264 }
265 
266 static const uint32_t crc32_table[256] = {
267 	0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
268 	0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
269 	0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
270 	0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
271 	0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
272 	0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
273 	0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
274 	0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
275 	0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
276 	0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
277 	0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
278 	0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
279 	0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
280 	0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
281 	0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
282 	0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
283 	0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
284 	0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
285 	0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
286 	0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
287 	0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
288 	0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
289 	0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
290 	0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
291 	0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
292 	0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
293 	0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
294 	0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
295 	0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
296 	0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
297 	0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
298 	0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
299 	0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
300 	0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
301 	0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
302 	0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
303 	0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
304 	0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
305 	0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
306 	0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
307 	0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
308 	0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
309 	0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
310 	0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
311 	0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
312 	0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
313 	0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
314 	0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
315 	0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
316 	0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
317 	0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
318 	0x2d02ef8dL
319 };
320 
321 static int
322 wep_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
323 {
324 #define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
325 	struct wep_ctx *ctx = key->wk_private;
326 	struct ieee80211vap *vap = ctx->wc_vap;
327 	struct mbuf *m = m0;
328 	uint8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
329 	uint8_t icv[IEEE80211_WEP_CRCLEN];
330 	uint32_t i, j, k, crc;
331 	size_t buflen, data_len;
332 	uint8_t S[256];
333 	uint8_t *pos;
334 	u_int off, keylen;
335 
336 	vap->iv_stats.is_crypto_wep++;
337 
338 	/* NB: this assumes the header was pulled up */
339 	memcpy(rc4key, mtod(m, uint8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
340 	memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen);
341 
342 	/* Setup RC4 state */
343 	for (i = 0; i < 256; i++)
344 		S[i] = i;
345 	j = 0;
346 	keylen = key->wk_keylen + IEEE80211_WEP_IVLEN;
347 	for (i = 0; i < 256; i++) {
348 		j = (j + S[i] + rc4key[i % keylen]) & 0xff;
349 		S_SWAP(i, j);
350 	}
351 
352 	off = hdrlen + wep.ic_header;
353 	data_len = m->m_pkthdr.len - off;
354 
355 	/* Compute CRC32 over unencrypted data and apply RC4 to data */
356 	crc = ~0;
357 	i = j = 0;
358 	pos = mtod(m, uint8_t *) + off;
359 	buflen = m->m_len - off;
360 	for (;;) {
361 		if (buflen > data_len)
362 			buflen = data_len;
363 		data_len -= buflen;
364 		for (k = 0; k < buflen; k++) {
365 			crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8);
366 			i = (i + 1) & 0xff;
367 			j = (j + S[i]) & 0xff;
368 			S_SWAP(i, j);
369 			*pos++ ^= S[(S[i] + S[j]) & 0xff];
370 		}
371 		if (m->m_next == NULL) {
372 			if (data_len != 0) {		/* out of data */
373 				IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO,
374 				    ether_sprintf(mtod(m0,
375 					struct ieee80211_frame *)->i_addr2),
376 				    "out of data for WEP (data_len %zu)",
377 				    data_len);
378 				/* XXX stat */
379 				return 0;
380 			}
381 			break;
382 		}
383 		m = m->m_next;
384 		pos = mtod(m, uint8_t *);
385 		buflen = m->m_len;
386 	}
387 	crc = ~crc;
388 
389 	/* Append little-endian CRC32 and encrypt it to produce ICV */
390 	icv[0] = crc;
391 	icv[1] = crc >> 8;
392 	icv[2] = crc >> 16;
393 	icv[3] = crc >> 24;
394 	for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) {
395 		i = (i + 1) & 0xff;
396 		j = (j + S[i]) & 0xff;
397 		S_SWAP(i, j);
398 		icv[k] ^= S[(S[i] + S[j]) & 0xff];
399 	}
400 	return m_append(m0, IEEE80211_WEP_CRCLEN, icv);
401 #undef S_SWAP
402 }
403 
404 static int
405 wep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
406 {
407 #define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
408 	struct wep_ctx *ctx = key->wk_private;
409 	struct ieee80211vap *vap = ctx->wc_vap;
410 	struct mbuf *m = m0;
411 	uint8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
412 	uint8_t icv[IEEE80211_WEP_CRCLEN];
413 	uint32_t i, j, k, crc;
414 	size_t buflen, data_len;
415 	uint8_t S[256];
416 	uint8_t *pos;
417 	u_int off, keylen;
418 
419 	vap->iv_stats.is_crypto_wep++;
420 
421 	/* NB: this assumes the header was pulled up */
422 	memcpy(rc4key, mtod(m, uint8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
423 	memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen);
424 
425 	/* Setup RC4 state */
426 	for (i = 0; i < 256; i++)
427 		S[i] = i;
428 	j = 0;
429 	keylen = key->wk_keylen + IEEE80211_WEP_IVLEN;
430 	for (i = 0; i < 256; i++) {
431 		j = (j + S[i] + rc4key[i % keylen]) & 0xff;
432 		S_SWAP(i, j);
433 	}
434 
435 	off = hdrlen + wep.ic_header;
436 	data_len = m->m_pkthdr.len - (off + wep.ic_trailer);
437 
438 	/* Compute CRC32 over unencrypted data and apply RC4 to data */
439 	crc = ~0;
440 	i = j = 0;
441 	pos = mtod(m, uint8_t *) + off;
442 	buflen = m->m_len - off;
443 	for (;;) {
444 		if (buflen > data_len)
445 			buflen = data_len;
446 		data_len -= buflen;
447 		for (k = 0; k < buflen; k++) {
448 			i = (i + 1) & 0xff;
449 			j = (j + S[i]) & 0xff;
450 			S_SWAP(i, j);
451 			*pos ^= S[(S[i] + S[j]) & 0xff];
452 			crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8);
453 			pos++;
454 		}
455 		m = m->m_next;
456 		if (m == NULL) {
457 			if (data_len != 0) {		/* out of data */
458 				IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO,
459 				    mtod(m0, struct ieee80211_frame *)->i_addr2,
460 				    "out of data for WEP (data_len %zu)",
461 				    data_len);
462 				return 0;
463 			}
464 			break;
465 		}
466 		pos = mtod(m, uint8_t *);
467 		buflen = m->m_len;
468 	}
469 	crc = ~crc;
470 
471 	/* Encrypt little-endian CRC32 and verify that it matches with
472 	 * received ICV */
473 	icv[0] = crc;
474 	icv[1] = crc >> 8;
475 	icv[2] = crc >> 16;
476 	icv[3] = crc >> 24;
477 	for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) {
478 		i = (i + 1) & 0xff;
479 		j = (j + S[i]) & 0xff;
480 		S_SWAP(i, j);
481 		/* XXX assumes ICV is contiguous in mbuf */
482 		if ((icv[k] ^ S[(S[i] + S[j]) & 0xff]) != *pos++) {
483 			/* ICV mismatch - drop frame */
484 			return 0;
485 		}
486 	}
487 	return 1;
488 #undef S_SWAP
489 }
490 
491 /*
492  * Module glue.
493  */
494 IEEE80211_CRYPTO_MODULE(wep, 1);
495