ieee80211_crypto_wep.c revision 139508
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 139508 2004-12-31 20:51:41Z 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; 123139508Ssam struct ieee80211com *ic = ctx->wc_ic; 124138568Ssam u_int32_t iv; 125138568Ssam u_int8_t *ivp; 126138568Ssam int hdrlen; 127138568Ssam 128139508Ssam hdrlen = ieee80211_hdrspace(ic, mtod(m, void *)); 129138568Ssam 130138568Ssam /* 131138568Ssam * Copy down 802.11 header and add the IV + KeyID. 132138568Ssam */ 133138568Ssam M_PREPEND(m, wep.ic_header, M_NOWAIT); 134138568Ssam if (m == NULL) 135138568Ssam return 0; 136138568Ssam ivp = mtod(m, u_int8_t *); 137138568Ssam ovbcopy(ivp + wep.ic_header, ivp, hdrlen); 138138568Ssam ivp += hdrlen; 139138568Ssam 140138568Ssam /* 141138568Ssam * XXX 142138568Ssam * IV must not duplicate during the lifetime of the key. 143138568Ssam * But no mechanism to renew keys is defined in IEEE 802.11 144138568Ssam * for WEP. And the IV may be duplicated at other stations 145138568Ssam * because the session key itself is shared. So we use a 146138568Ssam * pseudo random IV for now, though it is not the right way. 147138568Ssam * 148138568Ssam * NB: Rather than use a strictly random IV we select a 149138568Ssam * random one to start and then increment the value for 150138568Ssam * each frame. This is an explicit tradeoff between 151138568Ssam * overhead and security. Given the basic insecurity of 152138568Ssam * WEP this seems worthwhile. 153138568Ssam */ 154138568Ssam 155138568Ssam /* 156138568Ssam * Skip 'bad' IVs from Fluhrer/Mantin/Shamir: 157138568Ssam * (B, 255, N) with 3 <= B < 16 and 0 <= N <= 255 158138568Ssam */ 159138568Ssam iv = ctx->wc_iv; 160138568Ssam if ((iv & 0xff00) == 0xff00) { 161138568Ssam int B = (iv & 0xff0000) >> 16; 162138568Ssam if (3 <= B && B < 16) 163138568Ssam iv += 0x0100; 164138568Ssam } 165138568Ssam ctx->wc_iv = iv + 1; 166138568Ssam 167138568Ssam /* 168138568Ssam * NB: Preserve byte order of IV for packet 169138568Ssam * sniffers; it doesn't matter otherwise. 170138568Ssam */ 171138568Ssam#if _BYTE_ORDER == _BIG_ENDIAN 172138568Ssam ivp[0] = iv >> 0; 173138568Ssam ivp[1] = iv >> 8; 174138568Ssam ivp[2] = iv >> 16; 175138568Ssam#else 176138568Ssam ivp[2] = iv >> 0; 177138568Ssam ivp[1] = iv >> 8; 178138568Ssam ivp[0] = iv >> 16; 179138568Ssam#endif 180138568Ssam ivp[3] = keyid; 181138568Ssam 182138568Ssam /* 183138568Ssam * Finally, do software encrypt if neeed. 184138568Ssam */ 185138568Ssam if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) && 186138568Ssam !wep_encrypt(k, m, hdrlen)) 187138568Ssam return 0; 188138568Ssam 189138568Ssam return 1; 190138568Ssam} 191138568Ssam 192138568Ssam/* 193138568Ssam * Add MIC to the frame as needed. 194138568Ssam */ 195138568Ssamstatic int 196138568Ssamwep_enmic(struct ieee80211_key *k, struct mbuf *m) 197138568Ssam{ 198138568Ssam 199138568Ssam return 1; 200138568Ssam} 201138568Ssam 202138568Ssam/* 203138568Ssam * Validate and strip privacy headers (and trailer) for a 204138568Ssam * received frame. If necessary, decrypt the frame using 205138568Ssam * the specified key. 206138568Ssam */ 207138568Ssamstatic int 208138568Ssamwep_decap(struct ieee80211_key *k, struct mbuf *m) 209138568Ssam{ 210138568Ssam struct wep_ctx *ctx = k->wk_private; 211138568Ssam struct ieee80211_frame *wh; 212138568Ssam int hdrlen; 213138568Ssam 214138568Ssam wh = mtod(m, struct ieee80211_frame *); 215138568Ssam hdrlen = ieee80211_hdrsize(wh); 216138568Ssam 217138568Ssam /* 218138568Ssam * Check if the device handled the decrypt in hardware. 219138568Ssam * If so we just strip the header; otherwise we need to 220138568Ssam * handle the decrypt in software. 221138568Ssam */ 222138568Ssam if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) && 223138568Ssam !wep_decrypt(k, m, hdrlen)) { 224138568Ssam IEEE80211_DPRINTF(ctx->wc_ic, IEEE80211_MSG_CRYPTO, 225138568Ssam "[%s] WEP ICV mismatch on decrypt\n", 226138568Ssam ether_sprintf(wh->i_addr2)); 227138568Ssam ctx->wc_ic->ic_stats.is_rx_wepfail++; 228138568Ssam return 0; 229138568Ssam } 230138568Ssam 231138568Ssam /* 232138568Ssam * Copy up 802.11 header and strip crypto bits. 233138568Ssam */ 234138568Ssam ovbcopy(mtod(m, void *), mtod(m, u_int8_t *) + wep.ic_header, hdrlen); 235138568Ssam m_adj(m, wep.ic_header); 236138568Ssam m_adj(m, -wep.ic_trailer); 237138568Ssam 238138568Ssam return 1; 239138568Ssam} 240138568Ssam 241138568Ssam/* 242138568Ssam * Verify and strip MIC from the frame. 243138568Ssam */ 244138568Ssamstatic int 245138568Ssamwep_demic(struct ieee80211_key *k, struct mbuf *skb) 246138568Ssam{ 247138568Ssam return 1; 248138568Ssam} 249138568Ssam 250138568Ssamstatic const uint32_t crc32_table[256] = { 251138568Ssam 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 252138568Ssam 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, 253138568Ssam 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 254138568Ssam 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 255138568Ssam 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 256138568Ssam 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 257138568Ssam 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 258138568Ssam 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, 259138568Ssam 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 260138568Ssam 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, 261138568Ssam 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 262138568Ssam 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 263138568Ssam 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, 264138568Ssam 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 265138568Ssam 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 266138568Ssam 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, 267138568Ssam 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 268138568Ssam 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, 269138568Ssam 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 270138568Ssam 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 271138568Ssam 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, 272138568Ssam 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 273138568Ssam 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 274138568Ssam 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, 275138568Ssam 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 276138568Ssam 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, 277138568Ssam 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, 278138568Ssam 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, 279138568Ssam 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, 280138568Ssam 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, 281138568Ssam 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 282138568Ssam 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, 283138568Ssam 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 284138568Ssam 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, 285138568Ssam 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 286138568Ssam 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 287138568Ssam 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, 288138568Ssam 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 289138568Ssam 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 290138568Ssam 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, 291138568Ssam 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, 292138568Ssam 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, 293138568Ssam 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 294138568Ssam 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 295138568Ssam 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, 296138568Ssam 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 297138568Ssam 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 298138568Ssam 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, 299138568Ssam 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 300138568Ssam 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, 301138568Ssam 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 302138568Ssam 0x2d02ef8dL 303138568Ssam}; 304138568Ssam 305138568Ssamstatic int 306138568Ssamwep_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) 307138568Ssam{ 308138568Ssam#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0) 309138568Ssam struct wep_ctx *ctx = key->wk_private; 310138568Ssam struct mbuf *m = m0; 311138568Ssam u_int8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE]; 312138568Ssam uint8_t icv[IEEE80211_WEP_CRCLEN]; 313138568Ssam uint32_t i, j, k, crc; 314138568Ssam size_t buflen, data_len; 315138568Ssam uint8_t S[256]; 316138568Ssam uint8_t *pos; 317138568Ssam u_int off, keylen; 318138568Ssam 319138568Ssam ctx->wc_ic->ic_stats.is_crypto_wep++; 320138568Ssam 321138568Ssam /* NB: this assumes the header was pulled up */ 322138568Ssam memcpy(rc4key, mtod(m, u_int8_t *) + hdrlen, IEEE80211_WEP_IVLEN); 323138568Ssam memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen); 324138568Ssam 325138568Ssam /* Setup RC4 state */ 326138568Ssam for (i = 0; i < 256; i++) 327138568Ssam S[i] = i; 328138568Ssam j = 0; 329138568Ssam keylen = key->wk_keylen + IEEE80211_WEP_IVLEN; 330138568Ssam for (i = 0; i < 256; i++) { 331138568Ssam j = (j + S[i] + rc4key[i % keylen]) & 0xff; 332138568Ssam S_SWAP(i, j); 333138568Ssam } 334138568Ssam 335138568Ssam off = hdrlen + wep.ic_header; 336138568Ssam data_len = m->m_pkthdr.len - off; 337138568Ssam 338138568Ssam /* Compute CRC32 over unencrypted data and apply RC4 to data */ 339138568Ssam crc = ~0; 340138568Ssam i = j = 0; 341138568Ssam pos = mtod(m, uint8_t *) + off; 342138568Ssam buflen = m->m_len - off; 343138568Ssam for (;;) { 344138568Ssam if (buflen > data_len) 345138568Ssam buflen = data_len; 346138568Ssam data_len -= buflen; 347138568Ssam for (k = 0; k < buflen; k++) { 348138568Ssam crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8); 349138568Ssam i = (i + 1) & 0xff; 350138568Ssam j = (j + S[i]) & 0xff; 351138568Ssam S_SWAP(i, j); 352138568Ssam *pos++ ^= S[(S[i] + S[j]) & 0xff]; 353138568Ssam } 354138568Ssam if (m->m_next == NULL) { 355138568Ssam if (data_len != 0) { /* out of data */ 356138568Ssam IEEE80211_DPRINTF(ctx->wc_ic, 357138568Ssam IEEE80211_MSG_CRYPTO, 358138609Ssam "[%s] out of data for WEP (data_len %zu)\n", 359138568Ssam ether_sprintf(mtod(m0, 360138568Ssam struct ieee80211_frame *)->i_addr2), 361138568Ssam data_len); 362138568Ssam return 0; 363138568Ssam } 364138568Ssam break; 365138568Ssam } 366138568Ssam m = m->m_next; 367138568Ssam pos = mtod(m, uint8_t *); 368138568Ssam buflen = m->m_len; 369138568Ssam } 370138568Ssam crc = ~crc; 371138568Ssam 372138568Ssam /* Append little-endian CRC32 and encrypt it to produce ICV */ 373138568Ssam icv[0] = crc; 374138568Ssam icv[1] = crc >> 8; 375138568Ssam icv[2] = crc >> 16; 376138568Ssam icv[3] = crc >> 24; 377138568Ssam for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) { 378138568Ssam i = (i + 1) & 0xff; 379138568Ssam j = (j + S[i]) & 0xff; 380138568Ssam S_SWAP(i, j); 381138568Ssam icv[k] ^= S[(S[i] + S[j]) & 0xff]; 382138568Ssam } 383138568Ssam return m_append(m0, IEEE80211_WEP_CRCLEN, icv); 384138568Ssam#undef S_SWAP 385138568Ssam} 386138568Ssam 387138568Ssamstatic int 388138568Ssamwep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) 389138568Ssam{ 390138568Ssam#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0) 391138568Ssam struct wep_ctx *ctx = key->wk_private; 392138568Ssam struct mbuf *m = m0; 393138568Ssam u_int8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE]; 394138568Ssam uint8_t icv[IEEE80211_WEP_CRCLEN]; 395138568Ssam uint32_t i, j, k, crc; 396138568Ssam size_t buflen, data_len; 397138568Ssam uint8_t S[256]; 398138568Ssam uint8_t *pos; 399138568Ssam u_int off, keylen; 400138568Ssam 401138568Ssam ctx->wc_ic->ic_stats.is_crypto_wep++; 402138568Ssam 403138568Ssam /* NB: this assumes the header was pulled up */ 404138568Ssam memcpy(rc4key, mtod(m, u_int8_t *) + hdrlen, IEEE80211_WEP_IVLEN); 405138568Ssam memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen); 406138568Ssam 407138568Ssam /* Setup RC4 state */ 408138568Ssam for (i = 0; i < 256; i++) 409138568Ssam S[i] = i; 410138568Ssam j = 0; 411138568Ssam keylen = key->wk_keylen + IEEE80211_WEP_IVLEN; 412138568Ssam for (i = 0; i < 256; i++) { 413138568Ssam j = (j + S[i] + rc4key[i % keylen]) & 0xff; 414138568Ssam S_SWAP(i, j); 415138568Ssam } 416138568Ssam 417138568Ssam off = hdrlen + wep.ic_header; 418138568Ssam data_len = m->m_pkthdr.len - (off + wep.ic_trailer), 419138568Ssam 420138568Ssam /* Compute CRC32 over unencrypted data and apply RC4 to data */ 421138568Ssam crc = ~0; 422138568Ssam i = j = 0; 423138568Ssam pos = mtod(m, uint8_t *) + off; 424138568Ssam buflen = m->m_len - off; 425138568Ssam for (;;) { 426138568Ssam if (buflen > data_len) 427138568Ssam buflen = data_len; 428138568Ssam data_len -= buflen; 429138568Ssam for (k = 0; k < buflen; k++) { 430138568Ssam i = (i + 1) & 0xff; 431138568Ssam j = (j + S[i]) & 0xff; 432138568Ssam S_SWAP(i, j); 433138568Ssam *pos ^= S[(S[i] + S[j]) & 0xff]; 434138568Ssam crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8); 435138568Ssam pos++; 436138568Ssam } 437138568Ssam m = m->m_next; 438138568Ssam if (m == NULL) { 439138568Ssam if (data_len != 0) { /* out of data */ 440138568Ssam IEEE80211_DPRINTF(ctx->wc_ic, 441138568Ssam IEEE80211_MSG_CRYPTO, 442138609Ssam "[%s] out of data for WEP (data_len %zu)\n", 443138568Ssam ether_sprintf(mtod(m0, 444138568Ssam struct ieee80211_frame *)->i_addr2), 445138568Ssam data_len); 446138568Ssam return 0; 447138568Ssam } 448138568Ssam break; 449138568Ssam } 450138568Ssam pos = mtod(m, uint8_t *); 451138568Ssam buflen = m->m_len; 452138568Ssam } 453138568Ssam crc = ~crc; 454138568Ssam 455138568Ssam /* Encrypt little-endian CRC32 and verify that it matches with 456138568Ssam * received ICV */ 457138568Ssam icv[0] = crc; 458138568Ssam icv[1] = crc >> 8; 459138568Ssam icv[2] = crc >> 16; 460138568Ssam icv[3] = crc >> 24; 461138568Ssam for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) { 462138568Ssam i = (i + 1) & 0xff; 463138568Ssam j = (j + S[i]) & 0xff; 464138568Ssam S_SWAP(i, j); 465138568Ssam /* XXX assumes ICV is contiguous in mbuf */ 466138568Ssam if ((icv[k] ^ S[(S[i] + S[j]) & 0xff]) != *pos++) { 467138568Ssam /* ICV mismatch - drop frame */ 468138568Ssam return 0; 469138568Ssam } 470138568Ssam } 471138568Ssam return 1; 472138568Ssam#undef S_SWAP 473138568Ssam} 474138568Ssam 475138568Ssam/* 476138568Ssam * Module glue. 477138568Ssam */ 478138568Ssamstatic int 479138568Ssamwep_modevent(module_t mod, int type, void *unused) 480138568Ssam{ 481138568Ssam switch (type) { 482138568Ssam case MOD_LOAD: 483138568Ssam ieee80211_crypto_register(&wep); 484138568Ssam return 0; 485138568Ssam case MOD_UNLOAD: 486138568Ssam ieee80211_crypto_unregister(&wep); 487138568Ssam return 0; 488138568Ssam } 489138568Ssam return EINVAL; 490138568Ssam} 491138568Ssam 492138568Ssamstatic moduledata_t wep_mod = { 493138568Ssam "wlan_wep", 494138568Ssam wep_modevent, 495138568Ssam 0 496138568Ssam}; 497138568SsamDECLARE_MODULE(wlan_wep, wep_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); 498138568SsamMODULE_VERSION(wlan_wep, 1); 499138568SsamMODULE_DEPEND(wlan_wep, wlan, 1, 1, 1); 500