summaryrefslogtreecommitdiff
path: root/gpxe/src/net/80211/wep.c
blob: 1c37e0c30ec9e7022b0145e59b14959201712c8e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
/*
 * Copyright (c) 2009 Joshua Oreman <oremanj@rwcr.net>.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

FILE_LICENCE ( GPL2_OR_LATER );

#include <gpxe/net80211.h>
#include <gpxe/sec80211.h>
#include <gpxe/crypto.h>
#include <gpxe/arc4.h>
#include <gpxe/crc32.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

/** @file
 *
 * The WEP wireless encryption method (insecure!)
 *
 * The data field in a WEP-encrypted packet contains a 3-byte
 * initialisation vector, one-byte Key ID field (only the bottom two
 * bits are ever used), encrypted data, and a 4-byte encrypted CRC of
 * the plaintext data, called the ICV. To decrypt it, the IV is
 * prepended to the shared key and the data stream (including ICV) is
 * run through the ARC4 stream cipher; if the ICV matches a CRC32
 * calculated on the plaintext, the packet is valid.
 *
 * For efficiency and code-size reasons, this file assumes it is
 * running on a little-endian machine.
 */

/** Length of WEP initialisation vector */
#define WEP_IV_LEN	3

/** Length of WEP key ID byte */
#define WEP_KID_LEN	1

/** Length of WEP ICV checksum */
#define WEP_ICV_LEN	4

/** Maximum length of WEP key */
#define WEP_MAX_KEY	16

/** Amount of data placed before the encrypted bytes */
#define WEP_HEADER_LEN	4

/** Amount of data placed after the encrypted bytes */
#define WEP_TRAILER_LEN	4

/** Total WEP overhead bytes */
#define WEP_OVERHEAD	8

/** Context for WEP encryption and decryption */
struct wep_ctx
{
	/** Encoded WEP key
	 *
	 * The actual key bytes are stored beginning at offset 3, to
	 * leave room for easily inserting the IV before a particular
	 * operation.
	 */
	u8 key[WEP_IV_LEN + WEP_MAX_KEY];

	/** Length of WEP key (not including IV bytes) */
	int keylen;

	/** ARC4 context */
	struct arc4_ctx arc4;
};

/**
 * Initialize WEP algorithm
 *
 * @v crypto	802.11 cryptographic algorithm
 * @v key	WEP key to use
 * @v keylen	Length of WEP key
 * @v rsc	Initial receive sequence counter (unused)
 * @ret rc	Return status code
 *
 * Standard key lengths are 5 and 13 bytes; 16-byte keys are
 * occasionally supported as an extension to the standard.
 */
static int wep_init ( struct net80211_crypto *crypto, const void *key,
		      int keylen, const void *rsc __unused )
{
	struct wep_ctx *ctx = crypto->priv;

	ctx->keylen = ( keylen > WEP_MAX_KEY ? WEP_MAX_KEY : keylen );
	memcpy ( ctx->key + WEP_IV_LEN, key, ctx->keylen );

	return 0;
}

/**
 * Encrypt packet using WEP
 *
 * @v crypto	802.11 cryptographic algorithm
 * @v iob	I/O buffer of plaintext packet
 * @ret eiob	Newly allocated I/O buffer for encrypted packet, or NULL
 *
 * If memory allocation fails, @c NULL is returned.
 */
static struct io_buffer * wep_encrypt ( struct net80211_crypto *crypto,
					struct io_buffer *iob )
{
	struct wep_ctx *ctx = crypto->priv;
	struct io_buffer *eiob;
	struct ieee80211_frame *hdr;
	const int hdrlen = IEEE80211_TYP_FRAME_HEADER_LEN;
	int datalen = iob_len ( iob ) - hdrlen;
	int newlen = hdrlen + datalen + WEP_OVERHEAD;
	u32 iv, icv;

	eiob = alloc_iob ( newlen );
	if ( ! eiob )
		return NULL;

	memcpy ( iob_put ( eiob, hdrlen ), iob->data, hdrlen );
	hdr = eiob->data;
	hdr->fc |= IEEE80211_FC_PROTECTED;

	/* Calculate IV, put it in the header (with key ID byte = 0), and
	   set it up at the start of the encryption key. */
	iv = random() & 0xffffff; /* IV in bottom 3 bytes, top byte = KID = 0 */
	memcpy ( iob_put ( eiob, WEP_HEADER_LEN ), &iv, WEP_HEADER_LEN );
	memcpy ( ctx->key, &iv, WEP_IV_LEN );

	/* Encrypt the data using RC4 */
	cipher_setkey ( &arc4_algorithm, &ctx->arc4, ctx->key,
			ctx->keylen + WEP_IV_LEN );
	cipher_encrypt ( &arc4_algorithm, &ctx->arc4, iob->data + hdrlen,
			 iob_put ( eiob, datalen ), datalen );

	/* Add ICV */
	icv = ~crc32_le ( ~0, iob->data + hdrlen, datalen );
	cipher_encrypt ( &arc4_algorithm, &ctx->arc4, &icv,
			 iob_put ( eiob, WEP_ICV_LEN ), WEP_ICV_LEN );

	return eiob;
}

