1 /* $OpenBSD: ieee80211_crypto_wep.c,v 1.8 2011/04/05 11:48:28 blambert Exp $ */ 2 3 /*- 4 * Copyright (c) 2008 Damien Bergamini <damien.bergamini@free.fr> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* 20 * This code implements Wired Equivalent Privacy (WEP) defined in 21 * IEEE Std 802.11-2007 section 8.2.1. 22 */ 23 24 #include <sys/param.h> 25 #include <sys/systm.h> 26 #include <sys/mbuf.h> 27 #include <sys/malloc.h> 28 #include <sys/kernel.h> 29 #include <sys/socket.h> 30 #include <sys/endian.h> 31 32 #include <net/if.h> 33 #include <net/if_dl.h> 34 #include <net/if_media.h> 35 #include <net/if_arp.h> 36 #include <net/if_llc.h> 37 38 #ifdef INET 39 #include <netinet/in.h> 40 #include <netinet/if_ether.h> 41 #endif 42 43 #include <net80211/ieee80211_var.h> 44 #include <net80211/ieee80211_crypto.h> 45 46 #include <dev/rndvar.h> 47 #include <crypto/arc4.h> 48 49 /* WEP software crypto context */ 50 struct ieee80211_wep_ctx { 51 struct rc4_ctx rc4; 52 u_int32_t iv; 53 }; 54 55 /* 56 * Initialize software crypto context. This function can be overridden 57 * by drivers doing hardware crypto. 58 */ 59 int 60 ieee80211_wep_set_key(struct ieee80211com *ic, struct ieee80211_key *k) 61 { 62 struct ieee80211_wep_ctx *ctx; 63 64 ctx = malloc(sizeof(*ctx), M_DEVBUF, M_NOWAIT | M_ZERO); 65 if (ctx == NULL) 66 return ENOMEM; 67 k->k_priv = ctx; 68 return 0; 69 } 70 71 void 72 ieee80211_wep_delete_key(struct ieee80211com *ic, struct ieee80211_key *k) 73 { 74 if (k->k_priv != NULL) 75 free(k->k_priv, M_DEVBUF); 76 k->k_priv = NULL; 77 } 78 79 /* shortcut */ 80 #define IEEE80211_WEP_HDRLEN \ 81 (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN) 82 83 struct mbuf * 84 ieee80211_wep_encrypt(struct ieee80211com *ic, struct mbuf *m0, 85 struct ieee80211_key *k) 86 { 87 struct ieee80211_wep_ctx *ctx = k->k_priv; 88 u_int8_t wepseed[16]; 89 const struct ieee80211_frame *wh; 90 struct mbuf *n0, *m, *n; 91 u_int8_t *ivp, *icvp; 92 u_int32_t iv, crc; 93 int left, moff, noff, len, hdrlen; 94 95 MGET(n0, M_DONTWAIT, m0->m_type); 96 if (n0 == NULL) 97 goto nospace; 98 if (m_dup_pkthdr(n0, m0, M_DONTWAIT)) 99 goto nospace; 100 n0->m_pkthdr.len += IEEE80211_WEP_HDRLEN; 101 n0->m_len = MHLEN; 102 if (n0->m_pkthdr.len >= MINCLSIZE - IEEE80211_WEP_CRCLEN) { 103 MCLGET(n0, M_DONTWAIT); 104 if (n0->m_flags & M_EXT) 105 n0->m_len = n0->m_ext.ext_size; 106 } 107 if (n0->m_len > n0->m_pkthdr.len) 108 n0->m_len = n0->m_pkthdr.len; 109 110 /* copy 802.11 header */ 111 wh = mtod(m0, struct ieee80211_frame *); 112 hdrlen = ieee80211_get_hdrlen(wh); 113 memcpy(mtod(n0, caddr_t), wh, hdrlen); 114 115 /* select a new IV for every MPDU */ 116 iv = (ctx->iv != 0) ? ctx->iv : arc4random(); 117 /* skip weak IVs from Fluhrer/Mantin/Shamir */ 118 if (iv >= 0x03ff00 && (iv & 0xf8ff00) == 0x00ff00) 119 iv += 0x000100; 120 ctx->iv = iv + 1; 121 ivp = mtod(n0, u_int8_t *) + hdrlen; 122 ivp[0] = iv; 123 ivp[1] = iv >> 8; 124 ivp[2] = iv >> 16; 125 ivp[3] = k->k_id << 6; 126 127 /* compute WEP seed: concatenate IV and WEP Key */ 128 memcpy(wepseed, ivp, IEEE80211_WEP_IVLEN); 129 memcpy(wepseed + IEEE80211_WEP_IVLEN, k->k_key, k->k_len); 130 rc4_keysetup(&ctx->rc4, wepseed, IEEE80211_WEP_IVLEN + k->k_len); 131 132 /* encrypt frame body and compute WEP ICV */ 133 m = m0; 134 n = n0; 135 moff = hdrlen; 136 noff = hdrlen + IEEE80211_WEP_HDRLEN; 137 left = m0->m_pkthdr.len - moff; 138 crc = ~0; 139 while (left > 0) { 140 if (moff == m->m_len) { 141 /* nothing left to copy from m */ 142 m = m->m_next; 143 moff = 0; 144 } 145 if (noff == n->m_len) { 146 /* n is full and there's more data to copy */ 147 MGET(n->m_next, M_DONTWAIT, n->m_type); 148 if (n->m_next == NULL) 149 goto nospace; 150 n = n->m_next; 151 n->m_len = MLEN; 152 if (left >= MINCLSIZE - IEEE80211_WEP_CRCLEN) { 153 MCLGET(n, M_DONTWAIT); 154 if (n->m_flags & M_EXT) 155 n->m_len = n->m_ext.ext_size; 156 } 157 if (n->m_len > left) 158 n->m_len = left; 159 noff = 0; 160 } 161 len = min(m->m_len - moff, n->m_len - noff); 162 163 crc = ether_crc32_le_update(crc, mtod(m, caddr_t) + moff, len); 164 rc4_crypt(&ctx->rc4, mtod(m, caddr_t) + moff, 165 mtod(n, caddr_t) + noff, len); 166 167 moff += len; 168 noff += len; 169 left -= len; 170 } 171 172 /* reserve trailing space for WEP ICV */ 173 if (M_TRAILINGSPACE(n) < IEEE80211_WEP_CRCLEN) { 174 MGET(n->m_next, M_DONTWAIT, n->m_type); 175 if (n->m_next == NULL) 176 goto nospace; 177 n = n->m_next; 178 n->m_len = 0; 179 } 180 181 /* finalize WEP ICV */ 182 icvp = mtod(n, caddr_t) + n->m_len; 183 crc = ~crc; 184 icvp[0] = crc; 185 icvp[1] = crc >> 8; 186 icvp[2] = crc >> 16; 187 icvp[3] = crc >> 24; 188 rc4_crypt(&ctx->rc4, icvp, icvp, IEEE80211_WEP_CRCLEN); 189 n->m_len += IEEE80211_WEP_CRCLEN; 190 n0->m_pkthdr.len += IEEE80211_WEP_CRCLEN; 191 192 m_freem(m0); 193 return n0; 194 nospace: 195 ic->ic_stats.is_tx_nombuf++; 196 m_freem(m0); 197 if (n0 != NULL) 198 m_freem(n0); 199 return NULL; 200 } 201 202 struct mbuf * 203 ieee80211_wep_decrypt(struct ieee80211com *ic, struct mbuf *m0, 204 struct ieee80211_key *k) 205 { 206 struct ieee80211_wep_ctx *ctx = k->k_priv; 207 struct ieee80211_frame *wh; 208 u_int8_t wepseed[16]; 209 u_int32_t crc, crc0; 210 u_int8_t *ivp; 211 struct mbuf *n0, *m, *n; 212 int hdrlen, left, moff, noff, len; 213 214 wh = mtod(m0, struct ieee80211_frame *); 215 hdrlen = ieee80211_get_hdrlen(wh); 216 217 if (m0->m_pkthdr.len < hdrlen + IEEE80211_WEP_TOTLEN) { 218 m_freem(m0); 219 return NULL; 220 } 221 222 /* concatenate IV and WEP Key */ 223 ivp = (u_int8_t *)wh + hdrlen; 224 memcpy(wepseed, ivp, IEEE80211_WEP_IVLEN); 225 memcpy(wepseed + IEEE80211_WEP_IVLEN, k->k_key, k->k_len); 226 rc4_keysetup(&ctx->rc4, wepseed, IEEE80211_WEP_IVLEN + k->k_len); 227 228 MGET(n0, M_DONTWAIT, m0->m_type); 229 if (n0 == NULL) 230 goto nospace; 231 if (m_dup_pkthdr(n0, m0, M_DONTWAIT)) 232 goto nospace; 233 n0->m_pkthdr.len -= IEEE80211_WEP_TOTLEN; 234 n0->m_len = MHLEN; 235 if (n0->m_pkthdr.len >= MINCLSIZE) { 236 MCLGET(n0, M_DONTWAIT); 237 if (n0->m_flags & M_EXT) 238 n0->m_len = n0->m_ext.ext_size; 239 } 240 if (n0->m_len > n0->m_pkthdr.len) 241 n0->m_len = n0->m_pkthdr.len; 242 243 /* copy 802.11 header and clear protected bit */ 244 memcpy(mtod(n0, caddr_t), wh, hdrlen); 245 wh = mtod(n0, struct ieee80211_frame *); 246 wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; 247 248 /* decrypt frame body and compute WEP ICV */ 249 m = m0; 250 n = n0; 251 moff = hdrlen + IEEE80211_WEP_HDRLEN; 252 noff = hdrlen; 253 left = n0->m_pkthdr.len - noff; 254 crc = ~0; 255 while (left > 0) { 256 if (moff == m->m_len) { 257 /* nothing left to copy from m */ 258 m = m->m_next; 259 moff = 0; 260 } 261 if (noff == n->m_len) { 262 /* n is full and there's more data to copy */ 263 MGET(n->m_next, M_DONTWAIT, n->m_type); 264 if (n->m_next == NULL) 265 goto nospace; 266 n = n->m_next; 267 n->m_len = MLEN; 268 if (left >= MINCLSIZE) { 269 MCLGET(n, M_DONTWAIT); 270 if (n->m_flags & M_EXT) 271 n->m_len = n->m_ext.ext_size; 272 } 273 if (n->m_len > left) 274 n->m_len = left; 275 noff = 0; 276 } 277 len = min(m->m_len - moff, n->m_len - noff); 278 279 rc4_crypt(&ctx->rc4, mtod(m, caddr_t) + moff, 280 mtod(n, caddr_t) + noff, len); 281 crc = ether_crc32_le_update(crc, mtod(n, caddr_t) + noff, len); 282 283 moff += len; 284 noff += len; 285 left -= len; 286 } 287 288 /* decrypt ICV and compare it with calculated ICV */ 289 m_copydata(m, moff, IEEE80211_WEP_CRCLEN, (caddr_t)&crc0); 290 rc4_crypt(&ctx->rc4, (caddr_t)&crc0, (caddr_t)&crc0, 291 IEEE80211_WEP_CRCLEN); 292 crc = ~crc; 293 if (crc != letoh32(crc0)) { 294 ic->ic_stats.is_rx_decryptcrc++; 295 m_freem(m0); 296 m_freem(n0); 297 return NULL; 298 } 299 300 m_freem(m0); 301 return n0; 302 nospace: 303 ic->ic_stats.is_rx_nombuf++; 304 m_freem(m0); 305 if (n0 != NULL) 306 m_freem(n0); 307 return NULL; 308 } 309