xref: /openbsd-src/sys/net80211/ieee80211_crypto_ccmp.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
1 /*	$OpenBSD: ieee80211_crypto_ccmp.c,v 1.18 2015/11/24 13:45:06 mpi 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/rijndael.h>
43 
44 /* CCMP software crypto context */
45 struct ieee80211_ccmp_ctx {
46 	rijndael_ctx	rijndael;
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 	rijndael_set_key_enc_only(&ctx->rijndael, k->k_key, 128);
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 		free(k->k_priv, M_DEVBUF, 0);
71 	k->k_priv = NULL;
72 }
73 
74 /*-
75  * Counter with CBC-MAC (CCM) - see RFC3610.
76  * CCMP uses the following CCM parameters: M = 8, L = 2
77  */
78 static void
79 ieee80211_ccmp_phase1(rijndael_ctx *ctx, const struct ieee80211_frame *wh,
80     u_int64_t pn, int lm, u_int8_t b[16], u_int8_t a[16], u_int8_t s0[16])
81 {
82 	u_int8_t auth[32], nonce[13];
83 	u_int8_t *aad;
84 	u_int8_t tid = 0;
85 	int la, i;
86 
87 	/* construct AAD (additional authenticated data) */
88 	aad = &auth[2];	/* skip l(a), will be filled later */
89 	*aad = wh->i_fc[0];
90 	/* 11w: conditionally mask subtype field */
91 	if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
92 	    IEEE80211_FC0_TYPE_DATA)
93 		*aad &= ~IEEE80211_FC0_SUBTYPE_MASK |
94 		   IEEE80211_FC0_SUBTYPE_QOS;
95 	aad++;
96 	/* protected bit is already set in wh */
97 	*aad = wh->i_fc[1];
98 	*aad &= ~(IEEE80211_FC1_RETRY | IEEE80211_FC1_PWR_MGT |
99 	    IEEE80211_FC1_MORE_DATA);
100 	/* 11n: conditionally mask order bit */
101 	if (ieee80211_has_qos(wh))
102 		*aad &= ~IEEE80211_FC1_ORDER;
103 	aad++;
104 	IEEE80211_ADDR_COPY(aad, wh->i_addr1); aad += IEEE80211_ADDR_LEN;
105 	IEEE80211_ADDR_COPY(aad, wh->i_addr2); aad += IEEE80211_ADDR_LEN;
106 	IEEE80211_ADDR_COPY(aad, wh->i_addr3); aad += IEEE80211_ADDR_LEN;
107 	*aad++ = wh->i_seq[0] & ~0xf0;
108 	*aad++ = 0;
109 	if (ieee80211_has_addr4(wh)) {
110 		IEEE80211_ADDR_COPY(aad,
111 		    ((const struct ieee80211_frame_addr4 *)wh)->i_addr4);
112 		aad += IEEE80211_ADDR_LEN;
113 	}
114 	if (ieee80211_has_qos(wh)) {
115 		/*
116 		 * XXX 802.11-2012 11.4.3.3.3 g says the A-MSDU present bit
117 		 * must be set here if both STAs are SPP A-MSDU capable.
118 		 */
119 		*aad++ = tid = ieee80211_get_qos(wh) & IEEE80211_QOS_TID;
120 		*aad++ = 0;
121 	}
122 
123 	/* construct CCM nonce */
124 	nonce[ 0] = tid;
125 	if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
126 	    IEEE80211_FC0_TYPE_MGT)
127 		nonce[0] |= 1 << 4;	/* 11w: set management bit */
128 	IEEE80211_ADDR_COPY(&nonce[1], wh->i_addr2);
129 	nonce[ 7] = pn >> 40;	/* PN5 */
130 	nonce[ 8] = pn >> 32;	/* PN4 */
131 	nonce[ 9] = pn >> 24;	/* PN3 */
132 	nonce[10] = pn >> 16;	/* PN2 */
133 	nonce[11] = pn >> 8;	/* PN1 */
134 	nonce[12] = pn;		/* PN0 */
135 
136 	/* add 2 authentication blocks (including l(a) and padded AAD) */
137 	la = aad - &auth[2];		/* fill l(a) */
138 	auth[0] = la >> 8;
139 	auth[1] = la & 0xff;
140 	memset(aad, 0, 30 - la);	/* pad AAD with zeros */
141 
142 	/* construct first block B_0 */
143 	b[ 0] = 89;	/* Flags = 64*Adata + 8*((M-2)/2) + (L-1) */
144 	memcpy(&b[1], nonce, 13);
145 	b[14] = lm >> 8;
146 	b[15] = lm & 0xff;
147 	rijndael_encrypt(ctx, b, b);
148 
149 	for (i = 0; i < 16; i++)
150 		b[i] ^= auth[i];
151 	rijndael_encrypt(ctx, b, b);
152 	for (i = 0; i < 16; i++)
153 		b[i] ^= auth[16 + i];
154 	rijndael_encrypt(ctx, b, b);
155 
156 	/* construct S_0 */
157 	a[ 0] = 1;	/* Flags = L' = (L-1) */
158 	memcpy(&a[1], nonce, 13);
159 	a[14] = a[15] = 0;
160 	rijndael_encrypt(ctx, a, s0);
161 }
162 
163 struct mbuf *
164 ieee80211_ccmp_encrypt(struct ieee80211com *ic, struct mbuf *m0,
165     struct ieee80211_key *k)
166 {
167 	struct ieee80211_ccmp_ctx *ctx = k->k_priv;
168 	const struct ieee80211_frame *wh;
169 	const u_int8_t *src;
170 	u_int8_t *ivp, *mic, *dst;
171 	u_int8_t a[16], b[16], s0[16], s[16];
172 	struct mbuf *n0, *m, *n;
173 	int hdrlen, left, moff, noff, len;
174 	u_int16_t ctr;
175 	int i, j;
176 
177 	MGET(n0, M_DONTWAIT, m0->m_type);
178 	if (n0 == NULL)
179 		goto nospace;
180 	if (m_dup_pkthdr(n0, m0, M_DONTWAIT))
181 		goto nospace;
182 	n0->m_pkthdr.len += IEEE80211_CCMP_HDRLEN;
183 	n0->m_len = MHLEN;
184 	if (n0->m_pkthdr.len >= MINCLSIZE - IEEE80211_CCMP_MICLEN) {
185 		MCLGET(n0, M_DONTWAIT);
186 		if (n0->m_flags & M_EXT)
187 			n0->m_len = n0->m_ext.ext_size;
188 	}
189 	if (n0->m_len > n0->m_pkthdr.len)
190 		n0->m_len = n0->m_pkthdr.len;
191 
192 	/* copy 802.11 header */
193 	wh = mtod(m0, struct ieee80211_frame *);
194 	hdrlen = ieee80211_get_hdrlen(wh);
195 	memcpy(mtod(n0, caddr_t), wh, hdrlen);
196 
197 	k->k_tsc++;	/* increment the 48-bit PN */
198 
199 	/* construct CCMP header */
200 	ivp = mtod(n0, u_int8_t *) + hdrlen;
201 	ivp[0] = k->k_tsc;		/* PN0 */
202 	ivp[1] = k->k_tsc >> 8;		/* PN1 */
203 	ivp[2] = 0;			/* Rsvd */
204 	ivp[3] = k->k_id << 6 | IEEE80211_WEP_EXTIV;	/* KeyID | ExtIV */
205 	ivp[4] = k->k_tsc >> 16;	/* PN2 */
206 	ivp[5] = k->k_tsc >> 24;	/* PN3 */
207 	ivp[6] = k->k_tsc >> 32;	/* PN4 */
208 	ivp[7] = k->k_tsc >> 40;	/* PN5 */
209 
210 	/* construct initial B, A and S_0 blocks */
211 	ieee80211_ccmp_phase1(&ctx->rijndael, wh, k->k_tsc,
212 	    m0->m_pkthdr.len - hdrlen, b, a, s0);
213 
214 	/* construct S_1 */
215 	ctr = 1;
216 	a[14] = ctr >> 8;
217 	a[15] = ctr & 0xff;
218 	rijndael_encrypt(&ctx->rijndael, a, s);
219 
220 	/* encrypt frame body and compute MIC */
221 	j = 0;
222 	m = m0;
223 	n = n0;
224 	moff = hdrlen;
225 	noff = hdrlen + IEEE80211_CCMP_HDRLEN;
226 	left = m0->m_pkthdr.len - moff;
227 	while (left > 0) {
228 		if (moff == m->m_len) {
229 			/* nothing left to copy from m */
230 			m = m->m_next;
231 			moff = 0;
232 		}
233 		if (noff == n->m_len) {
234 			/* n is full and there's more data to copy */
235 			MGET(n->m_next, M_DONTWAIT, n->m_type);
236 			if (n->m_next == NULL)
237 				goto nospace;
238 			n = n->m_next;
239 			n->m_len = MLEN;
240 			if (left >= MINCLSIZE - IEEE80211_CCMP_MICLEN) {
241 				MCLGET(n, M_DONTWAIT);
242 				if (n->m_flags & M_EXT)
243 					n->m_len = n->m_ext.ext_size;
244 			}
245 			if (n->m_len > left)
246 				n->m_len = left;
247 			noff = 0;
248 		}
249 		len = min(m->m_len - moff, n->m_len - noff);
250 
251 		src = mtod(m, u_int8_t *) + moff;
252 		dst = mtod(n, u_int8_t *) + noff;
253 		for (i = 0; i < len; i++) {
254 			/* update MIC with clear text */
255 			b[j] ^= src[i];
256 			/* encrypt message */
257 			dst[i] = src[i] ^ s[j];
258 			if (++j < 16)
259 				continue;
260 			/* we have a full block, encrypt MIC */
261 			rijndael_encrypt(&ctx->rijndael, b, b);
262 			/* construct a new S_ctr block */
263 			ctr++;
264 			a[14] = ctr >> 8;
265 			a[15] = ctr & 0xff;
266 			rijndael_encrypt(&ctx->rijndael, a, s);
267 			j = 0;
268 		}
269 
270 		moff += len;
271 		noff += len;
272 		left -= len;
273 	}
274 	if (j != 0)	/* partial block, encrypt MIC */
275 		rijndael_encrypt(&ctx->rijndael, b, b);
276 
277 	/* reserve trailing space for MIC */
278 	if (M_TRAILINGSPACE(n) < IEEE80211_CCMP_MICLEN) {
279 		MGET(n->m_next, M_DONTWAIT, n->m_type);
280 		if (n->m_next == NULL)
281 			goto nospace;
282 		n = n->m_next;
283 		n->m_len = 0;
284 	}
285 	/* finalize MIC, U := T XOR first-M-bytes( S_0 ) */
286 	mic = mtod(n, u_int8_t *) + n->m_len;
287 	for (i = 0; i < IEEE80211_CCMP_MICLEN; i++)
288 		mic[i] = b[i] ^ s0[i];
289 	n->m_len += IEEE80211_CCMP_MICLEN;
290 	n0->m_pkthdr.len += IEEE80211_CCMP_MICLEN;
291 
292 	m_freem(m0);
293 	return n0;
294  nospace:
295 	ic->ic_stats.is_tx_nombuf++;
296 	m_freem(m0);
297 	m_freem(n0);
298 	return NULL;
299 }
300 
301 struct mbuf *
302 ieee80211_ccmp_decrypt(struct ieee80211com *ic, struct mbuf *m0,
303     struct ieee80211_key *k)
304 {
305 	struct ieee80211_ccmp_ctx *ctx = k->k_priv;
306 	struct ieee80211_frame *wh;
307 	u_int64_t pn, *prsc;
308 	const u_int8_t *ivp, *src;
309 	u_int8_t *dst;
310 	u_int8_t mic0[IEEE80211_CCMP_MICLEN];
311 	u_int8_t a[16], b[16], s0[16], s[16];
312 	struct mbuf *n0, *m, *n;
313 	int hdrlen, left, moff, noff, len;
314 	u_int16_t ctr;
315 	int i, j;
316 
317 	wh = mtod(m0, struct ieee80211_frame *);
318 	hdrlen = ieee80211_get_hdrlen(wh);
319 	ivp = (u_int8_t *)wh + hdrlen;
320 
321 	if (m0->m_pkthdr.len < hdrlen + IEEE80211_CCMP_HDRLEN +
322 	    IEEE80211_CCMP_MICLEN) {
323 		m_freem(m0);
324 		return NULL;
325 	}
326 	/* check that ExtIV bit is set */
327 	if (!(ivp[3] & IEEE80211_WEP_EXTIV)) {
328 		m_freem(m0);
329 		return NULL;
330 	}
331 
332 	/* retrieve last seen packet number for this frame type/priority */
333 	if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
334 	    IEEE80211_FC0_TYPE_DATA) {
335 		u_int8_t tid = ieee80211_has_qos(wh) ?
336 		    ieee80211_get_qos(wh) & IEEE80211_QOS_TID : 0;
337 		prsc = &k->k_rsc[tid];
338 	} else	/* 11w: management frames have their own counters */
339 		prsc = &k->k_mgmt_rsc;
340 
341 	/* extract the 48-bit PN from the CCMP header */
342 	pn = (u_int64_t)ivp[0]       |
343 	     (u_int64_t)ivp[1] <<  8 |
344 	     (u_int64_t)ivp[4] << 16 |
345 	     (u_int64_t)ivp[5] << 24 |
346 	     (u_int64_t)ivp[6] << 32 |
347 	     (u_int64_t)ivp[7] << 40;
348 	if (pn <= *prsc) {
349 		/* replayed frame, discard */
350 		ic->ic_stats.is_ccmp_replays++;
351 		m_freem(m0);
352 		return NULL;
353 	}
354 
355 	MGET(n0, M_DONTWAIT, m0->m_type);
356 	if (n0 == NULL)
357 		goto nospace;
358 	if (m_dup_pkthdr(n0, m0, M_DONTWAIT))
359 		goto nospace;
360 	n0->m_pkthdr.len -= IEEE80211_CCMP_HDRLEN + IEEE80211_CCMP_MICLEN;
361 	n0->m_len = MHLEN;
362 	if (n0->m_pkthdr.len >= MINCLSIZE) {
363 		MCLGET(n0, M_DONTWAIT);
364 		if (n0->m_flags & M_EXT)
365 			n0->m_len = n0->m_ext.ext_size;
366 	}
367 	if (n0->m_len > n0->m_pkthdr.len)
368 		n0->m_len = n0->m_pkthdr.len;
369 
370 	/* construct initial B, A and S_0 blocks */
371 	ieee80211_ccmp_phase1(&ctx->rijndael, wh, pn,
372 	    n0->m_pkthdr.len - hdrlen, b, a, s0);
373 
374 	/* copy 802.11 header and clear protected bit */
375 	memcpy(mtod(n0, caddr_t), wh, hdrlen);
376 	wh = mtod(n0, struct ieee80211_frame *);
377 	wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
378 
379 	/* construct S_1 */
380 	ctr = 1;
381 	a[14] = ctr >> 8;
382 	a[15] = ctr & 0xff;
383 	rijndael_encrypt(&ctx->rijndael, a, s);
384 
385 	/* decrypt frame body and compute MIC */
386 	j = 0;
387 	m = m0;
388 	n = n0;
389 	moff = hdrlen + IEEE80211_CCMP_HDRLEN;
390 	noff = hdrlen;
391 	left = n0->m_pkthdr.len - noff;
392 	while (left > 0) {
393 		if (moff == m->m_len) {
394 			/* nothing left to copy from m */
395 			m = m->m_next;
396 			moff = 0;
397 		}
398 		if (noff == n->m_len) {
399 			/* n is full and there's more data to copy */
400 			MGET(n->m_next, M_DONTWAIT, n->m_type);
401 			if (n->m_next == NULL)
402 				goto nospace;
403 			n = n->m_next;
404 			n->m_len = MLEN;
405 			if (left >= MINCLSIZE) {
406 				MCLGET(n, M_DONTWAIT);
407 				if (n->m_flags & M_EXT)
408 					n->m_len = n->m_ext.ext_size;
409 			}
410 			if (n->m_len > left)
411 				n->m_len = left;
412 			noff = 0;
413 		}
414 		len = min(m->m_len - moff, n->m_len - noff);
415 
416 		src = mtod(m, u_int8_t *) + moff;
417 		dst = mtod(n, u_int8_t *) + noff;
418 		for (i = 0; i < len; i++) {
419 			/* decrypt message */
420 			dst[i] = src[i] ^ s[j];
421 			/* update MIC with clear text */
422 			b[j] ^= dst[i];
423 			if (++j < 16)
424 				continue;
425 			/* we have a full block, encrypt MIC */
426 			rijndael_encrypt(&ctx->rijndael, b, b);
427 			/* construct a new S_ctr block */
428 			ctr++;
429 			a[14] = ctr >> 8;
430 			a[15] = ctr & 0xff;
431 			rijndael_encrypt(&ctx->rijndael, a, s);
432 			j = 0;
433 		}
434 
435 		moff += len;
436 		noff += len;
437 		left -= len;
438 	}
439 	if (j != 0)	/* partial block, encrypt MIC */
440 		rijndael_encrypt(&ctx->rijndael, b, b);
441 
442 	/* finalize MIC, U := T XOR first-M-bytes( S_0 ) */
443 	for (i = 0; i < IEEE80211_CCMP_MICLEN; i++)
444 		b[i] ^= s0[i];
445 
446 	/* check that it matches the MIC in received frame */
447 	m_copydata(m, moff, IEEE80211_CCMP_MICLEN, mic0);
448 	if (timingsafe_bcmp(mic0, b, IEEE80211_CCMP_MICLEN) != 0) {
449 		ic->ic_stats.is_ccmp_dec_errs++;
450 		m_freem(m0);
451 		m_freem(n0);
452 		return NULL;
453 	}
454 
455 	/* update last seen packet number (MIC is validated) */
456 	*prsc = pn;
457 
458 	m_freem(m0);
459 	return n0;
460  nospace:
461 	ic->ic_stats.is_rx_nombuf++;
462 	m_freem(m0);
463 	m_freem(n0);
464 	return NULL;
465 }
466