/*- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * IEEE 802.11 WEP crypto support. */ #include #include #include #include #include #include #include #include "if_media.h" #include #include "rc4.h" #define arc4_ctxlen() sizeof (struct rc4_state) #define arc4_setkey(_c,_k,_l) rc4_init(_c,_k,_l) #define arc4_encrypt(_c,_d,_s,_l) rc4_crypt_skip(_c,_s,_d,_l,0) #define arc4_decrypt(_c,_d,_s,_l) rc4_crypt_skip(_c,_s,_d,_l,0) static void *wep_attach(struct ieee80211com *, struct ieee80211_key *); static void wep_detach(struct ieee80211_key *); static int wep_setkey(struct ieee80211_key *); static int wep_encap(struct ieee80211_key *, struct sk_buff *, u_int8_t keyid); static int wep_decap(struct ieee80211_key *, struct sk_buff *, int); static int wep_enmic(struct ieee80211_key *, struct sk_buff *, int); static int wep_demic(struct ieee80211_key *, struct sk_buff *, int); static const struct ieee80211_cipher wep = { .ic_name = "WEP", .ic_cipher = IEEE80211_CIPHER_WEP, .ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN, .ic_trailer = IEEE80211_WEP_CRCLEN, .ic_miclen = 0, .ic_attach = wep_attach, .ic_detach = wep_detach, .ic_setkey = wep_setkey, .ic_encap = wep_encap, .ic_decap = wep_decap, .ic_enmic = wep_enmic, .ic_demic = wep_demic, }; static int wep_encrypt(struct ieee80211_key *, struct sk_buff *, int hdrlen); static int wep_decrypt(struct ieee80211_key *, struct sk_buff *, int hdrlen); struct wep_ctx_hw { /* for use with h/w support */ struct ieee80211com *wc_ic; /* for diagnostics */ u_int32_t wc_iv; /* initial vector for crypto */ }; struct wep_ctx { struct ieee80211com *wc_ic; /* for diagnostics */ u_int32_t wc_iv; /* initial vector for crypto */ struct rc4_state wc_rc4; /* rc4 state */ }; static void * wep_attach(struct ieee80211com *ic, struct ieee80211_key *k) { struct wep_ctx *ctx; _MOD_INC_USE(THIS_MODULE, return NULL); /* NB: only allocate rc4 state when doing s/w crypto */ MALLOC(ctx, struct wep_ctx *, k->wk_flags & IEEE80211_KEY_SWCRYPT ? sizeof(struct wep_ctx) : sizeof(struct wep_ctx_hw), M_DEVBUF, M_NOWAIT | M_ZERO); if (ctx == NULL) { ic->ic_stats.is_crypto_nomem++; _MOD_DEC_USE(THIS_MODULE); return NULL; } ctx->wc_ic = ic; get_random_bytes(&ctx->wc_iv, sizeof(ctx->wc_iv)); return ctx; } static void wep_detach(struct ieee80211_key *k) { struct wep_ctx *ctx = k->wk_private; FREE(ctx, M_DEVBUF); _MOD_DEC_USE(THIS_MODULE); } static int wep_setkey(struct ieee80211_key *k) { return k->wk_keylen >= 40/NBBY; } #ifndef _BYTE_ORDER #error "Don't know native byte order" #endif /* * Add privacy headers appropriate for the specified key. */ static int wep_encap(struct ieee80211_key *k, struct sk_buff *skb, u_int8_t keyid) { struct wep_ctx *ctx = k->wk_private; struct ieee80211com *ic = ctx->wc_ic; u_int32_t iv; u_int8_t *ivp; int hdrlen; hdrlen = ieee80211_hdrspace(ic, skb->data); /* * Copy down 802.11 header and add the IV + KeyID. */ ivp = skb_push(skb, wep.ic_header); memmove(ivp, ivp + wep.ic_header, hdrlen); ivp += hdrlen; /* * XXX * IV must not duplicate during the lifetime of the key. * But no mechanism to renew keys is defined in IEEE 802.11 * for WEP. And the IV may be duplicated at other stations * because the session key itself is shared. So we use a * pseudo random IV for now, though it is not the right way. * * NB: Rather than use a strictly random IV we select a * random one to start and then increment the value for * each frame. This is an explicit tradeoff between * overhead and security. Given the basic insecurity of * WEP this seems worthwhile. */ /* * Skip 'bad' IVs from Fluhrer/Mantin/Shamir: * (B, 255, N) with 3 <= B < 16 and 0 <= N <= 255 */ iv = ctx->wc_iv; if ((iv & 0xff00) == 0xff00) { int B = (iv & 0xff0000) >> 16; if (3 <= B && B < 16) iv += 0x0100; } ctx->wc_iv = iv + 1; /* * NB: Preserve byte order of IV for packet * sniffers; it doesn't matter otherwise. */ #if _BYTE_ORDER == _BIG_ENDIAN ivp[0] = iv >> 0; ivp[1] = iv >> 8; ivp[2] = iv >> 16; #else ivp[2] = iv >> 0; ivp[1] = iv >> 8; ivp[0] = iv >> 16; #endif ivp[3] = keyid; /* * Finally, do software encrypt if neeed. */ if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) && !wep_encrypt(k, skb, hdrlen)) return 0; return 1; } /* * Add MIC to the frame as needed. */ static int wep_enmic(struct ieee80211_key *k, struct sk_buff *skb, int force) { return 1; } /* * Validate and strip privacy headers (and trailer) for a * received frame. If necessary, decrypt the frame using * the specified key. */ static int wep_decap(struct ieee80211_key *k, struct sk_buff *skb, int hdrlen) { struct wep_ctx *ctx = k->wk_private; struct ieee80211_frame *wh; wh = (struct ieee80211_frame *)skb->data; /* * Check if the device handled the decrypt in hardware. * If so we just strip the header; otherwise we need to * handle the decrypt in software. */ if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) && !wep_decrypt(k, skb, hdrlen)) { IEEE80211_DPRINTF(ctx->wc_ic, IEEE80211_MSG_CRYPTO, "[%s] WEP ICV mismatch on decrypt\n", ether_sprintf(wh->i_addr2)); ctx->wc_ic->ic_stats.is_rx_wepfail++; return 0; } /* * Copy up 802.11 header and strip crypto bits. */ memmove(skb->data + wep.ic_header, skb->data, hdrlen); skb_pull(skb, wep.ic_header); skb_trim(skb, skb->len - wep.ic_trailer); return 1; } /* * Verify and strip MIC from the frame. */ static int wep_demic(struct ieee80211_key *k, struct sk_buff *skb, int force) { return 1; } /* * CRC 32 -- routine from RFC 2083 */ /* Table of CRCs of all 8-bit messages */ static u_int32_t crc_table[256]; /* Make the table for a fast CRC. */ static void crc_init(void) { u_int32_t c; int n, k; for (n = 0; n < 256; n++) { c = (u_int32_t)n; for (k = 0; k < 8; k++) { if (c & 1) c = 0xedb88320UL ^ (c >> 1); else c = c >> 1; } crc_table[n] = c; } } /* * Update a running CRC with the bytes buf[0..len-1]--the CRC * should be initialized to all 1's, and the transmitted value * is the 1's complement of the final running CRC */ static u_int32_t crc_update(u_int32_t crc, u_int8_t *buf, int len) { u_int8_t *endbuf; for (endbuf = buf + len; buf < endbuf; buf++) crc = crc_table[(crc ^ *buf) & 0xff] ^ (crc >> 8); return crc; } static int wep_encrypt(struct ieee80211_key *key, struct sk_buff *skb, int hdrlen) { struct wep_ctx *ctx = key->wk_private; u_int8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE]; u_int8_t crcbuf[IEEE80211_WEP_CRCLEN]; u_int8_t *icv; u_int32_t crc; KASSERT(key->wk_flags & IEEE80211_KEY_SWCRYPT, ("No s/w crypto state")); ctx->wc_ic->ic_stats.is_crypto_wep++; if (skb_tailroom(skb) < wep.ic_trailer) { struct ieee80211_frame *wh = (struct ieee80211_frame *) skb->data; /* NB: should not happen */ IEEE80211_DPRINTF(ctx->wc_ic, IEEE80211_MSG_CRYPTO, "[%s] No room for %s ICV, tailroom %u\n", ether_sprintf(wh->i_addr1), wep.ic_name, skb_tailroom(skb)); /* XXX statistic */ return 0; } memcpy(rc4key, skb->data + hdrlen, IEEE80211_WEP_IVLEN); memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen); arc4_setkey(&ctx->wc_rc4, rc4key, IEEE80211_WEP_IVLEN + key->wk_keylen); /* calculate CRC over unencrypted data */ crc = crc_update(~0, skb->data + hdrlen + wep.ic_header, skb->len - (hdrlen + wep.ic_header)); /* encrypt data */ arc4_encrypt(&ctx->wc_rc4, skb->data + hdrlen + wep.ic_header, skb->data + hdrlen + wep.ic_header, skb->len - (hdrlen + wep.ic_header)); /* tack on ICV */ *(u_int32_t *)crcbuf = htole32(~crc); icv = skb_put(skb, IEEE80211_WEP_CRCLEN); arc4_encrypt(&ctx->wc_rc4, icv, crcbuf, IEEE80211_WEP_CRCLEN); return 1; } static int wep_decrypt(struct ieee80211_key *key, struct sk_buff *skb, int hdrlen) { struct wep_ctx *ctx = key->wk_private; u_int8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE]; u_int8_t crcbuf[IEEE80211_WEP_CRCLEN]; u_int8_t *icv; u_int32_t crc; KASSERT(key->wk_flags & IEEE80211_KEY_SWCRYPT, ("No s/w crypto state")); ctx->wc_ic->ic_stats.is_crypto_wep++; memcpy(rc4key, skb->data + hdrlen, IEEE80211_WEP_IVLEN); memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen); arc4_setkey(&ctx->wc_rc4, rc4key, IEEE80211_WEP_IVLEN + key->wk_keylen); /* decrypt data */ arc4_decrypt(&ctx->wc_rc4, skb->data + hdrlen + wep.ic_header, skb->data + hdrlen + wep.ic_header, skb->len - (hdrlen + wep.ic_header + wep.ic_trailer)); /* calculate CRC over unencrypted data */ crc = crc_update(~0, skb->data + hdrlen + wep.ic_header, skb->len - (hdrlen + wep.ic_header + wep.ic_trailer)); /* decrypt ICV and compare to CRC */ icv = skb->data + (skb->len - IEEE80211_WEP_CRCLEN); arc4_decrypt(&ctx->wc_rc4, crcbuf, icv, IEEE80211_WEP_CRCLEN); return (crc == ~le32toh(*(u_int32_t *)crcbuf)); } /* * Module glue. */ MODULE_AUTHOR("Errno Consulting, Sam Leffler"); MODULE_DESCRIPTION("802.11 wireless support: WEP cipher"); #ifdef MODULE_LICENSE MODULE_LICENSE("Dual BSD/GPL"); #endif static int __init init_crypto_wep(void) { ieee80211_crypto_register(&wep); crc_init(); return 0; } module_init(init_crypto_wep); static void __exit exit_crypto_wep(void) { ieee80211_crypto_unregister(&wep); } module_exit(exit_crypto_wep);