Merge from vendor branch HEIMDAL:
[dragonfly.git] / sys / netproto / 802_11 / wlan / ieee80211_crypto.c
1 /*
2  * Copyright (c) 2001 Atsushi Onoe
3  * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * Alternatively, this software may be distributed under the terms of the
18  * GNU General Public License ("GPL") version 2 as published by the Free
19  * Software Foundation.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  * $FreeBSD: src/sys/net80211/ieee80211_crypto.c,v 1.3 2003/10/17 23:15:30 sam Exp $
33  * $DragonFly: src/sys/netproto/802_11/wlan/ieee80211_crypto.c,v 1.1 2004/07/26 16:30:17 joerg Exp $
34  */
35
36 #include "opt_inet.h"
37
38 #include <sys/param.h>
39 #include <sys/systm.h> 
40 #include <sys/mbuf.h>   
41 #include <sys/malloc.h>
42 #include <sys/kernel.h>
43 #include <sys/socket.h>
44 #include <sys/sockio.h>
45 #include <sys/endian.h>
46 #include <sys/errno.h>
47 #include <sys/bus.h>
48 #include <sys/proc.h>
49 #include <sys/sysctl.h>
50
51 #include <machine/atomic.h>
52  
53 #include <net/if.h>
54 #include <net/if_dl.h>
55 #include <net/if_media.h>
56 #include <net/if_arp.h>
57 #include <net/ethernet.h>
58 #include <net/if_llc.h>
59
60 #include <netproto/802_11/ieee80211_var.h>
61
62 #include <net/bpf.h>
63
64 #ifdef INET
65 #include <netinet/in.h> 
66 #include <netinet/if_ether.h>
67 #endif
68
69 #include <crypto/rc4/rc4.h>
70 #define arc4_ctxlen()                   sizeof (struct rc4_state)
71 #define arc4_setkey(_c,_k,_l)           rc4_init(_c,_k,_l)
72 #define arc4_encrypt(_c,_d,_s,_l)       rc4_crypt(_c,_s,_d,_l)
73
74 static  void ieee80211_crc_init(void);
75 static  uint32_t ieee80211_crc_update(uint32_t crc, uint8_t *buf, int len);
76
77 void
78 ieee80211_crypto_attach(struct ifnet *ifp)
79 {
80         struct ieee80211com *ic = (void *)ifp;
81
82         /*
83          * Setup crypto support.
84          */
85         ieee80211_crc_init();
86         ic->ic_iv = arc4random();
87 }
88
89 void
90 ieee80211_crypto_detach(struct ifnet *ifp)
91 {
92         struct ieee80211com *ic = (void *)ifp;
93
94         if (ic->ic_wep_ctx != NULL) {
95                 free(ic->ic_wep_ctx, M_DEVBUF);
96                 ic->ic_wep_ctx = NULL;
97         }
98 }
99
100 struct mbuf *
101 ieee80211_wep_crypt(struct ifnet *ifp, struct mbuf *m0, int txflag)
102 {
103         struct ieee80211com *ic = (void *)ifp;
104         struct mbuf *m, *n, *n0;
105         struct ieee80211_frame *wh;
106         int i, left, len, moff, noff, kid;
107         uint32_t iv, crc;
108         uint8_t *ivp;
109         void *ctx;
110         uint8_t keybuf[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
111         uint8_t crcbuf[IEEE80211_WEP_CRCLEN];
112
113         n0 = NULL;
114         if ((ctx = ic->ic_wep_ctx) == NULL) {
115                 ctx = malloc(arc4_ctxlen(), M_DEVBUF, M_NOWAIT | M_ZERO); /* XXX */
116                 if (ctx == NULL) {
117                         ic->ic_stats.is_crypto_nomem++;
118                         goto fail;
119                 }
120                 ic->ic_wep_ctx = ctx;
121         }
122         m = m0;
123         left = m->m_pkthdr.len;
124         MGET(n, MB_DONTWAIT, m->m_type);
125         n0 = n;
126         if (n == NULL) {
127                 if (txflag)
128                         ic->ic_stats.is_tx_nombuf++;
129                 else
130                         ic->ic_stats.is_rx_nombuf++;
131                 goto fail;
132         }
133         M_MOVE_PKTHDR(n, m);
134         len = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN;
135         if (txflag) {
136                 n->m_pkthdr.len += len;
137         } else {
138                 n->m_pkthdr.len -= len;
139                 left -= len;
140         }
141         n->m_len = MHLEN;
142         if (n->m_pkthdr.len >= MINCLSIZE) {
143                 MCLGET(n, MB_DONTWAIT);
144                 if (n->m_flags & M_EXT)
145                         n->m_len = n->m_ext.ext_size;
146         }
147         len = sizeof(struct ieee80211_frame);
148         memcpy(mtod(n, caddr_t), mtod(m, caddr_t), len);
149         wh = mtod(n, struct ieee80211_frame *);
150         left -= len;
151         moff = len;
152         noff = len;
153         if (txflag) {
154                 kid = ic->ic_wep_txkey;
155                 wh->i_fc[1] |= IEEE80211_FC1_WEP;
156                 iv = ic->ic_iv;
157                 /*
158                  * Skip 'bad' IVs from Fluhrer/Mantin/Shamir:
159                  * (B, 255, N) with 3 <= B < 8
160                  */
161                 if (iv >= 0x03ff00 &&
162                     (iv & 0xf8ff00) == 0x00ff00)
163                         iv += 0x000100;
164                 ic->ic_iv = iv + 1;
165                 /* put iv in little endian to prepare 802.11i */
166                 ivp = mtod(n, uint8_t *) + noff;
167                 for (i = 0; i < IEEE80211_WEP_IVLEN; i++) {
168                         ivp[i] = iv & 0xff;
169                         iv >>= 8;
170                 }
171                 ivp[IEEE80211_WEP_IVLEN] = kid << 6;    /* pad and keyid */
172                 noff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
173         } else {
174                 wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
175                 ivp = mtod(m, uint8_t *) + moff;
176                 kid = ivp[IEEE80211_WEP_IVLEN] >> 6;
177                 moff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
178         }
179         memcpy(keybuf, ivp, IEEE80211_WEP_IVLEN);
180         memcpy(keybuf + IEEE80211_WEP_IVLEN, ic->ic_nw_keys[kid].wk_key,
181             ic->ic_nw_keys[kid].wk_len);
182         arc4_setkey(ctx, keybuf,
183             IEEE80211_WEP_IVLEN + ic->ic_nw_keys[kid].wk_len);
184
185         /* encrypt with calculating CRC */
186         crc = ~0;
187         while (left > 0) {
188                 len = m->m_len - moff;
189                 if (len == 0) {
190                         m = m->m_next;
191                         moff = 0;
192                         continue;
193                 }
194                 if (len > n->m_len - noff) {
195                         len = n->m_len - noff;
196                         if (len == 0) {
197                                 MGET(n->m_next, MB_DONTWAIT, n->m_type);
198                                 if (n->m_next == NULL) {
199                                         if (txflag)
200                                                 ic->ic_stats.is_tx_nombuf++;
201                                         else
202                                                 ic->ic_stats.is_rx_nombuf++;
203                                         goto fail;
204                                 }
205                                 n = n->m_next;
206                                 n->m_len = MLEN;
207                                 if (left >= MINCLSIZE) {
208                                         MCLGET(n, MB_DONTWAIT);
209                                         if (n->m_flags & M_EXT)
210                                                 n->m_len = n->m_ext.ext_size;
211                                 }
212                                 noff = 0;
213                                 continue;
214                         }
215                 }
216                 if (len > left)
217                         len = left;
218                 arc4_encrypt(ctx, mtod(n, caddr_t) + noff,
219                     mtod(m, caddr_t) + moff, len);
220                 if (txflag)
221                         crc = ieee80211_crc_update(crc,
222                             mtod(m, uint8_t *) + moff, len);
223                 else
224                         crc = ieee80211_crc_update(crc,
225                             mtod(n, uint8_t *) + noff, len);
226                 left -= len;
227                 moff += len;
228                 noff += len;
229         }
230         crc = ~crc;
231         if (txflag) {
232                 *(uint32_t *)crcbuf = htole32(crc);
233                 if (n->m_len >= noff + sizeof(crcbuf))
234                         n->m_len = noff + sizeof(crcbuf);
235                 else {
236                         n->m_len = noff;
237                         MGET(n->m_next, MB_DONTWAIT, n->m_type);
238                         if (n->m_next == NULL) {
239                                 ic->ic_stats.is_tx_nombuf++;
240                                 goto fail;
241                         }
242                         n = n->m_next;
243                         n->m_len = sizeof(crcbuf);
244                         noff = 0;
245                 }
246                 arc4_encrypt(ctx, mtod(n, caddr_t) + noff, crcbuf,
247                     sizeof(crcbuf));
248         } else {
249                 n->m_len = noff;
250                 for (noff = 0; noff < sizeof(crcbuf); noff += len) {
251                         len = sizeof(crcbuf) - noff;
252                         if (len > m->m_len - moff)
253                                 len = m->m_len - moff;
254                         if (len > 0)
255                                 arc4_encrypt(ctx, crcbuf + noff,
256                                     mtod(m, caddr_t) + moff, len);
257                         m = m->m_next;
258                         moff = 0;
259                 }
260                 if (crc != le32toh(*(uint32_t *)crcbuf)) {
261 #ifdef IEEE80211_DEBUG
262                         if (ieee80211_debug) {
263                                 if_printf(ifp, "decrypt CRC error\n");
264                                 if (ieee80211_debug > 1)
265                                         ieee80211_dump_pkt(n0->m_data,
266                                             n0->m_len, -1, -1);
267                         }
268 #endif
269                         ic->ic_stats.is_rx_decryptcrc++;
270                         goto fail;
271                 }
272         }
273         m_freem(m0);
274         return n0;
275
276   fail:
277         m_freem(m0);
278         m_freem(n0);
279         return NULL;
280 }
281
282 /*
283  * CRC 32 -- routine from RFC 2083
284  */
285
286 /* Table of CRCs of all 8-bit messages */
287 static uint32_t ieee80211_crc_table[256];
288
289 /* Make the table for a fast CRC. */
290 static void
291 ieee80211_crc_init(void)
292 {
293         uint32_t c;
294         int n, k;
295
296         for (n = 0; n < 256; n++) {
297                 c = (uint32_t)n;
298                 for (k = 0; k < 8; k++) {
299                         if (c & 1)
300                                 c = 0xedb88320UL ^ (c >> 1);
301                         else
302                                 c = c >> 1;
303                 }
304                 ieee80211_crc_table[n] = c;
305         }
306 }
307
308 /*
309  * Update a running CRC with the bytes buf[0..len-1]--the CRC
310  * should be initialized to all 1's, and the transmitted value
311  * is the 1's complement of the final running CRC
312  */
313
314 static uint32_t
315 ieee80211_crc_update(uint32_t crc, uint8_t *buf, int len)
316 {
317         uint8_t *endbuf;
318
319         for (endbuf = buf + len; buf < endbuf; buf++)
320                 crc = ieee80211_crc_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
321         return crc;
322 }