File Index
Files and directories
Sign in
FILE

libipw_crypto_ccmp.c

File preview with syntax highlighting.

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