aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless/lib80211_crypt_wep.c
diff options
context:
space:
mode:
authorJohn W. Linville <linville@tuxdriver.com>2008-10-29 11:35:05 -0400
committerJohn W. Linville <linville@tuxdriver.com>2008-11-21 11:08:17 -0500
commit274bfb8dc5ffa16cb073801bebe76ab7f4e2e73d (patch)
tree04cd3f6a062496911b56737daa6a0858b769ccd6 /net/wireless/lib80211_crypt_wep.c
parentdfe1bafdbac1c7b48b636fb7ace799e78170e0d6 (diff)
lib80211: absorb crypto bits from net/ieee80211
These bits are shared already between ipw2x00 and hostap, and could probably be shared both more cleanly and with other drivers. This commit simply relocates the code to lib80211 and adjusts the drivers appropriately. Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/wireless/lib80211_crypt_wep.c')
-rw-r--r--net/wireless/lib80211_crypt_wep.c296
1 files changed, 296 insertions, 0 deletions
diff --git a/net/wireless/lib80211_crypt_wep.c b/net/wireless/lib80211_crypt_wep.c
new file mode 100644
index 000000000000..6d41e05ca33b
--- /dev/null
+++ b/net/wireless/lib80211_crypt_wep.c
@@ -0,0 +1,296 @@
1/*
2 * lib80211 crypt: host-based WEP encryption implementation for lib80211
3 *
4 * Copyright (c) 2002-2004, Jouni Malinen <j@w1.fi>
5 * Copyright (c) 2008, John W. Linville <linville@tuxdriver.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation. See README and COPYING for
10 * more details.
11 */
12
13#include <linux/err.h>
14#include <linux/module.h>
15#include <linux/init.h>
16#include <linux/slab.h>
17#include <linux/random.h>
18#include <linux/scatterlist.h>
19#include <linux/skbuff.h>
20#include <linux/mm.h>
21#include <asm/string.h>
22
23#include <net/lib80211.h>
24
25#include <linux/crypto.h>
26#include <linux/crc32.h>
27
28MODULE_AUTHOR("Jouni Malinen");
29MODULE_DESCRIPTION("lib80211 crypt: WEP");
30MODULE_LICENSE("GPL");
31
32struct lib80211_wep_data {
33 u32 iv;
34#define WEP_KEY_LEN 13
35 u8 key[WEP_KEY_LEN + 1];
36 u8 key_len;
37 u8 key_idx;
38 struct crypto_blkcipher *tx_tfm;
39 struct crypto_blkcipher *rx_tfm;
40};
41
42static void *lib80211_wep_init(int keyidx)
43{
44 struct lib80211_wep_data *priv;
45
46 priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
47 if (priv == NULL)
48 goto fail;
49 priv->key_idx = keyidx;
50
51 priv->tx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC);
52 if (IS_ERR(priv->tx_tfm)) {
53 printk(KERN_DEBUG "lib80211_crypt_wep: could not allocate "
54 "crypto API arc4\n");
55 priv->tx_tfm = NULL;
56 goto fail;
57 }
58
59 priv->rx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC);
60 if (IS_ERR(priv->rx_tfm)) {
61 printk(KERN_DEBUG "lib80211_crypt_wep: could not allocate "
62 "crypto API arc4\n");
63 priv->rx_tfm = NULL;
64 goto fail;
65 }
66 /* start WEP IV from a random value */
67 get_random_bytes(&priv->iv, 4);
68
69 return priv;
70
71 fail:
72 if (priv) {
73 if (priv->tx_tfm)
74 crypto_free_blkcipher(priv->tx_tfm);
75 if (priv->rx_tfm)
76 crypto_free_blkcipher(priv->rx_tfm);
77 kfree(priv);
78 }
79 return NULL;
80}
81
82static void lib80211_wep_deinit(void *priv)
83{
84 struct lib80211_wep_data *_priv = priv;
85 if (_priv) {
86 if (_priv->tx_tfm)
87 crypto_free_blkcipher(_priv->tx_tfm);
88 if (_priv->rx_tfm)
89 crypto_free_blkcipher(_priv->rx_tfm);
90 }
91 kfree(priv);
92}
93
94/* Add WEP IV/key info to a frame that has at least 4 bytes of headroom */
95static int lib80211_wep_build_iv(struct sk_buff *skb, int hdr_len,
96 u8 *key, int keylen, void *priv)
97{
98 struct lib80211_wep_data *wep = priv;
99 u32 klen, len;
100 u8 *pos;
101
102 if (skb_headroom(skb) < 4 || skb->len < hdr_len)
103 return -1;
104
105 len = skb->len - hdr_len;
106 pos = skb_push(skb, 4);
107 memmove(pos, pos + 4, hdr_len);
108 pos += hdr_len;
109
110 klen = 3 + wep->key_len;
111
112 wep->iv++;
113
114 /* Fluhrer, Mantin, and Shamir have reported weaknesses in the key
115 * scheduling algorithm of RC4. At least IVs (KeyByte + 3, 0xff, N)
116 * can be used to speedup attacks, so avoid using them. */
117 if ((wep->iv & 0xff00) == 0xff00) {
118 u8 B = (wep->iv >> 16) & 0xff;
119 if (B >= 3 && B < klen)
120 wep->iv += 0x0100;
121 }
122
123 /* Prepend 24-bit IV to RC4 key and TX frame */
124 *pos++ = (wep->iv >> 16) & 0xff;
125 *pos++ = (wep->iv >> 8) & 0xff;
126 *pos++ = wep->iv & 0xff;
127 *pos++ = wep->key_idx << 6;
128
129 return 0;
130}
131
132/* Perform WEP encryption on given skb that has at least 4 bytes of headroom
133 * for IV and 4 bytes of tailroom for ICV. Both IV and ICV will be transmitted,
134 * so the payload length increases with 8 bytes.
135 *
136 * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data))
137 */
138static int lib80211_wep_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
139{
140 struct lib80211_wep_data *wep = priv;
141 struct blkcipher_desc desc = { .tfm = wep->tx_tfm };
142 u32 crc, klen, len;
143 u8 *pos, *icv;
144 struct scatterlist sg;
145 u8 key[WEP_KEY_LEN + 3];
146
147 /* other checks are in lib80211_wep_build_iv */
148 if (skb_tailroom(skb) < 4)
149 return -1;
150
151 /* add the IV to the frame */
152 if (lib80211_wep_build_iv(skb, hdr_len, NULL, 0, priv))
153 return -1;
154
155 /* Copy the IV into the first 3 bytes of the key */
156 skb_copy_from_linear_data_offset(skb, hdr_len, key, 3);
157
158 /* Copy rest of the WEP key (the secret part) */
159 memcpy(key + 3, wep->key, wep->key_len);
160
161 len = skb->len - hdr_len - 4;
162 pos = skb->data + hdr_len + 4;
163 klen = 3 + wep->key_len;
164
165 /* Append little-endian CRC32 over only the data and encrypt it to produce ICV */
166 crc = ~crc32_le(~0, pos, len);
167 icv = skb_put(skb, 4);
168 icv[0] = crc;
169 icv[1] = crc >> 8;
170 icv[2] = crc >> 16;
171 icv[3] = crc >> 24;
172
173 crypto_blkcipher_setkey(wep->tx_tfm, key, klen);
174 sg_init_one(&sg, pos, len + 4);
175 return crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4);
176}
177
178/* Perform WEP decryption on given buffer. Buffer includes whole WEP part of
179 * the frame: IV (4 bytes), encrypted payload (including SNAP header),
180 * ICV (4 bytes). len includes both IV and ICV.
181 *
182 * Returns 0 if frame was decrypted successfully and ICV was correct and -1 on
183 * failure. If frame is OK, IV and ICV will be removed.
184 */
185static int lib80211_wep_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
186{
187 struct lib80211_wep_data *wep = priv;
188 struct blkcipher_desc desc = { .tfm = wep->rx_tfm };
189 u32 crc, klen, plen;
190 u8 key[WEP_KEY_LEN + 3];
191 u8 keyidx, *pos, icv[4];
192 struct scatterlist sg;
193
194 if (skb->len < hdr_len + 8)
195 return -1;
196
197 pos = skb->data + hdr_len;
198 key[0] = *pos++;
199 key[1] = *pos++;
200 key[2] = *pos++;
201 keyidx = *pos++ >> 6;
202 if (keyidx != wep->key_idx)
203 return -1;
204
205 klen = 3 + wep->key_len;
206
207 /* Copy rest of the WEP key (the secret part) */
208 memcpy(key + 3, wep->key, wep->key_len);
209
210 /* Apply RC4 to data and compute CRC32 over decrypted data */
211 plen = skb->len - hdr_len - 8;
212
213 crypto_blkcipher_setkey(wep->rx_tfm, key, klen);
214 sg_init_one(&sg, pos, plen + 4);
215 if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4))
216 return -7;
217
218 crc = ~crc32_le(~0, pos, plen);
219 icv[0] = crc;
220 icv[1] = crc >> 8;
221 icv[2] = crc >> 16;
222 icv[3] = crc >> 24;
223 if (memcmp(icv, pos + plen, 4) != 0) {
224 /* ICV mismatch - drop frame */
225 return -2;
226 }
227
228 /* Remove IV and ICV */
229 memmove(skb->data + 4, skb->data, hdr_len);
230 skb_pull(skb, 4);
231 skb_trim(skb, skb->len - 4);
232
233 return 0;
234}
235
236static int lib80211_wep_set_key(void *key, int len, u8 * seq, void *priv)
237{
238 struct lib80211_wep_data *wep = priv;
239
240 if (len < 0 || len > WEP_KEY_LEN)
241 return -1;
242
243 memcpy(wep->key, key, len);
244 wep->key_len = len;
245
246 return 0;
247}
248
249static int lib80211_wep_get_key(void *key, int len, u8 * seq, void *priv)
250{
251 struct lib80211_wep_data *wep = priv;
252
253 if (len < wep->key_len)
254 return -1;
255
256 memcpy(key, wep->key, wep->key_len);
257
258 return wep->key_len;
259}
260
261static char *lib80211_wep_print_stats(char *p, void *priv)
262{
263 struct lib80211_wep_data *wep = priv;
264 p += sprintf(p, "key[%d] alg=WEP len=%d\n", wep->key_idx, wep->key_len);
265 return p;
266}
267
268static struct lib80211_crypto_ops lib80211_crypt_wep = {
269 .name = "WEP",
270 .init = lib80211_wep_init,
271 .deinit = lib80211_wep_deinit,
272 .build_iv = lib80211_wep_build_iv,
273 .encrypt_mpdu = lib80211_wep_encrypt,
274 .decrypt_mpdu = lib80211_wep_decrypt,
275 .encrypt_msdu = NULL,
276 .decrypt_msdu = NULL,
277 .set_key = lib80211_wep_set_key,
278 .get_key = lib80211_wep_get_key,
279 .print_stats = lib80211_wep_print_stats,
280 .extra_mpdu_prefix_len = 4, /* IV */
281 .extra_mpdu_postfix_len = 4, /* ICV */
282 .owner = THIS_MODULE,
283};
284
285static int __init lib80211_crypto_wep_init(void)
286{
287 return lib80211_register_crypto_ops(&lib80211_crypt_wep);
288}
289
290static void __exit lib80211_crypto_wep_exit(void)
291{
292 lib80211_unregister_crypto_ops(&lib80211_crypt_wep);
293}
294
295module_init(lib80211_crypto_wep_init);
296module_exit(lib80211_crypto_wep_exit);