dot11decrypt: Add partial FT-EAP decryption support

Add partial support for decrypting captures with connections
established using FT-EAP. To support deriving keys for FT-EAP
the MSK is needed. This change adds MSK as a valid IEEE 802.11
protocol input key type preference as well.

Note that FT-EAP support comes with the following imitations:

- Keys can only be derived from the FT 4-way handshake messages.
- Roaming is not supported.
This commit is contained in:
Mikael Kanstrup 2021-01-11 23:47:22 +01:00 committed by Wireshark GitLab Utility
parent 76932c250a
commit fbb9056d20
7 changed files with 150 additions and 11 deletions

View File

@ -1485,6 +1485,57 @@ static gboolean Dot11DecryptIsFtAkm(int akm)
return FALSE;
}
/* Get xxkey portion of MSK */
/* From IEEE 802.11-2016 12.7.1.7.3 PMK-R0 */
static const guint8 *
Dot11DecryptGetXXKeyFromMSK(const guint8 *msk, size_t msk_len,
int akm, size_t *xxkey_len)
{
if (!xxkey_len) {
return NULL;
}
switch (akm) {
case 3:
if (msk_len < 64) {
return NULL;
}
*xxkey_len = 32;
return msk + 32;
case 13:
if (msk_len < 48) {
return NULL;
}
*xxkey_len = 48;
return msk;
default:
return NULL;
}
}
/* From IEEE 802.11-2016 12.7.1.3 Pairwise key hierarchy */
static void
Dot11DecryptDerivePmkFromMsk(const guint8 *msk, guint8 msk_len, int akm,
guint8 *pmk, guint8 *pmk_len)
{
if (!msk || !pmk || !pmk_len) {
return;
}
// When using AKM suite selector 00-0F-AC:12, the length of the PMK, PMK_bits,
// shall be 384 bits. With all other AKM suite selectors, the length of the PMK,
// PMK_bits, shall be 256 bits.
if (akm == 12) {
*pmk_len = 384 / 8;
} else {
*pmk_len = 256 / 8;
}
if (msk_len + *pmk_len < msk_len) {
*pmk_len = 0;
return;
}
// PMK = L(MSK, 0, PMK_bits).
memcpy(pmk, msk, *pmk_len);
}
/* Refer to IEEE 802.11i-2004, 8.5.3, pag. 85 */
static INT
Dot11DecryptRsna4WHandshake(
@ -1589,7 +1640,8 @@ Dot11DecryptRsna4WHandshake(
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_WPA_PMK ||
sa->key->KeyType == DOT11DECRYPT_KEY_TYPE_MSK)) {
DEBUG_PRINT_LINE("Try cached WPA key...", DEBUG_LEVEL_3);
tmp_key=sa->key;
} else {
@ -1601,7 +1653,8 @@ Dot11DecryptRsna4WHandshake(
/* 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_WPA_PMK ||
tmp_key->KeyType == DOT11DECRYPT_KEY_TYPE_MSK)
{
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. */
@ -1634,6 +1687,12 @@ Dot11DecryptRsna4WHandshake(
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];
@ -1644,14 +1703,29 @@ Dot11DecryptRsna4WHandshake(
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;
}
/* TODO Handle other AKMS (xxkey == PSK is only valid for FT PSK) */
dot11decrypt_derive_pmk_r0(tmp_pkt_key->KeyData.Wpa.Psk,
tmp_pkt_key->KeyData.Wpa.PskLen,
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,
@ -1920,6 +1994,9 @@ Dot11DecryptValidateKey(
case DOT11DECRYPT_KEY_TYPE_TK:
break;
case DOT11DECRYPT_KEY_TYPE_MSK:
break;
default:
ret=FALSE;
}
@ -2553,6 +2630,25 @@ parse_key_string(gchar* input_string, guint8 key_type)
dk->bits = (guint) dk->key->len * 4;
dk->ssid = NULL;
g_byte_array_free(key_ba, TRUE);
return dk;
}
case DOT11DECRYPT_KEY_TYPE_MSK:
{
key_ba = g_byte_array_new();
res = hex_str_to_bytes(input_string, key_ba, FALSE);
if (!res || key_ba->len < DOT11DECRYPT_MSK_MIN_LEN ||
key_ba->len > DOT11DECRYPT_MSK_MAX_LEN)
{
g_byte_array_free(key_ba, TRUE);
return NULL;
}
dk = g_new(decryption_key_t, 1);
dk->type = DOT11DECRYPT_KEY_TYPE_MSK;
dk->key = g_string_new(input_string);
dk->bits = (guint)dk->key->len * 4;
dk->ssid = NULL;
g_byte_array_free(key_ba, TRUE);
return dk;
}

View File

@ -30,11 +30,13 @@
#define DOT11DECRYPT_KEY_TYPE_WPA_PSK 4
#define DOT11DECRYPT_KEY_TYPE_WPA_PMK 5
#define DOT11DECRYPT_KEY_TYPE_TK 6
#define DOT11DECRYPT_KEY_TYPE_TKIP 7
#define DOT11DECRYPT_KEY_TYPE_CCMP 8
#define DOT11DECRYPT_KEY_TYPE_CCMP_256 9
#define DOT11DECRYPT_KEY_TYPE_GCMP 10
#define DOT11DECRYPT_KEY_TYPE_GCMP_256 11
#define DOT11DECRYPT_KEY_TYPE_MSK 7
#define DOT11DECRYPT_KEY_TYPE_TKIP 100
#define DOT11DECRYPT_KEY_TYPE_CCMP 101
#define DOT11DECRYPT_KEY_TYPE_CCMP_256 102
#define DOT11DECRYPT_KEY_TYPE_GCMP 103
#define DOT11DECRYPT_KEY_TYPE_GCMP_256 104
#define DOT11DECRYPT_KEY_TYPE_UNKNOWN -1
/* Decryption algorithms fields size definition (bytes) */
@ -50,6 +52,8 @@
#define DOT11DECRYPT_WPA_PMK_MAX_LEN 48
#define DOT11DECRYPT_WPA_PWD_PSK_LEN 32
#define DOT11DECRYPT_TK_MAX_LEN 32
#define DOT11DECRYPT_MSK_MIN_LEN 64
#define DOT11DECRYPT_MSK_MAX_LEN 128
/* */
/* */
/******************************************************************************/
@ -145,6 +149,11 @@ typedef struct _DOT11DECRYPT_KEY_ITEM {
guint8 Len;
} Tk;
struct DOT11DECRYPT_KEY_ITEMDATA_MSK {
guint8 Msk[DOT11DECRYPT_MSK_MAX_LEN];
guint8 Len;
} Msk;
struct DOT11DECRYPT_KEY_ITEMDATA_PWD {
/**
* The string (null-terminated) value of

View File

@ -73,6 +73,8 @@ extern "C" {
#define STRING_KEY_TYPE_WPA_PWD "wpa-pwd"
#define STRING_KEY_TYPE_WPA_PSK "wpa-psk"
#define STRING_KEY_TYPE_TK "tk"
#define STRING_KEY_TYPE_MSK "msk"
#ifdef __cplusplus
}

View File

@ -202,6 +202,12 @@ uat_wep_key_record_update_cb(void* r, char** err)
return FALSE;
}
break;
case DOT11DECRYPT_KEY_TYPE_MSK:
if (rec->key != DOT11DECRYPT_KEY_TYPE_MSK) {
*err = g_strdup("Invalid MSK key format");
return FALSE;
}
break;
default:
*err = g_strdup("Invalid key format");
return FALSE;
@ -2861,6 +2867,7 @@ static const value_string wep_type_vals[] = {
{ DOT11DECRYPT_KEY_TYPE_WPA_PWD, STRING_KEY_TYPE_WPA_PWD },
{ DOT11DECRYPT_KEY_TYPE_WPA_PSK, STRING_KEY_TYPE_WPA_PSK },
{ DOT11DECRYPT_KEY_TYPE_TK, STRING_KEY_TYPE_TK },
{ DOT11DECRYPT_KEY_TYPE_MSK, STRING_KEY_TYPE_MSK },
{ 0x00, NULL }
};
@ -28216,6 +28223,18 @@ set_dot11decrypt_keys(void)
keys->Keys[keys->nKeys] = key;
keys->nKeys += 1;
}
else if (dk->type == DOT11DECRYPT_KEY_TYPE_MSK)
{
key.KeyType = DOT11DECRYPT_KEY_TYPE_MSK;
bytes = g_byte_array_new();
hex_str_to_bytes(dk->key->str, bytes, FALSE);
memcpy(key.Msk.Msk, bytes->data, bytes->len);
key.Msk.Len = bytes->len;
keys->Keys[keys->nKeys] = key;
keys->nKeys += 1;
}
free_key_string(dk);
if (bytes) {
g_byte_array_free(bytes, TRUE);
@ -39928,7 +39947,8 @@ proto_register_ieee80211(void)
"wep:<wep hexadecimal key>\n"
"wpa-pwd:<passphrase>[:<ssid>]\n"
"wpa-psk:<wpa hexadecimal key>\n"
"tk:<hexadecimal key>\n"),
"tk:<hexadecimal key>\n"
"msk:<hexadecimal key>\n"),
UAT_END_FIELDS
};

Binary file not shown.

View File

@ -9,3 +9,4 @@
"wpa-psk","ecbfe709d6151eaba6a4fd9cba94fbb570c1fc4c15506fad3185b4a0a0cfda9a"
"wpa-psk","a4b0b2efa7f77d1006eccf1a814b62125c15fac5c137d9cdff8c75c43194268f"
"wpa-psk","fc738f5b63ba93ebf0a45d42c5a0b1b5064649fa98f59bc062c2944de3780fe276088c95daaf672deb6780051aa13563"
"msk","fc3fe399f0ab9eeb5b6e87b6e2b276d828e874de1773d4a925f5410d96565b22b1471711baffb8611b28d2a09cc1a6aaffbbfdf3cccf12db57f175c53bfe2b7b"

View File

@ -234,6 +234,17 @@ class case_decrypt_80211(subprocesstest.SubprocessTestCase):
self.assertTrue(self.grepOutput('DHCP Request')) # Verifies GTK decryption
self.assertTrue(self.grepOutput(r'Echo \(ping\) request')) # Verifies TK decryption
def test_80211_wpa2_ft_eap(self, cmd_tshark, capture_file):
'''IEEE 802.11 decode WPA2 FT EAP'''
# Included in git sources test/captures/wpa2-ft-eap.pcapng.gz
self.assertRun((cmd_tshark,
'-o', 'wlan.enable_decryption: TRUE',
'-r', capture_file('wpa2-ft-eap.pcapng.gz'),
'-Y', 'wlan.analysis.tk == 65471b64605bf2a04af296284cb4ae2a || wlan.analysis.gtk == 1783a5c28e046df6fb58cf4406c4b22c',
))
self.assertTrue(self.grepOutput('Who has 192.168.1.1')) # Verifies GTK decryption
self.assertTrue(self.grepOutput(r'Echo \(ping\) request')) # Verifies TK decryption
@fixtures.mark_usefixtures('test_env_80211_user_tk')
@fixtures.uses_fixtures
class case_decrypt_80211_user_tk(subprocesstest.SubprocessTestCase):