b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame] | 1 | /* |
| 2 | * CTR with CBC-MAC Protocol (CCMP) |
| 3 | * Copyright (c) 2010-2012, Jouni Malinen <j@w1.fi> |
| 4 | * |
| 5 | * This software may be distributed under the terms of the BSD license. |
| 6 | * See README for more details. |
| 7 | */ |
| 8 | |
| 9 | #include "utils/includes.h" |
| 10 | |
| 11 | #include "utils/common.h" |
| 12 | #include "common/ieee802_11_defs.h" |
| 13 | #include "crypto/aes.h" |
| 14 | #include "crypto/aes_wrap.h" |
| 15 | #include "wlantest.h" |
| 16 | |
| 17 | |
| 18 | static void ccmp_aad_nonce(const struct ieee80211_hdr *hdr, const u8 *data, |
| 19 | const u8 *a1, const u8 *a2, const u8 *a3, |
| 20 | u8 *aad, size_t *aad_len, u8 *nonce) |
| 21 | { |
| 22 | u16 fc, stype, seq; |
| 23 | int qos = 0, addr4 = 0; |
| 24 | u8 *pos; |
| 25 | |
| 26 | nonce[0] = 0; |
| 27 | |
| 28 | fc = le_to_host16(hdr->frame_control); |
| 29 | stype = WLAN_FC_GET_STYPE(fc); |
| 30 | if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == |
| 31 | (WLAN_FC_TODS | WLAN_FC_FROMDS)) |
| 32 | addr4 = 1; |
| 33 | |
| 34 | if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA) { |
| 35 | fc &= ~0x0070; /* Mask subtype bits */ |
| 36 | if (stype & 0x08) { |
| 37 | const u8 *qc; |
| 38 | qos = 1; |
| 39 | fc &= ~WLAN_FC_HTC; |
| 40 | qc = (const u8 *) (hdr + 1); |
| 41 | if (addr4) |
| 42 | qc += ETH_ALEN; |
| 43 | nonce[0] = qc[0] & 0x0f; |
| 44 | } |
| 45 | } else if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT) |
| 46 | nonce[0] |= 0x10; /* Management */ |
| 47 | |
| 48 | fc &= ~(WLAN_FC_RETRY | WLAN_FC_PWRMGT | WLAN_FC_MOREDATA); |
| 49 | fc |= WLAN_FC_ISWEP; |
| 50 | WPA_PUT_LE16(aad, fc); |
| 51 | pos = aad + 2; |
| 52 | os_memcpy(pos, hdr->addr1, 3 * ETH_ALEN); |
| 53 | if (a1) |
| 54 | os_memcpy(pos, a1, ETH_ALEN); |
| 55 | if (a2) |
| 56 | os_memcpy(pos + ETH_ALEN, a2, ETH_ALEN); |
| 57 | if (a3) |
| 58 | os_memcpy(pos + 2 * ETH_ALEN, a3, ETH_ALEN); |
| 59 | pos += 3 * ETH_ALEN; |
| 60 | seq = le_to_host16(hdr->seq_ctrl); |
| 61 | seq &= ~0xfff0; /* Mask Seq#; do not modify Frag# */ |
| 62 | WPA_PUT_LE16(pos, seq); |
| 63 | pos += 2; |
| 64 | |
| 65 | os_memcpy(pos, hdr + 1, addr4 * ETH_ALEN + qos * 2); |
| 66 | pos += addr4 * ETH_ALEN; |
| 67 | if (qos) { |
| 68 | pos[0] &= ~0x70; |
| 69 | if (1 /* FIX: either device has SPP A-MSDU Capab = 0 */) |
| 70 | pos[0] &= ~0x80; |
| 71 | pos++; |
| 72 | *pos++ = 0x00; |
| 73 | } |
| 74 | |
| 75 | *aad_len = pos - aad; |
| 76 | |
| 77 | if (a2) |
| 78 | os_memcpy(nonce + 1, a2, ETH_ALEN); |
| 79 | else |
| 80 | os_memcpy(nonce + 1, hdr->addr2, ETH_ALEN); |
| 81 | nonce[7] = data[7]; /* PN5 */ |
| 82 | nonce[8] = data[6]; /* PN4 */ |
| 83 | nonce[9] = data[5]; /* PN3 */ |
| 84 | nonce[10] = data[4]; /* PN2 */ |
| 85 | nonce[11] = data[1]; /* PN1 */ |
| 86 | nonce[12] = data[0]; /* PN0 */ |
| 87 | } |
| 88 | |
| 89 | |
| 90 | static void ccmp_aad_nonce_pv1(const u8 *hdr, const u8 *a1, const u8 *a2, |
| 91 | const u8 *a3, const u8 *pn, |
| 92 | u8 *aad, size_t *aad_len, u8 *nonce) |
| 93 | { |
| 94 | u16 fc, type; |
| 95 | u8 *pos; |
| 96 | |
| 97 | nonce[0] = BIT(5); /* PV1 */ |
| 98 | /* TODO: Priority for QMF; 0 is used for Data frames */ |
| 99 | |
| 100 | fc = WPA_GET_LE16(hdr); |
| 101 | type = (fc & (BIT(2) | BIT(3) | BIT(4))) >> 2; |
| 102 | |
| 103 | if (type == 1) |
| 104 | nonce[0] |= 0x10; /* Management */ |
| 105 | |
| 106 | fc &= ~(BIT(10) | BIT(11) | BIT(13) | BIT(14) | BIT(15)); |
| 107 | fc |= BIT(12); |
| 108 | WPA_PUT_LE16(aad, fc); |
| 109 | pos = aad + 2; |
| 110 | if (type == 0 || type == 3) { |
| 111 | const u8 *sc; |
| 112 | |
| 113 | os_memcpy(pos, a1, ETH_ALEN); |
| 114 | pos += ETH_ALEN; |
| 115 | os_memcpy(pos, a2, ETH_ALEN); |
| 116 | pos += ETH_ALEN; |
| 117 | |
| 118 | if (type == 0) { |
| 119 | /* Either A1 or A2 contains SID */ |
| 120 | sc = hdr + 2 + 2 + ETH_ALEN; |
| 121 | } else { |
| 122 | /* Both A1 and A2 contain full addresses */ |
| 123 | sc = hdr + 2 + 2 * ETH_ALEN; |
| 124 | } |
| 125 | /* SC with Sequence Number subfield (bits 4-15 of the Sequence |
| 126 | * Control field) masked to 0. */ |
| 127 | *pos++ = *sc & 0x0f; |
| 128 | *pos++ = 0; |
| 129 | |
| 130 | if (a3) { |
| 131 | os_memcpy(pos, a3, ETH_ALEN); |
| 132 | pos += ETH_ALEN; |
| 133 | } |
| 134 | } |
| 135 | |
| 136 | *aad_len = pos - aad; |
| 137 | |
| 138 | os_memcpy(nonce + 1, a2, ETH_ALEN); |
| 139 | nonce[7] = pn[5]; /* PN5 */ |
| 140 | nonce[8] = pn[4]; /* PN4 */ |
| 141 | nonce[9] = pn[3]; /* PN3 */ |
| 142 | nonce[10] = pn[2]; /* PN2 */ |
| 143 | nonce[11] = pn[1]; /* PN1 */ |
| 144 | nonce[12] = pn[0]; /* PN0 */ |
| 145 | } |
| 146 | |
| 147 | |
| 148 | u8 * ccmp_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr, |
| 149 | const u8 *a1, const u8 *a2, const u8 *a3, |
| 150 | const u8 *data, size_t data_len, |
| 151 | size_t *decrypted_len) |
| 152 | { |
| 153 | u8 aad[30], nonce[13]; |
| 154 | size_t aad_len; |
| 155 | size_t mlen; |
| 156 | u8 *plain; |
| 157 | |
| 158 | if (data_len < 8 + 8) |
| 159 | return NULL; |
| 160 | |
| 161 | plain = os_malloc(data_len + AES_BLOCK_SIZE); |
| 162 | if (plain == NULL) |
| 163 | return NULL; |
| 164 | |
| 165 | mlen = data_len - 8 - 8; |
| 166 | |
| 167 | os_memset(aad, 0, sizeof(aad)); |
| 168 | ccmp_aad_nonce(hdr, data, a1, a2, a3, aad, &aad_len, nonce); |
| 169 | wpa_hexdump(MSG_EXCESSIVE, "CCMP AAD", aad, aad_len); |
| 170 | wpa_hexdump(MSG_EXCESSIVE, "CCMP nonce", nonce, 13); |
| 171 | |
| 172 | if (aes_ccm_ad(tk, 16, nonce, 8, data + 8, mlen, aad, aad_len, |
| 173 | data + 8 + mlen, plain) < 0) { |
| 174 | u16 seq_ctrl = le_to_host16(hdr->seq_ctrl); |
| 175 | wpa_printf(MSG_INFO, "Invalid CCMP MIC in frame: A1=" MACSTR |
| 176 | " A2=" MACSTR " A3=" MACSTR " seq=%u frag=%u", |
| 177 | MAC2STR(hdr->addr1), MAC2STR(hdr->addr2), |
| 178 | MAC2STR(hdr->addr3), |
| 179 | WLAN_GET_SEQ_SEQ(seq_ctrl), |
| 180 | WLAN_GET_SEQ_FRAG(seq_ctrl)); |
| 181 | os_free(plain); |
| 182 | return NULL; |
| 183 | } |
| 184 | wpa_hexdump(MSG_EXCESSIVE, "CCMP decrypted", plain, mlen); |
| 185 | |
| 186 | *decrypted_len = mlen; |
| 187 | return plain; |
| 188 | } |
| 189 | |
| 190 | |
| 191 | void ccmp_get_pn(u8 *pn, const u8 *data) |
| 192 | { |
| 193 | pn[0] = data[7]; /* PN5 */ |
| 194 | pn[1] = data[6]; /* PN4 */ |
| 195 | pn[2] = data[5]; /* PN3 */ |
| 196 | pn[3] = data[4]; /* PN2 */ |
| 197 | pn[4] = data[1]; /* PN1 */ |
| 198 | pn[5] = data[0]; /* PN0 */ |
| 199 | } |
| 200 | |
| 201 | |
| 202 | u8 * ccmp_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen, |
| 203 | const u8 *qos, const u8 *a1, const u8 *a2, const u8 *a3, |
| 204 | const u8 *pn, int keyid, size_t *encrypted_len) |
| 205 | { |
| 206 | u8 aad[30], nonce[13]; |
| 207 | size_t aad_len, plen; |
| 208 | u8 *crypt, *pos; |
| 209 | struct ieee80211_hdr *hdr; |
| 210 | |
| 211 | if (len < hdrlen || hdrlen < 24) |
| 212 | return NULL; |
| 213 | plen = len - hdrlen; |
| 214 | |
| 215 | crypt = os_malloc(hdrlen + 8 + plen + 8 + AES_BLOCK_SIZE); |
| 216 | if (crypt == NULL) |
| 217 | return NULL; |
| 218 | |
| 219 | os_memcpy(crypt, frame, hdrlen); |
| 220 | hdr = (struct ieee80211_hdr *) crypt; |
| 221 | hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP); |
| 222 | pos = crypt + hdrlen; |
| 223 | *pos++ = pn[5]; /* PN0 */ |
| 224 | *pos++ = pn[4]; /* PN1 */ |
| 225 | *pos++ = 0x00; /* Rsvd */ |
| 226 | *pos++ = 0x20 | (keyid << 6); |
| 227 | *pos++ = pn[3]; /* PN2 */ |
| 228 | *pos++ = pn[2]; /* PN3 */ |
| 229 | *pos++ = pn[1]; /* PN4 */ |
| 230 | *pos++ = pn[0]; /* PN5 */ |
| 231 | |
| 232 | os_memset(aad, 0, sizeof(aad)); |
| 233 | ccmp_aad_nonce(hdr, crypt + hdrlen, a1, a2, a3, aad, &aad_len, nonce); |
| 234 | wpa_hexdump(MSG_EXCESSIVE, "CCMP AAD", aad, aad_len); |
| 235 | wpa_hexdump(MSG_EXCESSIVE, "CCMP nonce", nonce, 13); |
| 236 | |
| 237 | if (aes_ccm_ae(tk, 16, nonce, 8, frame + hdrlen, plen, aad, aad_len, |
| 238 | pos, pos + plen) < 0) { |
| 239 | os_free(crypt); |
| 240 | return NULL; |
| 241 | } |
| 242 | |
| 243 | wpa_hexdump(MSG_EXCESSIVE, "CCMP encrypted", crypt + hdrlen + 8, plen); |
| 244 | |
| 245 | *encrypted_len = hdrlen + 8 + plen + 8; |
| 246 | |
| 247 | return crypt; |
| 248 | } |
| 249 | |
| 250 | |
| 251 | u8 * ccmp_encrypt_pv1(const u8 *tk, const u8 *a1, const u8 *a2, const u8 *a3, |
| 252 | const u8 *frame, size_t len, |
| 253 | size_t hdrlen, const u8 *pn, int keyid, |
| 254 | size_t *encrypted_len) |
| 255 | { |
| 256 | u8 aad[24], nonce[13]; |
| 257 | size_t aad_len, plen; |
| 258 | u8 *crypt, *pos; |
| 259 | struct ieee80211_hdr *hdr; |
| 260 | |
| 261 | if (len < hdrlen || hdrlen < 12) |
| 262 | return NULL; |
| 263 | plen = len - hdrlen; |
| 264 | |
| 265 | crypt = os_malloc(hdrlen + plen + 8 + AES_BLOCK_SIZE); |
| 266 | if (crypt == NULL) |
| 267 | return NULL; |
| 268 | |
| 269 | os_memcpy(crypt, frame, hdrlen); |
| 270 | hdr = (struct ieee80211_hdr *) crypt; |
| 271 | hdr->frame_control |= host_to_le16(BIT(12)); /* Protected Frame */ |
| 272 | pos = crypt + hdrlen; |
| 273 | |
| 274 | os_memset(aad, 0, sizeof(aad)); |
| 275 | ccmp_aad_nonce_pv1(crypt, a1, a2, a3, pn, aad, &aad_len, nonce); |
| 276 | wpa_hexdump(MSG_EXCESSIVE, "CCMP AAD", aad, aad_len); |
| 277 | wpa_hexdump(MSG_EXCESSIVE, "CCMP nonce", nonce, sizeof(nonce)); |
| 278 | |
| 279 | if (aes_ccm_ae(tk, 16, nonce, 8, frame + hdrlen, plen, aad, aad_len, |
| 280 | pos, pos + plen) < 0) { |
| 281 | os_free(crypt); |
| 282 | return NULL; |
| 283 | } |
| 284 | |
| 285 | wpa_hexdump(MSG_EXCESSIVE, "CCMP encrypted", crypt + hdrlen, plen); |
| 286 | |
| 287 | *encrypted_len = hdrlen + plen + 8; |
| 288 | |
| 289 | return crypt; |
| 290 | } |
| 291 | |
| 292 | |
| 293 | u8 * ccmp_256_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr, |
| 294 | const u8 *a1, const u8 *a2, const u8 *a3, |
| 295 | const u8 *data, size_t data_len, size_t *decrypted_len) |
| 296 | { |
| 297 | u8 aad[30], nonce[13]; |
| 298 | size_t aad_len; |
| 299 | size_t mlen; |
| 300 | u8 *plain; |
| 301 | |
| 302 | if (data_len < 8 + 16) |
| 303 | return NULL; |
| 304 | |
| 305 | plain = os_malloc(data_len + AES_BLOCK_SIZE); |
| 306 | if (plain == NULL) |
| 307 | return NULL; |
| 308 | |
| 309 | mlen = data_len - 8 - 16; |
| 310 | |
| 311 | os_memset(aad, 0, sizeof(aad)); |
| 312 | ccmp_aad_nonce(hdr, data, a1, a2, a3, aad, &aad_len, nonce); |
| 313 | wpa_hexdump(MSG_EXCESSIVE, "CCMP-256 AAD", aad, aad_len); |
| 314 | wpa_hexdump(MSG_EXCESSIVE, "CCMP-256 nonce", nonce, 13); |
| 315 | |
| 316 | if (aes_ccm_ad(tk, 32, nonce, 16, data + 8, mlen, aad, aad_len, |
| 317 | data + 8 + mlen, plain) < 0) { |
| 318 | u16 seq_ctrl = le_to_host16(hdr->seq_ctrl); |
| 319 | wpa_printf(MSG_INFO, "Invalid CCMP-256 MIC in frame: A1=" MACSTR |
| 320 | " A2=" MACSTR " A3=" MACSTR " seq=%u frag=%u", |
| 321 | MAC2STR(hdr->addr1), MAC2STR(hdr->addr2), |
| 322 | MAC2STR(hdr->addr3), |
| 323 | WLAN_GET_SEQ_SEQ(seq_ctrl), |
| 324 | WLAN_GET_SEQ_FRAG(seq_ctrl)); |
| 325 | os_free(plain); |
| 326 | return NULL; |
| 327 | } |
| 328 | wpa_hexdump(MSG_EXCESSIVE, "CCMP-256 decrypted", plain, mlen); |
| 329 | |
| 330 | *decrypted_len = mlen; |
| 331 | return plain; |
| 332 | } |
| 333 | |
| 334 | |
| 335 | u8 * ccmp_256_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen, |
| 336 | const u8 *qos, const u8 *a1, const u8 *a2, const u8 *a3, |
| 337 | const u8 *pn, int keyid, size_t *encrypted_len) |
| 338 | { |
| 339 | u8 aad[30], nonce[13]; |
| 340 | size_t aad_len, plen; |
| 341 | u8 *crypt, *pos; |
| 342 | struct ieee80211_hdr *hdr; |
| 343 | |
| 344 | if (len < hdrlen || hdrlen < 24) |
| 345 | return NULL; |
| 346 | plen = len - hdrlen; |
| 347 | |
| 348 | crypt = os_malloc(hdrlen + 8 + plen + 16 + AES_BLOCK_SIZE); |
| 349 | if (crypt == NULL) |
| 350 | return NULL; |
| 351 | |
| 352 | os_memcpy(crypt, frame, hdrlen); |
| 353 | hdr = (struct ieee80211_hdr *) crypt; |
| 354 | hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP); |
| 355 | pos = crypt + hdrlen; |
| 356 | *pos++ = pn[5]; /* PN0 */ |
| 357 | *pos++ = pn[4]; /* PN1 */ |
| 358 | *pos++ = 0x00; /* Rsvd */ |
| 359 | *pos++ = 0x20 | (keyid << 6); |
| 360 | *pos++ = pn[3]; /* PN2 */ |
| 361 | *pos++ = pn[2]; /* PN3 */ |
| 362 | *pos++ = pn[1]; /* PN4 */ |
| 363 | *pos++ = pn[0]; /* PN5 */ |
| 364 | |
| 365 | os_memset(aad, 0, sizeof(aad)); |
| 366 | ccmp_aad_nonce(hdr, crypt + hdrlen, a1, a2, a3, aad, &aad_len, nonce); |
| 367 | wpa_hexdump(MSG_EXCESSIVE, "CCMP-256 AAD", aad, aad_len); |
| 368 | wpa_hexdump(MSG_EXCESSIVE, "CCMP-256 nonce", nonce, 13); |
| 369 | |
| 370 | if (aes_ccm_ae(tk, 32, nonce, 16, frame + hdrlen, plen, aad, aad_len, |
| 371 | pos, pos + plen) < 0) { |
| 372 | os_free(crypt); |
| 373 | return NULL; |
| 374 | } |
| 375 | |
| 376 | wpa_hexdump(MSG_EXCESSIVE, "CCMP-256 encrypted", crypt + hdrlen + 8, |
| 377 | plen); |
| 378 | |
| 379 | *encrypted_len = hdrlen + 8 + plen + 16; |
| 380 | |
| 381 | return crypt; |
| 382 | } |