1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> |
4 | <http://rt2x00.serialmonkey.com> |
5 | |
6 | */ |
7 | |
8 | /* |
9 | Module: rt2x00lib |
10 | Abstract: rt2x00 crypto specific routines. |
11 | */ |
12 | |
13 | #include <linux/kernel.h> |
14 | #include <linux/module.h> |
15 | |
16 | #include "rt2x00.h" |
17 | #include "rt2x00lib.h" |
18 | |
19 | enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key) |
20 | { |
21 | switch (key->cipher) { |
22 | case WLAN_CIPHER_SUITE_WEP40: |
23 | return CIPHER_WEP64; |
24 | case WLAN_CIPHER_SUITE_WEP104: |
25 | return CIPHER_WEP128; |
26 | case WLAN_CIPHER_SUITE_TKIP: |
27 | return CIPHER_TKIP; |
28 | case WLAN_CIPHER_SUITE_CCMP: |
29 | return CIPHER_AES; |
30 | default: |
31 | return CIPHER_NONE; |
32 | } |
33 | } |
34 | |
35 | void rt2x00crypto_create_tx_descriptor(struct rt2x00_dev *rt2x00dev, |
36 | struct sk_buff *skb, |
37 | struct txentry_desc *txdesc) |
38 | { |
39 | struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); |
40 | struct ieee80211_key_conf *hw_key = tx_info->control.hw_key; |
41 | |
42 | if (!rt2x00_has_cap_hw_crypto(rt2x00dev) || !hw_key) |
43 | return; |
44 | |
45 | __set_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags); |
46 | |
47 | txdesc->cipher = rt2x00crypto_key_to_cipher(key: hw_key); |
48 | |
49 | if (hw_key->flags & IEEE80211_KEY_FLAG_PAIRWISE) |
50 | __set_bit(ENTRY_TXD_ENCRYPT_PAIRWISE, &txdesc->flags); |
51 | |
52 | txdesc->key_idx = hw_key->hw_key_idx; |
53 | txdesc->iv_offset = txdesc->header_length; |
54 | txdesc->iv_len = hw_key->iv_len; |
55 | |
56 | if (!(hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) |
57 | __set_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags); |
58 | |
59 | if (!(hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) |
60 | __set_bit(ENTRY_TXD_ENCRYPT_MMIC, &txdesc->flags); |
61 | } |
62 | |
63 | unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev, |
64 | struct sk_buff *skb) |
65 | { |
66 | struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); |
67 | struct ieee80211_key_conf *key = tx_info->control.hw_key; |
68 | unsigned int overhead = 0; |
69 | |
70 | if (!rt2x00_has_cap_hw_crypto(rt2x00dev) || !key) |
71 | return overhead; |
72 | |
73 | /* |
74 | * Extend frame length to include IV/EIV/ICV/MMIC, |
75 | * note that these lengths should only be added when |
76 | * mac80211 does not generate it. |
77 | */ |
78 | overhead += key->icv_len; |
79 | |
80 | if (!(key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) |
81 | overhead += key->iv_len; |
82 | |
83 | if (!(key->flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) { |
84 | if (key->cipher == WLAN_CIPHER_SUITE_TKIP) |
85 | overhead += 8; |
86 | } |
87 | |
88 | return overhead; |
89 | } |
90 | |
91 | void rt2x00crypto_tx_copy_iv(struct sk_buff *skb, struct txentry_desc *txdesc) |
92 | { |
93 | struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); |
94 | |
95 | if (unlikely(!txdesc->iv_len)) |
96 | return; |
97 | |
98 | /* Copy IV/EIV data */ |
99 | memcpy(skbdesc->iv, skb->data + txdesc->iv_offset, txdesc->iv_len); |
100 | } |
101 | |
102 | void rt2x00crypto_tx_remove_iv(struct sk_buff *skb, struct txentry_desc *txdesc) |
103 | { |
104 | struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); |
105 | |
106 | if (unlikely(!txdesc->iv_len)) |
107 | return; |
108 | |
109 | /* Copy IV/EIV data */ |
110 | memcpy(skbdesc->iv, skb->data + txdesc->iv_offset, txdesc->iv_len); |
111 | |
112 | /* Move ieee80211 header */ |
113 | memmove(skb->data + txdesc->iv_len, skb->data, txdesc->iv_offset); |
114 | |
115 | /* Pull buffer to correct size */ |
116 | skb_pull(skb, len: txdesc->iv_len); |
117 | txdesc->length -= txdesc->iv_len; |
118 | |
119 | /* IV/EIV data has officially been stripped */ |
120 | skbdesc->flags |= SKBDESC_IV_STRIPPED; |
121 | } |
122 | |
123 | void rt2x00crypto_tx_insert_iv(struct sk_buff *skb, unsigned int ) |
124 | { |
125 | struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); |
126 | const unsigned int iv_len = |
127 | ((!!(skbdesc->iv[0])) * 4) + ((!!(skbdesc->iv[1])) * 4); |
128 | |
129 | if (!(skbdesc->flags & SKBDESC_IV_STRIPPED)) |
130 | return; |
131 | |
132 | skb_push(skb, len: iv_len); |
133 | |
134 | /* Move ieee80211 header */ |
135 | memmove(skb->data, skb->data + iv_len, header_length); |
136 | |
137 | /* Copy IV/EIV data */ |
138 | memcpy(skb->data + header_length, skbdesc->iv, iv_len); |
139 | |
140 | /* IV/EIV data has returned into the frame */ |
141 | skbdesc->flags &= ~SKBDESC_IV_STRIPPED; |
142 | } |
143 | |
144 | void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, |
145 | unsigned int , |
146 | struct rxdone_entry_desc *rxdesc) |
147 | { |
148 | unsigned int payload_len = rxdesc->size - header_length; |
149 | unsigned int align = ALIGN_SIZE(skb, header_length); |
150 | unsigned int iv_len; |
151 | unsigned int icv_len; |
152 | unsigned int transfer = 0; |
153 | |
154 | /* |
155 | * WEP64/WEP128: Provides IV & ICV |
156 | * TKIP: Provides IV/EIV & ICV |
157 | * AES: Provies IV/EIV & ICV |
158 | */ |
159 | switch (rxdesc->cipher) { |
160 | case CIPHER_WEP64: |
161 | case CIPHER_WEP128: |
162 | iv_len = 4; |
163 | icv_len = 4; |
164 | break; |
165 | case CIPHER_TKIP: |
166 | iv_len = 8; |
167 | icv_len = 4; |
168 | break; |
169 | case CIPHER_AES: |
170 | iv_len = 8; |
171 | icv_len = 8; |
172 | break; |
173 | default: |
174 | /* Unsupport type */ |
175 | return; |
176 | } |
177 | |
178 | /* |
179 | * Make room for new data. There are 2 possibilities |
180 | * either the alignment is already present between |
181 | * the 802.11 header and payload. In that case we |
182 | * have to move the header less than the iv_len |
183 | * since we can use the already available l2pad bytes |
184 | * for the iv data. |
185 | * When the alignment must be added manually we must |
186 | * move the header more then iv_len since we must |
187 | * make room for the payload move as well. |
188 | */ |
189 | if (rxdesc->dev_flags & RXDONE_L2PAD) { |
190 | skb_push(skb, len: iv_len - align); |
191 | skb_put(skb, len: icv_len); |
192 | |
193 | /* Move ieee80211 header */ |
194 | memmove(skb->data + transfer, |
195 | skb->data + transfer + (iv_len - align), |
196 | header_length); |
197 | transfer += header_length; |
198 | } else { |
199 | skb_push(skb, len: iv_len + align); |
200 | skb_put(skb, len: icv_len - align); |
201 | |
202 | /* Move ieee80211 header */ |
203 | memmove(skb->data + transfer, |
204 | skb->data + transfer + iv_len + align, |
205 | header_length); |
206 | transfer += header_length; |
207 | } |
208 | |
209 | /* Copy IV/EIV data */ |
210 | memcpy(skb->data + transfer, rxdesc->iv, iv_len); |
211 | transfer += iv_len; |
212 | |
213 | /* |
214 | * Move payload for alignment purposes. Note that |
215 | * this is only needed when no l2 padding is present. |
216 | */ |
217 | if (!(rxdesc->dev_flags & RXDONE_L2PAD)) { |
218 | memmove(skb->data + transfer, |
219 | skb->data + transfer + align, |
220 | payload_len); |
221 | } |
222 | |
223 | /* |
224 | * NOTE: Always count the payload as transferred, |
225 | * even when alignment was set to zero. This is required |
226 | * for determining the correct offset for the ICV data. |
227 | */ |
228 | transfer += payload_len; |
229 | |
230 | /* |
231 | * Copy ICV data |
232 | * AES appends 8 bytes, we can't fill the upper |
233 | * 4 bytes, but mac80211 doesn't care about what |
234 | * we provide here anyway and strips it immediately. |
235 | */ |
236 | memcpy(skb->data + transfer, &rxdesc->icv, 4); |
237 | transfer += icv_len; |
238 | |
239 | /* IV/EIV/ICV has been inserted into frame */ |
240 | rxdesc->size = transfer; |
241 | rxdesc->flags &= ~RX_FLAG_IV_STRIPPED; |
242 | } |
243 | |