forked from osmocom/wireshark
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:
parent
76932c250a
commit
fbb9056d20
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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.
|
@ -9,3 +9,4 @@
|
|||
"wpa-psk","ecbfe709d6151eaba6a4fd9cba94fbb570c1fc4c15506fad3185b4a0a0cfda9a"
|
||||
"wpa-psk","a4b0b2efa7f77d1006eccf1a814b62125c15fac5c137d9cdff8c75c43194268f"
|
||||
"wpa-psk","fc738f5b63ba93ebf0a45d42c5a0b1b5064649fa98f59bc062c2944de3780fe276088c95daaf672deb6780051aa13563"
|
||||
"msk","fc3fe399f0ab9eeb5b6e87b6e2b276d828e874de1773d4a925f5410d96565b22b1471711baffb8611b28d2a09cc1a6aaffbbfdf3cccf12db57f175c53bfe2b7b"
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue