dot11decrypt: Refactor Dot11DecryptRsna4WHandshake function

The Dot11DecryptRsna4WHandshake has grown unreasonably large. Refactor
the function and break out some functionality into smaller utility
functions to make it easier to understand.
This commit is contained in:
Mikael Kanstrup 2021-01-16 22:27:31 +01:00 committed by Wireshark GitLab Utility
parent fbb9056d20
commit 69aa20669c
1 changed files with 215 additions and 154 deletions

View File

@ -218,15 +218,26 @@ static const UCHAR * Dot11DecryptGetBssidAddress(
const DOT11DECRYPT_MAC_FRAME_ADDR4 *frame)
;
static void
static guint8
Dot11DecryptDerivePtk(
DOT11DECRYPT_SEC_ASSOCIATION *sa,
const DOT11DECRYPT_SEC_ASSOCIATION *sa,
const UCHAR *pmk,
size_t pmk_len,
const UCHAR snonce[32],
int key_version,
int akm,
int cipher);
int cipher,
guint8 *ptk, size_t *ptk_len);
static guint8
Dot11DecryptFtDerivePtk(
const PDOT11DECRYPT_CONTEXT ctx,
const DOT11DECRYPT_SEC_ASSOCIATION *sa,
const PDOT11DECRYPT_KEY_ITEM key,
const PDOT11DECRYPT_EAPOL_PARSED eapol_parsed,
int akm,
int cipher,
guint8 *ptk, size_t *ptk_len);
/**
* @param sa [IN/OUT] pointer to SA that will hold the key
@ -1536,6 +1547,33 @@ Dot11DecryptDerivePmkFromMsk(const guint8 *msk, guint8 msk_len, int akm,
memcpy(pmk, msk, *pmk_len);
}
static gboolean
Dot11DecryptIsWpaKeyType(guint8 key_type)
{
switch (key_type) {
case DOT11DECRYPT_KEY_TYPE_WPA_PWD:
case DOT11DECRYPT_KEY_TYPE_WPA_PSK:
case DOT11DECRYPT_KEY_TYPE_WPA_PMK:
case DOT11DECRYPT_KEY_TYPE_MSK:
return TRUE;
}
return FALSE;
}
static gboolean
Dot11DecryptIsPwdWildcardSsid(const PDOT11DECRYPT_CONTEXT ctx,
const DOT11DECRYPT_KEY_ITEM *key_item)
{
if (!ctx || !key_item || key_item->KeyType != DOT11DECRYPT_KEY_TYPE_WPA_PWD) {
return FALSE;
}
if (key_item->UserPwd.SsidLen == 0 && ctx->pkt_ssid_len > 0 &&
ctx->pkt_ssid_len <= DOT11DECRYPT_WPA_SSID_MAX_LEN) {
return TRUE;
}
return FALSE;
}
/* Refer to IEEE 802.11i-2004, 8.5.3, pag. 85 */
static INT
Dot11DecryptRsna4WHandshake(
@ -1628,165 +1666,100 @@ Dot11DecryptRsna4WHandshake(
int akm = -1;
int cipher = -1;
int group_cipher = -1;
guint8 ptk[DOT11DECRYPT_WPA_PTK_MAX_LEN];
size_t ptk_len;
/* now you can derive the PTK */
for (key_index=0; key_index<(INT)ctx->keys_nr || useCache; key_index++) {
/* use the cached one, or try all keys */
if (!useCache) {
DEBUG_PRINT_LINE("Try WPA key...", DEBUG_LEVEL_3);
tmp_key=&ctx->keys[key_index];
if (useCache && Dot11DecryptIsWpaKeyType(sa->key->KeyType)) {
DEBUG_PRINT_LINE("Try cached WPA key...", DEBUG_LEVEL_3);
tmp_key = sa->key;
/* Step back loop counter as cached key is used instead */
key_index--;
} else {
/* there is a cached key in the security association, if it's a WPA key try it... */
if (sa->key!=NULL &&
(sa->key->KeyType==DOT11DECRYPT_KEY_TYPE_WPA_PWD ||
sa->key->KeyType==DOT11DECRYPT_KEY_TYPE_WPA_PSK ||
sa->key->KeyType == DOT11DECRYPT_KEY_TYPE_WPA_PMK ||
sa->key->KeyType == DOT11DECRYPT_KEY_TYPE_MSK)) {
DEBUG_PRINT_LINE("Try cached WPA key...", DEBUG_LEVEL_3);
tmp_key=sa->key;
} else {
DEBUG_PRINT_LINE("Cached key is of a wrong type, try WPA key...", DEBUG_LEVEL_3);
tmp_key=&ctx->keys[key_index];
}
DEBUG_PRINT_LINE("Try WPA key...", DEBUG_LEVEL_3);
tmp_key = &ctx->keys[key_index];
}
useCache = FALSE;
/* obviously, try only WPA keys... */
if (tmp_key->KeyType==DOT11DECRYPT_KEY_TYPE_WPA_PWD ||
tmp_key->KeyType==DOT11DECRYPT_KEY_TYPE_WPA_PSK ||
tmp_key->KeyType == DOT11DECRYPT_KEY_TYPE_WPA_PMK ||
tmp_key->KeyType == DOT11DECRYPT_KEY_TYPE_MSK)
if (!Dot11DecryptIsWpaKeyType(tmp_key->KeyType)) {
continue;
}
if (tmp_key->KeyType == DOT11DECRYPT_KEY_TYPE_WPA_PWD &&
Dot11DecryptIsPwdWildcardSsid(ctx, tmp_key))
{
if (tmp_key->KeyType == DOT11DECRYPT_KEY_TYPE_WPA_PWD && tmp_key->UserPwd.SsidLen == 0 && ctx->pkt_ssid_len > 0 && ctx->pkt_ssid_len <= DOT11DECRYPT_WPA_SSID_MAX_LEN) {
/* We have a "wildcard" SSID. Use the one from the packet. */
memcpy(&pkt_key, tmp_key, sizeof(pkt_key));
memcpy(&pkt_key.UserPwd.Ssid, ctx->pkt_ssid, ctx->pkt_ssid_len);
pkt_key.UserPwd.SsidLen = ctx->pkt_ssid_len;
Dot11DecryptRsnaPwd2Psk(pkt_key.UserPwd.Passphrase, pkt_key.UserPwd.Ssid,
pkt_key.UserPwd.SsidLen, pkt_key.KeyData.Wpa.Psk);
tmp_pkt_key = &pkt_key;
} else {
tmp_pkt_key = tmp_key;
}
memcpy(eapol, eapol_raw, tot_len);
/* We have a "wildcard" SSID. Use the one from the packet. */
memcpy(&pkt_key, tmp_key, sizeof(pkt_key));
memcpy(&pkt_key.UserPwd.Ssid, ctx->pkt_ssid, ctx->pkt_ssid_len);
pkt_key.UserPwd.SsidLen = ctx->pkt_ssid_len;
Dot11DecryptRsnaPwd2Psk(pkt_key.UserPwd.Passphrase, pkt_key.UserPwd.Ssid,
pkt_key.UserPwd.SsidLen, pkt_key.KeyData.Wpa.Psk);
tmp_pkt_key = &pkt_key;
} else {
tmp_pkt_key = tmp_key;
}
memcpy(eapol, eapol_raw, tot_len);
/* From IEEE 802.11-2016 12.7.2 EAPOL-Key frames */
if (eapol_parsed->key_version == 0 || eapol_parsed->key_version == 3 ||
eapol_parsed->key_version == DOT11DECRYPT_WPA_KEY_VER_AES_CCMP)
{
/* PTK derivation is based on Authentication Key Management Type */
akm = eapol_parsed->akm;
cipher = eapol_parsed->cipher;
group_cipher = eapol_parsed->group_cipher;
} else if (eapol_parsed->key_version == DOT11DECRYPT_WPA_KEY_VER_NOT_CCMP) {
/* TKIP */
akm = 2;
cipher = 2;
group_cipher = 2;
} else {
DEBUG_PRINT_LINE("EAPOL key_version not supported", DEBUG_LEVEL_3);
return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
}
if (tmp_key->KeyType == DOT11DECRYPT_KEY_TYPE_MSK) {
Dot11DecryptDerivePmkFromMsk(tmp_key->Msk.Msk, tmp_key->Msk.Len, akm,
tmp_key->KeyData.Wpa.Psk,
&tmp_key->KeyData.Wpa.PskLen);
}
if (Dot11DecryptIsFtAkm(akm)) {
int hash_algo = Dot11DecryptGetHashAlgoFromAkm(akm);
guint8 pmk_r0[DOT11DECRYPT_WPA_PMK_MAX_LEN];
guint8 pmk_r1[DOT11DECRYPT_WPA_PMK_MAX_LEN];
guint8 pmk_r0_name[16];
guint8 pmk_r1_name[16];
guint8 ptk_name[16];
size_t pmk_r0_len;
size_t pmk_r1_len;
size_t ptk_len = Dot11DecryptGetPtkLen(akm, cipher) / 8;
const guint8 *xxkey = NULL;
size_t xxkey_len;
if (!eapol_parsed->mdid || !eapol_parsed->r0kh_id || !eapol_parsed->r1kh_id) {
DEBUG_PRINT_LINE("Fields missing for FT", DEBUG_LEVEL_3);
return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
}
if (tmp_key->KeyType == DOT11DECRYPT_KEY_TYPE_MSK)
{
xxkey = Dot11DecryptGetXXKeyFromMSK(tmp_key->Msk.Msk,
tmp_key->Msk.Len,
akm,
&xxkey_len);
}
if (!xxkey && tmp_pkt_key->KeyData.Wpa.PskLen > 0) {
xxkey = tmp_pkt_key->KeyData.Wpa.Psk;
xxkey_len = tmp_pkt_key->KeyData.Wpa.PskLen;
}
if (!xxkey) {
DEBUG_PRINT_LINE("no xxkey. Skipping", DEBUG_LEVEL_3);
continue;
}
dot11decrypt_derive_pmk_r0(xxkey, xxkey_len,
ctx->pkt_ssid, ctx->pkt_ssid_len,
eapol_parsed->mdid,
eapol_parsed->r0kh_id, eapol_parsed->r0kh_id_len,
id->sta, hash_algo,
pmk_r0, &pmk_r0_len, pmk_r0_name);
DEBUG_DUMP("PMK-R0", pmk_r0, pmk_r0_len);
DEBUG_DUMP("PMKR0Name", pmk_r0_name, 16);
dot11decrypt_derive_pmk_r1(pmk_r0, pmk_r0_len, pmk_r0_name,
eapol_parsed->r1kh_id, id->sta, hash_algo,
pmk_r1, &pmk_r1_len, pmk_r1_name);
DEBUG_DUMP("PMK-R1", pmk_r1, pmk_r1_len);
DEBUG_DUMP("PMKR1Name", pmk_r1_name, 16);
dot11decrypt_derive_ft_ptk(pmk_r1, pmk_r1_len, pmk_r1_name,
eapol_parsed->nonce, sa->wpa.nonce,
id->bssid, id->sta, hash_algo,
sa->wpa.ptk, ptk_len, ptk_name);
DEBUG_DUMP("PTK", sa->wpa.ptk, ptk_len);
} else {
/* derive the PTK from the BSSID, STA MAC, PMK, SNonce, ANonce */
Dot11DecryptDerivePtk(sa, /* authenticator nonce, bssid, station mac */
tmp_pkt_key->KeyData.Wpa.Psk, /* PSK == PMK */
tmp_pkt_key->KeyData.Wpa.PskLen,
eapol_parsed->nonce, /* supplicant nonce */
eapol_parsed->key_version,
akm,
cipher);
}
DEBUG_DUMP("TK", DOT11DECRYPT_GET_TK(sa->wpa.ptk, akm), Dot11DecryptGetTkLen(cipher) / 8);
ret = Dot11DecryptRsnaMicCheck(eapol_parsed,
eapol, /* eapol frame (header also) */
tot_len, /* eapol frame length */
DOT11DECRYPT_GET_KCK(sa->wpa.ptk, akm),
eapol_parsed->key_version,
akm);
/* If the MIC is valid, the Authenticator checks that the RSN information element bit-wise matches */
/* that from the (Re)Association Request message. */
/* i) TODO If these are not exactly the same, the Authenticator uses MLME-DEAUTHENTICATE.request */
/* primitive to terminate the association. */
/* ii) If they do match bit-wise, the Authenticator constructs Message 3. */
/* From IEEE 802.11-2016 12.7.2 EAPOL-Key frames */
if (eapol_parsed->key_version == 0 || eapol_parsed->key_version == 3 ||
eapol_parsed->key_version == DOT11DECRYPT_WPA_KEY_VER_AES_CCMP)
{
/* PTK derivation is based on Authentication Key Management Type */
akm = eapol_parsed->akm;
cipher = eapol_parsed->cipher;
group_cipher = eapol_parsed->group_cipher;
} else if (eapol_parsed->key_version == DOT11DECRYPT_WPA_KEY_VER_NOT_CCMP) {
/* TKIP */
akm = 2;
cipher = 2;
group_cipher = 2;
} else {
DEBUG_PRINT_LINE("EAPOL key_version not supported", DEBUG_LEVEL_3);
return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
}
if (!ret &&
(tmp_key->KeyType==DOT11DECRYPT_KEY_TYPE_WPA_PWD ||
tmp_key->KeyType==DOT11DECRYPT_KEY_TYPE_WPA_PSK ||
tmp_key->KeyType==DOT11DECRYPT_KEY_TYPE_WPA_PMK))
{
/* the temporary key is the correct one, cached in the Security Association */
if (tmp_pkt_key->KeyType == DOT11DECRYPT_KEY_TYPE_MSK) {
Dot11DecryptDerivePmkFromMsk(tmp_pkt_key->Msk.Msk, tmp_pkt_key->Msk.Len, akm,
tmp_pkt_key->KeyData.Wpa.Psk,
&tmp_pkt_key->KeyData.Wpa.PskLen);
}
sa->key=tmp_key;
break;
if (Dot11DecryptIsFtAkm(akm)) {
ret = Dot11DecryptFtDerivePtk(ctx, sa, tmp_pkt_key, eapol_parsed,
akm, cipher, ptk, &ptk_len);
} else {
/* the cached key was not valid, try other keys */
/* derive the PTK from the BSSID, STA MAC, PMK, SNonce, ANonce */
ret = Dot11DecryptDerivePtk(sa, /* authenticator nonce, bssid, station mac */
tmp_pkt_key->KeyData.Wpa.Psk, /* PSK == PMK */
tmp_pkt_key->KeyData.Wpa.PskLen,
eapol_parsed->nonce, /* supplicant nonce */
eapol_parsed->key_version,
akm, cipher, ptk, &ptk_len);
}
if (ret) {
/* Unsuccessful PTK derivation */
continue;
}
DEBUG_DUMP("TK", DOT11DECRYPT_GET_TK(ptk, akm), Dot11DecryptGetTkLen(cipher) / 8);
if (useCache==TRUE) {
useCache=FALSE;
key_index--;
}
ret = Dot11DecryptRsnaMicCheck(eapol_parsed,
eapol, /* eapol frame (header also) */
tot_len, /* eapol frame length */
DOT11DECRYPT_GET_KCK(ptk, akm),
eapol_parsed->key_version,
akm);
/* If the MIC is valid, the Authenticator checks that the RSN information element bit-wise matches */
/* that from the (Re)Association Request message. */
/* i) TODO If these are not exactly the same, the Authenticator uses MLME-DEAUTHENTICATE.request */
/* primitive to terminate the association. */
/* ii) If they do match bit-wise, the Authenticator constructs Message 3. */
if (ret == DOT11DECRYPT_RET_SUCCESS) {
/* the key is the correct one, cache it in the Security Association */
sa->key = tmp_key;
break;
}
}
@ -1798,7 +1771,8 @@ Dot11DecryptRsna4WHandshake(
sa->wpa.akm = akm;
sa->wpa.cipher = cipher;
sa->wpa.tmp_group_cipher = group_cipher;
sa->wpa.ptk_len = Dot11DecryptGetPtkLen(sa->wpa.akm, sa->wpa.cipher) / 8;
memcpy(sa->wpa.ptk, ptk, ptk_len);
sa->wpa.ptk_len = (INT)ptk_len;
sa->handshake = 2;
sa->validKey = TRUE; /* we can use the key to decode, even if we have not captured the other eapol packets */
@ -2269,15 +2243,16 @@ Dot11DecryptGetHashAlgoFromAkm(int akm)
* Min(AA, SPA) || Max(AA, SPA) ||
* Min(ANonce, SNonce) || Max(ANonce, SNonce))
*/
static void
static guint8
Dot11DecryptDerivePtk(
DOT11DECRYPT_SEC_ASSOCIATION *sa,
const DOT11DECRYPT_SEC_ASSOCIATION *sa,
const UCHAR *pmk,
size_t pmk_len,
const UCHAR snonce[32],
int key_version,
int akm,
int cipher)
int cipher,
guint8 *ptk, size_t *ptk_len)
{
#ifdef DOT11DECRYPT_DEBUG
#define MSGBUF_LEN 255
@ -2287,6 +2262,11 @@ Dot11DecryptDerivePtk(
int ptk_len_bits = -1;
int derive_func;
if (!sa || !pmk || !snonce || !ptk || !ptk_len) {
DEBUG_PRINT_LINE("Invalid input for PTK derivation", DEBUG_LEVEL_3);
return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
}
if (key_version == DOT11DECRYPT_WPA_KEY_VER_NOT_CCMP) {
/* TKIP */
ptk_len_bits = 512;
@ -2305,8 +2285,9 @@ Dot11DecryptDerivePtk(
}
if (ptk_len_bits == -1 || algo == -1) {
return;
return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
}
*ptk_len = ptk_len_bits / 8;
static const char *const label = "Pairwise key expansion";
guint8 context[DOT11DECRYPT_MAC_LEN * 2 + 32 * 2];
@ -2345,11 +2326,91 @@ Dot11DecryptDerivePtk(
}
if (derive_func == DOT11DECRYPT_DERIVE_USING_PRF) {
dot11decrypt_prf(pmk, pmk_len, label, context, offset, algo,
sa->wpa.ptk, ptk_len_bits / 8);
ptk, *ptk_len);
} else {
dot11decrypt_kdf(pmk, pmk_len, label, context, offset, algo,
sa->wpa.ptk, ptk_len_bits / 8);
ptk, *ptk_len);
}
DEBUG_DUMP("PTK", ptk, *ptk_len);
return DOT11DECRYPT_RET_SUCCESS;
}
/**
* For Fast BSS Transition AKMS derive PTK from sa, selected key and various information in
* eapol key frame.
* From IEEE 802.11-2016 12.7.1.7.1
*/
static guint8
Dot11DecryptFtDerivePtk(
const PDOT11DECRYPT_CONTEXT ctx,
const DOT11DECRYPT_SEC_ASSOCIATION *sa,
const PDOT11DECRYPT_KEY_ITEM key,
const PDOT11DECRYPT_EAPOL_PARSED eapol_parsed,
int akm, int cipher,
guint8 *ptk, size_t *ptk_len)
{
int hash_algo = Dot11DecryptGetHashAlgoFromAkm(akm);
guint8 pmk_r0[DOT11DECRYPT_WPA_PMK_MAX_LEN];
guint8 pmk_r1[DOT11DECRYPT_WPA_PMK_MAX_LEN];
guint8 pmk_r0_name[16];
guint8 pmk_r1_name[16];
guint8 ptk_name[16];
size_t pmk_r0_len;
size_t pmk_r1_len;
const guint8 *xxkey = NULL;
size_t xxkey_len;
int ptk_len_bits;
if (!sa || !key || !eapol_parsed || !ptk || !ptk_len) {
DEBUG_PRINT_LINE("Invalid input for FT PTK derivation", DEBUG_LEVEL_3);
return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
}
if (!eapol_parsed->mdid || !eapol_parsed->r0kh_id || !eapol_parsed->r1kh_id) {
DEBUG_PRINT_LINE("Fields missing for FT", DEBUG_LEVEL_3);
return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
}
ptk_len_bits = Dot11DecryptGetPtkLen(akm, cipher);
if (ptk_len_bits == -1) {
DEBUG_PRINT_LINE("Invalid akm or cipher", DEBUG_LEVEL_3);
return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
}
*ptk_len = ptk_len_bits / 8;
if (key->KeyType == DOT11DECRYPT_KEY_TYPE_MSK) {
xxkey = Dot11DecryptGetXXKeyFromMSK(key->Msk.Msk,
key->Msk.Len,
akm,
&xxkey_len);
}
if (!xxkey && key->KeyData.Wpa.PskLen > 0) {
xxkey = key->KeyData.Wpa.Psk;
xxkey_len = key->KeyData.Wpa.PskLen;
}
if (!xxkey) {
DEBUG_PRINT_LINE("no xxkey. Skipping", DEBUG_LEVEL_3);
return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
}
dot11decrypt_derive_pmk_r0(xxkey, xxkey_len,
ctx->pkt_ssid, ctx->pkt_ssid_len,
eapol_parsed->mdid,
eapol_parsed->r0kh_id, eapol_parsed->r0kh_id_len,
sa->saId.sta, hash_algo,
pmk_r0, &pmk_r0_len, pmk_r0_name);
DEBUG_DUMP("PMK-R0", pmk_r0, pmk_r0_len);
DEBUG_DUMP("PMKR0Name", pmk_r0_name, 16);
dot11decrypt_derive_pmk_r1(pmk_r0, pmk_r0_len, pmk_r0_name,
eapol_parsed->r1kh_id, sa->saId.sta, hash_algo,
pmk_r1, &pmk_r1_len, pmk_r1_name);
DEBUG_DUMP("PMK-R1", pmk_r1, pmk_r1_len);
DEBUG_DUMP("PMKR1Name", pmk_r1_name, 16);
dot11decrypt_derive_ft_ptk(pmk_r1, pmk_r1_len, pmk_r1_name,
eapol_parsed->nonce, sa->wpa.nonce,
sa->saId.bssid, sa->saId.sta, hash_algo,
ptk, *ptk_len, ptk_name);
DEBUG_DUMP("PTK", ptk, *ptk_len);
return DOT11DECRYPT_RET_SUCCESS;
}
#define MAX_SSID_LENGTH 32 /* maximum SSID length */