1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
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/*
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
50static	void *wep_attach(struct ieee80211vap *, struct ieee80211_key *);
51static	void wep_detach(struct ieee80211_key *);
52static	int wep_setkey(struct ieee80211_key *);
53static	void wep_setiv(struct ieee80211_key *, uint8_t *);
54static	int wep_encap(struct ieee80211_key *, struct mbuf *);
55static	int wep_decap(struct ieee80211_key *, struct mbuf *, int);
56static	int wep_enmic(struct ieee80211_key *, struct mbuf *, int);
57static	int wep_demic(struct ieee80211_key *, struct mbuf *, int);
58
59static 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
75static	int wep_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
76static	int wep_decrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
77
78struct 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 */
85static	int nrefs = 0;
86
87static void *
88wep_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	net80211_get_random_bytes(&ctx->wc_iv, sizeof(ctx->wc_iv));
102	nrefs++;			/* NB: we assume caller locking */
103	return ctx;
104}
105
106static void
107wep_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
116static int
117wep_setkey(struct ieee80211_key *k)
118{
119	return k->wk_keylen >= 40/NBBY;
120}
121
122static void
123wep_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 */
178static int
179wep_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	struct ieee80211_frame *wh;
184	uint8_t *ivp;
185	int hdrlen;
186	int is_mgmt;
187
188	hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));
189	wh = mtod(m, struct ieee80211_frame *);
190	is_mgmt = IEEE80211_IS_MGMT(wh);
191
192	/*
193	 * Check to see if IV is required.
194	 */
195	if (is_mgmt && (k->wk_flags & IEEE80211_KEY_NOIVMGT))
196		return 1;
197	if ((! is_mgmt) && (k->wk_flags & IEEE80211_KEY_NOIV))
198		return 1;
199
200	/*
201	 * Copy down 802.11 header and add the IV + KeyID.
202	 */
203	M_PREPEND(m, wep.ic_header, IEEE80211_M_NOWAIT);
204	if (m == NULL)
205		return 0;
206	ivp = mtod(m, uint8_t *);
207	ovbcopy(ivp + wep.ic_header, ivp, hdrlen);
208	ivp += hdrlen;
209
210	wep_setiv(k, ivp);
211
212	/*
213	 * Finally, do software encrypt if needed.
214	 */
215	if ((k->wk_flags & IEEE80211_KEY_SWENCRYPT) &&
216	    !wep_encrypt(k, m, hdrlen))
217		return 0;
218
219	return 1;
220}
221
222/*
223 * Add MIC to the frame as needed.
224 */
225static int
226wep_enmic(struct ieee80211_key *k, struct mbuf *m, int force)
227{
228
229	return 1;
230}
231
232/*
233 * Validate and strip privacy headers (and trailer) for a
234 * received frame.  If necessary, decrypt the frame using
235 * the specified key.
236 */
237static int
238wep_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
239{
240	struct wep_ctx *ctx = k->wk_private;
241	struct ieee80211vap *vap = ctx->wc_vap;
242	const struct ieee80211_rx_stats *rxs;
243
244	rxs = ieee80211_get_rx_params_ptr(m);
245
246	if ((rxs != NULL) && (rxs->c_pktflags & IEEE80211_RX_F_IV_STRIP))
247		goto finish;
248
249	/*
250	 * Check if the device handled the decrypt in hardware.
251	 * If so we just strip the header; otherwise we need to
252	 * handle the decrypt in software.
253	 */
254	if ((k->wk_flags & IEEE80211_KEY_SWDECRYPT) &&
255	    !wep_decrypt(k, m, hdrlen)) {
256#ifdef IEEE80211_DEBUG
257		struct ieee80211_frame *wh;
258
259		wh = mtod(m, struct ieee80211_frame *);
260#endif
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