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