1 /* $OpenBSD: ieee80211_crypto_wep.c,v 1.10 2014/07/12 18:44:22 tedu 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 37 #ifdef INET 38 #include <netinet/in.h> 39 #include <netinet/if_ether.h> 40 #endif 41 42 #include <net80211/ieee80211_var.h> 43 #include <net80211/ieee80211_crypto.h> 44 45 #include <dev/rndvar.h> 46 #include <crypto/arc4.h> 47 48 /* WEP software crypto context */ 49 struct ieee80211_wep_ctx { 50 struct rc4_ctx rc4; 51 u_int32_t iv; 52 }; 53 54 /* 55 * Initialize software crypto context. This function can be overridden 56 * by drivers doing hardware crypto. 57 */ 58 int 59 ieee80211_wep_set_key(struct ieee80211com *ic, struct ieee80211_key *k) 60 { 61 struct ieee80211_wep_ctx *ctx; 62 63 ctx = malloc(sizeof(*ctx), M_DEVBUF, M_NOWAIT | M_ZERO); 64 if (ctx == NULL) 65 return ENOMEM; 66 k->k_priv = ctx; 67 return 0; 68 } 69 70 void 71 ieee80211_wep_delete_key(struct ieee80211com *ic, struct ieee80211_key *k) 72 { 73 if (k->k_priv != NULL) 74 free(k->k_priv, M_DEVBUF, 0); 75 k->k_priv = NULL; 76 } 77 78 /* shortcut */ 79 #define IEEE80211_WEP_HDRLEN \ 80 (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN) 81 82 struct mbuf * 83 ieee80211_wep_encrypt(struct ieee80211com *ic, struct mbuf *m0, 84 struct ieee80211_key *k) 85 { 86 struct ieee80211_wep_ctx *ctx = k->k_priv; 87 u_int8_t wepseed[16]; 88 const struct ieee80211_frame *wh; 89 struct mbuf *n0, *m, *n; 90 u_int8_t *ivp, *icvp; 91 u_int32_t iv, crc; 92 int left, moff, noff, len, hdrlen; 93 94 MGET(n0, M_DONTWAIT, m0->m_type); 95 if (n0 == NULL) 96 goto nospace; 97 if (m_dup_pkthdr(n0, m0, M_DONTWAIT)) 98 goto nospace; 99 n0->m_pkthdr.len += IEEE80211_WEP_HDRLEN; 100 n0->m_len = MHLEN; 101 if (n0->m_pkthdr.len >= MINCLSIZE - IEEE80211_WEP_CRCLEN) { 102 MCLGET(n0, M_DONTWAIT); 103 if (n0->m_flags & M_EXT) 104 n0->m_len = n0->m_ext.ext_size; 105 } 106 if (n0->m_len > n0->m_pkthdr.len) 107 n0->m_len = n0->m_pkthdr.len; 108 109 /* copy 802.11 header */ 110 wh = mtod(m0, struct ieee80211_frame *); 111 hdrlen = ieee80211_get_hdrlen(wh); 112 memcpy(mtod(n0, caddr_t), wh, hdrlen); 113 114 /* select a new IV for every MPDU */ 115 iv = (ctx->iv != 0) ? ctx->iv : arc4random(); 116 /* skip weak IVs from Fluhrer/Mantin/Shamir */ 117 if (iv >= 0x03ff00 && (iv & 0xf8ff00) == 0x00ff00) 118 iv += 0x000100; 119 ctx->iv = iv + 1; 120 ivp = mtod(n0, u_int8_t *) + hdrlen; 121 ivp[0] = iv; 122 ivp[1] = iv >> 8; 123 ivp[2] = iv >> 16; 124 ivp[3] = k->k_id << 6; 125 126 /* compute WEP seed: concatenate IV and WEP Key */ 127 memcpy(wepseed, ivp, IEEE80211_WEP_IVLEN); 128 memcpy(wepseed + IEEE80211_WEP_IVLEN, k->k_key, k->k_len); 129 rc4_keysetup(&ctx->rc4, wepseed, IEEE80211_WEP_IVLEN + k->k_len); 130 131 /* encrypt frame body and compute WEP ICV */ 132 m = m0; 133 n = n0; 134 moff = hdrlen; 135 noff = hdrlen + IEEE80211_WEP_HDRLEN; 136 left = m0->m_pkthdr.len - moff; 137 crc = ~0; 138 while (left > 0) { 139 if (moff == m->m_len) { 140 /* nothing left to copy from m */ 141 m = m->m_next; 142 moff = 0; 143 } 144 if (noff == n->m_len) { 145 /* n is full and there's more data to copy */ 146 MGET(n->m_next, M_DONTWAIT, n->m_type); 147 if (n->m_next == NULL) 148 goto nospace; 149 n = n->m_next; 150 n->m_len = MLEN; 151 if (left >= MINCLSIZE - IEEE80211_WEP_CRCLEN) { 152 MCLGET(n, M_DONTWAIT); 153 if (n->m_flags & M_EXT) 154 n->m_len = n->m_ext.ext_size; 155 } 156 if (n->m_len > left) 157 n->m_len = left; 158 noff = 0; 159 } 160 len = min(m->m_len - moff, n->m_len - noff); 161 162 crc = ether_crc32_le_update(crc, mtod(m, caddr_t) + moff, len); 163 rc4_crypt(&ctx->rc4, mtod(m, caddr_t) + moff, 164 mtod(n, caddr_t) + noff, len); 165 166 moff += len; 167 noff += len; 168 left -= len; 169 } 170 171 /* reserve trailing space for WEP ICV */ 172 if (M_TRAILINGSPACE(n) < IEEE80211_WEP_CRCLEN) { 173 MGET(n->m_next, M_DONTWAIT, n->m_type); 174 if (n->m_next == NULL) 175 goto nospace; 176 n = n->m_next; 177 n->m_len = 0; 178 } 179 180 /* finalize WEP ICV */ 181 icvp = mtod(n, caddr_t) + n->m_len; 182 crc = ~crc; 183 icvp[0] = crc; 184 icvp[1] = crc >> 8; 185 icvp[2] = crc >> 16; 186 icvp[3] = crc >> 24; 187 rc4_crypt(&ctx->rc4, icvp, icvp, IEEE80211_WEP_CRCLEN); 188 n->m_len += IEEE80211_WEP_CRCLEN; 189 n0->m_pkthdr.len += IEEE80211_WEP_CRCLEN; 190 191 m_freem(m0); 192 return n0; 193 nospace: 194 ic->ic_stats.is_tx_nombuf++; 195 m_freem(m0); 196 if (n0 != NULL) 197 m_freem(n0); 198 return NULL; 199 } 200 201 struct mbuf * 202 ieee80211_wep_decrypt(struct ieee80211com *ic, struct mbuf *m0, 203 struct ieee80211_key *k) 204 { 205 struct ieee80211_wep_ctx *ctx = k->k_priv; 206 struct ieee80211_frame *wh; 207 u_int8_t wepseed[16]; 208 u_int32_t crc, crc0; 209 u_int8_t *ivp; 210 struct mbuf *n0, *m, *n; 211 int hdrlen, left, moff, noff, len; 212 213 wh = mtod(m0, struct ieee80211_frame *); 214 hdrlen = ieee80211_get_hdrlen(wh); 215 216 if (m0->m_pkthdr.len < hdrlen + IEEE80211_WEP_TOTLEN) { 217 m_freem(m0); 218 return NULL; 219 } 220 221 /* concatenate IV and WEP Key */ 222 ivp = (u_int8_t *)wh + hdrlen; 223 memcpy(wepseed, ivp, IEEE80211_WEP_IVLEN); 224 memcpy(wepseed + IEEE80211_WEP_IVLEN, k->k_key, k->k_len); 225 rc4_keysetup(&ctx->rc4, wepseed, IEEE80211_WEP_IVLEN + k->k_len); 226 227 MGET(n0, M_DONTWAIT, m0->m_type); 228 if (n0 == NULL) 229 goto nospace; 230 if (m_dup_pkthdr(n0, m0, M_DONTWAIT)) 231 goto nospace; 232 n0->m_pkthdr.len -= IEEE80211_WEP_TOTLEN; 233 n0->m_len = MHLEN; 234 if (n0->m_pkthdr.len >= MINCLSIZE) { 235 MCLGET(n0, M_DONTWAIT); 236 if (n0->m_flags & M_EXT) 237 n0->m_len = n0->m_ext.ext_size; 238 } 239 if (n0->m_len > n0->m_pkthdr.len) 240 n0->m_len = n0->m_pkthdr.len; 241 242 /* copy 802.11 header and clear protected bit */ 243 memcpy(mtod(n0, caddr_t), wh, hdrlen); 244 wh = mtod(n0, struct ieee80211_frame *); 245 wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; 246 247 /* decrypt frame body and compute WEP ICV */ 248 m = m0; 249 n = n0; 250 moff = hdrlen + IEEE80211_WEP_HDRLEN; 251 noff = hdrlen; 252 left = n0->m_pkthdr.len - noff; 253 crc = ~0; 254 while (left > 0) { 255 if (moff == m->m_len) { 256 /* nothing left to copy from m */ 257 m = m->m_next; 258 moff = 0; 259 } 260 if (noff == n->m_len) { 261 /* n is full and there's more data to copy */ 262 MGET(n->m_next, M_DONTWAIT, n->m_type); 263 if (n->m_next == NULL) 264 goto nospace; 265 n = n->m_next; 266 n->m_len = MLEN; 267 if (left >= MINCLSIZE) { 268 MCLGET(n, M_DONTWAIT); 269 if (n->m_flags & M_EXT) 270 n->m_len = n->m_ext.ext_size; 271 } 272 if (n->m_len > left) 273 n->m_len = left; 274 noff = 0; 275 } 276 len = min(m->m_len - moff, n->m_len - noff); 277 278 rc4_crypt(&ctx->rc4, mtod(m, caddr_t) + moff, 279 mtod(n, caddr_t) + noff, len); 280 crc = ether_crc32_le_update(crc, mtod(n, caddr_t) + noff, len); 281 282 moff += len; 283 noff += len; 284 left -= len; 285 } 286 287 /* decrypt ICV and compare it with calculated ICV */ 288 m_copydata(m, moff, IEEE80211_WEP_CRCLEN, (caddr_t)&crc0); 289 rc4_crypt(&ctx->rc4, (caddr_t)&crc0, (caddr_t)&crc0, 290 IEEE80211_WEP_CRCLEN); 291 crc = ~crc; 292 if (crc != letoh32(crc0)) { 293 ic->ic_stats.is_rx_decryptcrc++; 294 m_freem(m0); 295 m_freem(n0); 296 return NULL; 297 } 298 299 m_freem(m0); 300 return n0; 301 nospace: 302 ic->ic_stats.is_rx_nombuf++; 303 m_freem(m0); 304 if (n0 != NULL) 305 m_freem(n0); 306 return NULL; 307 } 308