1 /* $OpenBSD: ieee80211_crypto_ccmp.c,v 1.8 2008/12/03 17:25:41 damien 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 #include <net/if_llc.h> 37 38 #ifdef INET 39 #include <netinet/in.h> 40 #include <netinet/if_ether.h> 41 #endif 42 43 #include <net80211/ieee80211_var.h> 44 #include <net80211/ieee80211_crypto.h> 45 46 #include <crypto/rijndael.h> 47 48 /* CCMP software crypto context */ 49 struct ieee80211_ccmp_ctx { 50 rijndael_ctx rijndael; 51 }; 52 53 /* 54 * Initialize software crypto context. This function can be overridden 55 * by drivers doing hardware crypto. 56 */ 57 int 58 ieee80211_ccmp_set_key(struct ieee80211com *ic, struct ieee80211_key *k) 59 { 60 struct ieee80211_ccmp_ctx *ctx; 61 62 ctx = malloc(sizeof(*ctx), M_DEVBUF, M_NOWAIT | M_ZERO); 63 if (ctx == NULL) 64 return ENOMEM; 65 rijndael_set_key_enc_only(&ctx->rijndael, k->k_key, 128); 66 k->k_priv = ctx; 67 return 0; 68 } 69 70 void 71 ieee80211_ccmp_delete_key(struct ieee80211com *ic, struct ieee80211_key *k) 72 { 73 if (k->k_priv != NULL) 74 free(k->k_priv, M_DEVBUF); 75 k->k_priv = NULL; 76 } 77 78 /*- 79 * Counter with CBC-MAC (CCM) - see RFC3610. 80 * CCMP uses the following CCM parameters: M = 8, L = 2 81 */ 82 static void 83 ieee80211_ccmp_phase1(rijndael_ctx *ctx, const struct ieee80211_frame *wh, 84 u_int64_t pn, int lm, u_int8_t b[16], u_int8_t a[16], u_int8_t s0[16]) 85 { 86 u_int8_t auth[32], nonce[13]; 87 u_int8_t *aad; 88 u_int8_t tid = 0; 89 int la, i; 90 91 /* construct AAD (additional authenticated data) */ 92 aad = &auth[2]; /* skip l(a), will be filled later */ 93 *aad = wh->i_fc[0]; 94 /* 11w: conditionnally mask subtype field */ 95 if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == 96 IEEE80211_FC0_TYPE_DATA) 97 *aad &= ~IEEE80211_FC0_SUBTYPE_MASK; 98 aad++; 99 /* protected bit is already set in wh */ 100 *aad = wh->i_fc[1]; 101 *aad &= ~(IEEE80211_FC1_RETRY | IEEE80211_FC1_PWR_MGT | 102 IEEE80211_FC1_MORE_DATA); 103 /* 11n: conditionnally mask order bit */ 104 if (ieee80211_has_htc(wh)) 105 *aad &= ~IEEE80211_FC1_ORDER; 106 aad++; 107 IEEE80211_ADDR_COPY(aad, wh->i_addr1); aad += IEEE80211_ADDR_LEN; 108 IEEE80211_ADDR_COPY(aad, wh->i_addr2); aad += IEEE80211_ADDR_LEN; 109 IEEE80211_ADDR_COPY(aad, wh->i_addr3); aad += IEEE80211_ADDR_LEN; 110 *aad++ = wh->i_seq[0] & ~0xf0; 111 *aad++ = 0; 112 if (ieee80211_has_addr4(wh)) { 113 IEEE80211_ADDR_COPY(aad, 114 ((const struct ieee80211_frame_addr4 *)wh)->i_addr4); 115 aad += IEEE80211_ADDR_LEN; 116 } 117 if (ieee80211_has_qos(wh)) { 118 *aad++ = tid = ieee80211_get_qos(wh) & IEEE80211_QOS_TID; 119 *aad++ = 0; 120 } 121 122 /* construct CCM nonce */ 123 nonce[ 0] = tid; 124 if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == 125 IEEE80211_FC0_TYPE_MGT) 126 nonce[0] |= 1 << 4; /* 11w: set management bit */ 127 IEEE80211_ADDR_COPY(&nonce[1], wh->i_addr2); 128 nonce[ 7] = pn >> 40; /* PN5 */ 129 nonce[ 8] = pn >> 32; /* PN4 */ 130 nonce[ 9] = pn >> 24; /* PN3 */ 131 nonce[10] = pn >> 16; /* PN2 */ 132 nonce[11] = pn >> 8; /* PN1 */ 133 nonce[12] = pn; /* PN0 */ 134 135 /* add 2 authentication blocks (including l(a) and padded AAD) */ 136 la = aad - &auth[2]; /* fill l(a) */ 137 auth[0] = la >> 8; 138 auth[1] = la & 0xff; 139 memset(aad, 0, 30 - la); /* pad AAD with zeros */ 140 141 /* construct first block B_0 */ 142 b[ 0] = 89; /* Flags = 64*Adata + 8*((M-2)/2) + (L-1) */ 143 memcpy(&b[1], nonce, 13); 144 b[14] = lm >> 8; 145 b[15] = lm & 0xff; 146 rijndael_encrypt(ctx, b, b); 147 148 for (i = 0; i < 16; i++) 149 b[i] ^= auth[i]; 150 rijndael_encrypt(ctx, b, b); 151 for (i = 0; i < 16; i++) 152 b[i] ^= auth[16 + i]; 153 rijndael_encrypt(ctx, b, b); 154 155 /* construct S_0 */ 156 a[ 0] = 1; /* Flags = L' = (L-1) */ 157 memcpy(&a[1], nonce, 13); 158 a[14] = a[15] = 0; 159 rijndael_encrypt(ctx, a, s0); 160 } 161 162 struct mbuf * 163 ieee80211_ccmp_encrypt(struct ieee80211com *ic, struct mbuf *m0, 164 struct ieee80211_key *k) 165 { 166 struct ieee80211_ccmp_ctx *ctx = k->k_priv; 167 const struct ieee80211_frame *wh; 168 const u_int8_t *src; 169 u_int8_t *ivp, *mic, *dst; 170 u_int8_t a[16], b[16], s0[16], s[16]; 171 struct mbuf *n0, *m, *n; 172 int hdrlen, left, moff, noff, len; 173 u_int16_t ctr; 174 int i, j; 175 176 MGET(n0, M_DONTWAIT, m0->m_type); 177 if (n0 == NULL) 178 goto nospace; 179 M_DUP_PKTHDR(n0, m0); 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 M_DUP_PKTHDR(n0, m0); 358 n0->m_pkthdr.len -= IEEE80211_CCMP_HDRLEN + IEEE80211_CCMP_MICLEN; 359 n0->m_len = MHLEN; 360 if (n0->m_pkthdr.len >= MINCLSIZE) { 361 MCLGET(n0, M_DONTWAIT); 362 if (n0->m_flags & M_EXT) 363 n0->m_len = n0->m_ext.ext_size; 364 } 365 if (n0->m_len > n0->m_pkthdr.len) 366 n0->m_len = n0->m_pkthdr.len; 367 368 /* construct initial B, A and S_0 blocks */ 369 ieee80211_ccmp_phase1(&ctx->rijndael, wh, pn, 370 n0->m_pkthdr.len - hdrlen, b, a, s0); 371 372 /* copy 802.11 header and clear protected bit */ 373 memcpy(mtod(n0, caddr_t), wh, hdrlen); 374 wh = mtod(n0, struct ieee80211_frame *); 375 wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; 376 377 /* construct S_1 */ 378 ctr = 1; 379 a[14] = ctr >> 8; 380 a[15] = ctr & 0xff; 381 rijndael_encrypt(&ctx->rijndael, a, s); 382 383 /* decrypt frame body and compute MIC */ 384 j = 0; 385 m = m0; 386 n = n0; 387 moff = hdrlen + IEEE80211_CCMP_HDRLEN; 388 noff = hdrlen; 389 left = n0->m_pkthdr.len - noff; 390 while (left > 0) { 391 if (moff == m->m_len) { 392 /* nothing left to copy from m */ 393 m = m->m_next; 394 moff = 0; 395 } 396 if (noff == n->m_len) { 397 /* n is full and there's more data to copy */ 398 MGET(n->m_next, M_DONTWAIT, n->m_type); 399 if (n->m_next == NULL) 400 goto nospace; 401 n = n->m_next; 402 n->m_len = MLEN; 403 if (left >= MINCLSIZE) { 404 MCLGET(n, M_DONTWAIT); 405 if (n->m_flags & M_EXT) 406 n->m_len = n->m_ext.ext_size; 407 } 408 if (n->m_len > left) 409 n->m_len = left; 410 noff = 0; 411 } 412 len = min(m->m_len - moff, n->m_len - noff); 413 414 src = mtod(m, u_int8_t *) + moff; 415 dst = mtod(n, u_int8_t *) + noff; 416 for (i = 0; i < len; i++) { 417 /* decrypt message */ 418 dst[i] = src[i] ^ s[j]; 419 /* update MIC with clear text */ 420 b[j] ^= dst[i]; 421 if (++j < 16) 422 continue; 423 /* we have a full block, encrypt MIC */ 424 rijndael_encrypt(&ctx->rijndael, b, b); 425 /* construct a new S_ctr block */ 426 ctr++; 427 a[14] = ctr >> 8; 428 a[15] = ctr & 0xff; 429 rijndael_encrypt(&ctx->rijndael, a, s); 430 j = 0; 431 } 432 433 moff += len; 434 noff += len; 435 left -= len; 436 } 437 if (j != 0) /* partial block, encrypt MIC */ 438 rijndael_encrypt(&ctx->rijndael, b, b); 439 440 /* finalize MIC, U := T XOR first-M-bytes( S_0 ) */ 441 for (i = 0; i < IEEE80211_CCMP_MICLEN; i++) 442 b[i] ^= s0[i]; 443 444 /* check that it matches the MIC in received frame */ 445 m_copydata(m, moff, IEEE80211_CCMP_MICLEN, mic0); 446 if (memcmp(mic0, b, IEEE80211_CCMP_MICLEN) != 0) { 447 ic->ic_stats.is_ccmp_dec_errs++; 448 m_freem(m0); 449 m_freem(n0); 450 return NULL; 451 } 452 453 /* update last seen packet number (MIC is validated) */ 454 *prsc = pn; 455 456 m_freem(m0); 457 return n0; 458 nospace: 459 ic->ic_stats.is_rx_nombuf++; 460 m_freem(m0); 461 if (n0 != NULL) 462 m_freem(n0); 463 return NULL; 464 } 465