众所周知,在Wi-Fi的使用过程中,为了确保数据的安全性,我们会对其数据进行加密。为了达到加密的目的,就需要加密密钥,对于Wi-Fi来说,最主要的两个加密密钥是PTK(成对临时密钥)和GTK(组临时密钥),其PTK用于加密单播数据,而GTK用于加密广播和多播数据。其PTK和GTK的生成,就不得不提Wi-Fi的四步握手了。本篇我们不过多的介绍四步握手,而是对四步握手的EAPOL帧中的MIC字段进行一个详细的理解。

消息完整性码(Message Integrity Code,MIC)是用于确保数据完整性和真实性的安全机制。在Wi-Fi通信中,MIC的主要作用是检测和防止数据在传输过程中被篡改。

如上是Wi-Fi的4-way handshake过程,我们可以清晰的看到M2/M3/M4都包含了MIC,以确保数据的准确性。接下来我们详细的讲解下MIC的生成过程。
EAPOL(Extensible Authentication Protocol over LAN)是用于在局域网(LAN)中传输EAP消息的协议。EAPOL帧在IEEE 802.1X认证过程中起着关键作用,特别是在Wi-Fi网络中用于WPA、WPA2和WPA3的四次握手协议。EAPOL帧的格式具体如下所示:

EAPOL帧的字段详解如下所示:




MIC的计算方式如下所示,根据AKM的不同,MIC的生成算法和长度也不一样。

