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