1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * lib80211 crypt: host-based WEP encryption implementation for lib80211 4 * 5 * Copyright (c) 2002-2004, Jouni Malinen <j@w1.fi> 6 * Copyright (c) 2008, John W. Linville <linville@tuxdriver.com> 7 */ 8 9 #include <linux/err.h> 10 #include <linux/fips.h> 11 #include <linux/module.h> 12 #include <linux/init.h> 13 #include <linux/slab.h> 14 #include <linux/random.h> 15 #include <linux/scatterlist.h> 16 #include <linux/skbuff.h> 17 #include <linux/mm.h> 18 #include <asm/string.h> 19 20 #include <net/lib80211.h> 21 22 #include <crypto/arc4.h> 23 #include <linux/crc32.h> 24 25 MODULE_AUTHOR("Jouni Malinen"); 26 MODULE_DESCRIPTION("lib80211 crypt: WEP"); 27 MODULE_LICENSE("GPL"); 28 29 struct lib80211_wep_data { 30 u32 iv; 31 #define WEP_KEY_LEN 13 32 u8 key[WEP_KEY_LEN + 1]; 33 u8 key_len; 34 u8 key_idx; 35 struct arc4_ctx tx_ctx; 36 struct arc4_ctx rx_ctx; 37 }; 38 39 static void *lib80211_wep_init(int keyidx) 40 { 41 struct lib80211_wep_data *priv; 42 43 if (fips_enabled) 44 return NULL; 45 46 priv = kzalloc(sizeof(*priv), GFP_ATOMIC); 47 if (priv == NULL) 48 return NULL; 49 priv->key_idx = keyidx; 50 51 /* start WEP IV from a random value */ 52 get_random_bytes(&priv->iv, 4); 53 54 return priv; 55 } 56 57 static void lib80211_wep_deinit(void *priv) 58 { 59 kfree_sensitive(priv); 60 } 61 62 /* Add WEP IV/key info to a frame that has at least 4 bytes of headroom */ 63 static int lib80211_wep_build_iv(struct sk_buff *skb, int hdr_len, 64 u8 *key, int keylen, void *priv) 65 { 66 struct lib80211_wep_data *wep = priv; 67 u32 klen; 68 u8 *pos; 69 70 if (skb_headroom(skb) < 4 || skb->len < hdr_len) 71 return -1; 72 73 pos = skb_push(skb, 4); 74 memmove(pos, pos + 4, hdr_len); 75 pos += hdr_len; 76 77 klen = 3 + wep->key_len; 78 79 wep->iv++; 80 81 /* Fluhrer, Mantin, and Shamir have reported weaknesses in the key 82 * scheduling algorithm of RC4. At least IVs (KeyByte + 3, 0xff, N) 83 * can be used to speedup attacks, so avoid using them. */ 84 if ((wep->iv & 0xff00) == 0xff00) { 85 u8 B = (wep->iv >> 16) & 0xff; 86 if (B >= 3 && B < klen) 87 wep->iv += 0x0100; 88 } 89 90 /* Prepend 24-bit IV to RC4 key and TX frame */ 91 *pos++ = (wep->iv >> 16) & 0xff; 92 *pos++ = (wep->iv >> 8) & 0xff; 93 *pos++ = wep->iv & 0xff; 94 *pos++ = wep->key_idx << 6; 95 96 return 0; 97 } 98 99 /* Perform WEP encryption on given skb that has at least 4 bytes of headroom 100 * for IV and 4 bytes of tailroom for ICV. Both IV and ICV will be transmitted, 101 * so the payload length increases with 8 bytes. 102 * 103 * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data)) 104 */ 105 static int lib80211_wep_encrypt(struct sk_buff *skb, int hdr_len, void *priv) 106 { 107 struct lib80211_wep_data *wep = priv; 108 u32 crc, klen, len; 109 u8 *pos, *icv; 110 u8 key[WEP_KEY_LEN + 3]; 111 112 /* other checks are in lib80211_wep_build_iv */ 113 if (skb_tailroom(skb) < 4) 114 return -1; 115 116 /* add the IV to the frame */ 117 if (lib80211_wep_build_iv(skb, hdr_len, NULL, 0, priv)) 118 return -1; 119 120 /* Copy the IV into the first 3 bytes of the key */ 121 skb_copy_from_linear_data_offset(skb, hdr_len, key, 3); 122 123 /* Copy rest of the WEP key (the secret part) */ 124 memcpy(key + 3, wep->key, wep->key_len); 125 126 len = skb->len - hdr_len - 4; 127 pos = skb->data + hdr_len + 4; 128 klen = 3 + wep->key_len; 129 130 /* Append little-endian CRC32 over only the data and encrypt it to produce ICV */ 131 crc = ~crc32_le(~0, pos, len); 132 icv = skb_put(skb, 4); 133 icv[0] = crc; 134 icv[1] = crc >> 8; 135 icv[2] = crc >> 16; 136 icv[3] = crc >> 24; 137 138 arc4_setkey(&wep->tx_ctx, key, klen); 139 arc4_crypt(&wep->tx_ctx, pos, pos, len + 4); 140 141 return 0; 142 } 143 144 /* Perform WEP decryption on given buffer. Buffer includes whole WEP part of 145 * the frame: IV (4 bytes), encrypted payload (including SNAP header), 146 * ICV (4 bytes). len includes both IV and ICV. 147 * 148 * Returns 0 if frame was decrypted successfully and ICV was correct and -1 on 149 * failure. If frame is OK, IV and ICV will be removed. 150 */ 151 static int lib80211_wep_decrypt(struct sk_buff *skb, int hdr_len, void *priv) 152 { 153 struct lib80211_wep_data *wep = priv; 154 u32 crc, klen, plen; 155 u8 key[WEP_KEY_LEN + 3]; 156 u8 keyidx, *pos, icv[4]; 157 158 if (skb->len < hdr_len + 8) 159 return -1; 160 161 pos = skb->data + hdr_len; 162 key[0] = *pos++; 163 key[1] = *pos++; 164 key[2] = *pos++; 165 keyidx = *pos++ >> 6; 166 if (keyidx != wep->key_idx) 167 return -1; 168 169 klen = 3 + wep->key_len; 170 171 /* Copy rest of the WEP key (the secret part) */ 172 memcpy(key + 3, wep->key, wep->key_len); 173 174 /* Apply RC4 to data and compute CRC32 over decrypted data */ 175 plen = skb->len - hdr_len - 8; 176 177 arc4_setkey(&wep->rx_ctx, key, klen); 178 arc4_crypt(&wep->rx_ctx, pos, pos, plen + 4); 179 180 crc = ~crc32_le(~0, pos, plen); 181 icv[0] = crc; 182 icv[1] = crc >> 8; 183 icv[2] = crc >> 16; 184 icv[3] = crc >> 24; 185 if (memcmp(icv, pos + plen, 4) != 0) { 186 /* ICV mismatch - drop frame */ 187 return -2; 188 } 189 190 /* Remove IV and ICV */ 191 memmove(skb->data + 4, skb->data, hdr_len); 192 skb_pull(skb, 4); 193 skb_trim(skb, skb->len - 4); 194 195 return 0; 196 } 197 198 static int lib80211_wep_set_key(void *key, int len, u8 * seq, void *priv) 199 { 200 struct lib80211_wep_data *wep = priv; 201 202 if (len < 0 || len > WEP_KEY_LEN) 203 return -1; 204 205 memcpy(wep->key, key, len); 206 wep->key_len = len; 207 208 return 0; 209 } 210 211 static int lib80211_wep_get_key(void *key, int len, u8 * seq, void *priv) 212 { 213 struct lib80211_wep_data *wep = priv; 214 215 if (len < wep->key_len) 216 return -1; 217 218 memcpy(key, wep->key, wep->key_len); 219 220 return wep->key_len; 221 } 222 223 static void lib80211_wep_print_stats(struct seq_file *m, void *priv) 224 { 225 struct lib80211_wep_data *wep = priv; 226 seq_printf(m, "key[%d] alg=WEP len=%d\n", wep->key_idx, wep->key_len); 227 } 228 229 static struct lib80211_crypto_ops lib80211_crypt_wep = { 230 .name = "WEP", 231 .init = lib80211_wep_init, 232 .deinit = lib80211_wep_deinit, 233 .encrypt_mpdu = lib80211_wep_encrypt, 234 .decrypt_mpdu = lib80211_wep_decrypt, 235 .encrypt_msdu = NULL, 236 .decrypt_msdu = NULL, 237 .set_key = lib80211_wep_set_key, 238 .get_key = lib80211_wep_get_key, 239 .print_stats = lib80211_wep_print_stats, 240 .extra_mpdu_prefix_len = 4, /* IV */ 241 .extra_mpdu_postfix_len = 4, /* ICV */ 242 .owner = THIS_MODULE, 243 }; 244 245 static int __init lib80211_crypto_wep_init(void) 246 { 247 return lib80211_register_crypto_ops(&lib80211_crypt_wep); 248 } 249 250 static void __exit lib80211_crypto_wep_exit(void) 251 { 252 lib80211_unregister_crypto_ops(&lib80211_crypt_wep); 253 } 254 255 module_init(lib80211_crypto_wep_init); 256 module_exit(lib80211_crypto_wep_exit); 257
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.