ieee80211_crypto.c revision 116742
1116742Ssam/*-
2116742Ssam * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
3116742Ssam * All rights reserved.
4116742Ssam *
5116742Ssam * Redistribution and use in source and binary forms, with or without
6116742Ssam * modification, are permitted provided that the following conditions
7116742Ssam * are met:
8116742Ssam * 1. Redistributions of source code must retain the above copyright
9116742Ssam *    notice, this list of conditions and the following disclaimer,
10116742Ssam *    without modification.
11116742Ssam * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12116742Ssam *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13116742Ssam *    redistribution must be conditioned upon including a substantially
14116742Ssam *    similar Disclaimer requirement for further binary redistribution.
15116742Ssam * 3. Neither the names of the above-listed copyright holders nor the names
16116742Ssam *    of any contributors may be used to endorse or promote products derived
17116742Ssam *    from this software without specific prior written permission.
18116742Ssam *
19116742Ssam * Alternatively, this software may be distributed under the terms of the
20116742Ssam * GNU General Public License ("GPL") version 2 as published by the Free
21116742Ssam * Software Foundation.
22116742Ssam *
23116742Ssam * NO WARRANTY
24116742Ssam * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25116742Ssam * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26116742Ssam * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
27116742Ssam * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
28116742Ssam * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
29116742Ssam * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30116742Ssam * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31116742Ssam * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
32116742Ssam * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33116742Ssam * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
34116742Ssam * THE POSSIBILITY OF SUCH DAMAGES.
35116742Ssam *
36116742Ssam * $Id: ieee80211_crypto.c,v 1.2 2003/06/22 06:16:32 sam Exp $
37116742Ssam */
38116742Ssam
39116742Ssam#include <sys/cdefs.h>
40116742Ssam__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_crypto.c 116742 2003-06-23 16:55:01Z sam $");
41116742Ssam
42116742Ssam#include "opt_inet.h"
43116742Ssam
44116742Ssam#include <sys/param.h>
45116742Ssam#include <sys/systm.h>
46116742Ssam#include <sys/mbuf.h>
47116742Ssam#include <sys/malloc.h>
48116742Ssam#include <sys/kernel.h>
49116742Ssam#include <sys/socket.h>
50116742Ssam#include <sys/sockio.h>
51116742Ssam#include <sys/endian.h>
52116742Ssam#include <sys/errno.h>
53116742Ssam#include <sys/bus.h>
54116742Ssam#include <sys/proc.h>
55116742Ssam#include <sys/sysctl.h>
56116742Ssam
57116742Ssam#include <machine/atomic.h>
58116742Ssam
59116742Ssam#include <net/if.h>
60116742Ssam#include <net/if_dl.h>
61116742Ssam#include <net/if_media.h>
62116742Ssam#include <net/if_arp.h>
63116742Ssam#include <net/ethernet.h>
64116742Ssam#include <net/if_llc.h>
65116742Ssam
66116742Ssam#include <net80211/ieee80211_var.h>
67116742Ssam
68116742Ssam#include <net/bpf.h>
69116742Ssam
70116742Ssam#ifdef INET
71116742Ssam#include <netinet/in.h>
72116742Ssam#include <netinet/if_ether.h>
73116742Ssam#endif
74116742Ssam
75116742Ssam#include <crypto/rc4/rc4.h>
76116742Ssam#define	arc4_ctxlen()			sizeof (struct rc4_state)
77116742Ssam#define	arc4_setkey(_c,_k,_l)		rc4_init(_c,_k,_l)
78116742Ssam#define	arc4_encrypt(_c,_d,_s,_l)	rc4_crypt(_c,_s,_d,_l)
79116742Ssam
80116742Ssamstatic	void ieee80211_crc_init(void);
81116742Ssamstatic	u_int32_t ieee80211_crc_update(u_int32_t crc, u_int8_t *buf, int len);
82116742Ssam
83116742Ssamvoid
84116742Ssamieee80211_crypto_attach(struct ifnet *ifp)
85116742Ssam{
86116742Ssam	struct ieee80211com *ic = (void *)ifp;
87116742Ssam
88116742Ssam	/*
89116742Ssam	 * Setup crypto support.
90116742Ssam	 */
91116742Ssam	ieee80211_crc_init();
92116742Ssam	ic->ic_iv = arc4random();
93116742Ssam}
94116742Ssam
95116742Ssamvoid
96116742Ssamieee80211_crypto_detach(struct ifnet *ifp)
97116742Ssam{
98116742Ssam	struct ieee80211com *ic = (void *)ifp;
99116742Ssam
100116742Ssam	if (ic->ic_wep_ctx != NULL) {
101116742Ssam		free(ic->ic_wep_ctx, M_DEVBUF);
102116742Ssam		ic->ic_wep_ctx = NULL;
103116742Ssam	}
104116742Ssam}
105116742Ssam
106116742Ssamstruct mbuf *
107116742Ssamieee80211_wep_crypt(struct ifnet *ifp, struct mbuf *m0, int txflag)
108116742Ssam{
109116742Ssam	struct ieee80211com *ic = (void *)ifp;
110116742Ssam	struct mbuf *m, *n, *n0;
111116742Ssam	struct ieee80211_frame *wh;
112116742Ssam	int i, left, len, moff, noff, kid;
113116742Ssam	u_int32_t iv, crc;
114116742Ssam	u_int8_t *ivp;
115116742Ssam	void *ctx;
116116742Ssam	u_int8_t keybuf[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
117116742Ssam	u_int8_t crcbuf[IEEE80211_WEP_CRCLEN];
118116742Ssam
119116742Ssam	n0 = NULL;
120116742Ssam	if ((ctx = ic->ic_wep_ctx) == NULL) {
121116742Ssam		ctx = malloc(arc4_ctxlen(), M_DEVBUF, M_NOWAIT);
122116742Ssam		if (ctx == NULL)
123116742Ssam			goto fail;
124116742Ssam		ic->ic_wep_ctx = ctx;
125116742Ssam	}
126116742Ssam	m = m0;
127116742Ssam	left = m->m_pkthdr.len;
128116742Ssam	MGET(n, M_DONTWAIT, m->m_type);
129116742Ssam	n0 = n;
130116742Ssam	if (n == NULL)
131116742Ssam		goto fail;
132116742Ssam	M_MOVE_PKTHDR(n, m);
133116742Ssam	len = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN;
134116742Ssam	if (txflag) {
135116742Ssam		n->m_pkthdr.len += len;
136116742Ssam	} else {
137116742Ssam		n->m_pkthdr.len -= len;
138116742Ssam		left -= len;
139116742Ssam	}
140116742Ssam	n->m_len = MHLEN;
141116742Ssam	if (n->m_pkthdr.len >= MINCLSIZE) {
142116742Ssam		MCLGET(n, M_DONTWAIT);
143116742Ssam		if (n->m_flags & M_EXT)
144116742Ssam			n->m_len = n->m_ext.ext_size;
145116742Ssam	}
146116742Ssam	len = sizeof(struct ieee80211_frame);
147116742Ssam	memcpy(mtod(n, caddr_t), mtod(m, caddr_t), len);
148116742Ssam	wh = mtod(n, struct ieee80211_frame *);
149116742Ssam	left -= len;
150116742Ssam	moff = len;
151116742Ssam	noff = len;
152116742Ssam	if (txflag) {
153116742Ssam		kid = ic->ic_wep_txkey;
154116742Ssam		wh->i_fc[1] |= IEEE80211_FC1_WEP;
155116742Ssam                iv = ic->ic_iv;
156116742Ssam		/*
157116742Ssam		 * Skip 'bad' IVs from Fluhrer/Mantin/Shamir:
158116742Ssam		 * (B, 255, N) with 3 <= B < 8
159116742Ssam		 */
160116742Ssam		if (iv >= 0x03ff00 &&
161116742Ssam		    (iv & 0xf8ff00) == 0x00ff00)
162116742Ssam			iv += 0x000100;
163116742Ssam		ic->ic_iv = iv + 1;
164116742Ssam		/* put iv in little endian to prepare 802.11i */
165116742Ssam		ivp = mtod(n, u_int8_t *) + noff;
166116742Ssam		for (i = 0; i < IEEE80211_WEP_IVLEN; i++) {
167116742Ssam			ivp[i] = iv & 0xff;
168116742Ssam			iv >>= 8;
169116742Ssam		}
170116742Ssam		ivp[IEEE80211_WEP_IVLEN] = kid << 6;	/* pad and keyid */
171116742Ssam		noff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
172116742Ssam	} else {
173116742Ssam		wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
174116742Ssam		ivp = mtod(m, u_int8_t *) + moff;
175116742Ssam		kid = ivp[IEEE80211_WEP_IVLEN] >> 6;
176116742Ssam		moff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
177116742Ssam	}
178116742Ssam	memcpy(keybuf, ivp, IEEE80211_WEP_IVLEN);
179116742Ssam	memcpy(keybuf + IEEE80211_WEP_IVLEN, ic->ic_nw_keys[kid].wk_key,
180116742Ssam	    ic->ic_nw_keys[kid].wk_len);
181116742Ssam	arc4_setkey(ctx, keybuf,
182116742Ssam	    IEEE80211_WEP_IVLEN + ic->ic_nw_keys[kid].wk_len);
183116742Ssam
184116742Ssam	/* encrypt with calculating CRC */
185116742Ssam	crc = ~0;
186116742Ssam	while (left > 0) {
187116742Ssam		len = m->m_len - moff;
188116742Ssam		if (len == 0) {
189116742Ssam			m = m->m_next;
190116742Ssam			moff = 0;
191116742Ssam			continue;
192116742Ssam		}
193116742Ssam		if (len > n->m_len - noff) {
194116742Ssam			len = n->m_len - noff;
195116742Ssam			if (len == 0) {
196116742Ssam				MGET(n->m_next, M_DONTWAIT, n->m_type);
197116742Ssam				if (n->m_next == NULL)
198116742Ssam					goto fail;
199116742Ssam				n = n->m_next;
200116742Ssam				n->m_len = MLEN;
201116742Ssam				if (left >= MINCLSIZE) {
202116742Ssam					MCLGET(n, M_DONTWAIT);
203116742Ssam					if (n->m_flags & M_EXT)
204116742Ssam						n->m_len = n->m_ext.ext_size;
205116742Ssam				}
206116742Ssam				noff = 0;
207116742Ssam				continue;
208116742Ssam			}
209116742Ssam		}
210116742Ssam		if (len > left)
211116742Ssam			len = left;
212116742Ssam		arc4_encrypt(ctx, mtod(n, caddr_t) + noff,
213116742Ssam		    mtod(m, caddr_t) + moff, len);
214116742Ssam		if (txflag)
215116742Ssam			crc = ieee80211_crc_update(crc,
216116742Ssam			    mtod(m, u_int8_t *) + moff, len);
217116742Ssam		else
218116742Ssam			crc = ieee80211_crc_update(crc,
219116742Ssam			    mtod(n, u_int8_t *) + noff, len);
220116742Ssam		left -= len;
221116742Ssam		moff += len;
222116742Ssam		noff += len;
223116742Ssam	}
224116742Ssam	crc = ~crc;
225116742Ssam	if (txflag) {
226116742Ssam		*(u_int32_t *)crcbuf = htole32(crc);
227116742Ssam		if (n->m_len >= noff + sizeof(crcbuf))
228116742Ssam			n->m_len = noff + sizeof(crcbuf);
229116742Ssam		else {
230116742Ssam			n->m_len = noff;
231116742Ssam			MGET(n->m_next, M_DONTWAIT, n->m_type);
232116742Ssam			if (n->m_next == NULL)
233116742Ssam				goto fail;
234116742Ssam			n = n->m_next;
235116742Ssam			n->m_len = sizeof(crcbuf);
236116742Ssam			noff = 0;
237116742Ssam		}
238116742Ssam		arc4_encrypt(ctx, mtod(n, caddr_t) + noff, crcbuf,
239116742Ssam		    sizeof(crcbuf));
240116742Ssam	} else {
241116742Ssam		n->m_len = noff;
242116742Ssam		for (noff = 0; noff < sizeof(crcbuf); noff += len) {
243116742Ssam			len = sizeof(crcbuf) - noff;
244116742Ssam			if (len > m->m_len - moff)
245116742Ssam				len = m->m_len - moff;
246116742Ssam			if (len > 0)
247116742Ssam				arc4_encrypt(ctx, crcbuf + noff,
248116742Ssam				    mtod(m, caddr_t) + moff, len);
249116742Ssam			m = m->m_next;
250116742Ssam			moff = 0;
251116742Ssam		}
252116742Ssam		if (crc != le32toh(*(u_int32_t *)crcbuf)) {
253116742Ssam#ifdef IEEE80211_DEBUG
254116742Ssam			if (ieee80211_debug) {
255116742Ssam				if_printf(ifp, "decrypt CRC error\n");
256116742Ssam				if (ieee80211_debug > 1)
257116742Ssam					ieee80211_dump_pkt(n0->m_data,
258116742Ssam					    n0->m_len, -1, -1);
259116742Ssam			}
260116742Ssam#endif
261116742Ssam			goto fail;
262116742Ssam		}
263116742Ssam	}
264116742Ssam	m_freem(m0);
265116742Ssam	return n0;
266116742Ssam
267116742Ssam  fail:
268116742Ssam	m_freem(m0);
269116742Ssam	m_freem(n0);
270116742Ssam	return NULL;
271116742Ssam}
272116742Ssam
273116742Ssam/*
274116742Ssam * CRC 32 -- routine from RFC 2083
275116742Ssam */
276116742Ssam
277116742Ssam/* Table of CRCs of all 8-bit messages */
278116742Ssamstatic u_int32_t ieee80211_crc_table[256];
279116742Ssam
280116742Ssam/* Make the table for a fast CRC. */
281116742Ssamstatic void
282116742Ssamieee80211_crc_init(void)
283116742Ssam{
284116742Ssam	u_int32_t c;
285116742Ssam	int n, k;
286116742Ssam
287116742Ssam	for (n = 0; n < 256; n++) {
288116742Ssam		c = (u_int32_t)n;
289116742Ssam		for (k = 0; k < 8; k++) {
290116742Ssam			if (c & 1)
291116742Ssam				c = 0xedb88320UL ^ (c >> 1);
292116742Ssam			else
293116742Ssam				c = c >> 1;
294116742Ssam		}
295116742Ssam		ieee80211_crc_table[n] = c;
296116742Ssam	}
297116742Ssam}
298116742Ssam
299116742Ssam/*
300116742Ssam * Update a running CRC with the bytes buf[0..len-1]--the CRC
301116742Ssam * should be initialized to all 1's, and the transmitted value
302116742Ssam * is the 1's complement of the final running CRC
303116742Ssam */
304116742Ssam
305116742Ssamstatic u_int32_t
306116742Ssamieee80211_crc_update(u_int32_t crc, u_int8_t *buf, int len)
307116742Ssam{
308116742Ssam	u_int8_t *endbuf;
309116742Ssam
310116742Ssam	for (endbuf = buf + len; buf < endbuf; buf++)
311116742Ssam		crc = ieee80211_crc_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
312116742Ssam	return crc;
313116742Ssam}
314