diff --git a/epan/crypt/dot11decrypt.c b/epan/crypt/dot11decrypt.c index a2de4e2862..d86f4d59e9 100644 --- a/epan/crypt/dot11decrypt.c +++ b/epan/crypt/dot11decrypt.c @@ -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; } diff --git a/epan/crypt/dot11decrypt_user.h b/epan/crypt/dot11decrypt_user.h index e6bf15cb46..19cc5147f2 100644 --- a/epan/crypt/dot11decrypt_user.h +++ b/epan/crypt/dot11decrypt_user.h @@ -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 diff --git a/epan/crypt/wep-wpadefs.h b/epan/crypt/wep-wpadefs.h index 4395994a6f..ae6aa988cf 100644 --- a/epan/crypt/wep-wpadefs.h +++ b/epan/crypt/wep-wpadefs.h @@ -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 } diff --git a/epan/dissectors/packet-ieee80211.c b/epan/dissectors/packet-ieee80211.c index 51e1c38138..aec8b49e49 100644 --- a/epan/dissectors/packet-ieee80211.c +++ b/epan/dissectors/packet-ieee80211.c @@ -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:\n" "wpa-pwd:[:]\n" "wpa-psk:\n" - "tk:\n"), + "tk:\n" + "msk:\n"), UAT_END_FIELDS }; diff --git a/test/captures/wpa2-ft-eap.pcapng.gz b/test/captures/wpa2-ft-eap.pcapng.gz new file mode 100644 index 0000000000..5ec811a345 Binary files /dev/null and b/test/captures/wpa2-ft-eap.pcapng.gz differ diff --git a/test/config/80211_keys.tmpl b/test/config/80211_keys.tmpl index 2341dd4254..da825ac850 100644 --- a/test/config/80211_keys.tmpl +++ b/test/config/80211_keys.tmpl @@ -9,3 +9,4 @@ "wpa-psk","ecbfe709d6151eaba6a4fd9cba94fbb570c1fc4c15506fad3185b4a0a0cfda9a" "wpa-psk","a4b0b2efa7f77d1006eccf1a814b62125c15fac5c137d9cdff8c75c43194268f" "wpa-psk","fc738f5b63ba93ebf0a45d42c5a0b1b5064649fa98f59bc062c2944de3780fe276088c95daaf672deb6780051aa13563" +"msk","fc3fe399f0ab9eeb5b6e87b6e2b276d828e874de1773d4a925f5410d96565b22b1471711baffb8611b28d2a09cc1a6aaffbbfdf3cccf12db57f175c53bfe2b7b" diff --git a/test/suite_decryption.py b/test/suite_decryption.py index e045b56ee8..66e65ae76d 100644 --- a/test/suite_decryption.py +++ b/test/suite_decryption.py @@ -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):