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