diff options
Diffstat (limited to 'net/mac80211/wep.c')
-rw-r--r-- | net/mac80211/wep.c | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c new file mode 100644 index 000000000000..1ad3d75281cc --- /dev/null +++ b/net/mac80211/wep.c | |||
@@ -0,0 +1,328 @@ | |||
1 | /* | ||
2 | * Software WEP encryption implementation | ||
3 | * Copyright 2002, Jouni Malinen <jkmaline@cc.hut.fi> | ||
4 | * Copyright 2003, Instant802 Networks, Inc. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #include <linux/netdevice.h> | ||
12 | #include <linux/types.h> | ||
13 | #include <linux/random.h> | ||
14 | #include <linux/compiler.h> | ||
15 | #include <linux/crc32.h> | ||
16 | #include <linux/crypto.h> | ||
17 | #include <linux/err.h> | ||
18 | #include <linux/mm.h> | ||
19 | #include <asm/scatterlist.h> | ||
20 | |||
21 | #include <net/mac80211.h> | ||
22 | #include "ieee80211_i.h" | ||
23 | #include "wep.h" | ||
24 | |||
25 | |||
26 | int ieee80211_wep_init(struct ieee80211_local *local) | ||
27 | { | ||
28 | /* start WEP IV from a random value */ | ||
29 | get_random_bytes(&local->wep_iv, WEP_IV_LEN); | ||
30 | |||
31 | local->wep_tx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, | ||
32 | CRYPTO_ALG_ASYNC); | ||
33 | if (IS_ERR(local->wep_tx_tfm)) | ||
34 | return -ENOMEM; | ||
35 | |||
36 | local->wep_rx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, | ||
37 | CRYPTO_ALG_ASYNC); | ||
38 | if (IS_ERR(local->wep_rx_tfm)) { | ||
39 | crypto_free_blkcipher(local->wep_tx_tfm); | ||
40 | return -ENOMEM; | ||
41 | } | ||
42 | |||
43 | return 0; | ||
44 | } | ||
45 | |||
46 | void ieee80211_wep_free(struct ieee80211_local *local) | ||
47 | { | ||
48 | crypto_free_blkcipher(local->wep_tx_tfm); | ||
49 | crypto_free_blkcipher(local->wep_rx_tfm); | ||
50 | } | ||
51 | |||
52 | static inline int ieee80211_wep_weak_iv(u32 iv, int keylen) | ||
53 | { | ||
54 | /* Fluhrer, Mantin, and Shamir have reported weaknesses in the | ||
55 | * key scheduling algorithm of RC4. At least IVs (KeyByte + 3, | ||
56 | * 0xff, N) can be used to speedup attacks, so avoid using them. */ | ||
57 | if ((iv & 0xff00) == 0xff00) { | ||
58 | u8 B = (iv >> 16) & 0xff; | ||
59 | if (B >= 3 && B < 3 + keylen) | ||
60 | return 1; | ||
61 | } | ||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | |||
66 | void ieee80211_wep_get_iv(struct ieee80211_local *local, | ||
67 | struct ieee80211_key *key, u8 *iv) | ||
68 | { | ||
69 | local->wep_iv++; | ||
70 | if (ieee80211_wep_weak_iv(local->wep_iv, key->keylen)) | ||
71 | local->wep_iv += 0x0100; | ||
72 | |||
73 | if (!iv) | ||
74 | return; | ||
75 | |||
76 | *iv++ = (local->wep_iv >> 16) & 0xff; | ||
77 | *iv++ = (local->wep_iv >> 8) & 0xff; | ||
78 | *iv++ = local->wep_iv & 0xff; | ||
79 | *iv++ = key->keyidx << 6; | ||
80 | } | ||
81 | |||
82 | |||
83 | u8 * ieee80211_wep_add_iv(struct ieee80211_local *local, | ||
84 | struct sk_buff *skb, | ||
85 | struct ieee80211_key *key) | ||
86 | { | ||
87 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | ||
88 | u16 fc; | ||
89 | int hdrlen; | ||
90 | u8 *newhdr; | ||
91 | |||
92 | fc = le16_to_cpu(hdr->frame_control); | ||
93 | fc |= IEEE80211_FCTL_PROTECTED; | ||
94 | hdr->frame_control = cpu_to_le16(fc); | ||
95 | |||
96 | if ((skb_headroom(skb) < WEP_IV_LEN || | ||
97 | skb_tailroom(skb) < WEP_ICV_LEN)) { | ||
98 | I802_DEBUG_INC(local->tx_expand_skb_head); | ||
99 | if (unlikely(pskb_expand_head(skb, WEP_IV_LEN, WEP_ICV_LEN, | ||
100 | GFP_ATOMIC))) | ||
101 | return NULL; | ||
102 | } | ||
103 | |||
104 | hdrlen = ieee80211_get_hdrlen(fc); | ||
105 | newhdr = skb_push(skb, WEP_IV_LEN); | ||
106 | memmove(newhdr, newhdr + WEP_IV_LEN, hdrlen); | ||
107 | ieee80211_wep_get_iv(local, key, newhdr + hdrlen); | ||
108 | return newhdr + hdrlen; | ||
109 | } | ||
110 | |||
111 | |||
112 | void ieee80211_wep_remove_iv(struct ieee80211_local *local, | ||
113 | struct sk_buff *skb, | ||
114 | struct ieee80211_key *key) | ||
115 | { | ||
116 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | ||
117 | u16 fc; | ||
118 | int hdrlen; | ||
119 | |||
120 | fc = le16_to_cpu(hdr->frame_control); | ||
121 | hdrlen = ieee80211_get_hdrlen(fc); | ||
122 | memmove(skb->data + WEP_IV_LEN, skb->data, hdrlen); | ||
123 | skb_pull(skb, WEP_IV_LEN); | ||
124 | } | ||
125 | |||
126 | |||
127 | /* Perform WEP encryption using given key. data buffer must have tailroom | ||
128 | * for 4-byte ICV. data_len must not include this ICV. Note: this function | ||
129 | * does _not_ add IV. data = RC4(data | CRC32(data)) */ | ||
130 | void ieee80211_wep_encrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, | ||
131 | size_t klen, u8 *data, size_t data_len) | ||
132 | { | ||
133 | struct blkcipher_desc desc = { .tfm = tfm }; | ||
134 | struct scatterlist sg; | ||
135 | __le32 *icv; | ||
136 | |||
137 | icv = (__le32 *)(data + data_len); | ||
138 | *icv = cpu_to_le32(~crc32_le(~0, data, data_len)); | ||
139 | |||
140 | crypto_blkcipher_setkey(tfm, rc4key, klen); | ||
141 | sg.page = virt_to_page(data); | ||
142 | sg.offset = offset_in_page(data); | ||
143 | sg.length = data_len + WEP_ICV_LEN; | ||
144 | crypto_blkcipher_encrypt(&desc, &sg, &sg, sg.length); | ||
145 | } | ||
146 | |||
147 | |||
148 | /* Perform WEP encryption on given skb. 4 bytes of extra space (IV) in the | ||
149 | * beginning of the buffer 4 bytes of extra space (ICV) in the end of the | ||
150 | * buffer will be added. Both IV and ICV will be transmitted, so the | ||
151 | * payload length increases with 8 bytes. | ||
152 | * | ||
153 | * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data)) | ||
154 | */ | ||
155 | int ieee80211_wep_encrypt(struct ieee80211_local *local, struct sk_buff *skb, | ||
156 | struct ieee80211_key *key) | ||
157 | { | ||
158 | u32 klen; | ||
159 | u8 *rc4key, *iv; | ||
160 | size_t len; | ||
161 | |||
162 | if (!key || key->alg != ALG_WEP) | ||
163 | return -1; | ||
164 | |||
165 | klen = 3 + key->keylen; | ||
166 | rc4key = kmalloc(klen, GFP_ATOMIC); | ||
167 | if (!rc4key) | ||
168 | return -1; | ||
169 | |||
170 | iv = ieee80211_wep_add_iv(local, skb, key); | ||
171 | if (!iv) { | ||
172 | kfree(rc4key); | ||
173 | return -1; | ||
174 | } | ||
175 | |||
176 | len = skb->len - (iv + WEP_IV_LEN - skb->data); | ||
177 | |||
178 | /* Prepend 24-bit IV to RC4 key */ | ||
179 | memcpy(rc4key, iv, 3); | ||
180 | |||
181 | /* Copy rest of the WEP key (the secret part) */ | ||
182 | memcpy(rc4key + 3, key->key, key->keylen); | ||
183 | |||
184 | /* Add room for ICV */ | ||
185 | skb_put(skb, WEP_ICV_LEN); | ||
186 | |||
187 | ieee80211_wep_encrypt_data(local->wep_tx_tfm, rc4key, klen, | ||
188 | iv + WEP_IV_LEN, len); | ||
189 | |||
190 | kfree(rc4key); | ||
191 | |||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | |||
196 | /* Perform WEP decryption using given key. data buffer includes encrypted | ||
197 | * payload, including 4-byte ICV, but _not_ IV. data_len must not include ICV. | ||
198 | * Return 0 on success and -1 on ICV mismatch. */ | ||
199 | int ieee80211_wep_decrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, | ||
200 | size_t klen, u8 *data, size_t data_len) | ||
201 | { | ||
202 | struct blkcipher_desc desc = { .tfm = tfm }; | ||
203 | struct scatterlist sg; | ||
204 | __le32 crc; | ||
205 | |||
206 | crypto_blkcipher_setkey(tfm, rc4key, klen); | ||
207 | sg.page = virt_to_page(data); | ||
208 | sg.offset = offset_in_page(data); | ||
209 | sg.length = data_len + WEP_ICV_LEN; | ||
210 | crypto_blkcipher_decrypt(&desc, &sg, &sg, sg.length); | ||
211 | |||
212 | crc = cpu_to_le32(~crc32_le(~0, data, data_len)); | ||
213 | if (memcmp(&crc, data + data_len, WEP_ICV_LEN) != 0) | ||
214 | /* ICV mismatch */ | ||
215 | return -1; | ||
216 | |||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | |||
221 | /* Perform WEP decryption on given skb. Buffer includes whole WEP part of | ||
222 | * the frame: IV (4 bytes), encrypted payload (including SNAP header), | ||
223 | * ICV (4 bytes). skb->len includes both IV and ICV. | ||
224 | * | ||
225 | * Returns 0 if frame was decrypted successfully and ICV was correct and -1 on | ||
226 | * failure. If frame is OK, IV and ICV will be removed, i.e., decrypted payload | ||
227 | * is moved to the beginning of the skb and skb length will be reduced. | ||
228 | */ | ||
229 | int ieee80211_wep_decrypt(struct ieee80211_local *local, struct sk_buff *skb, | ||
230 | struct ieee80211_key *key) | ||
231 | { | ||
232 | u32 klen; | ||
233 | u8 *rc4key; | ||
234 | u8 keyidx; | ||
235 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | ||
236 | u16 fc; | ||
237 | int hdrlen; | ||
238 | size_t len; | ||
239 | int ret = 0; | ||
240 | |||
241 | fc = le16_to_cpu(hdr->frame_control); | ||
242 | if (!(fc & IEEE80211_FCTL_PROTECTED)) | ||
243 | return -1; | ||
244 | |||
245 | hdrlen = ieee80211_get_hdrlen(fc); | ||
246 | |||
247 | if (skb->len < 8 + hdrlen) | ||
248 | return -1; | ||
249 | |||
250 | len = skb->len - hdrlen - 8; | ||
251 | |||
252 | keyidx = skb->data[hdrlen + 3] >> 6; | ||
253 | |||
254 | if (!key || keyidx != key->keyidx || key->alg != ALG_WEP) | ||
255 | return -1; | ||
256 | |||
257 | klen = 3 + key->keylen; | ||
258 | |||
259 | rc4key = kmalloc(klen, GFP_ATOMIC); | ||
260 | if (!rc4key) | ||
261 | return -1; | ||
262 | |||
263 | /* Prepend 24-bit IV to RC4 key */ | ||
264 | memcpy(rc4key, skb->data + hdrlen, 3); | ||
265 | |||
266 | /* Copy rest of the WEP key (the secret part) */ | ||
267 | memcpy(rc4key + 3, key->key, key->keylen); | ||
268 | |||
269 | if (ieee80211_wep_decrypt_data(local->wep_rx_tfm, rc4key, klen, | ||
270 | skb->data + hdrlen + WEP_IV_LEN, | ||
271 | len)) { | ||
272 | printk(KERN_DEBUG "WEP decrypt failed (ICV)\n"); | ||
273 | ret = -1; | ||
274 | } | ||
275 | |||
276 | kfree(rc4key); | ||
277 | |||
278 | /* Trim ICV */ | ||
279 | skb_trim(skb, skb->len - WEP_ICV_LEN); | ||
280 | |||
281 | /* Remove IV */ | ||
282 | memmove(skb->data + WEP_IV_LEN, skb->data, hdrlen); | ||
283 | skb_pull(skb, WEP_IV_LEN); | ||
284 | |||
285 | return ret; | ||
286 | } | ||
287 | |||
288 | |||
289 | int ieee80211_wep_get_keyidx(struct sk_buff *skb) | ||
290 | { | ||
291 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | ||
292 | u16 fc; | ||
293 | int hdrlen; | ||
294 | |||
295 | fc = le16_to_cpu(hdr->frame_control); | ||
296 | if (!(fc & IEEE80211_FCTL_PROTECTED)) | ||
297 | return -1; | ||
298 | |||
299 | hdrlen = ieee80211_get_hdrlen(fc); | ||
300 | |||
301 | if (skb->len < 8 + hdrlen) | ||
302 | return -1; | ||
303 | |||
304 | return skb->data[hdrlen + 3] >> 6; | ||
305 | } | ||
306 | |||
307 | |||
308 | u8 * ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key) | ||
309 | { | ||
310 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | ||
311 | u16 fc; | ||
312 | int hdrlen; | ||
313 | u8 *ivpos; | ||
314 | u32 iv; | ||
315 | |||
316 | fc = le16_to_cpu(hdr->frame_control); | ||
317 | if (!(fc & IEEE80211_FCTL_PROTECTED)) | ||
318 | return NULL; | ||
319 | |||
320 | hdrlen = ieee80211_get_hdrlen(fc); | ||
321 | ivpos = skb->data + hdrlen; | ||
322 | iv = (ivpos[0] << 16) | (ivpos[1] << 8) | ivpos[2]; | ||
323 | |||
324 | if (ieee80211_wep_weak_iv(iv, key->keylen)) | ||
325 | return ivpos; | ||
326 | |||
327 | return NULL; | ||
328 | } | ||