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