1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Host AP crypt: host-based CCMP encryption implementation for Host AP driver |
3 | * |
4 | * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi> |
5 | */ |
6 | |
7 | #include <linux/module.h> |
8 | #include <linux/init.h> |
9 | #include <linux/slab.h> |
10 | #include <linux/random.h> |
11 | #include <linux/skbuff.h> |
12 | #include <linux/netdevice.h> |
13 | #include <linux/if_ether.h> |
14 | #include <linux/if_arp.h> |
15 | #include <linux/string.h> |
16 | #include <linux/wireless.h> |
17 | #include "rtllib.h" |
18 | |
19 | #include <linux/crypto.h> |
20 | #include <crypto/aead.h> |
21 | |
22 | #include <linux/scatterlist.h> |
23 | |
24 | #define AES_BLOCK_LEN 16 |
25 | #define CCMP_HDR_LEN 8 |
26 | #define CCMP_MIC_LEN 8 |
27 | #define CCMP_TK_LEN 16 |
28 | #define CCMP_PN_LEN 6 |
29 | |
30 | struct rtllib_ccmp_data { |
31 | u8 key[CCMP_TK_LEN]; |
32 | int key_set; |
33 | |
34 | u8 tx_pn[CCMP_PN_LEN]; |
35 | u8 rx_pn[CCMP_PN_LEN]; |
36 | |
37 | u32 dot11rsna_stats_ccmp_format_errors; |
38 | u32 dot11rsna_stats_ccmp_replays; |
39 | u32 dot11rsna_stats_ccmp_decrypt_errors; |
40 | |
41 | int key_idx; |
42 | |
43 | struct crypto_aead *tfm; |
44 | |
45 | /* scratch buffers for virt_to_page() (crypto API) */ |
46 | u8 tx_aad[2 * AES_BLOCK_LEN]; |
47 | u8 rx_aad[2 * AES_BLOCK_LEN]; |
48 | }; |
49 | |
50 | static void *rtllib_ccmp_init(int key_idx) |
51 | { |
52 | struct rtllib_ccmp_data *priv; |
53 | |
54 | priv = kzalloc(size: sizeof(*priv), GFP_ATOMIC); |
55 | if (!priv) |
56 | goto fail; |
57 | priv->key_idx = key_idx; |
58 | |
59 | priv->tfm = crypto_alloc_aead(alg_name: "ccm(aes)" , type: 0, CRYPTO_ALG_ASYNC); |
60 | if (IS_ERR(ptr: priv->tfm)) { |
61 | pr_debug("Could not allocate crypto API aes\n" ); |
62 | priv->tfm = NULL; |
63 | goto fail; |
64 | } |
65 | return priv; |
66 | |
67 | fail: |
68 | if (priv) { |
69 | if (priv->tfm) |
70 | crypto_free_aead(tfm: priv->tfm); |
71 | kfree(objp: priv); |
72 | } |
73 | |
74 | return NULL; |
75 | } |
76 | |
77 | static void rtllib_ccmp_deinit(void *priv) |
78 | { |
79 | struct rtllib_ccmp_data *_priv = priv; |
80 | |
81 | if (_priv && _priv->tfm) |
82 | crypto_free_aead(tfm: _priv->tfm); |
83 | kfree(objp: priv); |
84 | } |
85 | |
86 | static int ccmp_init_iv_and_aad(struct ieee80211_hdr *hdr, |
87 | u8 *pn, u8 *iv, u8 *aad) |
88 | { |
89 | u8 *pos, qc = 0; |
90 | size_t aad_len; |
91 | u16 fc; |
92 | int a4_included, qc_included; |
93 | |
94 | fc = le16_to_cpu(hdr->frame_control); |
95 | a4_included = ieee80211_has_a4(fc: hdr->frame_control); |
96 | |
97 | qc_included = ((WLAN_FC_GET_TYPE(fc) == RTLLIB_FTYPE_DATA) && |
98 | (WLAN_FC_GET_STYPE(fc) & 0x80)); |
99 | aad_len = 22; |
100 | if (a4_included) |
101 | aad_len += 6; |
102 | if (qc_included) { |
103 | pos = (u8 *)&hdr->addr4; |
104 | if (a4_included) |
105 | pos += 6; |
106 | qc = *pos & 0x0f; |
107 | aad_len += 2; |
108 | } |
109 | /* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC |
110 | * mode authentication are not allowed to collide, yet both are derived |
111 | * from the same vector. We only set L := 1 here to indicate that the |
112 | * data size can be represented in (L+1) bytes. The CCM layer will take |
113 | * care of storing the data length in the top (L+1) bytes and setting |
114 | * and clearing the other bits as is required to derive the two IVs. |
115 | */ |
116 | iv[0] = 0x1; |
117 | |
118 | /* Nonce: QC | A2 | PN */ |
119 | iv[1] = qc; |
120 | memcpy(iv + 2, hdr->addr2, ETH_ALEN); |
121 | memcpy(iv + 8, pn, CCMP_PN_LEN); |
122 | |
123 | /* AAD: |
124 | * FC with bits 4..6 and 11..13 masked to zero; 14 is always one |
125 | * A1 | A2 | A3 |
126 | * SC with bits 4..15 (seq#) masked to zero |
127 | * A4 (if present) |
128 | * QC (if present) |
129 | */ |
130 | pos = (u8 *)hdr; |
131 | aad[0] = pos[0] & 0x8f; |
132 | aad[1] = pos[1] & 0xc7; |
133 | memcpy(&aad[2], &hdr->addr1, ETH_ALEN); |
134 | memcpy(&aad[8], &hdr->addr2, ETH_ALEN); |
135 | memcpy(&aad[14], &hdr->addr3, ETH_ALEN); |
136 | pos = (u8 *)&hdr->seq_ctrl; |
137 | aad[20] = pos[0] & 0x0f; |
138 | aad[21] = 0; /* all bits masked */ |
139 | memset(aad + 22, 0, 8); |
140 | if (a4_included) |
141 | memcpy(aad + 22, hdr->addr4, ETH_ALEN); |
142 | if (qc_included) { |
143 | aad[a4_included ? 28 : 22] = qc; |
144 | /* rest of QC masked */ |
145 | } |
146 | |
147 | return aad_len; |
148 | } |
149 | |
150 | static int rtllib_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv) |
151 | { |
152 | struct rtllib_ccmp_data *key = priv; |
153 | int i; |
154 | u8 *pos; |
155 | struct ieee80211_hdr *hdr; |
156 | struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb + |
157 | MAX_DEV_ADDR_SIZE); |
158 | if (skb_headroom(skb) < CCMP_HDR_LEN || |
159 | skb_tailroom(skb) < CCMP_MIC_LEN || |
160 | skb->len < hdr_len) |
161 | return -1; |
162 | |
163 | pos = skb_push(skb, CCMP_HDR_LEN); |
164 | memmove(pos, pos + CCMP_HDR_LEN, hdr_len); |
165 | pos += hdr_len; |
166 | |
167 | i = CCMP_PN_LEN - 1; |
168 | while (i >= 0) { |
169 | key->tx_pn[i]++; |
170 | if (key->tx_pn[i] != 0) |
171 | break; |
172 | i--; |
173 | } |
174 | |
175 | *pos++ = key->tx_pn[5]; |
176 | *pos++ = key->tx_pn[4]; |
177 | *pos++ = 0; |
178 | *pos++ = (key->key_idx << 6) | (1 << 5) /* Ext IV included */; |
179 | *pos++ = key->tx_pn[3]; |
180 | *pos++ = key->tx_pn[2]; |
181 | *pos++ = key->tx_pn[1]; |
182 | *pos++ = key->tx_pn[0]; |
183 | |
184 | hdr = (struct ieee80211_hdr *)skb->data; |
185 | if (!tcb_desc->bHwSec) { |
186 | struct aead_request *req; |
187 | struct scatterlist sg[2]; |
188 | u8 *aad = key->tx_aad; |
189 | u8 iv[AES_BLOCK_LEN]; |
190 | int aad_len, ret; |
191 | int data_len = skb->len - hdr_len - CCMP_HDR_LEN; |
192 | |
193 | req = aead_request_alloc(tfm: key->tfm, GFP_ATOMIC); |
194 | if (!req) |
195 | return -ENOMEM; |
196 | |
197 | aad_len = ccmp_init_iv_and_aad(hdr, pn: key->tx_pn, iv, aad); |
198 | |
199 | skb_put(skb, CCMP_MIC_LEN); |
200 | sg_init_table(sg, 2); |
201 | sg_set_buf(sg: &sg[0], buf: aad, buflen: aad_len); |
202 | sg_set_buf(sg: &sg[1], buf: skb->data + hdr_len + CCMP_HDR_LEN, |
203 | buflen: data_len + CCMP_MIC_LEN); |
204 | |
205 | aead_request_set_callback(req, flags: 0, NULL, NULL); |
206 | aead_request_set_ad(req, assoclen: aad_len); |
207 | aead_request_set_crypt(req, src: sg, dst: sg, cryptlen: data_len, iv); |
208 | |
209 | ret = crypto_aead_encrypt(req); |
210 | aead_request_free(req); |
211 | |
212 | return ret; |
213 | } |
214 | |
215 | return 0; |
216 | } |
217 | |
218 | static int rtllib_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv) |
219 | { |
220 | struct rtllib_ccmp_data *key = priv; |
221 | u8 keyidx, *pos; |
222 | struct ieee80211_hdr *hdr; |
223 | struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb + |
224 | MAX_DEV_ADDR_SIZE); |
225 | u8 pn[6]; |
226 | |
227 | if (skb->len < hdr_len + CCMP_HDR_LEN + CCMP_MIC_LEN) { |
228 | key->dot11rsna_stats_ccmp_format_errors++; |
229 | return -1; |
230 | } |
231 | |
232 | hdr = (struct ieee80211_hdr *)skb->data; |
233 | pos = skb->data + hdr_len; |
234 | keyidx = pos[3]; |
235 | if (!(keyidx & (1 << 5))) { |
236 | if (net_ratelimit()) { |
237 | pr_debug("CCMP: received packet without ExtIV flag from %pM\n" , |
238 | hdr->addr2); |
239 | } |
240 | key->dot11rsna_stats_ccmp_format_errors++; |
241 | return -2; |
242 | } |
243 | keyidx >>= 6; |
244 | if (key->key_idx != keyidx) { |
245 | pr_debug("CCMP: RX tkey->key_idx=%d frame keyidx=%d priv=%p\n" , |
246 | key->key_idx, keyidx, priv); |
247 | return -6; |
248 | } |
249 | if (!key->key_set) { |
250 | if (net_ratelimit()) { |
251 | pr_debug("CCMP: received packet from %pM with keyid=%d that does not have a configured key\n" , |
252 | hdr->addr2, keyidx); |
253 | } |
254 | return -3; |
255 | } |
256 | |
257 | pn[0] = pos[7]; |
258 | pn[1] = pos[6]; |
259 | pn[2] = pos[5]; |
260 | pn[3] = pos[4]; |
261 | pn[4] = pos[1]; |
262 | pn[5] = pos[0]; |
263 | pos += 8; |
264 | if (memcmp(p: pn, q: key->rx_pn, CCMP_PN_LEN) <= 0) { |
265 | key->dot11rsna_stats_ccmp_replays++; |
266 | return -4; |
267 | } |
268 | if (!tcb_desc->bHwSec) { |
269 | size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN; |
270 | struct aead_request *req; |
271 | struct scatterlist sg[2]; |
272 | u8 *aad = key->rx_aad; |
273 | u8 iv[AES_BLOCK_LEN]; |
274 | int aad_len, ret; |
275 | |
276 | req = aead_request_alloc(tfm: key->tfm, GFP_ATOMIC); |
277 | if (!req) |
278 | return -ENOMEM; |
279 | |
280 | aad_len = ccmp_init_iv_and_aad(hdr, pn, iv, aad); |
281 | |
282 | sg_init_table(sg, 2); |
283 | sg_set_buf(sg: &sg[0], buf: aad, buflen: aad_len); |
284 | sg_set_buf(sg: &sg[1], buf: pos, buflen: data_len); |
285 | |
286 | aead_request_set_callback(req, flags: 0, NULL, NULL); |
287 | aead_request_set_ad(req, assoclen: aad_len); |
288 | aead_request_set_crypt(req, src: sg, dst: sg, cryptlen: data_len, iv); |
289 | |
290 | ret = crypto_aead_decrypt(req); |
291 | aead_request_free(req); |
292 | |
293 | if (ret) { |
294 | if (net_ratelimit()) { |
295 | pr_debug("CCMP: decrypt failed: STA= %pM\n" , |
296 | hdr->addr2); |
297 | } |
298 | key->dot11rsna_stats_ccmp_decrypt_errors++; |
299 | return -5; |
300 | } |
301 | |
302 | memcpy(key->rx_pn, pn, CCMP_PN_LEN); |
303 | } |
304 | /* Remove hdr and MIC */ |
305 | memmove(skb->data + CCMP_HDR_LEN, skb->data, hdr_len); |
306 | skb_pull(skb, CCMP_HDR_LEN); |
307 | skb_trim(skb, len: skb->len - CCMP_MIC_LEN); |
308 | |
309 | return keyidx; |
310 | } |
311 | |
312 | static int rtllib_ccmp_set_key(void *key, int len, u8 *seq, void *priv) |
313 | { |
314 | struct rtllib_ccmp_data *data = priv; |
315 | int keyidx; |
316 | struct crypto_aead *tfm = data->tfm; |
317 | |
318 | keyidx = data->key_idx; |
319 | memset(data, 0, sizeof(*data)); |
320 | data->key_idx = keyidx; |
321 | data->tfm = tfm; |
322 | if (len == CCMP_TK_LEN) { |
323 | memcpy(data->key, key, CCMP_TK_LEN); |
324 | data->key_set = 1; |
325 | if (seq) { |
326 | data->rx_pn[0] = seq[5]; |
327 | data->rx_pn[1] = seq[4]; |
328 | data->rx_pn[2] = seq[3]; |
329 | data->rx_pn[3] = seq[2]; |
330 | data->rx_pn[4] = seq[1]; |
331 | data->rx_pn[5] = seq[0]; |
332 | } |
333 | if (crypto_aead_setauthsize(tfm: data->tfm, CCMP_MIC_LEN) || |
334 | crypto_aead_setkey(tfm: data->tfm, key: data->key, CCMP_TK_LEN)) |
335 | return -1; |
336 | } else if (len == 0) { |
337 | data->key_set = 0; |
338 | } else { |
339 | return -1; |
340 | } |
341 | |
342 | return 0; |
343 | } |
344 | |
345 | static int rtllib_ccmp_get_key(void *key, int len, u8 *seq, void *priv) |
346 | { |
347 | struct rtllib_ccmp_data *data = priv; |
348 | |
349 | if (len < CCMP_TK_LEN) |
350 | return -1; |
351 | |
352 | if (!data->key_set) |
353 | return 0; |
354 | memcpy(key, data->key, CCMP_TK_LEN); |
355 | |
356 | if (seq) { |
357 | seq[0] = data->tx_pn[5]; |
358 | seq[1] = data->tx_pn[4]; |
359 | seq[2] = data->tx_pn[3]; |
360 | seq[3] = data->tx_pn[2]; |
361 | seq[4] = data->tx_pn[1]; |
362 | seq[5] = data->tx_pn[0]; |
363 | } |
364 | |
365 | return CCMP_TK_LEN; |
366 | } |
367 | |
368 | static void rtllib_ccmp_print_stats(struct seq_file *m, void *priv) |
369 | { |
370 | struct rtllib_ccmp_data *ccmp = priv; |
371 | |
372 | seq_printf(m, |
373 | fmt: "key[%d] alg=CCMP key_set=%d tx_pn=%pM rx_pn=%pM format_errors=%d replays=%d decrypt_errors=%d\n" , |
374 | ccmp->key_idx, ccmp->key_set, |
375 | ccmp->tx_pn, ccmp->rx_pn, |
376 | ccmp->dot11rsna_stats_ccmp_format_errors, |
377 | ccmp->dot11rsna_stats_ccmp_replays, |
378 | ccmp->dot11rsna_stats_ccmp_decrypt_errors); |
379 | } |
380 | |
381 | static struct lib80211_crypto_ops rtllib_crypt_ccmp = { |
382 | .name = "R-CCMP" , |
383 | .init = rtllib_ccmp_init, |
384 | .deinit = rtllib_ccmp_deinit, |
385 | .encrypt_mpdu = rtllib_ccmp_encrypt, |
386 | .decrypt_mpdu = rtllib_ccmp_decrypt, |
387 | .encrypt_msdu = NULL, |
388 | .decrypt_msdu = NULL, |
389 | .set_key = rtllib_ccmp_set_key, |
390 | .get_key = rtllib_ccmp_get_key, |
391 | .print_stats = rtllib_ccmp_print_stats, |
392 | .extra_mpdu_prefix_len = CCMP_HDR_LEN, |
393 | .extra_mpdu_postfix_len = CCMP_MIC_LEN, |
394 | .owner = THIS_MODULE, |
395 | }; |
396 | |
397 | static int __init rtllib_crypto_ccmp_init(void) |
398 | { |
399 | return lib80211_register_crypto_ops(ops: &rtllib_crypt_ccmp); |
400 | } |
401 | |
402 | static void __exit rtllib_crypto_ccmp_exit(void) |
403 | { |
404 | lib80211_unregister_crypto_ops(ops: &rtllib_crypt_ccmp); |
405 | } |
406 | |
407 | module_init(rtllib_crypto_ccmp_init); |
408 | module_exit(rtllib_crypto_ccmp_exit); |
409 | |
410 | MODULE_LICENSE("GPL" ); |
411 | |