1/* src/p80211/p80211wep.c
2*
3* WEP encode/decode for P80211.
4*
5* Copyright (C) 2002 AbsoluteValue Systems, Inc.  All Rights Reserved.
6* --------------------------------------------------------------------
7*
8* linux-wlan
9*
10*   The contents of this file are subject to the Mozilla Public
11*   License Version 1.1 (the "License"); you may not use this file
12*   except in compliance with the License. You may obtain a copy of
13*   the License at http://www.mozilla.org/MPL/
14*
15*   Software distributed under the License is distributed on an "AS
16*   IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
17*   implied. See the License for the specific language governing
18*   rights and limitations under the License.
19*
20*   Alternatively, the contents of this file may be used under the
21*   terms of the GNU Public License version 2 (the "GPL"), in which
22*   case the provisions of the GPL are applicable instead of the
23*   above.  If you wish to allow the use of your version of this file
24*   only under the terms of the GPL and not to allow others to use
25*   your version of this file under the MPL, indicate your decision
26*   by deleting the provisions above and replace them with the notice
27*   and other provisions required by the GPL.  If you do not delete
28*   the provisions above, a recipient may use your version of this
29*   file under either the MPL or the GPL.
30*
31* --------------------------------------------------------------------
32*
33* Inquiries regarding the linux-wlan Open Source project can be
34* made directly to:
35*
36* AbsoluteValue Systems Inc.
37* info@linux-wlan.com
38* http://www.linux-wlan.com
39*
40* --------------------------------------------------------------------
41*
42* Portions of the development of this software were funded by
43* Intersil Corporation as part of PRISM(R) chipset product development.
44*
45* --------------------------------------------------------------------
46*/
47
48/*================================================================*/
49/* System Includes */
50
51#include <linux/netdevice.h>
52#include <linux/wireless.h>
53#include <linux/random.h>
54#include <linux/kernel.h>
55
56/* #define WEP_DEBUG	*/
57
58#include "p80211hdr.h"
59#include "p80211types.h"
60#include "p80211msg.h"
61#include "p80211conv.h"
62#include "p80211netdev.h"
63
64#define WEP_KEY(x)       (((x) & 0xC0) >> 6)
65
66static const u32 wep_crc32_table[256] = {
67	0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
68	0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
69	0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
70	0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
71	0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
72	0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
73	0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
74	0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
75	0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
76	0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
77	0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
78	0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
79	0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
80	0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
81	0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
82	0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
83	0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
84	0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
85	0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
86	0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
87	0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
88	0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
89	0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
90	0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
91	0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
92	0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
93	0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
94	0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
95	0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
96	0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
97	0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
98	0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
99	0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
100	0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
101	0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
102	0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
103	0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
104	0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
105	0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
106	0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
107	0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
108	0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
109	0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
110	0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
111	0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
112	0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
113	0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
114	0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
115	0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
116	0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
117	0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
118	0x2d02ef8dL
119};
120
121/* keylen in bytes! */
122
123int wep_change_key(wlandevice_t *wlandev, int keynum, u8 *key, int keylen)
124{
125	if (keylen < 0)
126		return -1;
127	if (keylen >= MAX_KEYLEN)
128		return -1;
129	if (key == NULL)
130		return -1;
131	if (keynum < 0)
132		return -1;
133	if (keynum >= NUM_WEPKEYS)
134		return -1;
135
136#ifdef WEP_DEBUG
137	pr_debug("WEP key %d len %d = %*phC\n", keynum, keylen,
138			  8, key);
139#endif
140
141	wlandev->wep_keylens[keynum] = keylen;
142	memcpy(wlandev->wep_keys[keynum], key, keylen);
143
144	return 0;
145}
146
147/*
148  4-byte IV at start of buffer, 4-byte ICV at end of buffer.
149  if successful, buf start is payload begin, length -= 8;
150 */
151int wep_decrypt(wlandevice_t *wlandev, u8 *buf, u32 len, int key_override,
152		u8 *iv, u8 *icv)
153{
154	u32 i, j, k, crc, keylen;
155	u8 s[256], key[64], c_crc[4];
156	u8 keyidx;
157
158	/* Needs to be at least 8 bytes of payload */
159	if (len <= 0)
160		return -1;
161
162	/* initialize the first bytes of the key from the IV */
163	key[0] = iv[0];
164	key[1] = iv[1];
165	key[2] = iv[2];
166	keyidx = WEP_KEY(iv[3]);
167
168	if (key_override >= 0)
169		keyidx = key_override;
170
171	if (keyidx >= NUM_WEPKEYS)
172		return -2;
173
174	keylen = wlandev->wep_keylens[keyidx];
175
176	if (keylen == 0)
177		return -3;
178
179	/* copy the rest of the key over from the designated key */
180	memcpy(key + 3, wlandev->wep_keys[keyidx], keylen);
181
182	keylen += 3;		/* add in IV bytes */
183
184#ifdef WEP_DEBUG
185	pr_debug("D %d: %*ph (%d %d) %*phC\n", len, 3, key,
186			  keyidx, keylen, 5, key + 3);
187#endif
188
189	/* set up the RC4 state */
190	for (i = 0; i < 256; i++)
191		s[i] = i;
192	j = 0;
193	for (i = 0; i < 256; i++) {
194		j = (j + s[i] + key[i % keylen]) & 0xff;
195		swap(i, j);
196	}
197
198	/* Apply the RC4 to the data, update the CRC32 */
199	crc = ~0;
200	i = j = 0;
201	for (k = 0; k < len; k++) {
202		i = (i + 1) & 0xff;
203		j = (j + s[i]) & 0xff;
204		swap(i, j);
205		buf[k] ^= s[(s[i] + s[j]) & 0xff];
206		crc = wep_crc32_table[(crc ^ buf[k]) & 0xff] ^ (crc >> 8);
207	}
208	crc = ~crc;
209
210	/* now let's check the crc */
211	c_crc[0] = crc;
212	c_crc[1] = crc >> 8;
213	c_crc[2] = crc >> 16;
214	c_crc[3] = crc >> 24;
215
216	for (k = 0; k < 4; k++) {
217		i = (i + 1) & 0xff;
218		j = (j + s[i]) & 0xff;
219		swap(i, j);
220		if ((c_crc[k] ^ s[(s[i] + s[j]) & 0xff]) != icv[k])
221			return -(4 | (k << 4));	/* ICV mismatch */
222	}
223
224	return 0;
225}
226
227/* encrypts in-place. */
228int wep_encrypt(wlandevice_t *wlandev, u8 *buf, u8 *dst, u32 len, int keynum,
229		u8 *iv, u8 *icv)
230{
231	u32 i, j, k, crc, keylen;
232	u8 s[256], key[64];
233
234	/* no point in WEPping an empty frame */
235	if (len <= 0)
236		return -1;
237
238	/* we need to have a real key.. */
239	if (keynum >= NUM_WEPKEYS)
240		return -2;
241	keylen = wlandev->wep_keylens[keynum];
242	if (keylen <= 0)
243		return -3;
244
245	/* use a random IV.  And skip known weak ones. */
246	get_random_bytes(iv, 3);
247	while ((iv[1] == 0xff) && (iv[0] >= 3) && (iv[0] < keylen))
248		get_random_bytes(iv, 3);
249
250	iv[3] = (keynum & 0x03) << 6;
251
252	key[0] = iv[0];
253	key[1] = iv[1];
254	key[2] = iv[2];
255
256	/* copy the rest of the key over from the designated key */
257	memcpy(key + 3, wlandev->wep_keys[keynum], keylen);
258
259	keylen += 3;		/* add in IV bytes */
260
261#ifdef WEP_DEBUG
262	pr_debug("E %d (%d/%d %d) %*ph %*phC\n", len,
263			  iv[3], keynum, keylen, 3, key, 5, key + 3);
264#endif
265
266	/* set up the RC4 state */
267	for (i = 0; i < 256; i++)
268		s[i] = i;
269	j = 0;
270	for (i = 0; i < 256; i++) {
271		j = (j + s[i] + key[i % keylen]) & 0xff;
272		swap(i, j);
273	}
274
275	/* Update CRC32 then apply RC4 to the data */
276	crc = ~0;
277	i = j = 0;
278	for (k = 0; k < len; k++) {
279		crc = wep_crc32_table[(crc ^ buf[k]) & 0xff] ^ (crc >> 8);
280		i = (i + 1) & 0xff;
281		j = (j + s[i]) & 0xff;
282		swap(i, j);
283		dst[k] = buf[k] ^ s[(s[i] + s[j]) & 0xff];
284	}
285	crc = ~crc;
286
287	/* now let's encrypt the crc */
288	icv[0] = crc;
289	icv[1] = crc >> 8;
290	icv[2] = crc >> 16;
291	icv[3] = crc >> 24;
292
293	for (k = 0; k < 4; k++) {
294		i = (i + 1) & 0xff;
295		j = (j + s[i]) & 0xff;
296		swap(i, j);
297		icv[k] ^= s[(s[i] + s[j]) & 0xff];
298	}
299
300	return 0;
301}
302