ieee80211_crypto_wep.c revision 138568
1138568Ssam/*- 2138568Ssam * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting 3138568Ssam * All rights reserved. 4138568Ssam * 5138568Ssam * Redistribution and use in source and binary forms, with or without 6138568Ssam * modification, are permitted provided that the following conditions 7138568Ssam * are met: 8138568Ssam * 1. Redistributions of source code must retain the above copyright 9138568Ssam * notice, this list of conditions and the following disclaimer. 10138568Ssam * 2. Redistributions in binary form must reproduce the above copyright 11138568Ssam * notice, this list of conditions and the following disclaimer in the 12138568Ssam * documentation and/or other materials provided with the distribution. 13138568Ssam * 3. The name of the author may not be used to endorse or promote products 14138568Ssam * derived from this software without specific prior written permission. 15138568Ssam * 16138568Ssam * Alternatively, this software may be distributed under the terms of the 17138568Ssam * GNU General Public License ("GPL") version 2 as published by the Free 18138568Ssam * Software Foundation. 19138568Ssam * 20138568Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21138568Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22138568Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23138568Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24138568Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25138568Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26138568Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27138568Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28138568Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29138568Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30138568Ssam */ 31138568Ssam 32138568Ssam#include <sys/cdefs.h> 33138568Ssam__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_crypto_wep.c 138568 2004-12-08 17:26:47Z sam $"); 34138568Ssam 35138568Ssam/* 36138568Ssam * IEEE 802.11 WEP crypto support. 37138568Ssam */ 38138568Ssam#include <sys/param.h> 39138568Ssam#include <sys/systm.h> 40138568Ssam#include <sys/mbuf.h> 41138568Ssam#include <sys/malloc.h> 42138568Ssam#include <sys/kernel.h> 43138568Ssam#include <sys/module.h> 44138568Ssam#include <sys/endian.h> 45138568Ssam 46138568Ssam#include <sys/socket.h> 47138568Ssam 48138568Ssam#include <net/if.h> 49138568Ssam#include <net/if_media.h> 50138568Ssam#include <net/ethernet.h> 51138568Ssam 52138568Ssam#include <net80211/ieee80211_var.h> 53138568Ssam 54138568Ssamstatic void *wep_attach(struct ieee80211com *, struct ieee80211_key *); 55138568Ssamstatic void wep_detach(struct ieee80211_key *); 56138568Ssamstatic int wep_setkey(struct ieee80211_key *); 57138568Ssamstatic int wep_encap(struct ieee80211_key *, struct mbuf *, u_int8_t keyid); 58138568Ssamstatic int wep_decap(struct ieee80211_key *, struct mbuf *); 59138568Ssamstatic int wep_enmic(struct ieee80211_key *, struct mbuf *); 60138568Ssamstatic int wep_demic(struct ieee80211_key *, struct mbuf *); 61138568Ssam 62138568Ssamstatic const struct ieee80211_cipher wep = { 63138568Ssam .ic_name = "WEP", 64138568Ssam .ic_cipher = IEEE80211_CIPHER_WEP, 65138568Ssam .ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN, 66138568Ssam .ic_trailer = IEEE80211_WEP_CRCLEN, 67138568Ssam .ic_miclen = 0, 68138568Ssam .ic_attach = wep_attach, 69138568Ssam .ic_detach = wep_detach, 70138568Ssam .ic_setkey = wep_setkey, 71138568Ssam .ic_encap = wep_encap, 72138568Ssam .ic_decap = wep_decap, 73138568Ssam .ic_enmic = wep_enmic, 74138568Ssam .ic_demic = wep_demic, 75138568Ssam}; 76138568Ssam 77138568Ssamstatic int wep_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen); 78138568Ssamstatic int wep_decrypt(struct ieee80211_key *, struct mbuf *, int hdrlen); 79138568Ssam 80138568Ssamstruct wep_ctx { 81138568Ssam struct ieee80211com *wc_ic; /* for diagnostics */ 82138568Ssam u_int32_t wc_iv; /* initial vector for crypto */ 83138568Ssam}; 84138568Ssam 85138568Ssamstatic void * 86138568Ssamwep_attach(struct ieee80211com *ic, struct ieee80211_key *k) 87138568Ssam{ 88138568Ssam struct wep_ctx *ctx; 89138568Ssam 90138568Ssam MALLOC(ctx, struct wep_ctx *, sizeof(struct wep_ctx), 91138568Ssam M_DEVBUF, M_NOWAIT | M_ZERO); 92138568Ssam if (ctx == NULL) { 93138568Ssam ic->ic_stats.is_crypto_nomem++; 94138568Ssam return NULL; 95138568Ssam } 96138568Ssam 97138568Ssam ctx->wc_ic = ic; 98138568Ssam get_random_bytes(&ctx->wc_iv, sizeof(ctx->wc_iv)); 99138568Ssam return ctx; 100138568Ssam} 101138568Ssam 102138568Ssamstatic void 103138568Ssamwep_detach(struct ieee80211_key *k) 104138568Ssam{ 105138568Ssam struct wep_ctx *ctx = k->wk_private; 106138568Ssam 107138568Ssam FREE(ctx, M_DEVBUF); 108138568Ssam} 109138568Ssam 110138568Ssamstatic int 111138568Ssamwep_setkey(struct ieee80211_key *k) 112138568Ssam{ 113138568Ssam return k->wk_keylen >= 40/NBBY; 114138568Ssam} 115138568Ssam 116138568Ssam/* 117138568Ssam * Add privacy headers appropriate for the specified key. 118138568Ssam */ 119138568Ssamstatic int 120138568Ssamwep_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid) 121138568Ssam{ 122138568Ssam struct wep_ctx *ctx = k->wk_private; 123138568Ssam u_int32_t iv; 124138568Ssam u_int8_t *ivp; 125138568Ssam int hdrlen; 126138568Ssam 127138568Ssam hdrlen = ieee80211_hdrsize(mtod(m, void *)); 128138568Ssam 129138568Ssam /* 130138568Ssam * Copy down 802.11 header and add the IV + KeyID. 131138568Ssam */ 132138568Ssam M_PREPEND(m, wep.ic_header, M_NOWAIT); 133138568Ssam if (m == NULL) 134138568Ssam return 0; 135138568Ssam ivp = mtod(m, u_int8_t *); 136138568Ssam ovbcopy(ivp + wep.ic_header, ivp, hdrlen); 137138568Ssam ivp += hdrlen; 138138568Ssam 139138568Ssam /* 140138568Ssam * XXX 141138568Ssam * IV must not duplicate during the lifetime of the key. 142138568Ssam * But no mechanism to renew keys is defined in IEEE 802.11 143138568Ssam * for WEP. And the IV may be duplicated at other stations 144138568Ssam * because the session key itself is shared. So we use a 145138568Ssam * pseudo random IV for now, though it is not the right way. 146138568Ssam * 147138568Ssam * NB: Rather than use a strictly random IV we select a 148138568Ssam * random one to start and then increment the value for 149138568Ssam * each frame. This is an explicit tradeoff between 150138568Ssam * overhead and security. Given the basic insecurity of 151138568Ssam * WEP this seems worthwhile. 152138568Ssam */ 153138568Ssam 154138568Ssam /* 155138568Ssam * Skip 'bad' IVs from Fluhrer/Mantin/Shamir: 156138568Ssam * (B, 255, N) with 3 <= B < 16 and 0 <= N <= 255 157138568Ssam */ 158138568Ssam iv = ctx->wc_iv; 159138568Ssam if ((iv & 0xff00) == 0xff00) { 160138568Ssam int B = (iv & 0xff0000) >> 16; 161138568Ssam if (3 <= B && B < 16) 162138568Ssam iv += 0x0100; 163138568Ssam } 164138568Ssam ctx->wc_iv = iv + 1; 165138568Ssam 166138568Ssam /* 167138568Ssam * NB: Preserve byte order of IV for packet 168138568Ssam * sniffers; it doesn't matter otherwise. 169138568Ssam */ 170138568Ssam#if _BYTE_ORDER == _BIG_ENDIAN 171138568Ssam ivp[0] = iv >> 0; 172138568Ssam ivp[1] = iv >> 8; 173138568Ssam ivp[2] = iv >> 16; 174138568Ssam#else 175138568Ssam ivp[2] = iv >> 0; 176138568Ssam ivp[1] = iv >> 8; 177138568Ssam ivp[0] = iv >> 16; 178138568Ssam#endif 179138568Ssam ivp[3] = keyid; 180138568Ssam 181138568Ssam /* 182138568Ssam * Finally, do software encrypt if neeed. 183138568Ssam */ 184138568Ssam if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) && 185138568Ssam !wep_encrypt(k, m, hdrlen)) 186138568Ssam return 0; 187138568Ssam 188138568Ssam return 1; 189138568Ssam} 190138568Ssam 191138568Ssam/* 192138568Ssam * Add MIC to the frame as needed. 193138568Ssam */ 194138568Ssamstatic int 195138568Ssamwep_enmic(struct ieee80211_key *k, struct mbuf *m) 196138568Ssam{ 197138568Ssam 198138568Ssam return 1; 199138568Ssam} 200138568Ssam 201138568Ssam/* 202138568Ssam * Validate and strip privacy headers (and trailer) for a 203138568Ssam * received frame. If necessary, decrypt the frame using 204138568Ssam * the specified key. 205138568Ssam */ 206138568Ssamstatic int 207138568Ssamwep_decap(struct ieee80211_key *k, struct mbuf *m) 208138568Ssam{ 209138568Ssam struct wep_ctx *ctx = k->wk_private; 210138568Ssam struct ieee80211_frame *wh; 211138568Ssam int hdrlen; 212138568Ssam 213138568Ssam wh = mtod(m, struct ieee80211_frame *); 214138568Ssam hdrlen = ieee80211_hdrsize(wh); 215138568Ssam 216138568Ssam /* 217138568Ssam * Check if the device handled the decrypt in hardware. 218138568Ssam * If so we just strip the header; otherwise we need to 219138568Ssam * handle the decrypt in software. 220138568Ssam */ 221138568Ssam if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) && 222138568Ssam !wep_decrypt(k, m, hdrlen)) { 223138568Ssam IEEE80211_DPRINTF(ctx->wc_ic, IEEE80211_MSG_CRYPTO, 224138568Ssam "[%s] WEP ICV mismatch on decrypt\n", 225138568Ssam ether_sprintf(wh->i_addr2)); 226138568Ssam ctx->wc_ic->ic_stats.is_rx_wepfail++; 227138568Ssam return 0; 228138568Ssam } 229138568Ssam 230138568Ssam /* 231138568Ssam * Copy up 802.11 header and strip crypto bits. 232138568Ssam */ 233138568Ssam ovbcopy(mtod(m, void *), mtod(m, u_int8_t *) + wep.ic_header, hdrlen); 234138568Ssam m_adj(m, wep.ic_header); 235138568Ssam m_adj(m, -wep.ic_trailer); 236138568Ssam 237138568Ssam return 1; 238138568Ssam} 239138568Ssam 240138568Ssam/* 241138568Ssam * Verify and strip MIC from the frame. 242138568Ssam */ 243138568Ssamstatic int 244138568Ssamwep_demic(struct ieee80211_key *k, struct mbuf *skb) 245138568Ssam{ 246138568Ssam return 1; 247138568Ssam} 248138568Ssam 249138568Ssamstatic const uint32_t crc32_table[256] = { 250138568Ssam 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 251138568Ssam 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, 252138568Ssam 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 253138568Ssam 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 254138568Ssam 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 255138568Ssam 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 256138568Ssam 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 257138568Ssam 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, 258138568Ssam 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 259138568Ssam 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, 260138568Ssam 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 261138568Ssam 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 262138568Ssam 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, 263138568Ssam 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 264138568Ssam 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 265138568Ssam 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, 266138568Ssam 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 267138568Ssam 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, 268138568Ssam 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 269138568Ssam 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 270138568Ssam 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, 271138568Ssam 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 272138568Ssam 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 273138568Ssam 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, 274138568Ssam 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 275138568Ssam 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, 276138568Ssam 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, 277138568Ssam 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, 278138568Ssam 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, 279138568Ssam 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, 280138568Ssam 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 281138568Ssam 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, 282138568Ssam 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 283138568Ssam 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, 284138568Ssam 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 285138568Ssam 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 286138568Ssam 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, 287138568Ssam 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 288138568Ssam 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 289138568Ssam 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, 290138568Ssam 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, 291138568Ssam 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, 292138568Ssam 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 293138568Ssam 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 294138568Ssam 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, 295138568Ssam 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 296138568Ssam 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 297138568Ssam 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, 298138568Ssam 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 299138568Ssam 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, 300138568Ssam 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 301138568Ssam 0x2d02ef8dL 302138568Ssam}; 303138568Ssam 304138568Ssamstatic int 305138568Ssamwep_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) 306138568Ssam{ 307138568Ssam#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0) 308138568Ssam struct wep_ctx *ctx = key->wk_private; 309138568Ssam struct mbuf *m = m0; 310138568Ssam u_int8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE]; 311138568Ssam uint8_t icv[IEEE80211_WEP_CRCLEN]; 312138568Ssam uint32_t i, j, k, crc; 313138568Ssam size_t buflen, data_len; 314138568Ssam uint8_t S[256]; 315138568Ssam uint8_t *pos; 316138568Ssam u_int off, keylen; 317138568Ssam 318138568Ssam ctx->wc_ic->ic_stats.is_crypto_wep++; 319138568Ssam 320138568Ssam /* NB: this assumes the header was pulled up */ 321138568Ssam memcpy(rc4key, mtod(m, u_int8_t *) + hdrlen, IEEE80211_WEP_IVLEN); 322138568Ssam memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen); 323138568Ssam 324138568Ssam /* Setup RC4 state */ 325138568Ssam for (i = 0; i < 256; i++) 326138568Ssam S[i] = i; 327138568Ssam j = 0; 328138568Ssam keylen = key->wk_keylen + IEEE80211_WEP_IVLEN; 329138568Ssam for (i = 0; i < 256; i++) { 330138568Ssam j = (j + S[i] + rc4key[i % keylen]) & 0xff; 331138568Ssam S_SWAP(i, j); 332138568Ssam } 333138568Ssam 334138568Ssam off = hdrlen + wep.ic_header; 335138568Ssam data_len = m->m_pkthdr.len - off; 336138568Ssam 337138568Ssam /* Compute CRC32 over unencrypted data and apply RC4 to data */ 338138568Ssam crc = ~0; 339138568Ssam i = j = 0; 340138568Ssam pos = mtod(m, uint8_t *) + off; 341138568Ssam buflen = m->m_len - off; 342138568Ssam for (;;) { 343138568Ssam if (buflen > data_len) 344138568Ssam buflen = data_len; 345138568Ssam data_len -= buflen; 346138568Ssam for (k = 0; k < buflen; k++) { 347138568Ssam crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8); 348138568Ssam i = (i + 1) & 0xff; 349138568Ssam j = (j + S[i]) & 0xff; 350138568Ssam S_SWAP(i, j); 351138568Ssam *pos++ ^= S[(S[i] + S[j]) & 0xff]; 352138568Ssam } 353138568Ssam if (m->m_next == NULL) { 354138568Ssam if (data_len != 0) { /* out of data */ 355138568Ssam IEEE80211_DPRINTF(ctx->wc_ic, 356138568Ssam IEEE80211_MSG_CRYPTO, 357138568Ssam "[%s] out of data for WEP (data_len %u)\n", 358138568Ssam ether_sprintf(mtod(m0, 359138568Ssam struct ieee80211_frame *)->i_addr2), 360138568Ssam data_len); 361138568Ssam return 0; 362138568Ssam } 363138568Ssam break; 364138568Ssam } 365138568Ssam m = m->m_next; 366138568Ssam pos = mtod(m, uint8_t *); 367138568Ssam buflen = m->m_len; 368138568Ssam } 369138568Ssam crc = ~crc; 370138568Ssam 371138568Ssam /* Append little-endian CRC32 and encrypt it to produce ICV */ 372138568Ssam icv[0] = crc; 373138568Ssam icv[1] = crc >> 8; 374138568Ssam icv[2] = crc >> 16; 375138568Ssam icv[3] = crc >> 24; 376138568Ssam for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) { 377138568Ssam i = (i + 1) & 0xff; 378138568Ssam j = (j + S[i]) & 0xff; 379138568Ssam S_SWAP(i, j); 380138568Ssam icv[k] ^= S[(S[i] + S[j]) & 0xff]; 381138568Ssam } 382138568Ssam return m_append(m0, IEEE80211_WEP_CRCLEN, icv); 383138568Ssam#undef S_SWAP 384138568Ssam} 385138568Ssam 386138568Ssamstatic int 387138568Ssamwep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) 388138568Ssam{ 389138568Ssam#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0) 390138568Ssam struct wep_ctx *ctx = key->wk_private; 391138568Ssam struct mbuf *m = m0; 392138568Ssam u_int8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE]; 393138568Ssam uint8_t icv[IEEE80211_WEP_CRCLEN]; 394138568Ssam uint32_t i, j, k, crc; 395138568Ssam size_t buflen, data_len; 396138568Ssam uint8_t S[256]; 397138568Ssam uint8_t *pos; 398138568Ssam u_int off, keylen; 399138568Ssam 400138568Ssam ctx->wc_ic->ic_stats.is_crypto_wep++; 401138568Ssam 402138568Ssam /* NB: this assumes the header was pulled up */ 403138568Ssam memcpy(rc4key, mtod(m, u_int8_t *) + hdrlen, IEEE80211_WEP_IVLEN); 404138568Ssam memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen); 405138568Ssam 406138568Ssam /* Setup RC4 state */ 407138568Ssam for (i = 0; i < 256; i++) 408138568Ssam S[i] = i; 409138568Ssam j = 0; 410138568Ssam keylen = key->wk_keylen + IEEE80211_WEP_IVLEN; 411138568Ssam for (i = 0; i < 256; i++) { 412138568Ssam j = (j + S[i] + rc4key[i % keylen]) & 0xff; 413138568Ssam S_SWAP(i, j); 414138568Ssam } 415138568Ssam 416138568Ssam off = hdrlen + wep.ic_header; 417138568Ssam data_len = m->m_pkthdr.len - (off + wep.ic_trailer), 418138568Ssam 419138568Ssam /* Compute CRC32 over unencrypted data and apply RC4 to data */ 420138568Ssam crc = ~0; 421138568Ssam i = j = 0; 422138568Ssam pos = mtod(m, uint8_t *) + off; 423138568Ssam buflen = m->m_len - off; 424138568Ssam for (;;) { 425138568Ssam if (buflen > data_len) 426138568Ssam buflen = data_len; 427138568Ssam data_len -= buflen; 428138568Ssam for (k = 0; k < buflen; k++) { 429138568Ssam i = (i + 1) & 0xff; 430138568Ssam j = (j + S[i]) & 0xff; 431138568Ssam S_SWAP(i, j); 432138568Ssam *pos ^= S[(S[i] + S[j]) & 0xff]; 433138568Ssam crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8); 434138568Ssam pos++; 435138568Ssam } 436138568Ssam m = m->m_next; 437138568Ssam if (m == NULL) { 438138568Ssam if (data_len != 0) { /* out of data */ 439138568Ssam IEEE80211_DPRINTF(ctx->wc_ic, 440138568Ssam IEEE80211_MSG_CRYPTO, 441138568Ssam "[%s] out of data for WEP (data_len %u)\n", 442138568Ssam ether_sprintf(mtod(m0, 443138568Ssam struct ieee80211_frame *)->i_addr2), 444138568Ssam data_len); 445138568Ssam return 0; 446138568Ssam } 447138568Ssam break; 448138568Ssam } 449138568Ssam pos = mtod(m, uint8_t *); 450138568Ssam buflen = m->m_len; 451138568Ssam } 452138568Ssam crc = ~crc; 453138568Ssam 454138568Ssam /* Encrypt little-endian CRC32 and verify that it matches with 455138568Ssam * received ICV */ 456138568Ssam icv[0] = crc; 457138568Ssam icv[1] = crc >> 8; 458138568Ssam icv[2] = crc >> 16; 459138568Ssam icv[3] = crc >> 24; 460138568Ssam for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) { 461138568Ssam i = (i + 1) & 0xff; 462138568Ssam j = (j + S[i]) & 0xff; 463138568Ssam S_SWAP(i, j); 464138568Ssam /* XXX assumes ICV is contiguous in mbuf */ 465138568Ssam if ((icv[k] ^ S[(S[i] + S[j]) & 0xff]) != *pos++) { 466138568Ssam /* ICV mismatch - drop frame */ 467138568Ssam return 0; 468138568Ssam } 469138568Ssam } 470138568Ssam return 1; 471138568Ssam#undef S_SWAP 472138568Ssam} 473138568Ssam 474138568Ssam/* 475138568Ssam * Module glue. 476138568Ssam */ 477138568Ssamstatic int 478138568Ssamwep_modevent(module_t mod, int type, void *unused) 479138568Ssam{ 480138568Ssam switch (type) { 481138568Ssam case MOD_LOAD: 482138568Ssam ieee80211_crypto_register(&wep); 483138568Ssam return 0; 484138568Ssam case MOD_UNLOAD: 485138568Ssam ieee80211_crypto_unregister(&wep); 486138568Ssam return 0; 487138568Ssam } 488138568Ssam return EINVAL; 489138568Ssam} 490138568Ssam 491138568Ssamstatic moduledata_t wep_mod = { 492138568Ssam "wlan_wep", 493138568Ssam wep_modevent, 494138568Ssam 0 495138568Ssam}; 496138568SsamDECLARE_MODULE(wlan_wep, wep_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); 497138568SsamMODULE_VERSION(wlan_wep, 1); 498138568SsamMODULE_DEPEND(wlan_wep, wlan, 1, 1, 1); 499138568SsamMODULE_DEPEND(wlan_wep, rc4, 1, 1, 1); 500