/**
 * Decrypt packet using WEP
 *
 * @v crypto	802.11 cryptographic algorithm
 * @v eiob	I/O buffer of encrypted packet
 * @ret iob	Newly allocated I/O buffer for plaintext packet, or NULL
 *
 * If a consistency check for the decryption fails (usually indicating
 * an invalid key), @c NULL is returned.
 */
static struct io_buffer * wep_decrypt ( struct net80211_crypto *crypto,
					struct io_buffer *eiob )
{
	struct wep_ctx *ctx = crypto->priv;
	struct io_buffer *iob;
	struct ieee80211_frame *hdr;
	const int hdrlen = IEEE80211_TYP_FRAME_HEADER_LEN;
	int datalen = iob_len ( eiob ) - hdrlen - WEP_OVERHEAD;
	int newlen = hdrlen + datalen;
	u32 iv, icv, crc;

	iob = alloc_iob ( newlen );
	if ( ! iob )
		return NULL;

	memcpy ( iob_put ( iob, hdrlen ), eiob->data, hdrlen );
	hdr = iob->data;
	hdr->fc &= ~IEEE80211_FC_PROTECTED;

	/* Strip off IV and use it to initialize cryptosystem */
	memcpy ( &iv, eiob->data + hdrlen, 4 );
	iv &= 0xffffff;		/* ignore key ID byte */
	memcpy ( ctx->key, &iv, WEP_IV_LEN );

	/* Decrypt the data using RC4 */
	cipher_setkey ( &arc4_algorithm, &ctx->arc4, ctx->key,
			ctx->keylen + WEP_IV_LEN );
	cipher_decrypt ( &arc4_algorithm, &ctx->arc4, eiob->data + hdrlen +
			 WEP_HEADER_LEN, iob_put ( iob, datalen ), datalen );

	/* Strip off ICV and verify it */
	cipher_decrypt ( &arc4_algorithm, &ctx->arc4, eiob->data + hdrlen +
			 WEP_HEADER_LEN + datalen, &icv, WEP_ICV_LEN );
	crc = ~crc32_le ( ~0, iob->data + hdrlen, datalen );
	if ( crc != icv ) {
		DBGC ( crypto, "WEP %p CRC mismatch: expect %08x, get %08x\n",
		       crypto, icv, crc );
		free_iob ( iob );
		return NULL;
	}
	return iob;
}

/** WEP cryptosystem for 802.11 */
struct net80211_crypto wep_crypto __net80211_crypto = {
	.algorithm = NET80211_CRYPT_WEP,
	.init = wep_init,
	.encrypt = wep_encrypt,
	.decrypt = wep_decrypt,
	.priv_len = sizeof ( struct wep_ctx ),
};

/**
 * Initialize trivial 802.11 security handshaker
 *
 * @v dev	802.11 device
 * @v ctx	Security handshaker
 *
 * This simply fetches a WEP key from netX/key, and if it exists,
 * installs WEP cryptography on the 802.11 device. No real handshaking
 * is performed.
 */
static int trivial_init ( struct net80211_device *dev )
{
	u8 key[WEP_MAX_KEY];	/* support up to 128-bit keys */
	int len;
	int rc;

	if ( dev->associating &&
	     dev->associating->crypto == NET80211_CRYPT_NONE )
		return 0;	/* no crypto? OK. */

	len = fetch_setting ( netdev_settings ( dev->netdev ),
			      &net80211_key_setting, key, WEP_MAX_KEY );

	if ( len <= 0 ) {
		DBGC ( dev, "802.11 %p cannot do WEP without a key\n", dev );
		return -EACCES;
	}

	/* Full 128-bit keys are a nonstandard extension, but they're
	   utterly trivial to support, so we do. */
	if ( len != 5 && len != 13 && len != 16 ) {
		DBGC ( dev, "802.11 %p invalid WEP key length %d\n",
		       dev, len );
		return -EINVAL;
	}

	DBGC ( dev, "802.11 %p installing %d-bit WEP\n", dev, len * 8 );

	rc = sec80211_install ( &dev->crypto, NET80211_CRYPT_WEP, key, len,
				NULL );
	if ( rc < 0 )
		return rc;

	return 0;
}

/**
 * Check for key change on trivial 802.11 security handshaker
 *
 * @v dev	802.11 device
 * @v ctx	Security handshaker
 */
static int trivial_change_key ( struct net80211_device *dev )
{
	u8 key[WEP_MAX_KEY];
	int len;
	int change = 0;

	/* If going from WEP to clear, or something else to WEP, reassociate. */
	if ( ! dev->crypto || ( dev->crypto->init != wep_init ) )
		change ^= 1;

	len = fetch_setting ( netdev_settings ( dev->netdev ),
			      &net80211_key_setting, key, WEP_MAX_KEY );
	if ( len <= 0 )
		change ^= 1;

	/* Changing crypto type => return nonzero to reassociate. */
	if ( change )
		return -EINVAL;

	/* Going from no crypto to still no crypto => nothing to do. */
	if ( len <= 0 )
		return 0;

	/* Otherwise, reinitialise WEP with new key. */
	return wep_init ( dev->crypto, key, len, NULL );
}

/** Trivial 802.11 security handshaker */
struct net80211_handshaker trivial_handshaker __net80211_handshaker = {
	.protocol = NET80211_SECPROT_NONE,
	.init = trivial_init,
	.change_key = trivial_change_key,
	.priv_len = 0,
};