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