关于Wi-Fi的加密认证过程,可以参考如下链接,今天我们来理解如何生成PTK。
1. PTK的生成是如何选择相关算法的?
生成PTK的具体算法在Wi-Fi协议中规定,并且不同的协议版本可能使用不同的算法。以下是一些常用的算法:
- HMAC-SHA1:在WPA和WPA2中用于生成PTK。
- HMAC-SHA256:在WPA3中用于生成PTK,以提供更高的安全性。
如上图所示,PTK密钥生成的层级,我们这里重点讲解下生成算法,我们可以看到生成的算法是PRF-Length,其中Length的计算方法如下所示:
- Length =KCK bits + KEK bits + TK bits.
- 其中KCK_bits和KEK_bits的值参考下图:
-
- 其中TK_bits的值参考下图:
-
-
- 上图的Cipher_suite参考下图:
-
2. hostapd/wpa_supplicant code
其hostapd和wpa_supplicant中利用pmk生成ptk的代码如下所示:
/**
* wpa_pmk_to_ptk - Calculate PTK from PMK, addresses, and nonces
* @pmk: Pairwise master key
* @pmk_len: Length of PMK
* @label: Label to use in derivation
* @addr1: AA or SA
* @addr2: SA or AA
* @nonce1: ANonce or SNonce
* @nonce2: SNonce or ANonce
* @ptk: Buffer for pairwise transient key
* @akmp: Negotiated AKM
* @cipher: Negotiated pairwise cipher
* @kdk_len: The length in octets that should be derived for KDK
* Returns: 0 on success, -1 on failure
*
* IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
* PTK = PRF-X(PMK, "Pairwise key expansion",
* Min(AA, SA) || Max(AA, SA) ||
* Min(ANonce, SNonce) || Max(ANonce, SNonce)
* [ || Z.x ])
*
* The optional Z.x component is used only with DPP and that part is not defined
* in IEEE 802.11.
*/
int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
const u8 *addr1, const u8 *addr2,
const u8 *nonce1, const u8 *nonce2,
struct wpa_ptk *ptk, int akmp, int cipher,
const u8 *z, size_t z_len, size_t kdk_len)
{
#define MAX_Z_LEN 66 /* with NIST P-521 */
u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN + MAX_Z_LEN];
size_t data_len = 2 * ETH_ALEN + 2 * WPA_NONCE_LEN;
u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN +
WPA_KDK_MAX_LEN];
size_t ptk_len;
#ifdef CONFIG_OWE
int owe_ptk_workaround = 0;
if (akmp == (WPA_KEY_MGMT_OWE | WPA_KEY_MGMT_PSK_SHA256)) {
owe_ptk_workaround = 1;
akmp = WPA_KEY_MGMT_OWE;
}
#endif /* CONFIG_OWE */
if (pmk_len == 0) {
wpa_printf(MSG_ERROR, "WPA: No PMK set for PTK derivation");
return -1;
}
if (z_len > MAX_Z_LEN)
return -1;
if (os_memcmp(addr1, addr2, ETH_ALEN) < 0) {
os_memcpy(data, addr1, ETH_ALEN);
os_memcpy(data + ETH_ALEN, addr2, ETH_ALEN);
} else {
os_memcpy(data, addr2, ETH_ALEN);
os_memcpy(data + ETH_ALEN, addr1, ETH_ALEN);
}
if (os_memcmp(nonce1, nonce2, WPA_NONCE_LEN) < 0) {
os_memcpy(data + 2 * ETH_ALEN, nonce1, WPA_NONCE_LEN);
os_memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce2,
WPA_NONCE_LEN);
} else {
os_memcpy(data + 2 * ETH_ALEN, nonce2, WPA_NONCE_LEN);
os_memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce1,
WPA_NONCE_LEN);
}
if (z && z_len) {
os_memcpy(data + 2 * ETH_ALEN + 2 * WPA_NONCE_LEN, z, z_len);
data_len += z_len;
}
if (kdk_len > WPA_KDK_MAX_LEN) {
wpa_printf(MSG_ERROR,
"WPA: KDK len=%zu exceeds max supported len",
kdk_len);
return -1;
}
ptk->kck_len = wpa_kck_len(akmp, pmk_len);
ptk->kek_len = wpa_kek_len(akmp, pmk_len);
ptk->tk_len = wpa_cipher_key_len(cipher);
ptk->kdk_len = kdk_len;
if (ptk->tk_len == 0) {
wpa_printf(MSG_ERROR,
"WPA: Unsupported cipher (0x%x) used in PTK derivation",
cipher);
return -1;
}
ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len + ptk->kdk_len;
if (wpa_key_mgmt_sha384(akmp)) {
#ifdef CONFIG_SHA384
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)");
if (sha384_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
#else /* CONFIG_SHA384 */
return -1;
#endif /* CONFIG_SHA384 */
} else if (wpa_key_mgmt_sha256(akmp)) {
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)");
if (sha256_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
#ifdef CONFIG_OWE
} else if (akmp == WPA_KEY_MGMT_OWE && (pmk_len == 32 ||
owe_ptk_workaround)) {
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)");
if (sha256_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
} else if (akmp == WPA_KEY_MGMT_OWE && pmk_len == 48) {
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)");
if (sha384_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
} else if (akmp == WPA_KEY_MGMT_OWE && pmk_len == 64) {
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA512)");
if (sha512_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
} else if (akmp == WPA_KEY_MGMT_OWE) {
wpa_printf(MSG_INFO, "OWE: Unknown PMK length %u",
(unsigned int) pmk_len);
return -1;
#endif /* CONFIG_OWE */
#ifdef CONFIG_DPP
} else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 32) {
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)");
if (sha256_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
} else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 48) {
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)");
if (sha384_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
} else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 64) {
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA512)");
if (sha512_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
} else if (akmp == WPA_KEY_MGMT_DPP) {
wpa_printf(MSG_INFO, "DPP: Unknown PMK length %u",
(unsigned int) pmk_len);
return -1;
#endif /* CONFIG_DPP */
#ifdef CONFIG_SAE
} else if (wpa_key_mgmt_sae_ext_key(akmp)) {
if (pmk_len == 32) {
wpa_printf(MSG_DEBUG,
"SAE: PTK derivation using PRF(SHA256)");
if (sha256_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
#ifdef CONFIG_SHA384
} else if (pmk_len == 48) {
wpa_printf(MSG_DEBUG,
"SAE: PTK derivation using PRF(SHA384)");
if (sha384_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
#endif /* CONFIG_SHA384 */
#ifdef CONFIG_SHA512
} else if (pmk_len == 64) {
wpa_printf(MSG_DEBUG,
"SAE: PTK derivation using PRF(SHA512)");
if (sha512_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
#endif /* CONFIG_SHA512 */
} else {
wpa_printf(MSG_INFO, "SAE: Unknown PMK length %u",
(unsigned int) pmk_len);
return -1;
}
#endif /* CONFIG_SAE */
} else {
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA1)");
if (sha1_prf(pmk, pmk_len, label, data, data_len, tmp,
ptk_len) < 0)
return -1;
}
wpa_printf(MSG_DEBUG, "WPA: PTK derivation - A1=" MACSTR " A2=" MACSTR,
MAC2STR(addr1), MAC2STR(addr2));
wpa_hexdump(MSG_DEBUG, "WPA: Nonce1", nonce1, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "WPA: Nonce2", nonce2, WPA_NONCE_LEN);
if (z && z_len)
wpa_hexdump_key(MSG_DEBUG, "WPA: Z.x", z, z_len);
wpa_hexdump_key(MSG_DEBUG, "WPA: PMK", pmk, pmk_len);
wpa_hexdump_key(MSG_DEBUG, "WPA: PTK", tmp, ptk_len);
os_memcpy(ptk->kck, tmp, ptk->kck_len);
wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", ptk->kck, ptk->kck_len);
os_memcpy(ptk->kek, tmp + ptk->kck_len, ptk->kek_len);
wpa_hexdump_key(MSG_DEBUG, "WPA: KEK", ptk->kek, ptk->kek_len);
os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len);
wpa_hexdump_key(MSG_DEBUG, "WPA: TK", ptk->tk, ptk->tk_len);
if (kdk_len) {
os_memcpy(ptk->kdk, tmp + ptk->kck_len + ptk->kek_len +
ptk->tk_len, ptk->kdk_len);
wpa_hexdump_key(MSG_DEBUG, "WPA: KDK", ptk->kdk, ptk->kdk_len);
}
ptk->kek2_len = 0;
ptk->kck2_len = 0;
os_memset(tmp, 0, sizeof(tmp));
os_memset(data, 0, data_len);
return 0;
}