1/*	$OpenBSD: ieee80211_crypto_ccmp.c,v 1.22 2020/05/15 14:21:09 stsp Exp $	*/
2
3/*-
4 * Copyright (c) 2008 Damien Bergamini <damien.bergamini@free.fr>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19/*
20 * This code implements the CTR with CBC-MAC protocol (CCMP) defined in
21 * IEEE Std 802.11-2007 section 8.3.3.
22 */
23
24#include <sys/param.h>
25#include <sys/systm.h>
26#include <sys/mbuf.h>
27#include <sys/malloc.h>
28#include <sys/kernel.h>
29#include <sys/socket.h>
30#include <sys/endian.h>
31
32#include <net/if.h>
33#include <net/if_dl.h>
34#include <net/if_media.h>
35
36#include <netinet/in.h>
37#include <netinet/if_ether.h>
38
39#include <net80211/ieee80211_var.h>
40#include <net80211/ieee80211_crypto.h>
41
42#include <crypto/aes.h>
43
44/* CCMP software crypto context */
45struct ieee80211_ccmp_ctx {
46	AES_CTX		aesctx;
47};
48
49/*
50 * Initialize software crypto context.  This function can be overridden
51 * by drivers doing hardware crypto.
52 */
53int
54ieee80211_ccmp_set_key(struct ieee80211com *ic, struct ieee80211_key *k)
55{
56	struct ieee80211_ccmp_ctx *ctx;
57
58	ctx = malloc(sizeof(*ctx), M_DEVBUF, M_NOWAIT | M_ZERO);
59	if (ctx == NULL)
60		return ENOMEM;
61	AES_Setkey(&ctx->aesctx, k->k_key, 16);
62	k->k_priv = ctx;
63	return 0;
64}
65
66void
67ieee80211_ccmp_delete_key(struct ieee80211com *ic, struct ieee80211_key *k)
68{
69	if (k->k_priv != NULL) {
70		explicit_bzero(k->k_priv, sizeof(struct ieee80211_ccmp_ctx));
71		free(k->k_priv, M_DEVBUF, sizeof(struct ieee80211_ccmp_ctx));
72	}
73	k->k_priv = NULL;
74}
75
76/*-
77 * Counter with CBC-MAC (CCM) - see RFC3610.
78 * CCMP uses the following CCM parameters: M = 8, L = 2
79 */
80static void
81ieee80211_ccmp_phase1(AES_CTX *ctx, const struct ieee80211_frame *wh,
82    u_int64_t pn, int lm, u_int8_t b[16], u_int8_t a[16], u_int8_t s0[16])
83{
84	u_int8_t auth[32], nonce[13];
85	u_int8_t *aad;
86	u_int8_t tid = 0;
87	int la, i;
88
89	/* construct AAD (additional authenticated data) */
90	aad = &auth[2];	/* skip l(a), will be filled later */
91	*aad = wh->i_fc[0];
92	/* 11w: conditionally mask subtype field */
93	if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
94	    IEEE80211_FC0_TYPE_DATA)
95		*aad &= ~IEEE80211_FC0_SUBTYPE_MASK |
96		   IEEE80211_FC0_SUBTYPE_QOS;
97	aad++;
98	/* protected bit is already set in wh */
99	*aad = wh->i_fc[1];
100	*aad &= ~(IEEE80211_FC1_RETRY | IEEE80211_FC1_PWR_MGT |
101	    IEEE80211_FC1_MORE_DATA);
102	/* 11n: conditionally mask order bit */
103	if (ieee80211_has_qos(wh))
104		*aad &= ~IEEE80211_FC1_ORDER;
105	aad++;
106	IEEE80211_ADDR_COPY(aad, wh->i_addr1); aad += IEEE80211_ADDR_LEN;
107	IEEE80211_ADDR_COPY(aad, wh->i_addr2); aad += IEEE80211_ADDR_LEN;
108	IEEE80211_ADDR_COPY(aad, wh->i_addr3); aad += IEEE80211_ADDR_LEN;
109	*aad++ = wh->i_seq[0] & ~0xf0;
110	*aad++ = 0;
111	if (ieee80211_has_addr4(wh)) {
112		IEEE80211_ADDR_COPY(aad,
113		    ((const struct ieee80211_frame_addr4 *)wh)->i_addr4);
114		aad += IEEE80211_ADDR_LEN;
115	}
116	if (ieee80211_has_qos(wh)) {
117		/*
118		 * XXX 802.11-2012 11.4.3.3.3 g says the A-MSDU present bit
119		 * must be set here if both STAs are SPP A-MSDU capable.
120		 */
121		*aad++ = tid = ieee80211_get_qos(wh) & IEEE80211_QOS_TID;
122		*aad++ = 0;
123	}
124
125	/* construct CCM nonce */
126	nonce[ 0] = tid;
127	if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
128	    IEEE80211_FC0_TYPE_MGT)
129		nonce[0] |= 1 << 4;	/* 11w: set management bit */
130	IEEE80211_ADDR_COPY(&nonce[1], wh->i_addr2);
131	nonce[ 7] = pn >> 40;	/* PN5 */
132	nonce[ 8] = pn >> 32;	/* PN4 */
133	nonce[ 9] = pn >> 24;	/* PN3 */
134	nonce[10] = pn >> 16;	/* PN2 */
135	nonce[11] = pn >> 8;	/* PN1 */
136	nonce[12] = pn;		/* PN0 */
137
138	/* add 2 authentication blocks (including l(a) and padded AAD) */
139	la = aad - &auth[2];		/* fill l(a) */
140	auth[0] = la >> 8;
141	auth[1] = la & 0xff;
142	memset(aad, 0, 30 - la);	/* pad AAD with zeros */
143
144	/* construct first block B_0 */
145	b[ 0] = 89;	/* Flags = 64*Adata + 8*((M-2)/2) + (L-1) */
146	memcpy(&b[1], nonce, 13);
147	b[14] = lm >> 8;
148	b[15] = lm & 0xff;
149	AES_Encrypt(ctx, b, b);
150
151	for (i = 0; i < 16; i++)
152		b[i] ^= auth[i];
153	AES_Encrypt(ctx, b, b);
154	for (i = 0; i < 16; i++)
155		b[i] ^= auth[16 + i];
156	AES_Encrypt(ctx, b, b);
157
158	/* construct S_0 */
159	a[ 0] = 1;	/* Flags = L' = (L-1) */
160	memcpy(&a[1], nonce, 13);
161	a[14] = a[15] = 0;
162	AES_Encrypt(ctx, a, s0);
163}
164
165struct mbuf *
166ieee80211_ccmp_encrypt(struct ieee80211com *ic, struct mbuf *m0,
167    struct ieee80211_key *k)
168{
169	struct ieee80211_ccmp_ctx *ctx = k->k_priv;
170	const struct ieee80211_frame *wh;
171	const u_int8_t *src;
172	u_int8_t *ivp, *mic, *dst;
173	u_int8_t a[16], b[16], s0[16], s[16];
174	struct mbuf *n0, *m, *n;
175	int hdrlen, left, moff, noff, len;
176	u_int16_t ctr;
177	int i, j;
178
179	MGET(n0, M_DONTWAIT, m0->m_type);
180	if (n0 == NULL)
181		goto nospace;
182	if (m_dup_pkthdr(n0, m0, M_DONTWAIT))
183		goto nospace;
184	n0->m_pkthdr.len += IEEE80211_CCMP_HDRLEN;
185	n0->m_len = MHLEN;
186	if (n0->m_pkthdr.len >= MINCLSIZE - IEEE80211_CCMP_MICLEN) {
187		MCLGET(n0, M_DONTWAIT);
188		if (n0->m_flags & M_EXT)
189			n0->m_len = n0->m_ext.ext_size;
190	}
191	if (n0->m_len > n0->m_pkthdr.len)
192		n0->m_len = n0->m_pkthdr.len;
193
194	/* copy 802.11 header */
195	wh = mtod(m0, struct ieee80211_frame *);
196	hdrlen = ieee80211_get_hdrlen(wh);
197	memcpy(mtod(n0, caddr_t), wh, hdrlen);
198
199	k->k_tsc++;	/* increment the 48-bit PN */
200
201	/* construct CCMP header */
202	ivp = mtod(n0, u_int8_t *) + hdrlen;
203	ivp[0] = k->k_tsc;		/* PN0 */
204	ivp[1] = k->k_tsc >> 8;		/* PN1 */
205	ivp[2] = 0;			/* Rsvd */
206	ivp[3] = k->k_id << 6 | IEEE80211_WEP_EXTIV;	/* KeyID | ExtIV */
207	ivp[4] = k->k_tsc >> 16;	/* PN2 */
208	ivp[5] = k->k_tsc >> 24;	/* PN3 */
209	ivp[6] = k->k_tsc >> 32;	/* PN4 */
210	ivp[7] = k->k_tsc >> 40;	/* PN5 */
211
212	/* construct initial B, A and S_0 blocks */
213	ieee80211_ccmp_phase1(&ctx->aesctx, wh, k->k_tsc,
214	    m0->m_pkthdr.len - hdrlen, b, a, s0);
215
216	/* construct S_1 */
217	ctr = 1;
218	a[14] = ctr >> 8;
219	a[15] = ctr & 0xff;
220	AES_Encrypt(&ctx->aesctx, a, s);
221
222	/* encrypt frame body and compute MIC */
223	j = 0;
224	m = m0;
225	n = n0;
226	moff = hdrlen;
227	noff = hdrlen + IEEE80211_CCMP_HDRLEN;
228	left = m0->m_pkthdr.len - moff;
229	while (left > 0) {
230		if (moff == m->m_len) {
231			/* nothing left to copy from m */
232			m = m->m_next;
233			moff = 0;
234		}
235		if (noff == n->m_len) {
236			/* n is full and there's more data to copy */
237			MGET(n->m_next, M_DONTWAIT, n->m_type);
238			if (n->m_next == NULL)
239				goto nospace;
240			n = n->m_next;
241			n->m_len = MLEN;
242			if (left >= MINCLSIZE - IEEE80211_CCMP_MICLEN) {
243				MCLGET(n, M_DONTWAIT);
244				if (n->m_flags & M_EXT)
245					n->m_len = n->m_ext.ext_size;
246			}
247			if (n->m_len > left)
248				n->m_len = left;
249			noff = 0;
250		}
251		len = min(m->m_len - moff, n->m_len - noff);
252
253		src = mtod(m, u_int8_t *) + moff;
254		dst = mtod(n, u_int8_t *) + noff;
255		for (i = 0; i < len; i++) {
256			/* update MIC with clear text */
257			b[j] ^= src[i];
258			/* encrypt message */
259			dst[i] = src[i] ^ s[j];
260			if (++j < 16)
261				continue;
262			/* we have a full block, encrypt MIC */
263			AES_Encrypt(&ctx->aesctx, b, b);
264			/* construct a new S_ctr block */
265			ctr++;
266			a[14] = ctr >> 8;
267			a[15] = ctr & 0xff;
268			AES_Encrypt(&ctx->aesctx, a, s);
269			j = 0;
270		}
271
272		moff += len;
273		noff += len;
274		left -= len;
275	}
276	if (j != 0)	/* partial block, encrypt MIC */
277		AES_Encrypt(&ctx->aesctx, b, b);
278
279	/* reserve trailing space for MIC */
280	if (m_trailingspace(n) < IEEE80211_CCMP_MICLEN) {
281		MGET(n->m_next, M_DONTWAIT, n->m_type);
282		if (n->m_next == NULL)
283			goto nospace;
284		n = n->m_next;
285		n->m_len = 0;
286	}
287	/* finalize MIC, U := T XOR first-M-bytes( S_0 ) */
288	mic = mtod(n, u_int8_t *) + n->m_len;
289	for (i = 0; i < IEEE80211_CCMP_MICLEN; i++)
290		mic[i] = b[i] ^ s0[i];
291	n->m_len += IEEE80211_CCMP_MICLEN;
292	n0->m_pkthdr.len += IEEE80211_CCMP_MICLEN;
293
294	m_freem(m0);
295	return n0;
296 nospace:
297	ic->ic_stats.is_tx_nombuf++;
298	m_freem(m0);
299	m_freem(n0);
300	return NULL;
301}
302
303int
304ieee80211_ccmp_get_pn(uint64_t *pn, uint64_t **prsc, struct mbuf *m,
305    struct ieee80211_key *k)
306{
307	struct ieee80211_frame *wh;
308	int hdrlen;
309	const u_int8_t *ivp;
310
311	wh = mtod(m, struct ieee80211_frame *);
312	hdrlen = ieee80211_get_hdrlen(wh);
313	if (m->m_pkthdr.len < hdrlen + IEEE80211_CCMP_HDRLEN)
314		return EINVAL;
315
316	ivp = (u_int8_t *)wh + hdrlen;
317
318	/* check that ExtIV bit is set */
319	if (!(ivp[3] & IEEE80211_WEP_EXTIV))
320		return EINVAL;
321
322	/* retrieve last seen packet number for this frame type/priority */
323	if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
324	    IEEE80211_FC0_TYPE_DATA) {
325		u_int8_t tid = ieee80211_has_qos(wh) ?
326		    ieee80211_get_qos(wh) & IEEE80211_QOS_TID : 0;
327		*prsc = &k->k_rsc[tid];
328	} else	/* 11w: management frames have their own counters */
329		*prsc = &k->k_mgmt_rsc;
330
331	/* extract the 48-bit PN from the CCMP header */
332	*pn = (u_int64_t)ivp[0]      |
333	     (u_int64_t)ivp[1] <<  8 |
334	     (u_int64_t)ivp[4] << 16 |
335	     (u_int64_t)ivp[5] << 24 |
336	     (u_int64_t)ivp[6] << 32 |
337	     (u_int64_t)ivp[7] << 40;
338
339	return 0;
340}
341
342struct mbuf *
343ieee80211_ccmp_decrypt(struct ieee80211com *ic, struct mbuf *m0,
344    struct ieee80211_key *k)
345{
346	struct ieee80211_ccmp_ctx *ctx = k->k_priv;
347	struct ieee80211_frame *wh;
348	u_int64_t pn, *prsc;
349	const u_int8_t *src;
350	u_int8_t *dst;
351	u_int8_t mic0[IEEE80211_CCMP_MICLEN];
352	u_int8_t a[16], b[16], s0[16], s[16];
353	struct mbuf *n0, *m, *n;
354	int hdrlen, left, moff, noff, len;
355	u_int16_t ctr;
356	int i, j;
357
358	wh = mtod(m0, struct ieee80211_frame *);
359	hdrlen = ieee80211_get_hdrlen(wh);
360	if (m0->m_pkthdr.len < hdrlen + IEEE80211_CCMP_HDRLEN +
361	    IEEE80211_CCMP_MICLEN) {
362		m_freem(m0);
363		return NULL;
364	}
365
366	/*
367	 * Get the frame's Packet Number (PN) and a pointer to our last-seen
368	 * Receive Sequence Counter (RSC) which we can use to detect replays.
369	 */
370	if (ieee80211_ccmp_get_pn(&pn, &prsc, m0, k) != 0) {
371		m_freem(m0);
372		return NULL;
373	}
374	if (pn <= *prsc) {
375		/* replayed frame, discard */
376		ic->ic_stats.is_ccmp_replays++;
377		m_freem(m0);
378		return NULL;
379	}
380
381	MGET(n0, M_DONTWAIT, m0->m_type);
382	if (n0 == NULL)
383		goto nospace;
384	if (m_dup_pkthdr(n0, m0, M_DONTWAIT))
385		goto nospace;
386	n0->m_pkthdr.len -= IEEE80211_CCMP_HDRLEN + IEEE80211_CCMP_MICLEN;
387	n0->m_len = MHLEN;
388	if (n0->m_pkthdr.len >= MINCLSIZE) {
389		MCLGET(n0, M_DONTWAIT);
390		if (n0->m_flags & M_EXT)
391			n0->m_len = n0->m_ext.ext_size;
392	}
393	if (n0->m_len > n0->m_pkthdr.len)
394		n0->m_len = n0->m_pkthdr.len;
395
396	/* construct initial B, A and S_0 blocks */
397	ieee80211_ccmp_phase1(&ctx->aesctx, wh, pn,
398	    n0->m_pkthdr.len - hdrlen, b, a, s0);
399
400	/* copy 802.11 header and clear protected bit */
401	memcpy(mtod(n0, caddr_t), wh, hdrlen);
402	wh = mtod(n0, struct ieee80211_frame *);
403	wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
404
405	/* construct S_1 */
406	ctr = 1;
407	a[14] = ctr >> 8;
408	a[15] = ctr & 0xff;
409	AES_Encrypt(&ctx->aesctx, a, s);
410
411	/* decrypt frame body and compute MIC */
412	j = 0;
413	m = m0;
414	n = n0;
415	moff = hdrlen + IEEE80211_CCMP_HDRLEN;
416	noff = hdrlen;
417	left = n0->m_pkthdr.len - noff;
418	while (left > 0) {
419		if (moff == m->m_len) {
420			/* nothing left to copy from m */
421			m = m->m_next;
422			moff = 0;
423		}
424		if (noff == n->m_len) {
425			/* n is full and there's more data to copy */
426			MGET(n->m_next, M_DONTWAIT, n->m_type);
427			if (n->m_next == NULL)
428				goto nospace;
429			n = n->m_next;
430			n->m_len = MLEN;
431			if (left >= MINCLSIZE) {
432				MCLGET(n, M_DONTWAIT);
433				if (n->m_flags & M_EXT)
434					n->m_len = n->m_ext.ext_size;
435			}
436			if (n->m_len > left)
437				n->m_len = left;
438			noff = 0;
439		}
440		len = min(m->m_len - moff, n->m_len - noff);
441
442		src = mtod(m, u_int8_t *) + moff;
443		dst = mtod(n, u_int8_t *) + noff;
444		for (i = 0; i < len; i++) {
445			/* decrypt message */
446			dst[i] = src[i] ^ s[j];
447			/* update MIC with clear text */
448			b[j] ^= dst[i];
449			if (++j < 16)
450				continue;
451			/* we have a full block, encrypt MIC */
452			AES_Encrypt(&ctx->aesctx, b, b);
453			/* construct a new S_ctr block */
454			ctr++;
455			a[14] = ctr >> 8;
456			a[15] = ctr & 0xff;
457			AES_Encrypt(&ctx->aesctx, a, s);
458			j = 0;
459		}
460
461		moff += len;
462		noff += len;
463		left -= len;
464	}
465	if (j != 0)	/* partial block, encrypt MIC */
466		AES_Encrypt(&ctx->aesctx, b, b);
467
468	/* finalize MIC, U := T XOR first-M-bytes( S_0 ) */
469	for (i = 0; i < IEEE80211_CCMP_MICLEN; i++)
470		b[i] ^= s0[i];
471
472	/* check that it matches the MIC in received frame */
473	m_copydata(m, moff, IEEE80211_CCMP_MICLEN, mic0);
474	if (timingsafe_bcmp(mic0, b, IEEE80211_CCMP_MICLEN) != 0) {
475		ic->ic_stats.is_ccmp_dec_errs++;
476		m_freem(m0);
477		m_freem(n0);
478		return NULL;
479	}
480
481	/* update last seen packet number (MIC is validated) */
482	*prsc = pn;
483
484	m_freem(m0);
485	return n0;
486 nospace:
487	ic->ic_stats.is_rx_nombuf++;
488	m_freem(m0);
489	m_freem(n0);
490	return NULL;
491}
492