| 1 | // SPDX-License-Identifier: GPL-2.0-only | 
|---|
| 2 | /* | 
|---|
| 3 | * Software WEP encryption implementation | 
|---|
| 4 | * Copyright 2002, Jouni Malinen <jkmaline@cc.hut.fi> | 
|---|
| 5 | * Copyright 2003, Instant802 Networks, Inc. | 
|---|
| 6 | * Copyright (C) 2023 Intel Corporation | 
|---|
| 7 | */ | 
|---|
| 8 |  | 
|---|
| 9 | #include <linux/netdevice.h> | 
|---|
| 10 | #include <linux/types.h> | 
|---|
| 11 | #include <linux/random.h> | 
|---|
| 12 | #include <linux/compiler.h> | 
|---|
| 13 | #include <linux/crc32.h> | 
|---|
| 14 | #include <linux/crypto.h> | 
|---|
| 15 | #include <linux/err.h> | 
|---|
| 16 | #include <linux/mm.h> | 
|---|
| 17 | #include <linux/scatterlist.h> | 
|---|
| 18 | #include <linux/slab.h> | 
|---|
| 19 | #include <linux/unaligned.h> | 
|---|
| 20 |  | 
|---|
| 21 | #include <net/mac80211.h> | 
|---|
| 22 | #include "ieee80211_i.h" | 
|---|
| 23 | #include "wep.h" | 
|---|
| 24 |  | 
|---|
| 25 |  | 
|---|
| 26 | void ieee80211_wep_init(struct ieee80211_local *local) | 
|---|
| 27 | { | 
|---|
| 28 | /* start WEP IV from a random value */ | 
|---|
| 29 | get_random_bytes(buf: &local->wep_iv, IEEE80211_WEP_IV_LEN); | 
|---|
| 30 | } | 
|---|
| 31 |  | 
|---|
| 32 | static inline bool ieee80211_wep_weak_iv(u32 iv, int keylen) | 
|---|
| 33 | { | 
|---|
| 34 | /* | 
|---|
| 35 | * Fluhrer, Mantin, and Shamir have reported weaknesses in the | 
|---|
| 36 | * key scheduling algorithm of RC4. At least IVs (KeyByte + 3, | 
|---|
| 37 | * 0xff, N) can be used to speedup attacks, so avoid using them. | 
|---|
| 38 | */ | 
|---|
| 39 | if ((iv & 0xff00) == 0xff00) { | 
|---|
| 40 | u8 B = (iv >> 16) & 0xff; | 
|---|
| 41 | if (B >= 3 && B < 3 + keylen) | 
|---|
| 42 | return true; | 
|---|
| 43 | } | 
|---|
| 44 | return false; | 
|---|
| 45 | } | 
|---|
| 46 |  | 
|---|
| 47 |  | 
|---|
| 48 | static void ieee80211_wep_get_iv(struct ieee80211_local *local, | 
|---|
| 49 | int keylen, int keyidx, u8 *iv) | 
|---|
| 50 | { | 
|---|
| 51 | local->wep_iv++; | 
|---|
| 52 | if (ieee80211_wep_weak_iv(iv: local->wep_iv, keylen)) | 
|---|
| 53 | local->wep_iv += 0x0100; | 
|---|
| 54 |  | 
|---|
| 55 | if (!iv) | 
|---|
| 56 | return; | 
|---|
| 57 |  | 
|---|
| 58 | *iv++ = (local->wep_iv >> 16) & 0xff; | 
|---|
| 59 | *iv++ = (local->wep_iv >> 8) & 0xff; | 
|---|
| 60 | *iv++ = local->wep_iv & 0xff; | 
|---|
| 61 | *iv++ = keyidx << 6; | 
|---|
| 62 | } | 
|---|
| 63 |  | 
|---|
| 64 |  | 
|---|
| 65 | static u8 *ieee80211_wep_add_iv(struct ieee80211_local *local, | 
|---|
| 66 | struct sk_buff *skb, | 
|---|
| 67 | int keylen, int keyidx) | 
|---|
| 68 | { | 
|---|
| 69 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; | 
|---|
| 70 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | 
|---|
| 71 | unsigned int hdrlen; | 
|---|
| 72 | u8 *newhdr; | 
|---|
| 73 |  | 
|---|
| 74 | hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); | 
|---|
| 75 |  | 
|---|
| 76 | if (WARN_ON(skb_headroom(skb) < IEEE80211_WEP_IV_LEN)) | 
|---|
| 77 | return NULL; | 
|---|
| 78 |  | 
|---|
| 79 | hdrlen = ieee80211_hdrlen(fc: hdr->frame_control); | 
|---|
| 80 | newhdr = skb_push(skb, IEEE80211_WEP_IV_LEN); | 
|---|
| 81 | memmove(dest: newhdr, src: newhdr + IEEE80211_WEP_IV_LEN, count: hdrlen); | 
|---|
| 82 |  | 
|---|
| 83 | /* the HW only needs room for the IV, but not the actual IV */ | 
|---|
| 84 | if (info->control.hw_key && | 
|---|
| 85 | (info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) | 
|---|
| 86 | return newhdr + hdrlen; | 
|---|
| 87 |  | 
|---|
| 88 | ieee80211_wep_get_iv(local, keylen, keyidx, iv: newhdr + hdrlen); | 
|---|
| 89 | return newhdr + hdrlen; | 
|---|
| 90 | } | 
|---|
| 91 |  | 
|---|
| 92 |  | 
|---|
| 93 | static void ieee80211_wep_remove_iv(struct ieee80211_local *local, | 
|---|
| 94 | struct sk_buff *skb, | 
|---|
| 95 | struct ieee80211_key *key) | 
|---|
| 96 | { | 
|---|
| 97 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; | 
|---|
| 98 | unsigned int hdrlen; | 
|---|
| 99 |  | 
|---|
| 100 | hdrlen = ieee80211_hdrlen(fc: hdr->frame_control); | 
|---|
| 101 | memmove(dest: skb->data + IEEE80211_WEP_IV_LEN, src: skb->data, count: hdrlen); | 
|---|
| 102 | skb_pull(skb, IEEE80211_WEP_IV_LEN); | 
|---|
| 103 | } | 
|---|
| 104 |  | 
|---|
| 105 |  | 
|---|
| 106 | /* Perform WEP encryption using given key. data buffer must have tailroom | 
|---|
| 107 | * for 4-byte ICV. data_len must not include this ICV. Note: this function | 
|---|
| 108 | * does _not_ add IV. data = RC4(data | CRC32(data)) */ | 
|---|
| 109 | int ieee80211_wep_encrypt_data(struct arc4_ctx *ctx, u8 *rc4key, | 
|---|
| 110 | size_t klen, u8 *data, size_t data_len) | 
|---|
| 111 | { | 
|---|
| 112 | __le32 icv; | 
|---|
| 113 |  | 
|---|
| 114 | icv = cpu_to_le32(~crc32_le(~0, data, data_len)); | 
|---|
| 115 | put_unaligned(icv, (__le32 *)(data + data_len)); | 
|---|
| 116 |  | 
|---|
| 117 | arc4_setkey(ctx, in_key: rc4key, key_len: klen); | 
|---|
| 118 | arc4_crypt(ctx, out: data, in: data, len: data_len + IEEE80211_WEP_ICV_LEN); | 
|---|
| 119 | memzero_explicit(s: ctx, count: sizeof(*ctx)); | 
|---|
| 120 |  | 
|---|
| 121 | return 0; | 
|---|
| 122 | } | 
|---|
| 123 |  | 
|---|
| 124 |  | 
|---|
| 125 | /* Perform WEP encryption on given skb. 4 bytes of extra space (IV) in the | 
|---|
| 126 | * beginning of the buffer 4 bytes of extra space (ICV) in the end of the | 
|---|
| 127 | * buffer will be added. Both IV and ICV will be transmitted, so the | 
|---|
| 128 | * payload length increases with 8 bytes. | 
|---|
| 129 | * | 
|---|
| 130 | * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data)) | 
|---|
| 131 | */ | 
|---|
| 132 | int ieee80211_wep_encrypt(struct ieee80211_local *local, | 
|---|
| 133 | struct sk_buff *skb, | 
|---|
| 134 | const u8 *key, int keylen, int keyidx) | 
|---|
| 135 | { | 
|---|
| 136 | u8 *iv; | 
|---|
| 137 | size_t len; | 
|---|
| 138 | u8 rc4key[3 + WLAN_KEY_LEN_WEP104]; | 
|---|
| 139 |  | 
|---|
| 140 | if (WARN_ON(skb_tailroom(skb) < IEEE80211_WEP_ICV_LEN)) | 
|---|
| 141 | return -1; | 
|---|
| 142 |  | 
|---|
| 143 | iv = ieee80211_wep_add_iv(local, skb, keylen, keyidx); | 
|---|
| 144 | if (!iv) | 
|---|
| 145 | return -1; | 
|---|
| 146 |  | 
|---|
| 147 | len = skb->len - (iv + IEEE80211_WEP_IV_LEN - skb->data); | 
|---|
| 148 |  | 
|---|
| 149 | /* Prepend 24-bit IV to RC4 key */ | 
|---|
| 150 | memcpy(to: rc4key, from: iv, len: 3); | 
|---|
| 151 |  | 
|---|
| 152 | /* Copy rest of the WEP key (the secret part) */ | 
|---|
| 153 | memcpy(to: rc4key + 3, from: key, len: keylen); | 
|---|
| 154 |  | 
|---|
| 155 | /* Add room for ICV */ | 
|---|
| 156 | skb_put(skb, IEEE80211_WEP_ICV_LEN); | 
|---|
| 157 |  | 
|---|
| 158 | return ieee80211_wep_encrypt_data(ctx: &local->wep_tx_ctx, rc4key, klen: keylen + 3, | 
|---|
| 159 | data: iv + IEEE80211_WEP_IV_LEN, data_len: len); | 
|---|
| 160 | } | 
|---|
| 161 |  | 
|---|
| 162 |  | 
|---|
| 163 | /* Perform WEP decryption using given key. data buffer includes encrypted | 
|---|
| 164 | * payload, including 4-byte ICV, but _not_ IV. data_len must not include ICV. | 
|---|
| 165 | * Return 0 on success and -1 on ICV mismatch. */ | 
|---|
| 166 | int ieee80211_wep_decrypt_data(struct arc4_ctx *ctx, u8 *rc4key, | 
|---|
| 167 | size_t klen, u8 *data, size_t data_len) | 
|---|
| 168 | { | 
|---|
| 169 | __le32 crc; | 
|---|
| 170 |  | 
|---|
| 171 | arc4_setkey(ctx, in_key: rc4key, key_len: klen); | 
|---|
| 172 | arc4_crypt(ctx, out: data, in: data, len: data_len + IEEE80211_WEP_ICV_LEN); | 
|---|
| 173 | memzero_explicit(s: ctx, count: sizeof(*ctx)); | 
|---|
| 174 |  | 
|---|
| 175 | crc = cpu_to_le32(~crc32_le(~0, data, data_len)); | 
|---|
| 176 | if (memcmp(&crc, data + data_len, IEEE80211_WEP_ICV_LEN) != 0) | 
|---|
| 177 | /* ICV mismatch */ | 
|---|
| 178 | return -1; | 
|---|
| 179 |  | 
|---|
| 180 | return 0; | 
|---|
| 181 | } | 
|---|
| 182 |  | 
|---|
| 183 |  | 
|---|
| 184 | /* Perform WEP decryption on given skb. Buffer includes whole WEP part of | 
|---|
| 185 | * the frame: IV (4 bytes), encrypted payload (including SNAP header), | 
|---|
| 186 | * ICV (4 bytes). skb->len includes both IV and ICV. | 
|---|
| 187 | * | 
|---|
| 188 | * Returns 0 if frame was decrypted successfully and ICV was correct and -1 on | 
|---|
| 189 | * failure. If frame is OK, IV and ICV will be removed, i.e., decrypted payload | 
|---|
| 190 | * is moved to the beginning of the skb and skb length will be reduced. | 
|---|
| 191 | */ | 
|---|
| 192 | static int ieee80211_wep_decrypt(struct ieee80211_local *local, | 
|---|
| 193 | struct sk_buff *skb, | 
|---|
| 194 | struct ieee80211_key *key) | 
|---|
| 195 | { | 
|---|
| 196 | u32 klen; | 
|---|
| 197 | u8 rc4key[3 + WLAN_KEY_LEN_WEP104]; | 
|---|
| 198 | u8 keyidx; | 
|---|
| 199 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; | 
|---|
| 200 | unsigned int hdrlen; | 
|---|
| 201 | size_t len; | 
|---|
| 202 | int ret = 0; | 
|---|
| 203 |  | 
|---|
| 204 | if (!ieee80211_has_protected(fc: hdr->frame_control)) | 
|---|
| 205 | return -1; | 
|---|
| 206 |  | 
|---|
| 207 | hdrlen = ieee80211_hdrlen(fc: hdr->frame_control); | 
|---|
| 208 | if (skb->len < hdrlen + IEEE80211_WEP_IV_LEN + IEEE80211_WEP_ICV_LEN) | 
|---|
| 209 | return -1; | 
|---|
| 210 |  | 
|---|
| 211 | len = skb->len - hdrlen - IEEE80211_WEP_IV_LEN - IEEE80211_WEP_ICV_LEN; | 
|---|
| 212 |  | 
|---|
| 213 | keyidx = skb->data[hdrlen + 3] >> 6; | 
|---|
| 214 |  | 
|---|
| 215 | if (!key || keyidx != key->conf.keyidx) | 
|---|
| 216 | return -1; | 
|---|
| 217 |  | 
|---|
| 218 | klen = 3 + key->conf.keylen; | 
|---|
| 219 |  | 
|---|
| 220 | /* Prepend 24-bit IV to RC4 key */ | 
|---|
| 221 | memcpy(to: rc4key, from: skb->data + hdrlen, len: 3); | 
|---|
| 222 |  | 
|---|
| 223 | /* Copy rest of the WEP key (the secret part) */ | 
|---|
| 224 | memcpy(to: rc4key + 3, from: key->conf.key, len: key->conf.keylen); | 
|---|
| 225 |  | 
|---|
| 226 | if (ieee80211_wep_decrypt_data(ctx: &local->wep_rx_ctx, rc4key, klen, | 
|---|
| 227 | data: skb->data + hdrlen + | 
|---|
| 228 | IEEE80211_WEP_IV_LEN, data_len: len)) | 
|---|
| 229 | ret = -1; | 
|---|
| 230 |  | 
|---|
| 231 | /* Trim ICV */ | 
|---|
| 232 | skb_trim(skb, len: skb->len - IEEE80211_WEP_ICV_LEN); | 
|---|
| 233 |  | 
|---|
| 234 | /* Remove IV */ | 
|---|
| 235 | memmove(dest: skb->data + IEEE80211_WEP_IV_LEN, src: skb->data, count: hdrlen); | 
|---|
| 236 | skb_pull(skb, IEEE80211_WEP_IV_LEN); | 
|---|
| 237 |  | 
|---|
| 238 | return ret; | 
|---|
| 239 | } | 
|---|
| 240 |  | 
|---|
| 241 | ieee80211_rx_result | 
|---|
| 242 | ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx) | 
|---|
| 243 | { | 
|---|
| 244 | struct sk_buff *skb = rx->skb; | 
|---|
| 245 | struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); | 
|---|
| 246 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; | 
|---|
| 247 | __le16 fc = hdr->frame_control; | 
|---|
| 248 |  | 
|---|
| 249 | if (!ieee80211_is_data(fc) && !ieee80211_is_auth(fc)) | 
|---|
| 250 | return RX_CONTINUE; | 
|---|
| 251 |  | 
|---|
| 252 | if (!(status->flag & RX_FLAG_DECRYPTED)) { | 
|---|
| 253 | if (skb_linearize(skb: rx->skb)) | 
|---|
| 254 | return RX_DROP_U_OOM; | 
|---|
| 255 | if (ieee80211_wep_decrypt(local: rx->local, skb: rx->skb, key: rx->key)) | 
|---|
| 256 | return RX_DROP_U_WEP_DEC_FAIL; | 
|---|
| 257 | } else if (!(status->flag & RX_FLAG_IV_STRIPPED)) { | 
|---|
| 258 | if (!pskb_may_pull(skb: rx->skb, len: ieee80211_hdrlen(fc) + | 
|---|
| 259 | IEEE80211_WEP_IV_LEN)) | 
|---|
| 260 | return RX_DROP_U_NO_IV; | 
|---|
| 261 | ieee80211_wep_remove_iv(local: rx->local, skb: rx->skb, key: rx->key); | 
|---|
| 262 | /* remove ICV */ | 
|---|
| 263 | if (!(status->flag & RX_FLAG_ICV_STRIPPED) && | 
|---|
| 264 | pskb_trim(skb: rx->skb, len: rx->skb->len - IEEE80211_WEP_ICV_LEN)) | 
|---|
| 265 | return RX_DROP_U_NO_ICV; | 
|---|
| 266 | } | 
|---|
| 267 |  | 
|---|
| 268 | return RX_CONTINUE; | 
|---|
| 269 | } | 
|---|
| 270 |  | 
|---|
| 271 | static int wep_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) | 
|---|
| 272 | { | 
|---|
| 273 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | 
|---|
| 274 | struct ieee80211_key_conf *hw_key = info->control.hw_key; | 
|---|
| 275 |  | 
|---|
| 276 | if (!hw_key) { | 
|---|
| 277 | if (ieee80211_wep_encrypt(local: tx->local, skb, key: tx->key->conf.key, | 
|---|
| 278 | keylen: tx->key->conf.keylen, | 
|---|
| 279 | keyidx: tx->key->conf.keyidx)) | 
|---|
| 280 | return -1; | 
|---|
| 281 | } else if ((hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) || | 
|---|
| 282 | (hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) { | 
|---|
| 283 | if (!ieee80211_wep_add_iv(local: tx->local, skb, | 
|---|
| 284 | keylen: tx->key->conf.keylen, | 
|---|
| 285 | keyidx: tx->key->conf.keyidx)) | 
|---|
| 286 | return -1; | 
|---|
| 287 | } | 
|---|
| 288 |  | 
|---|
| 289 | return 0; | 
|---|
| 290 | } | 
|---|
| 291 |  | 
|---|
| 292 | ieee80211_tx_result | 
|---|
| 293 | ieee80211_crypto_wep_encrypt(struct ieee80211_tx_data *tx) | 
|---|
| 294 | { | 
|---|
| 295 | struct sk_buff *skb; | 
|---|
| 296 |  | 
|---|
| 297 | ieee80211_tx_set_protected(tx); | 
|---|
| 298 |  | 
|---|
| 299 | skb_queue_walk(&tx->skbs, skb) { | 
|---|
| 300 | if (wep_encrypt_skb(tx, skb) < 0) { | 
|---|
| 301 | I802_DEBUG_INC(tx->local->tx_handlers_drop_wep); | 
|---|
| 302 | return TX_DROP; | 
|---|
| 303 | } | 
|---|
| 304 | } | 
|---|
| 305 |  | 
|---|
| 306 | return TX_CONTINUE; | 
|---|
| 307 | } | 
|---|
| 308 |  | 
|---|