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