18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * lib80211 crypt: host-based WEP encryption implementation for lib80211 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2002-2004, Jouni Malinen <j@w1.fi> 68c2ecf20Sopenharmony_ci * Copyright (c) 2008, John W. Linville <linville@tuxdriver.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/err.h> 108c2ecf20Sopenharmony_ci#include <linux/fips.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/random.h> 158c2ecf20Sopenharmony_ci#include <linux/scatterlist.h> 168c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 178c2ecf20Sopenharmony_ci#include <linux/mm.h> 188c2ecf20Sopenharmony_ci#include <asm/string.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <net/lib80211.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <crypto/arc4.h> 238c2ecf20Sopenharmony_ci#include <linux/crc32.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jouni Malinen"); 268c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("lib80211 crypt: WEP"); 278c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistruct lib80211_wep_data { 308c2ecf20Sopenharmony_ci u32 iv; 318c2ecf20Sopenharmony_ci#define WEP_KEY_LEN 13 328c2ecf20Sopenharmony_ci u8 key[WEP_KEY_LEN + 1]; 338c2ecf20Sopenharmony_ci u8 key_len; 348c2ecf20Sopenharmony_ci u8 key_idx; 358c2ecf20Sopenharmony_ci struct arc4_ctx tx_ctx; 368c2ecf20Sopenharmony_ci struct arc4_ctx rx_ctx; 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic void *lib80211_wep_init(int keyidx) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci struct lib80211_wep_data *priv; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci if (fips_enabled) 448c2ecf20Sopenharmony_ci return NULL; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci priv = kzalloc(sizeof(*priv), GFP_ATOMIC); 478c2ecf20Sopenharmony_ci if (priv == NULL) 488c2ecf20Sopenharmony_ci return NULL; 498c2ecf20Sopenharmony_ci priv->key_idx = keyidx; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci /* start WEP IV from a random value */ 528c2ecf20Sopenharmony_ci get_random_bytes(&priv->iv, 4); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci return priv; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic void lib80211_wep_deinit(void *priv) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci kfree_sensitive(priv); 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* Add WEP IV/key info to a frame that has at least 4 bytes of headroom */ 638c2ecf20Sopenharmony_cistatic int lib80211_wep_build_iv(struct sk_buff *skb, int hdr_len, 648c2ecf20Sopenharmony_ci u8 *key, int keylen, void *priv) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct lib80211_wep_data *wep = priv; 678c2ecf20Sopenharmony_ci u32 klen; 688c2ecf20Sopenharmony_ci u8 *pos; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci if (skb_headroom(skb) < 4 || skb->len < hdr_len) 718c2ecf20Sopenharmony_ci return -1; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci pos = skb_push(skb, 4); 748c2ecf20Sopenharmony_ci memmove(pos, pos + 4, hdr_len); 758c2ecf20Sopenharmony_ci pos += hdr_len; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci klen = 3 + wep->key_len; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci wep->iv++; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci /* Fluhrer, Mantin, and Shamir have reported weaknesses in the key 828c2ecf20Sopenharmony_ci * scheduling algorithm of RC4. At least IVs (KeyByte + 3, 0xff, N) 838c2ecf20Sopenharmony_ci * can be used to speedup attacks, so avoid using them. */ 848c2ecf20Sopenharmony_ci if ((wep->iv & 0xff00) == 0xff00) { 858c2ecf20Sopenharmony_ci u8 B = (wep->iv >> 16) & 0xff; 868c2ecf20Sopenharmony_ci if (B >= 3 && B < klen) 878c2ecf20Sopenharmony_ci wep->iv += 0x0100; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* Prepend 24-bit IV to RC4 key and TX frame */ 918c2ecf20Sopenharmony_ci *pos++ = (wep->iv >> 16) & 0xff; 928c2ecf20Sopenharmony_ci *pos++ = (wep->iv >> 8) & 0xff; 938c2ecf20Sopenharmony_ci *pos++ = wep->iv & 0xff; 948c2ecf20Sopenharmony_ci *pos++ = wep->key_idx << 6; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return 0; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci/* Perform WEP encryption on given skb that has at least 4 bytes of headroom 1008c2ecf20Sopenharmony_ci * for IV and 4 bytes of tailroom for ICV. Both IV and ICV will be transmitted, 1018c2ecf20Sopenharmony_ci * so the payload length increases with 8 bytes. 1028c2ecf20Sopenharmony_ci * 1038c2ecf20Sopenharmony_ci * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data)) 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_cistatic int lib80211_wep_encrypt(struct sk_buff *skb, int hdr_len, void *priv) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct lib80211_wep_data *wep = priv; 1088c2ecf20Sopenharmony_ci u32 crc, klen, len; 1098c2ecf20Sopenharmony_ci u8 *pos, *icv; 1108c2ecf20Sopenharmony_ci u8 key[WEP_KEY_LEN + 3]; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* other checks are in lib80211_wep_build_iv */ 1138c2ecf20Sopenharmony_ci if (skb_tailroom(skb) < 4) 1148c2ecf20Sopenharmony_ci return -1; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* add the IV to the frame */ 1178c2ecf20Sopenharmony_ci if (lib80211_wep_build_iv(skb, hdr_len, NULL, 0, priv)) 1188c2ecf20Sopenharmony_ci return -1; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* Copy the IV into the first 3 bytes of the key */ 1218c2ecf20Sopenharmony_ci skb_copy_from_linear_data_offset(skb, hdr_len, key, 3); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* Copy rest of the WEP key (the secret part) */ 1248c2ecf20Sopenharmony_ci memcpy(key + 3, wep->key, wep->key_len); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci len = skb->len - hdr_len - 4; 1278c2ecf20Sopenharmony_ci pos = skb->data + hdr_len + 4; 1288c2ecf20Sopenharmony_ci klen = 3 + wep->key_len; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci /* Append little-endian CRC32 over only the data and encrypt it to produce ICV */ 1318c2ecf20Sopenharmony_ci crc = ~crc32_le(~0, pos, len); 1328c2ecf20Sopenharmony_ci icv = skb_put(skb, 4); 1338c2ecf20Sopenharmony_ci icv[0] = crc; 1348c2ecf20Sopenharmony_ci icv[1] = crc >> 8; 1358c2ecf20Sopenharmony_ci icv[2] = crc >> 16; 1368c2ecf20Sopenharmony_ci icv[3] = crc >> 24; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci arc4_setkey(&wep->tx_ctx, key, klen); 1398c2ecf20Sopenharmony_ci arc4_crypt(&wep->tx_ctx, pos, pos, len + 4); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci return 0; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/* Perform WEP decryption on given buffer. Buffer includes whole WEP part of 1458c2ecf20Sopenharmony_ci * the frame: IV (4 bytes), encrypted payload (including SNAP header), 1468c2ecf20Sopenharmony_ci * ICV (4 bytes). len includes both IV and ICV. 1478c2ecf20Sopenharmony_ci * 1488c2ecf20Sopenharmony_ci * Returns 0 if frame was decrypted successfully and ICV was correct and -1 on 1498c2ecf20Sopenharmony_ci * failure. If frame is OK, IV and ICV will be removed. 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_cistatic int lib80211_wep_decrypt(struct sk_buff *skb, int hdr_len, void *priv) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct lib80211_wep_data *wep = priv; 1548c2ecf20Sopenharmony_ci u32 crc, klen, plen; 1558c2ecf20Sopenharmony_ci u8 key[WEP_KEY_LEN + 3]; 1568c2ecf20Sopenharmony_ci u8 keyidx, *pos, icv[4]; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (skb->len < hdr_len + 8) 1598c2ecf20Sopenharmony_ci return -1; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci pos = skb->data + hdr_len; 1628c2ecf20Sopenharmony_ci key[0] = *pos++; 1638c2ecf20Sopenharmony_ci key[1] = *pos++; 1648c2ecf20Sopenharmony_ci key[2] = *pos++; 1658c2ecf20Sopenharmony_ci keyidx = *pos++ >> 6; 1668c2ecf20Sopenharmony_ci if (keyidx != wep->key_idx) 1678c2ecf20Sopenharmony_ci return -1; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci klen = 3 + wep->key_len; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* Copy rest of the WEP key (the secret part) */ 1728c2ecf20Sopenharmony_ci memcpy(key + 3, wep->key, wep->key_len); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* Apply RC4 to data and compute CRC32 over decrypted data */ 1758c2ecf20Sopenharmony_ci plen = skb->len - hdr_len - 8; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci arc4_setkey(&wep->rx_ctx, key, klen); 1788c2ecf20Sopenharmony_ci arc4_crypt(&wep->rx_ctx, pos, pos, plen + 4); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci crc = ~crc32_le(~0, pos, plen); 1818c2ecf20Sopenharmony_ci icv[0] = crc; 1828c2ecf20Sopenharmony_ci icv[1] = crc >> 8; 1838c2ecf20Sopenharmony_ci icv[2] = crc >> 16; 1848c2ecf20Sopenharmony_ci icv[3] = crc >> 24; 1858c2ecf20Sopenharmony_ci if (memcmp(icv, pos + plen, 4) != 0) { 1868c2ecf20Sopenharmony_ci /* ICV mismatch - drop frame */ 1878c2ecf20Sopenharmony_ci return -2; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* Remove IV and ICV */ 1918c2ecf20Sopenharmony_ci memmove(skb->data + 4, skb->data, hdr_len); 1928c2ecf20Sopenharmony_ci skb_pull(skb, 4); 1938c2ecf20Sopenharmony_ci skb_trim(skb, skb->len - 4); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return 0; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic int lib80211_wep_set_key(void *key, int len, u8 * seq, void *priv) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct lib80211_wep_data *wep = priv; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (len < 0 || len > WEP_KEY_LEN) 2038c2ecf20Sopenharmony_ci return -1; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci memcpy(wep->key, key, len); 2068c2ecf20Sopenharmony_ci wep->key_len = len; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci return 0; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic int lib80211_wep_get_key(void *key, int len, u8 * seq, void *priv) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci struct lib80211_wep_data *wep = priv; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (len < wep->key_len) 2168c2ecf20Sopenharmony_ci return -1; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci memcpy(key, wep->key, wep->key_len); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci return wep->key_len; 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic void lib80211_wep_print_stats(struct seq_file *m, void *priv) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct lib80211_wep_data *wep = priv; 2268c2ecf20Sopenharmony_ci seq_printf(m, "key[%d] alg=WEP len=%d\n", wep->key_idx, wep->key_len); 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic struct lib80211_crypto_ops lib80211_crypt_wep = { 2308c2ecf20Sopenharmony_ci .name = "WEP", 2318c2ecf20Sopenharmony_ci .init = lib80211_wep_init, 2328c2ecf20Sopenharmony_ci .deinit = lib80211_wep_deinit, 2338c2ecf20Sopenharmony_ci .encrypt_mpdu = lib80211_wep_encrypt, 2348c2ecf20Sopenharmony_ci .decrypt_mpdu = lib80211_wep_decrypt, 2358c2ecf20Sopenharmony_ci .encrypt_msdu = NULL, 2368c2ecf20Sopenharmony_ci .decrypt_msdu = NULL, 2378c2ecf20Sopenharmony_ci .set_key = lib80211_wep_set_key, 2388c2ecf20Sopenharmony_ci .get_key = lib80211_wep_get_key, 2398c2ecf20Sopenharmony_ci .print_stats = lib80211_wep_print_stats, 2408c2ecf20Sopenharmony_ci .extra_mpdu_prefix_len = 4, /* IV */ 2418c2ecf20Sopenharmony_ci .extra_mpdu_postfix_len = 4, /* ICV */ 2428c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2438c2ecf20Sopenharmony_ci}; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int __init lib80211_crypto_wep_init(void) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci return lib80211_register_crypto_ops(&lib80211_crypt_wep); 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic void __exit lib80211_crypto_wep_exit(void) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci lib80211_unregister_crypto_ops(&lib80211_crypt_wep); 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cimodule_init(lib80211_crypto_wep_init); 2568c2ecf20Sopenharmony_cimodule_exit(lib80211_crypto_wep_exit); 257