18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> 48c2ecf20Sopenharmony_ci <http://rt2x00.serialmonkey.com> 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci/* 98c2ecf20Sopenharmony_ci Module: rt2x00lib 108c2ecf20Sopenharmony_ci Abstract: rt2x00 crypto specific routines. 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "rt2x00.h" 178c2ecf20Sopenharmony_ci#include "rt2x00lib.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cienum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key) 208c2ecf20Sopenharmony_ci{ 218c2ecf20Sopenharmony_ci switch (key->cipher) { 228c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP40: 238c2ecf20Sopenharmony_ci return CIPHER_WEP64; 248c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP104: 258c2ecf20Sopenharmony_ci return CIPHER_WEP128; 268c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_TKIP: 278c2ecf20Sopenharmony_ci return CIPHER_TKIP; 288c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_CCMP: 298c2ecf20Sopenharmony_ci return CIPHER_AES; 308c2ecf20Sopenharmony_ci default: 318c2ecf20Sopenharmony_ci return CIPHER_NONE; 328c2ecf20Sopenharmony_ci } 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_civoid rt2x00crypto_create_tx_descriptor(struct rt2x00_dev *rt2x00dev, 368c2ecf20Sopenharmony_ci struct sk_buff *skb, 378c2ecf20Sopenharmony_ci struct txentry_desc *txdesc) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); 408c2ecf20Sopenharmony_ci struct ieee80211_key_conf *hw_key = tx_info->control.hw_key; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci if (!rt2x00_has_cap_hw_crypto(rt2x00dev) || !hw_key) 438c2ecf20Sopenharmony_ci return; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci __set_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci txdesc->cipher = rt2x00crypto_key_to_cipher(hw_key); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci if (hw_key->flags & IEEE80211_KEY_FLAG_PAIRWISE) 508c2ecf20Sopenharmony_ci __set_bit(ENTRY_TXD_ENCRYPT_PAIRWISE, &txdesc->flags); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci txdesc->key_idx = hw_key->hw_key_idx; 538c2ecf20Sopenharmony_ci txdesc->iv_offset = txdesc->header_length; 548c2ecf20Sopenharmony_ci txdesc->iv_len = hw_key->iv_len; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci if (!(hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) 578c2ecf20Sopenharmony_ci __set_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if (!(hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) 608c2ecf20Sopenharmony_ci __set_bit(ENTRY_TXD_ENCRYPT_MMIC, &txdesc->flags); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ciunsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev, 648c2ecf20Sopenharmony_ci struct sk_buff *skb) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); 678c2ecf20Sopenharmony_ci struct ieee80211_key_conf *key = tx_info->control.hw_key; 688c2ecf20Sopenharmony_ci unsigned int overhead = 0; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci if (!rt2x00_has_cap_hw_crypto(rt2x00dev) || !key) 718c2ecf20Sopenharmony_ci return overhead; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* 748c2ecf20Sopenharmony_ci * Extend frame length to include IV/EIV/ICV/MMIC, 758c2ecf20Sopenharmony_ci * note that these lengths should only be added when 768c2ecf20Sopenharmony_ci * mac80211 does not generate it. 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_ci overhead += key->icv_len; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (!(key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) 818c2ecf20Sopenharmony_ci overhead += key->iv_len; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (!(key->flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) { 848c2ecf20Sopenharmony_ci if (key->cipher == WLAN_CIPHER_SUITE_TKIP) 858c2ecf20Sopenharmony_ci overhead += 8; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return overhead; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_civoid rt2x00crypto_tx_copy_iv(struct sk_buff *skb, struct txentry_desc *txdesc) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (unlikely(!txdesc->iv_len)) 968c2ecf20Sopenharmony_ci return; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* Copy IV/EIV data */ 998c2ecf20Sopenharmony_ci memcpy(skbdesc->iv, skb->data + txdesc->iv_offset, txdesc->iv_len); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_civoid rt2x00crypto_tx_remove_iv(struct sk_buff *skb, struct txentry_desc *txdesc) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (unlikely(!txdesc->iv_len)) 1078c2ecf20Sopenharmony_ci return; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* Copy IV/EIV data */ 1108c2ecf20Sopenharmony_ci memcpy(skbdesc->iv, skb->data + txdesc->iv_offset, txdesc->iv_len); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* Move ieee80211 header */ 1138c2ecf20Sopenharmony_ci memmove(skb->data + txdesc->iv_len, skb->data, txdesc->iv_offset); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* Pull buffer to correct size */ 1168c2ecf20Sopenharmony_ci skb_pull(skb, txdesc->iv_len); 1178c2ecf20Sopenharmony_ci txdesc->length -= txdesc->iv_len; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci /* IV/EIV data has officially been stripped */ 1208c2ecf20Sopenharmony_ci skbdesc->flags |= SKBDESC_IV_STRIPPED; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_civoid rt2x00crypto_tx_insert_iv(struct sk_buff *skb, unsigned int header_length) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); 1268c2ecf20Sopenharmony_ci const unsigned int iv_len = 1278c2ecf20Sopenharmony_ci ((!!(skbdesc->iv[0])) * 4) + ((!!(skbdesc->iv[1])) * 4); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (!(skbdesc->flags & SKBDESC_IV_STRIPPED)) 1308c2ecf20Sopenharmony_ci return; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci skb_push(skb, iv_len); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* Move ieee80211 header */ 1358c2ecf20Sopenharmony_ci memmove(skb->data, skb->data + iv_len, header_length); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* Copy IV/EIV data */ 1388c2ecf20Sopenharmony_ci memcpy(skb->data + header_length, skbdesc->iv, iv_len); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* IV/EIV data has returned into the frame */ 1418c2ecf20Sopenharmony_ci skbdesc->flags &= ~SKBDESC_IV_STRIPPED; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_civoid rt2x00crypto_rx_insert_iv(struct sk_buff *skb, 1458c2ecf20Sopenharmony_ci unsigned int header_length, 1468c2ecf20Sopenharmony_ci struct rxdone_entry_desc *rxdesc) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci unsigned int payload_len = rxdesc->size - header_length; 1498c2ecf20Sopenharmony_ci unsigned int align = ALIGN_SIZE(skb, header_length); 1508c2ecf20Sopenharmony_ci unsigned int iv_len; 1518c2ecf20Sopenharmony_ci unsigned int icv_len; 1528c2ecf20Sopenharmony_ci unsigned int transfer = 0; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci /* 1558c2ecf20Sopenharmony_ci * WEP64/WEP128: Provides IV & ICV 1568c2ecf20Sopenharmony_ci * TKIP: Provides IV/EIV & ICV 1578c2ecf20Sopenharmony_ci * AES: Provies IV/EIV & ICV 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_ci switch (rxdesc->cipher) { 1608c2ecf20Sopenharmony_ci case CIPHER_WEP64: 1618c2ecf20Sopenharmony_ci case CIPHER_WEP128: 1628c2ecf20Sopenharmony_ci iv_len = 4; 1638c2ecf20Sopenharmony_ci icv_len = 4; 1648c2ecf20Sopenharmony_ci break; 1658c2ecf20Sopenharmony_ci case CIPHER_TKIP: 1668c2ecf20Sopenharmony_ci iv_len = 8; 1678c2ecf20Sopenharmony_ci icv_len = 4; 1688c2ecf20Sopenharmony_ci break; 1698c2ecf20Sopenharmony_ci case CIPHER_AES: 1708c2ecf20Sopenharmony_ci iv_len = 8; 1718c2ecf20Sopenharmony_ci icv_len = 8; 1728c2ecf20Sopenharmony_ci break; 1738c2ecf20Sopenharmony_ci default: 1748c2ecf20Sopenharmony_ci /* Unsupport type */ 1758c2ecf20Sopenharmony_ci return; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* 1798c2ecf20Sopenharmony_ci * Make room for new data. There are 2 possibilities 1808c2ecf20Sopenharmony_ci * either the alignment is already present between 1818c2ecf20Sopenharmony_ci * the 802.11 header and payload. In that case we 1828c2ecf20Sopenharmony_ci * we have to move the header less then the iv_len 1838c2ecf20Sopenharmony_ci * since we can use the already available l2pad bytes 1848c2ecf20Sopenharmony_ci * for the iv data. 1858c2ecf20Sopenharmony_ci * When the alignment must be added manually we must 1868c2ecf20Sopenharmony_ci * move the header more then iv_len since we must 1878c2ecf20Sopenharmony_ci * make room for the payload move as well. 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_ci if (rxdesc->dev_flags & RXDONE_L2PAD) { 1908c2ecf20Sopenharmony_ci skb_push(skb, iv_len - align); 1918c2ecf20Sopenharmony_ci skb_put(skb, icv_len); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* Move ieee80211 header */ 1948c2ecf20Sopenharmony_ci memmove(skb->data + transfer, 1958c2ecf20Sopenharmony_ci skb->data + transfer + (iv_len - align), 1968c2ecf20Sopenharmony_ci header_length); 1978c2ecf20Sopenharmony_ci transfer += header_length; 1988c2ecf20Sopenharmony_ci } else { 1998c2ecf20Sopenharmony_ci skb_push(skb, iv_len + align); 2008c2ecf20Sopenharmony_ci if (align < icv_len) 2018c2ecf20Sopenharmony_ci skb_put(skb, icv_len - align); 2028c2ecf20Sopenharmony_ci else if (align > icv_len) 2038c2ecf20Sopenharmony_ci skb_trim(skb, rxdesc->size + iv_len + icv_len); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* Move ieee80211 header */ 2068c2ecf20Sopenharmony_ci memmove(skb->data + transfer, 2078c2ecf20Sopenharmony_ci skb->data + transfer + iv_len + align, 2088c2ecf20Sopenharmony_ci header_length); 2098c2ecf20Sopenharmony_ci transfer += header_length; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* Copy IV/EIV data */ 2138c2ecf20Sopenharmony_ci memcpy(skb->data + transfer, rxdesc->iv, iv_len); 2148c2ecf20Sopenharmony_ci transfer += iv_len; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* 2178c2ecf20Sopenharmony_ci * Move payload for alignment purposes. Note that 2188c2ecf20Sopenharmony_ci * this is only needed when no l2 padding is present. 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_ci if (!(rxdesc->dev_flags & RXDONE_L2PAD)) { 2218c2ecf20Sopenharmony_ci memmove(skb->data + transfer, 2228c2ecf20Sopenharmony_ci skb->data + transfer + align, 2238c2ecf20Sopenharmony_ci payload_len); 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* 2278c2ecf20Sopenharmony_ci * NOTE: Always count the payload as transferred, 2288c2ecf20Sopenharmony_ci * even when alignment was set to zero. This is required 2298c2ecf20Sopenharmony_ci * for determining the correct offset for the ICV data. 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_ci transfer += payload_len; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* 2348c2ecf20Sopenharmony_ci * Copy ICV data 2358c2ecf20Sopenharmony_ci * AES appends 8 bytes, we can't fill the upper 2368c2ecf20Sopenharmony_ci * 4 bytes, but mac80211 doesn't care about what 2378c2ecf20Sopenharmony_ci * we provide here anyway and strips it immediately. 2388c2ecf20Sopenharmony_ci */ 2398c2ecf20Sopenharmony_ci memcpy(skb->data + transfer, &rxdesc->icv, 4); 2408c2ecf20Sopenharmony_ci transfer += icv_len; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* IV/EIV/ICV has been inserted into frame */ 2438c2ecf20Sopenharmony_ci rxdesc->size = transfer; 2448c2ecf20Sopenharmony_ci rxdesc->flags &= ~RX_FLAG_IV_STRIPPED; 2458c2ecf20Sopenharmony_ci} 246