ieee80211_crypto.c revision 147870
1116742Ssam/*-
2116904Ssam * Copyright (c) 2001 Atsushi Onoe
3139530Ssam * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
4116742Ssam * All rights reserved.
5116742Ssam *
6116742Ssam * Redistribution and use in source and binary forms, with or without
7116742Ssam * modification, are permitted provided that the following conditions
8116742Ssam * are met:
9116742Ssam * 1. Redistributions of source code must retain the above copyright
10116904Ssam *    notice, this list of conditions and the following disclaimer.
11116904Ssam * 2. Redistributions in binary form must reproduce the above copyright
12116904Ssam *    notice, this list of conditions and the following disclaimer in the
13116904Ssam *    documentation and/or other materials provided with the distribution.
14116904Ssam * 3. The name of the author may not be used to endorse or promote products
15116904Ssam *    derived from this software without specific prior written permission.
16116742Ssam *
17116742Ssam * Alternatively, this software may be distributed under the terms of the
18116742Ssam * GNU General Public License ("GPL") version 2 as published by the Free
19116742Ssam * Software Foundation.
20116742Ssam *
21116904Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22116904Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23116904Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24116904Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25116904Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26116904Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27116904Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28116904Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29116904Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30116904Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31116742Ssam */
32116742Ssam
33116742Ssam#include <sys/cdefs.h>
34116742Ssam__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_crypto.c 147870 2005-07-09 23:15:30Z sam $");
35116742Ssam
36138568Ssam/*
37138568Ssam * IEEE 802.11 generic crypto support.
38138568Ssam */
39116742Ssam#include <sys/param.h>
40116742Ssam#include <sys/mbuf.h>
41138568Ssam
42116742Ssam#include <sys/socket.h>
43116742Ssam
44116742Ssam#include <net/if.h>
45116742Ssam#include <net/if_media.h>
46138568Ssam#include <net/ethernet.h>		/* XXX ETHER_HDR_LEN */
47116742Ssam
48116742Ssam#include <net80211/ieee80211_var.h>
49116742Ssam
50138568Ssam/*
51138568Ssam * Table of registered cipher modules.
52138568Ssam */
53138568Ssamstatic	const struct ieee80211_cipher *ciphers[IEEE80211_CIPHER_MAX];
54116742Ssam
55138568Ssamstatic	int _ieee80211_crypto_delkey(struct ieee80211com *,
56138568Ssam		struct ieee80211_key *);
57116742Ssam
58138568Ssam/*
59138568Ssam * Default "null" key management routines.
60138568Ssam */
61138568Ssamstatic int
62138568Ssamnull_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k)
63138568Ssam{
64147870Ssam	if (!(&ic->ic_nw_keys[0] <= k &&
65147870Ssam	     k < &ic->ic_nw_keys[IEEE80211_WEP_NKID])) {
66147870Ssam		/*
67147870Ssam		 * Not in the global key table, the driver should handle this
68147870Ssam		 * by allocating a slot in the h/w key table/cache.  In
69147870Ssam		 * lieu of that return key slot 0 for any unicast key
70147870Ssam		 * request.  We disallow the request if this is a group key.
71147870Ssam		 * This default policy does the right thing for legacy hardware
72147870Ssam		 * with a 4 key table.  It also handles devices that pass
73147870Ssam		 * packets through untouched when marked with the WEP bit
74147870Ssam		 * and key index 0.
75147870Ssam		 */
76147870Ssam		if ((k->wk_flags & IEEE80211_KEY_GROUP) == 0)
77147870Ssam			return 0;	/* NB: use key index 0 for ucast key */
78147870Ssam		else
79147870Ssam			return IEEE80211_KEYIX_NONE;
80147870Ssam	}
81147870Ssam	return k - ic->ic_nw_keys;
82138568Ssam}
83138568Ssamstatic int
84138568Ssamnull_key_delete(struct ieee80211com *ic, const struct ieee80211_key *k)
85138568Ssam{
86138568Ssam	return 1;
87138568Ssam}
88138568Ssamstatic 	int
89138568Ssamnull_key_set(struct ieee80211com *ic, const struct ieee80211_key *k,
90138568Ssam	     const u_int8_t mac[IEEE80211_ADDR_LEN])
91138568Ssam{
92138568Ssam	return 1;
93138568Ssam}
94138568Ssamstatic void null_key_update(struct ieee80211com *ic) {}
95116742Ssam
96138568Ssam/*
97138568Ssam * Write-arounds for common operations.
98138568Ssam */
99138568Ssamstatic __inline void
100138568Ssamcipher_detach(struct ieee80211_key *key)
101138568Ssam{
102138568Ssam	key->wk_cipher->ic_detach(key);
103138568Ssam}
104116742Ssam
105138568Ssamstatic __inline void *
106138568Ssamcipher_attach(struct ieee80211com *ic, struct ieee80211_key *key)
107138568Ssam{
108138568Ssam	return key->wk_cipher->ic_attach(ic, key);
109138568Ssam}
110138568Ssam
111138568Ssam/*
112138568Ssam * Wrappers for driver key management methods.
113138568Ssam */
114138568Ssamstatic __inline int
115138568Ssamdev_key_alloc(struct ieee80211com *ic,
116138568Ssam	const struct ieee80211_key *key)
117138568Ssam{
118138568Ssam	return ic->ic_crypto.cs_key_alloc(ic, key);
119138568Ssam}
120138568Ssam
121138568Ssamstatic __inline int
122138568Ssamdev_key_delete(struct ieee80211com *ic,
123138568Ssam	const struct ieee80211_key *key)
124138568Ssam{
125138568Ssam	return ic->ic_crypto.cs_key_delete(ic, key);
126138568Ssam}
127138568Ssam
128138568Ssamstatic __inline int
129138568Ssamdev_key_set(struct ieee80211com *ic, const struct ieee80211_key *key,
130138568Ssam	const u_int8_t mac[IEEE80211_ADDR_LEN])
131138568Ssam{
132138568Ssam	return ic->ic_crypto.cs_key_set(ic, key, mac);
133138568Ssam}
134138568Ssam
135138568Ssam/*
136138568Ssam * Setup crypto support.
137138568Ssam */
138116742Ssamvoid
139138568Ssamieee80211_crypto_attach(struct ieee80211com *ic)
140116742Ssam{
141138568Ssam	struct ieee80211_crypto_state *cs = &ic->ic_crypto;
142138568Ssam	int i;
143116742Ssam
144138568Ssam	/* NB: we assume everything is pre-zero'd */
145138568Ssam	cs->cs_def_txkey = IEEE80211_KEYIX_NONE;
146138568Ssam	ciphers[IEEE80211_CIPHER_NONE] = &ieee80211_cipher_none;
147138568Ssam	for (i = 0; i < IEEE80211_WEP_NKID; i++)
148144960Ssam		ieee80211_crypto_resetkey(ic, &cs->cs_nw_keys[i],
149144960Ssam			IEEE80211_KEYIX_NONE);
150116742Ssam	/*
151138568Ssam	 * Initialize the driver key support routines to noop entries.
152138568Ssam	 * This is useful especially for the cipher test modules.
153116742Ssam	 */
154138568Ssam	cs->cs_key_alloc = null_key_alloc;
155138568Ssam	cs->cs_key_set = null_key_set;
156138568Ssam	cs->cs_key_delete = null_key_delete;
157138568Ssam	cs->cs_key_update_begin = null_key_update;
158138568Ssam	cs->cs_key_update_end = null_key_update;
159116742Ssam}
160116742Ssam
161138568Ssam/*
162138568Ssam * Teardown crypto support.
163138568Ssam */
164116742Ssamvoid
165138568Ssamieee80211_crypto_detach(struct ieee80211com *ic)
166116742Ssam{
167138568Ssam	ieee80211_crypto_delglobalkeys(ic);
168138568Ssam}
169116742Ssam
170138568Ssam/*
171138568Ssam * Register a crypto cipher module.
172138568Ssam */
173138568Ssamvoid
174138568Ssamieee80211_crypto_register(const struct ieee80211_cipher *cip)
175138568Ssam{
176138568Ssam	if (cip->ic_cipher >= IEEE80211_CIPHER_MAX) {
177138568Ssam		printf("%s: cipher %s has an invalid cipher index %u\n",
178138568Ssam			__func__, cip->ic_name, cip->ic_cipher);
179138568Ssam		return;
180116742Ssam	}
181138568Ssam	if (ciphers[cip->ic_cipher] != NULL && ciphers[cip->ic_cipher] != cip) {
182138568Ssam		printf("%s: cipher %s registered with a different template\n",
183138568Ssam			__func__, cip->ic_name);
184138568Ssam		return;
185138568Ssam	}
186138568Ssam	ciphers[cip->ic_cipher] = cip;
187116742Ssam}
188116742Ssam
189138568Ssam/*
190138568Ssam * Unregister a crypto cipher module.
191138568Ssam */
192138568Ssamvoid
193138568Ssamieee80211_crypto_unregister(const struct ieee80211_cipher *cip)
194116742Ssam{
195138568Ssam	if (cip->ic_cipher >= IEEE80211_CIPHER_MAX) {
196138568Ssam		printf("%s: cipher %s has an invalid cipher index %u\n",
197138568Ssam			__func__, cip->ic_name, cip->ic_cipher);
198138568Ssam		return;
199116742Ssam	}
200138568Ssam	if (ciphers[cip->ic_cipher] != NULL && ciphers[cip->ic_cipher] != cip) {
201138568Ssam		printf("%s: cipher %s registered with a different template\n",
202138568Ssam			__func__, cip->ic_name);
203138568Ssam		return;
204121180Ssam	}
205138568Ssam	/* NB: don't complain about not being registered */
206138568Ssam	/* XXX disallow if references */
207138568Ssam	ciphers[cip->ic_cipher] = NULL;
208138568Ssam}
209138568Ssam
210138568Ssamint
211138568Ssamieee80211_crypto_available(u_int cipher)
212138568Ssam{
213138568Ssam	return cipher < IEEE80211_CIPHER_MAX && ciphers[cipher] != NULL;
214138568Ssam}
215138568Ssam
216138568Ssam/* XXX well-known names! */
217138568Ssamstatic const char *cipher_modnames[] = {
218138568Ssam	"wlan_wep",	/* IEEE80211_CIPHER_WEP */
219138568Ssam	"wlan_tkip",	/* IEEE80211_CIPHER_TKIP */
220138568Ssam	"wlan_aes_ocb",	/* IEEE80211_CIPHER_AES_OCB */
221138568Ssam	"wlan_ccmp",	/* IEEE80211_CIPHER_AES_CCM */
222138568Ssam	"wlan_ckip",	/* IEEE80211_CIPHER_CKIP */
223138568Ssam};
224138568Ssam
225138568Ssam/*
226138568Ssam * Establish a relationship between the specified key and cipher
227144960Ssam * and, if necessary, allocate a hardware index from the driver.
228144960Ssam * Note that when a fixed key index is required it must be specified
229144960Ssam * and we blindly assign it w/o consulting the driver (XXX).
230138568Ssam *
231138568Ssam * This must be the first call applied to a key; all the other key
232138568Ssam * routines assume wk_cipher is setup.
233138568Ssam *
234138568Ssam * Locking must be handled by the caller using:
235138568Ssam *	ieee80211_key_update_begin(ic);
236138568Ssam *	ieee80211_key_update_end(ic);
237138568Ssam */
238138568Ssamint
239138568Ssamieee80211_crypto_newkey(struct ieee80211com *ic,
240144960Ssam	int cipher, int flags, struct ieee80211_key *key)
241138568Ssam{
242138568Ssam#define	N(a)	(sizeof(a) / sizeof(a[0]))
243138568Ssam	const struct ieee80211_cipher *cip;
244138568Ssam	void *keyctx;
245138568Ssam	int oflags;
246138568Ssam
247138568Ssam	/*
248138568Ssam	 * Validate cipher and set reference to cipher routines.
249138568Ssam	 */
250138568Ssam	if (cipher >= IEEE80211_CIPHER_MAX) {
251138568Ssam		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
252138568Ssam			"%s: invalid cipher %u\n", __func__, cipher);
253138568Ssam		ic->ic_stats.is_crypto_badcipher++;
254138568Ssam		return 0;
255116742Ssam	}
256138568Ssam	cip = ciphers[cipher];
257138568Ssam	if (cip == NULL) {
258116742Ssam		/*
259138568Ssam		 * Auto-load cipher module if we have a well-known name
260138568Ssam		 * for it.  It might be better to use string names rather
261138568Ssam		 * than numbers and craft a module name based on the cipher
262138568Ssam		 * name; e.g. wlan_cipher_<cipher-name>.
263116742Ssam		 */
264138568Ssam		if (cipher < N(cipher_modnames)) {
265138568Ssam			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
266138568Ssam				"%s: unregistered cipher %u, load module %s\n",
267138568Ssam				__func__, cipher, cipher_modnames[cipher]);
268138568Ssam			ieee80211_load_module(cipher_modnames[cipher]);
269138568Ssam			/*
270138568Ssam			 * If cipher module loaded it should immediately
271138568Ssam			 * call ieee80211_crypto_register which will fill
272138568Ssam			 * in the entry in the ciphers array.
273138568Ssam			 */
274138568Ssam			cip = ciphers[cipher];
275116742Ssam		}
276138568Ssam		if (cip == NULL) {
277138568Ssam			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
278138568Ssam				"%s: unable to load cipher %u, module %s\n",
279138568Ssam				__func__, cipher,
280138568Ssam				cipher < N(cipher_modnames) ?
281138568Ssam					cipher_modnames[cipher] : "<unknown>");
282138568Ssam			ic->ic_stats.is_crypto_nocipher++;
283138568Ssam			return 0;
284138568Ssam		}
285116742Ssam	}
286116742Ssam
287138568Ssam	oflags = key->wk_flags;
288144960Ssam	flags &= IEEE80211_KEY_COMMON;
289138568Ssam	/*
290138568Ssam	 * If the hardware does not support the cipher then
291138568Ssam	 * fallback to a host-based implementation.
292138568Ssam	 */
293138568Ssam	if ((ic->ic_caps & (1<<cipher)) == 0) {
294138568Ssam		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
295138568Ssam		    "%s: no h/w support for cipher %s, falling back to s/w\n",
296138568Ssam		    __func__, cip->ic_name);
297144960Ssam		flags |= IEEE80211_KEY_SWCRYPT;
298138568Ssam	}
299138568Ssam	/*
300138568Ssam	 * Hardware TKIP with software MIC is an important
301138568Ssam	 * combination; we handle it by flagging each key,
302138568Ssam	 * the cipher modules honor it.
303138568Ssam	 */
304138568Ssam	if (cipher == IEEE80211_CIPHER_TKIP &&
305138568Ssam	    (ic->ic_caps & IEEE80211_C_TKIPMIC) == 0) {
306138568Ssam		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
307138568Ssam		    "%s: no h/w support for TKIP MIC, falling back to s/w\n",
308138568Ssam		    __func__);
309144960Ssam		flags |= IEEE80211_KEY_SWMIC;
310138568Ssam	}
311138568Ssam
312138568Ssam	/*
313138568Ssam	 * Bind cipher to key instance.  Note we do this
314138568Ssam	 * after checking the device capabilities so the
315138568Ssam	 * cipher module can optimize space usage based on
316138568Ssam	 * whether or not it needs to do the cipher work.
317138568Ssam	 */
318144960Ssam	if (key->wk_cipher != cip || key->wk_flags != flags) {
319138568Ssamagain:
320144960Ssam		/*
321144960Ssam		 * Fillin the flags so cipher modules can see s/w
322144960Ssam		 * crypto requirements and potentially allocate
323144960Ssam		 * different state and/or attach different method
324144960Ssam		 * pointers.
325144960Ssam		 *
326144960Ssam		 * XXX this is not right when s/w crypto fallback
327144960Ssam		 *     fails and we try to restore previous state.
328144960Ssam		 */
329144960Ssam		key->wk_flags = flags;
330138568Ssam		keyctx = cip->ic_attach(ic, key);
331138568Ssam		if (keyctx == NULL) {
332138568Ssam			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
333138568Ssam				"%s: unable to attach cipher %s\n",
334138568Ssam				__func__, cip->ic_name);
335138568Ssam			key->wk_flags = oflags;	/* restore old flags */
336138568Ssam			ic->ic_stats.is_crypto_attachfail++;
337138568Ssam			return 0;
338116742Ssam		}
339138568Ssam		cipher_detach(key);
340138568Ssam		key->wk_cipher = cip;		/* XXX refcnt? */
341138568Ssam		key->wk_private = keyctx;
342138568Ssam	}
343144960Ssam	/*
344144960Ssam	 * Commit to requested usage so driver can see the flags.
345144960Ssam	 */
346144960Ssam	key->wk_flags = flags;
347138568Ssam
348138568Ssam	/*
349138568Ssam	 * Ask the driver for a key index if we don't have one.
350138568Ssam	 * Note that entries in the global key table always have
351138568Ssam	 * an index; this means it's safe to call this routine
352138568Ssam	 * for these entries just to setup the reference to the
353138568Ssam	 * cipher template.  Note also that when using software
354138568Ssam	 * crypto we also call the driver to give us a key index.
355138568Ssam	 */
356138568Ssam	if (key->wk_keyix == IEEE80211_KEYIX_NONE) {
357138568Ssam		key->wk_keyix = dev_key_alloc(ic, key);
358138568Ssam		if (key->wk_keyix == IEEE80211_KEYIX_NONE) {
359138568Ssam			/*
360138568Ssam			 * Driver has no room; fallback to doing crypto
361138568Ssam			 * in the host.  We change the flags and start the
362138568Ssam			 * procedure over.  If we get back here then there's
363138568Ssam			 * no hope and we bail.  Note that this can leave
364138568Ssam			 * the key in a inconsistent state if the caller
365138568Ssam			 * continues to use it.
366138568Ssam			 */
367138568Ssam			if ((key->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) {
368138568Ssam				ic->ic_stats.is_crypto_swfallback++;
369138568Ssam				IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
370138568Ssam				    "%s: no h/w resources for cipher %s, "
371138568Ssam				    "falling back to s/w\n", __func__,
372138568Ssam				    cip->ic_name);
373138568Ssam				oflags = key->wk_flags;
374144960Ssam				flags |= IEEE80211_KEY_SWCRYPT;
375138568Ssam				if (cipher == IEEE80211_CIPHER_TKIP)
376144960Ssam					flags |= IEEE80211_KEY_SWMIC;
377138568Ssam				goto again;
378116742Ssam			}
379138568Ssam			ic->ic_stats.is_crypto_keyfail++;
380138568Ssam			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
381138568Ssam			    "%s: unable to setup cipher %s\n",
382138568Ssam			    __func__, cip->ic_name);
383138568Ssam			return 0;
384116742Ssam		}
385116742Ssam	}
386138568Ssam	return 1;
387138568Ssam#undef N
388138568Ssam}
389138568Ssam
390138568Ssam/*
391138568Ssam * Remove the key (no locking, for internal use).
392138568Ssam */
393138568Ssamstatic int
394138568Ssam_ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key)
395138568Ssam{
396138568Ssam	u_int16_t keyix;
397138568Ssam
398138568Ssam	KASSERT(key->wk_cipher != NULL, ("No cipher!"));
399138568Ssam
400139504Ssam	IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
401139504Ssam	    "%s: %s keyix %u flags 0x%x rsc %ju tsc %ju len %u\n",
402139504Ssam	    __func__, key->wk_cipher->ic_name,
403139504Ssam	    key->wk_keyix, key->wk_flags,
404139504Ssam	    key->wk_keyrsc, key->wk_keytsc, key->wk_keylen);
405139504Ssam
406138568Ssam	keyix = key->wk_keyix;
407138568Ssam	if (keyix != IEEE80211_KEYIX_NONE) {
408138568Ssam		/*
409138568Ssam		 * Remove hardware entry.
410138568Ssam		 */
411138568Ssam		/* XXX key cache */
412138568Ssam		if (!dev_key_delete(ic, key)) {
413138568Ssam			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
414138568Ssam			    "%s: driver did not delete key index %u\n",
415138568Ssam			    __func__, keyix);
416138568Ssam			ic->ic_stats.is_crypto_delkey++;
417138568Ssam			/* XXX recovery? */
418116742Ssam		}
419116742Ssam	}
420138568Ssam	cipher_detach(key);
421138568Ssam	memset(key, 0, sizeof(*key));
422144960Ssam	ieee80211_crypto_resetkey(ic, key, IEEE80211_KEYIX_NONE);
423138568Ssam	return 1;
424138568Ssam}
425116742Ssam
426138568Ssam/*
427138568Ssam * Remove the specified key.
428138568Ssam */
429138568Ssamint
430138568Ssamieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key)
431138568Ssam{
432138568Ssam	int status;
433138568Ssam
434138568Ssam	ieee80211_key_update_begin(ic);
435138568Ssam	status = _ieee80211_crypto_delkey(ic, key);
436138568Ssam	ieee80211_key_update_end(ic);
437138568Ssam	return status;
438116742Ssam}
439116742Ssam
440116742Ssam/*
441138568Ssam * Clear the global key table.
442116742Ssam */
443138568Ssamvoid
444138568Ssamieee80211_crypto_delglobalkeys(struct ieee80211com *ic)
445138568Ssam{
446138568Ssam	int i;
447116742Ssam
448138568Ssam	ieee80211_key_update_begin(ic);
449138568Ssam	for (i = 0; i < IEEE80211_WEP_NKID; i++)
450138568Ssam		(void) _ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[i]);
451138568Ssam	ieee80211_key_update_end(ic);
452138568Ssam}
453116742Ssam
454138568Ssam/*
455138568Ssam * Set the contents of the specified key.
456138568Ssam *
457138568Ssam * Locking must be handled by the caller using:
458138568Ssam *	ieee80211_key_update_begin(ic);
459138568Ssam *	ieee80211_key_update_end(ic);
460138568Ssam */
461138568Ssamint
462138568Ssamieee80211_crypto_setkey(struct ieee80211com *ic, struct ieee80211_key *key,
463138568Ssam		const u_int8_t macaddr[IEEE80211_ADDR_LEN])
464116742Ssam{
465138568Ssam	const struct ieee80211_cipher *cip = key->wk_cipher;
466116742Ssam
467138568Ssam	KASSERT(cip != NULL, ("No cipher!"));
468138568Ssam
469139504Ssam	IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
470139504Ssam	    "%s: %s keyix %u flags 0x%x mac %s rsc %ju tsc %ju len %u\n",
471139504Ssam	    __func__, cip->ic_name, key->wk_keyix,
472139504Ssam	    key->wk_flags, ether_sprintf(macaddr),
473139504Ssam	    key->wk_keyrsc, key->wk_keytsc, key->wk_keylen);
474139504Ssam
475138568Ssam	/*
476138568Ssam	 * Give cipher a chance to validate key contents.
477138568Ssam	 * XXX should happen before modifying state.
478138568Ssam	 */
479138568Ssam	if (!cip->ic_setkey(key)) {
480138568Ssam		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
481138568Ssam		    "%s: cipher %s rejected key index %u len %u flags 0x%x\n",
482138568Ssam		    __func__, cip->ic_name, key->wk_keyix,
483138568Ssam		    key->wk_keylen, key->wk_flags);
484138568Ssam		ic->ic_stats.is_crypto_setkey_cipher++;
485138568Ssam		return 0;
486138568Ssam	}
487138568Ssam	if (key->wk_keyix == IEEE80211_KEYIX_NONE) {
488138568Ssam		/* XXX nothing allocated, should not happen */
489138568Ssam		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
490138568Ssam		    "%s: no key index; should not happen!\n", __func__);
491138568Ssam		ic->ic_stats.is_crypto_setkey_nokey++;
492138568Ssam		return 0;
493138568Ssam	}
494138568Ssam	return dev_key_set(ic, key, macaddr);
495138568Ssam}
496138568Ssam
497138568Ssam/*
498138568Ssam * Add privacy headers appropriate for the specified key.
499138568Ssam */
500138568Ssamstruct ieee80211_key *
501138568Ssamieee80211_crypto_encap(struct ieee80211com *ic,
502138568Ssam	struct ieee80211_node *ni, struct mbuf *m)
503138568Ssam{
504138568Ssam	struct ieee80211_key *k;
505138568Ssam	struct ieee80211_frame *wh;
506138568Ssam	const struct ieee80211_cipher *cip;
507144960Ssam	u_int8_t keyid;
508138568Ssam
509138568Ssam	/*
510138568Ssam	 * Multicast traffic always uses the multicast key.
511138568Ssam	 * Otherwise if a unicast key is set we use that and
512138568Ssam	 * it is always key index 0.  When no unicast key is
513138568Ssam	 * set we fall back to the default transmit key.
514138568Ssam	 */
515138568Ssam	wh = mtod(m, struct ieee80211_frame *);
516138568Ssam	if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
517138568Ssam	    ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none) {
518138568Ssam		if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE) {
519138568Ssam			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
520139504Ssam			    "[%s] no default transmit key (%s) deftxkey %u\n",
521139504Ssam			    ether_sprintf(wh->i_addr1), __func__,
522139504Ssam			    ic->ic_def_txkey);
523138568Ssam			ic->ic_stats.is_tx_nodefkey++;
524138568Ssam			return NULL;
525116742Ssam		}
526144960Ssam		keyid = ic->ic_def_txkey;
527138568Ssam		k = &ic->ic_nw_keys[ic->ic_def_txkey];
528138568Ssam	} else {
529144960Ssam		keyid = 0;
530138568Ssam		k = &ni->ni_ucastkey;
531116742Ssam	}
532138568Ssam	cip = k->wk_cipher;
533144960Ssam	return (cip->ic_encap(k, m, keyid<<6) ? k : NULL);
534116742Ssam}
535116742Ssam
536116742Ssam/*
537138568Ssam * Validate and strip privacy headers (and trailer) for a
538138568Ssam * received frame that has the WEP/Privacy bit set.
539116742Ssam */
540138568Ssamstruct ieee80211_key *
541138568Ssamieee80211_crypto_decap(struct ieee80211com *ic,
542147252Ssam	struct ieee80211_node *ni, struct mbuf *m, int hdrlen)
543116742Ssam{
544138568Ssam#define	IEEE80211_WEP_HDRLEN	(IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN)
545138568Ssam#define	IEEE80211_WEP_MINLEN \
546138568Ssam	(sizeof(struct ieee80211_frame) + ETHER_HDR_LEN + \
547138568Ssam	IEEE80211_WEP_HDRLEN + IEEE80211_WEP_CRCLEN)
548138568Ssam	struct ieee80211_key *k;
549138568Ssam	struct ieee80211_frame *wh;
550138568Ssam	const struct ieee80211_cipher *cip;
551139506Ssam	const u_int8_t *ivp;
552138568Ssam	u_int8_t keyid;
553116742Ssam
554138568Ssam	/* NB: this minimum size data frame could be bigger */
555138568Ssam	if (m->m_pkthdr.len < IEEE80211_WEP_MINLEN) {
556138568Ssam		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
557138568Ssam			"%s: WEP data frame too short, len %u\n",
558138568Ssam			__func__, m->m_pkthdr.len);
559138568Ssam		ic->ic_stats.is_rx_tooshort++;	/* XXX need unique stat? */
560138568Ssam		return NULL;
561138568Ssam	}
562138568Ssam
563138568Ssam	/*
564138568Ssam	 * Locate the key. If unicast and there is no unicast
565138568Ssam	 * key then we fall back to the key id in the header.
566138568Ssam	 * This assumes unicast keys are only configured when
567138568Ssam	 * the key id in the header is meaningless (typically 0).
568138568Ssam	 */
569138568Ssam	wh = mtod(m, struct ieee80211_frame *);
570139506Ssam	ivp = mtod(m, const u_int8_t *) + hdrlen;	/* XXX contig */
571138568Ssam	keyid = ivp[IEEE80211_WEP_IVLEN];
572138568Ssam	if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
573138568Ssam	    ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none)
574138568Ssam		k = &ic->ic_nw_keys[keyid >> 6];
575138568Ssam	else
576138568Ssam		k = &ni->ni_ucastkey;
577138568Ssam
578138568Ssam	/*
579138568Ssam	 * Insure crypto header is contiguous for all decap work.
580138568Ssam	 */
581138568Ssam	cip = k->wk_cipher;
582138568Ssam	if (m->m_len < hdrlen + cip->ic_header &&
583138568Ssam	    (m = m_pullup(m, hdrlen + cip->ic_header)) == NULL) {
584138568Ssam		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
585138568Ssam		    "[%s] unable to pullup %s header\n",
586138568Ssam		    ether_sprintf(wh->i_addr2), cip->ic_name);
587138568Ssam		ic->ic_stats.is_rx_wepfail++;	/* XXX */
588138568Ssam		return 0;
589138568Ssam	}
590138568Ssam
591147252Ssam	return (cip->ic_decap(k, m, hdrlen) ? k : NULL);
592138568Ssam#undef IEEE80211_WEP_MINLEN
593138568Ssam#undef IEEE80211_WEP_HDRLEN
594116742Ssam}
595