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