xref: /haiku/src/libs/compat/openbsd_wlan/net80211/ieee80211_crypto_ccmp.c (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
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 */
45 struct 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  */
53 int
54 ieee80211_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 
66 void
67 ieee80211_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  */
80 static void
81 ieee80211_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 
165 struct mbuf *
166 ieee80211_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 
303 int
304 ieee80211_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 
342 struct mbuf *
343 ieee80211_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