ieee80211_crypto_wep.c revision 186302
1317019Sdim/*- 2317019Sdim * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting 3353358Sdim * All rights reserved. 4353358Sdim * 5353358Sdim * Redistribution and use in source and binary forms, with or without 6317019Sdim * modification, are permitted provided that the following conditions 7317019Sdim * are met: 8317019Sdim * 1. Redistributions of source code must retain the above copyright 9317019Sdim * notice, this list of conditions and the following disclaimer. 10317019Sdim * 2. Redistributions in binary form must reproduce the above copyright 11317019Sdim * notice, this list of conditions and the following disclaimer in the 12317019Sdim * documentation and/or other materials provided with the distribution. 13344779Sdim * 14317019Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15317019Sdim * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16344779Sdim * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17317019Sdim * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18317019Sdim * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19317019Sdim * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20317019Sdim * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21317019Sdim * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22317019Sdim * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23317019Sdim * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24317019Sdim */ 25317019Sdim 26317019Sdim#include <sys/cdefs.h> 27317019Sdim__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_crypto_wep.c 186302 2008-12-18 23:00:09Z sam $"); 28317019Sdim 29317019Sdim/* 30317019Sdim * IEEE 802.11 WEP crypto support. 31317019Sdim */ 32317019Sdim#include "opt_wlan.h" 33317019Sdim 34317019Sdim#include <sys/param.h> 35317019Sdim#include <sys/systm.h> 36317019Sdim#include <sys/mbuf.h> 37317019Sdim#include <sys/malloc.h> 38317019Sdim#include <sys/kernel.h> 39317019Sdim#include <sys/module.h> 40317019Sdim#include <sys/endian.h> 41317019Sdim 42317019Sdim#include <sys/socket.h> 43317019Sdim 44317019Sdim#include <net/if.h> 45317019Sdim#include <net/if_media.h> 46317019Sdim#include <net/ethernet.h> 47317019Sdim 48317019Sdim#include <net80211/ieee80211_var.h> 49317019Sdim 50317019Sdimstatic void *wep_attach(struct ieee80211vap *, struct ieee80211_key *); 51317019Sdimstatic void wep_detach(struct ieee80211_key *); 52317019Sdimstatic int wep_setkey(struct ieee80211_key *); 53317019Sdimstatic int wep_encap(struct ieee80211_key *, struct mbuf *, uint8_t keyid); 54317019Sdimstatic int wep_decap(struct ieee80211_key *, struct mbuf *, int hdrlen); 55317019Sdimstatic int wep_enmic(struct ieee80211_key *, struct mbuf *, int); 56317019Sdimstatic int wep_demic(struct ieee80211_key *, struct mbuf *, int); 57317019Sdim 58317019Sdimstatic const struct ieee80211_cipher wep = { 59317019Sdim .ic_name = "WEP", 60317019Sdim .ic_cipher = IEEE80211_CIPHER_WEP, 61317019Sdim .ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN, 62317019Sdim .ic_trailer = IEEE80211_WEP_CRCLEN, 63317019Sdim .ic_miclen = 0, 64317019Sdim .ic_attach = wep_attach, 65317019Sdim .ic_detach = wep_detach, 66317019Sdim .ic_setkey = wep_setkey, 67317019Sdim .ic_encap = wep_encap, 68317019Sdim .ic_decap = wep_decap, 69317019Sdim .ic_enmic = wep_enmic, 70317019Sdim .ic_demic = wep_demic, 71317019Sdim}; 72317019Sdim 73317019Sdimstatic int wep_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen); 74317019Sdimstatic int wep_decrypt(struct ieee80211_key *, struct mbuf *, int hdrlen); 75317019Sdim 76317019Sdimstruct wep_ctx { 77317019Sdim struct ieee80211vap *wc_vap; /* for diagnostics+statistics */ 78317019Sdim struct ieee80211com *wc_ic; 79317019Sdim uint32_t wc_iv; /* initial vector for crypto */ 80317019Sdim}; 81317019Sdim 82317019Sdim/* number of references from net80211 layer */ 83317019Sdimstatic int nrefs = 0; 84317019Sdim 85317019Sdimstatic void * 86317019Sdimwep_attach(struct ieee80211vap *vap, struct ieee80211_key *k) 87317019Sdim{ 88317019Sdim struct wep_ctx *ctx; 89317019Sdim 90317019Sdim ctx = (struct wep_ctx *) malloc(sizeof(struct wep_ctx), 91317019Sdim M_80211_CRYPTO, M_NOWAIT | M_ZERO); 92317019Sdim if (ctx == NULL) { 93317019Sdim vap->iv_stats.is_crypto_nomem++; 94317019Sdim return NULL; 95317019Sdim } 96317019Sdim 97317019Sdim ctx->wc_vap = vap; 98317019Sdim ctx->wc_ic = vap->iv_ic; 99317019Sdim get_random_bytes(&ctx->wc_iv, sizeof(ctx->wc_iv)); 100317019Sdim nrefs++; /* NB: we assume caller locking */ 101317019Sdim return ctx; 102317019Sdim} 103317019Sdim 104317019Sdimstatic void 105317019Sdimwep_detach(struct ieee80211_key *k) 106317019Sdim{ 107317019Sdim struct wep_ctx *ctx = k->wk_private; 108317019Sdim 109317019Sdim free(ctx, M_80211_CRYPTO); 110317019Sdim KASSERT(nrefs > 0, ("imbalanced attach/detach")); 111317019Sdim nrefs--; /* NB: we assume caller locking */ 112317019Sdim} 113317019Sdim 114317019Sdimstatic int 115317019Sdimwep_setkey(struct ieee80211_key *k) 116317019Sdim{ 117317019Sdim return k->wk_keylen >= 40/NBBY; 118317019Sdim} 119317019Sdim 120317019Sdim/* 121317019Sdim * Add privacy headers appropriate for the specified key. 122317019Sdim */ 123317019Sdimstatic int 124317019Sdimwep_encap(struct ieee80211_key *k, struct mbuf *m, uint8_t keyid) 125317019Sdim{ 126317019Sdim struct wep_ctx *ctx = k->wk_private; 127317019Sdim struct ieee80211com *ic = ctx->wc_ic; 128317019Sdim uint32_t iv; 129317019Sdim uint8_t *ivp; 130317019Sdim int hdrlen; 131317019Sdim 132317019Sdim hdrlen = ieee80211_hdrspace(ic, mtod(m, void *)); 133317019Sdim 134317019Sdim /* 135317019Sdim * Copy down 802.11 header and add the IV + KeyID. 136317019Sdim */ 137317019Sdim M_PREPEND(m, wep.ic_header, M_NOWAIT); 138317019Sdim if (m == NULL) 139317019Sdim return 0; 140317019Sdim ivp = mtod(m, uint8_t *); 141317019Sdim ovbcopy(ivp + wep.ic_header, ivp, hdrlen); 142317019Sdim ivp += hdrlen; 143317019Sdim 144317019Sdim /* 145317019Sdim * XXX 146317019Sdim * IV must not duplicate during the lifetime of the key. 147317019Sdim * But no mechanism to renew keys is defined in IEEE 802.11 148317019Sdim * for WEP. And the IV may be duplicated at other stations 149317019Sdim * because the session key itself is shared. So we use a 150317019Sdim * pseudo random IV for now, though it is not the right way. 151317019Sdim * 152317019Sdim * NB: Rather than use a strictly random IV we select a 153317019Sdim * random one to start and then increment the value for 154317019Sdim * each frame. This is an explicit tradeoff between 155317019Sdim * overhead and security. Given the basic insecurity of 156327952Sdim * WEP this seems worthwhile. 157327952Sdim */ 158327952Sdim 159317019Sdim /* 160353358Sdim * Skip 'bad' IVs from Fluhrer/Mantin/Shamir: 161353358Sdim * (B, 255, N) with 3 <= B < 16 and 0 <= N <= 255 162317019Sdim */ 163317019Sdim iv = ctx->wc_iv; 164317019Sdim if ((iv & 0xff00) == 0xff00) { 165317019Sdim int B = (iv & 0xff0000) >> 16; 166317019Sdim if (3 <= B && B < 16) 167317019Sdim iv += 0x0100; 168317019Sdim } 169317019Sdim ctx->wc_iv = iv + 1; 170360784Sdim 171360784Sdim /* 172360784Sdim * NB: Preserve byte order of IV for packet 173360784Sdim * sniffers; it doesn't matter otherwise. 174317019Sdim */ 175317019Sdim#if _BYTE_ORDER == _BIG_ENDIAN 176317019Sdim ivp[0] = iv >> 0; 177317019Sdim ivp[1] = iv >> 8; 178317019Sdim ivp[2] = iv >> 16; 179317019Sdim#else 180317019Sdim ivp[2] = iv >> 0; 181327952Sdim ivp[1] = iv >> 8; 182327952Sdim ivp[0] = iv >> 16; 183327952Sdim#endif 184327952Sdim ivp[3] = keyid; 185327952Sdim 186327952Sdim /* 187327952Sdim * Finally, do software encrypt if neeed. 188327952Sdim */ 189327952Sdim if ((k->wk_flags & IEEE80211_KEY_SWENCRYPT) && 190327952Sdim !wep_encrypt(k, m, hdrlen)) 191327952Sdim return 0; 192327952Sdim 193327952Sdim return 1; 194327952Sdim} 195327952Sdim 196327952Sdim/* 197317019Sdim * Add MIC to the frame as needed. 198344779Sdim */ 199344779Sdimstatic int 200344779Sdimwep_enmic(struct ieee80211_key *k, struct mbuf *m, int force) 201317019Sdim{ 202317019Sdim 203317019Sdim return 1; 204317019Sdim} 205317019Sdim 206317019Sdim/* 207317019Sdim * Validate and strip privacy headers (and trailer) for a 208317019Sdim * received frame. If necessary, decrypt the frame using 209317019Sdim * the specified key. 210317019Sdim */ 211317019Sdimstatic int 212317019Sdimwep_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen) 213317019Sdim{ 214317019Sdim struct wep_ctx *ctx = k->wk_private; 215317019Sdim struct ieee80211vap *vap = ctx->wc_vap; 216317019Sdim struct ieee80211_frame *wh; 217317019Sdim 218317019Sdim wh = mtod(m, struct ieee80211_frame *); 219317019Sdim 220317019Sdim /* 221317019Sdim * Check if the device handled the decrypt in hardware. 222317019Sdim * If so we just strip the header; otherwise we need to 223317019Sdim * handle the decrypt in software. 224317019Sdim */ 225317019Sdim if ((k->wk_flags & IEEE80211_KEY_SWDECRYPT) && 226317019Sdim !wep_decrypt(k, m, hdrlen)) { 227317019Sdim IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, 228317019Sdim "%s", "WEP ICV mismatch on decrypt"); 229317019Sdim vap->iv_stats.is_rx_wepfail++; 230317019Sdim return 0; 231317019Sdim } 232317019Sdim 233317019Sdim /* 234317019Sdim * Copy up 802.11 header and strip crypto bits. 235317019Sdim */ 236317019Sdim ovbcopy(mtod(m, void *), mtod(m, uint8_t *) + wep.ic_header, hdrlen); 237317019Sdim m_adj(m, wep.ic_header); 238317019Sdim m_adj(m, -wep.ic_trailer); 239317019Sdim 240317019Sdim return 1; 241317019Sdim} 242322740Sdim 243317019Sdim/* 244317019Sdim * Verify and strip MIC from the frame. 245317019Sdim */ 246317019Sdimstatic int 247317019Sdimwep_demic(struct ieee80211_key *k, struct mbuf *skb, int force) 248317019Sdim{ 249317019Sdim return 1; 250317019Sdim} 251317019Sdim 252317019Sdimstatic const uint32_t crc32_table[256] = { 253317019Sdim 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 254317019Sdim 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, 255317019Sdim 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 256327952Sdim 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 257327952Sdim 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 258327952Sdim 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 259317019Sdim 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 260317019Sdim 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, 261344779Sdim 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 262344779Sdim 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, 263344779Sdim 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 264344779Sdim 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 265360784Sdim 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, 266360784Sdim 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 267360784Sdim 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 268317019Sdim 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, 269317019Sdim 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 270317019Sdim 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, 271317019Sdim 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 272317019Sdim 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 273317019Sdim 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, 274317019Sdim 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 275317019Sdim 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 276317019Sdim 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, 277317019Sdim 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 278317019Sdim 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, 279317019Sdim 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, 280317019Sdim 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, 281317019Sdim 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, 282317019Sdim 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, 283317019Sdim 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 284317019Sdim 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, 285317019Sdim 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 286327952Sdim 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, 287317019Sdim 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 288327952Sdim 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 289327952Sdim 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, 290327952Sdim 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 291327952Sdim 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 292317019Sdim 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, 293317019Sdim 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, 294327952Sdim 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, 295317019Sdim 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 296317019Sdim 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 297317019Sdim 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, 298317019Sdim 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 299344779Sdim 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 300344779Sdim 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, 301344779Sdim 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 302317019Sdim 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, 303317019Sdim 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 304317019Sdim 0x2d02ef8dL 305317019Sdim}; 306317019Sdim 307317019Sdimstatic int 308317019Sdimwep_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) 309317019Sdim{ 310317019Sdim#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0) 311317019Sdim struct wep_ctx *ctx = key->wk_private; 312317019Sdim struct ieee80211vap *vap = ctx->wc_vap; 313317019Sdim struct mbuf *m = m0; 314317019Sdim uint8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE]; 315317019Sdim uint8_t icv[IEEE80211_WEP_CRCLEN]; 316317019Sdim uint32_t i, j, k, crc; 317317019Sdim size_t buflen, data_len; 318317019Sdim uint8_t S[256]; 319317019Sdim uint8_t *pos; 320317019Sdim u_int off, keylen; 321360784Sdim 322360784Sdim vap->iv_stats.is_crypto_wep++; 323360784Sdim 324317019Sdim /* NB: this assumes the header was pulled up */ 325317019Sdim memcpy(rc4key, mtod(m, uint8_t *) + hdrlen, IEEE80211_WEP_IVLEN); 326317019Sdim memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen); 327317019Sdim 328317019Sdim /* Setup RC4 state */ 329317019Sdim for (i = 0; i < 256; i++) 330317019Sdim S[i] = i; 331317019Sdim j = 0; 332317019Sdim keylen = key->wk_keylen + IEEE80211_WEP_IVLEN; 333317019Sdim for (i = 0; i < 256; i++) { 334317019Sdim j = (j + S[i] + rc4key[i % keylen]) & 0xff; 335317019Sdim S_SWAP(i, j); 336317019Sdim } 337317019Sdim 338317019Sdim off = hdrlen + wep.ic_header; 339317019Sdim data_len = m->m_pkthdr.len - off; 340317019Sdim 341317019Sdim /* Compute CRC32 over unencrypted data and apply RC4 to data */ 342327952Sdim crc = ~0; 343327952Sdim i = j = 0; 344317019Sdim pos = mtod(m, uint8_t *) + off; 345317019Sdim buflen = m->m_len - off; 346317019Sdim for (;;) { 347327952Sdim if (buflen > data_len) 348317019Sdim buflen = data_len; 349317019Sdim data_len -= buflen; 350317019Sdim for (k = 0; k < buflen; k++) { 351317019Sdim crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8); 352317019Sdim i = (i + 1) & 0xff; 353317019Sdim j = (j + S[i]) & 0xff; 354327952Sdim S_SWAP(i, j); 355317019Sdim *pos++ ^= S[(S[i] + S[j]) & 0xff]; 356327952Sdim } 357327952Sdim if (m->m_next == NULL) { 358317019Sdim if (data_len != 0) { /* out of data */ 359317019Sdim IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, 360317019Sdim ether_sprintf(mtod(m0, 361317019Sdim struct ieee80211_frame *)->i_addr2), 362327952Sdim "out of data for WEP (data_len %zu)", 363327952Sdim data_len); 364317019Sdim /* XXX stat */ 365317019Sdim return 0; 366317019Sdim } 367317019Sdim break; 368327952Sdim } 369327952Sdim m = m->m_next; 370317019Sdim pos = mtod(m, uint8_t *); 371317019Sdim buflen = m->m_len; 372317019Sdim } 373317019Sdim crc = ~crc; 374317019Sdim 375317019Sdim /* Append little-endian CRC32 and encrypt it to produce ICV */ 376317019Sdim icv[0] = crc; 377317019Sdim icv[1] = crc >> 8; 378317019Sdim icv[2] = crc >> 16; 379327952Sdim icv[3] = crc >> 24; 380317019Sdim for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) { 381317019Sdim i = (i + 1) & 0xff; 382317019Sdim j = (j + S[i]) & 0xff; 383317019Sdim S_SWAP(i, j); 384327952Sdim icv[k] ^= S[(S[i] + S[j]) & 0xff]; 385317019Sdim } 386317019Sdim return m_append(m0, IEEE80211_WEP_CRCLEN, icv); 387317019Sdim#undef S_SWAP 388317019Sdim} 389327952Sdim 390317019Sdimstatic int 391317019Sdimwep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen) 392317019Sdim{ 393317019Sdim#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0) 394327952Sdim struct wep_ctx *ctx = key->wk_private; 395317019Sdim struct ieee80211vap *vap = ctx->wc_vap; 396317019Sdim struct mbuf *m = m0; 397317019Sdim uint8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE]; 398317019Sdim uint8_t icv[IEEE80211_WEP_CRCLEN]; 399327952Sdim uint32_t i, j, k, crc; 400317019Sdim size_t buflen, data_len; 401317019Sdim uint8_t S[256]; 402317019Sdim uint8_t *pos; 403317019Sdim u_int off, keylen; 404327952Sdim 405317019Sdim vap->iv_stats.is_crypto_wep++; 406317019Sdim 407317019Sdim /* NB: this assumes the header was pulled up */ 408317019Sdim memcpy(rc4key, mtod(m, uint8_t *) + hdrlen, IEEE80211_WEP_IVLEN); 409317019Sdim memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen); 410317019Sdim 411317019Sdim /* Setup RC4 state */ 412317019Sdim for (i = 0; i < 256; i++) 413317019Sdim S[i] = i; 414317019Sdim j = 0; 415317019Sdim keylen = key->wk_keylen + IEEE80211_WEP_IVLEN; 416317019Sdim for (i = 0; i < 256; i++) { 417317019Sdim j = (j + S[i] + rc4key[i % keylen]) & 0xff; 418317019Sdim S_SWAP(i, j); 419317019Sdim } 420317019Sdim 421317019Sdim off = hdrlen + wep.ic_header; 422317019Sdim data_len = m->m_pkthdr.len - (off + wep.ic_trailer), 423317019Sdim 424317019Sdim /* Compute CRC32 over unencrypted data and apply RC4 to data */ 425317019Sdim crc = ~0; 426317019Sdim i = j = 0; 427317019Sdim pos = mtod(m, uint8_t *) + off; 428317019Sdim buflen = m->m_len - off; 429317019Sdim for (;;) { 430320572Sdim if (buflen > data_len) 431320572Sdim buflen = data_len; 432320572Sdim data_len -= buflen; 433320572Sdim for (k = 0; k < buflen; k++) { 434320572Sdim i = (i + 1) & 0xff; 435320572Sdim j = (j + S[i]) & 0xff; 436320970Sdim S_SWAP(i, j); 437320970Sdim *pos ^= S[(S[i] + S[j]) & 0xff]; 438320572Sdim crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8); 439317019Sdim pos++; 440344779Sdim } 441317019Sdim m = m->m_next; 442317019Sdim if (m == NULL) { 443327952Sdim if (data_len != 0) { /* out of data */ 444327952Sdim IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, 445317019Sdim mtod(m0, struct ieee80211_frame *)->i_addr2, 446317019Sdim "out of data for WEP (data_len %zu)", 447317019Sdim data_len); 448317019Sdim return 0; 449317019Sdim } 450317019Sdim break; 451317019Sdim } 452317019Sdim pos = mtod(m, uint8_t *); 453317019Sdim buflen = m->m_len; 454317019Sdim } 455317019Sdim crc = ~crc; 456317019Sdim 457317019Sdim /* Encrypt little-endian CRC32 and verify that it matches with 458317019Sdim * received ICV */ 459317019Sdim icv[0] = crc; 460317019Sdim icv[1] = crc >> 8; 461317019Sdim icv[2] = crc >> 16; 462317019Sdim icv[3] = crc >> 24; 463317019Sdim for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) { 464317019Sdim i = (i + 1) & 0xff; 465317019Sdim j = (j + S[i]) & 0xff; 466317019Sdim S_SWAP(i, j); 467317019Sdim /* XXX assumes ICV is contiguous in mbuf */ 468317019Sdim if ((icv[k] ^ S[(S[i] + S[j]) & 0xff]) != *pos++) { 469317019Sdim /* ICV mismatch - drop frame */ 470317019Sdim return 0; 471317019Sdim } 472317019Sdim } 473317019Sdim return 1; 474317019Sdim#undef S_SWAP 475317019Sdim} 476317019Sdim 477317019Sdim/* 478317019Sdim * Module glue. 479317019Sdim */ 480317019SdimIEEE80211_CRYPTO_MODULE(wep, 1); 481317019Sdim