- /**
- * wpa_eapol_key_mic - Calculate EAPOL-Key MIC
- * @key: EAPOL-Key Key Confirmation Key (KCK)
- * @key_len: KCK length in octets
- * @akmp: WPA_KEY_MGMT_* used in key derivation
- * @ver: Key descriptor version (WPA_KEY_INFO_TYPE_*)
- * @buf: Pointer to the beginning of the EAPOL header (version field)
- * @len: Length of the EAPOL frame (from EAPOL header to the end of the frame)
- * @mic: Pointer to the buffer to which the EAPOL-Key MIC is written
- * Returns: 0 on success, -1 on failure
- *
- * Calculate EAPOL-Key MIC for an EAPOL-Key packet. The EAPOL-Key MIC field has
- * to be cleared (all zeroes) when calling this function.
- *
- * Note: 'IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames' has an error in the
- * description of the Key MIC calculation. It includes packet data from the
- * beginning of the EAPOL-Key header, not EAPOL header. This incorrect change
- * happened during final editing of the standard and the correct behavior is
- * defined in the last draft (IEEE 802.11i/D10).
- */
- int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver,
- const u8 *buf, size_t len, u8 *mic)
- {
- u8 hash[SHA512_MAC_LEN];
-
- if (key_len == 0) {
- wpa_printf(MSG_DEBUG,
- "WPA: KCK not set - cannot calculate MIC");
- return -1;
- }
-
- switch (ver) {
- #ifndef CONFIG_FIPS
- case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4:
- wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key MIC using HMAC-MD5");
- return hmac_md5(key, key_len, buf, len, mic);
- #endif /* CONFIG_FIPS */
- case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES:
- wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key MIC using HMAC-SHA1");
- if (hmac_sha1(key, key_len, buf, len, hash))
- return -1;
- os_memcpy(mic, hash, MD5_MAC_LEN);
- break;
- case WPA_KEY_INFO_TYPE_AES_128_CMAC:
- wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key MIC using AES-CMAC");
- return omac1_aes_128(key, buf, len, mic);
- case WPA_KEY_INFO_TYPE_AKM_DEFINED:
- switch (akmp) {
- #ifdef CONFIG_SAE
- case WPA_KEY_MGMT_SAE:
- case WPA_KEY_MGMT_FT_SAE:
- wpa_printf(MSG_DEBUG,
- "WPA: EAPOL-Key MIC using AES-CMAC (AKM-defined - SAE)");
- return omac1_aes_128(key, buf, len, mic);
- case WPA_KEY_MGMT_SAE_EXT_KEY:
- case WPA_KEY_MGMT_FT_SAE_EXT_KEY:
- wpa_printf(MSG_DEBUG,
- "WPA: EAPOL-Key MIC using HMAC-SHA%u (AKM-defined - SAE-EXT-KEY)",
- (unsigned int) key_len * 8 * 2);
- if (key_len == 128 / 8) {
- if (hmac_sha256(key, key_len, buf, len, hash))
- return -1;
- #ifdef CONFIG_SHA384
- } else if (key_len == 192 / 8) {
- if (hmac_sha384(key, key_len, buf, len, hash))
- return -1;
- #endif /* CONFIG_SHA384 */
- #ifdef CONFIG_SHA512
- } else if (key_len == 256 / 8) {
- if (hmac_sha512(key, key_len, buf, len, hash))
- return -1;
- #endif /* CONFIG_SHA512 */
- } else {
- wpa_printf(MSG_INFO,
- "SAE: Unsupported KCK length: %u",
- (unsigned int) key_len);
- return -1;
- }
- os_memcpy(mic, hash, key_len);
- break;
- #endif /* CONFIG_SAE */
- #ifdef CONFIG_HS20
- case WPA_KEY_MGMT_OSEN:
- wpa_printf(MSG_DEBUG,
- "WPA: EAPOL-Key MIC using AES-CMAC (AKM-defined - OSEN)");
- return omac1_aes_128(key, buf, len, mic);
- #endif /* CONFIG_HS20 */
- #ifdef CONFIG_SUITEB
- case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
- wpa_printf(MSG_DEBUG,
- "WPA: EAPOL-Key MIC using HMAC-SHA256 (AKM-defined - Suite B)");
- if (hmac_sha256(key, key_len, buf, len, hash))
- return -1;
- os_memcpy(mic, hash, MD5_MAC_LEN);
- break;
- #endif /* CONFIG_SUITEB */
- #ifdef CONFIG_SUITEB192
- case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
- wpa_printf(MSG_DEBUG,
- "WPA: EAPOL-Key MIC using HMAC-SHA384 (AKM-defined - Suite B 192-bit)");
- if (hmac_sha384(key, key_len, buf, len, hash))
- return -1;
- os_memcpy(mic, hash, 24);
- break;
- #endif /* CONFIG_SUITEB192 */
- #ifdef CONFIG_OWE
- case WPA_KEY_MGMT_OWE:
- wpa_printf(MSG_DEBUG,
- "WPA: EAPOL-Key MIC using HMAC-SHA%u (AKM-defined - OWE)",
- (unsigned int) key_len * 8 * 2);
- if (key_len == 128 / 8) {
- if (hmac_sha256(key, key_len, buf, len, hash))
- return -1;
- } else if (key_len == 192 / 8) {
- if (hmac_sha384(key, key_len, buf, len, hash))
- return -1;
- } else if (key_len == 256 / 8) {
- if (hmac_sha512(key, key_len, buf, len, hash))
- return -1;
- } else {
- wpa_printf(MSG_INFO,
- "OWE: Unsupported KCK length: %u",
- (unsigned int) key_len);
- return -1;
- }
- os_memcpy(mic, hash, key_len);
- break;
- #endif /* CONFIG_OWE */
- #ifdef CONFIG_DPP
- case WPA_KEY_MGMT_DPP:
- wpa_printf(MSG_DEBUG,
- "WPA: EAPOL-Key MIC using HMAC-SHA%u (AKM-defined - DPP)",
- (unsigned int) key_len * 8 * 2);
- if (key_len == 128 / 8) {
- if (hmac_sha256(key, key_len, buf, len, hash))
- return -1;
- } else if (key_len == 192 / 8) {
- if (hmac_sha384(key, key_len, buf, len, hash))
- return -1;
- } else if (key_len == 256 / 8) {
- if (hmac_sha512(key, key_len, buf, len, hash))
- return -1;
- } else {
- wpa_printf(MSG_INFO,
- "DPP: Unsupported KCK length: %u",
- (unsigned int) key_len);
- return -1;
- }
- os_memcpy(mic, hash, key_len);
- break;
- #endif /* CONFIG_DPP */
- #ifdef CONFIG_SHA384
- case WPA_KEY_MGMT_IEEE8021X_SHA384:
- #ifdef CONFIG_IEEE80211R
- case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
- #endif /* CONFIG_IEEE80211R */
- wpa_printf(MSG_DEBUG,
- "WPA: EAPOL-Key MIC using HMAC-SHA384 (AKM-defined - 802.1X SHA384)");
- if (hmac_sha384(key, key_len, buf, len, hash))
- return -1;
- os_memcpy(mic, hash, 24);
- break;
- #endif /* CONFIG_SHA384 */
- default:
- wpa_printf(MSG_DEBUG,
- "WPA: EAPOL-Key MIC algorithm not known (AKM-defined - akmp=0x%x)",
- akmp);
- return -1;
- }
- break;
- default:
- wpa_printf(MSG_DEBUG,
- "WPA: EAPOL-Key MIC algorithm not known (ver=%d)",
- ver);
- return -1;
- }
-
- return 0;
- }