diff options
-rw-r--r-- | epan/crypt/CMakeLists.txt | 1 | ||||
-rw-r--r-- | epan/crypt/dot11decrypt.c | 26 | ||||
-rw-r--r-- | epan/crypt/dot11decrypt_gcmp.c | 111 | ||||
-rw-r--r-- | epan/crypt/dot11decrypt_int.h | 37 | ||||
-rw-r--r-- | epan/crypt/dot11decrypt_system.h | 18 | ||||
-rw-r--r-- | epan/crypt/dot11decrypt_user.h | 2 | ||||
-rw-r--r-- | epan/dissectors/packet-ieee80211.c | 17 | ||||
-rw-r--r-- | test/captures/wpa-gcmp-256.pcapng.gz | bin | 0 -> 5079 bytes | |||
-rw-r--r-- | test/captures/wpa-gcmp.pcapng.gz | bin | 0 -> 5584 bytes | |||
-rw-r--r-- | test/suite_decryption.py | 28 |
10 files changed, 222 insertions, 18 deletions
diff --git a/epan/crypt/CMakeLists.txt b/epan/crypt/CMakeLists.txt index 101921b243..999d647fa8 100644 --- a/epan/crypt/CMakeLists.txt +++ b/epan/crypt/CMakeLists.txt @@ -43,6 +43,7 @@ else() list(APPEND CRYPT_FILES dot11decrypt_ccmp.c dot11decrypt_util.c + dot11decrypt_gcmp.c ) endif() diff --git a/epan/crypt/dot11decrypt.c b/epan/crypt/dot11decrypt.c index 9db5db5744..cdb2ee41a1 100644 --- a/epan/crypt/dot11decrypt.c +++ b/epan/crypt/dot11decrypt.c @@ -345,6 +345,12 @@ Dot11DecryptCopyKey(PDOT11DECRYPT_SEC_ASSOCIATION sa, PDOT11DECRYPT_KEY_ITEM key case 5: key->KeyType = DOT11DECRYPT_KEY_TYPE_WEP_104; break; + case 8: + key->KeyType = DOT11DECRYPT_KEY_TYPE_GCMP; + break; + case 9: + key->KeyType = DOT11DECRYPT_KEY_TYPE_GCMP_256; + break; case 10: key->KeyType = DOT11DECRYPT_KEY_TYPE_CCMP_256; break; @@ -355,8 +361,6 @@ Dot11DecryptCopyKey(PDOT11DECRYPT_SEC_ASSOCIATION sa, PDOT11DECRYPT_KEY_ITEM key case 3: Reserved case 6: BIP-CMAC-128 case 7: Group addressed traffic not allowed - case 8: GCMP-128 - case 9: GCMP-256 case 11: BIP-GMAC-128 case 12: BIP-GMAC-256 case 13: BIP-CMAC-256 */ @@ -1204,6 +1208,24 @@ Dot11DecryptRsnaMng( /* remove MIC and ICV from the end of packet */ *decrypt_len -= DOT11DECRYPT_TKIP_MICLEN + DOT11DECRYPT_WEP_ICV; break; + } else if (sa->wpa.cipher == 8 || sa->wpa.cipher == 9) { + DEBUG_PRINT_LINE("GCMP", DEBUG_LEVEL_3); + + if (*decrypt_len < DOT11DECRYPT_GCMP_TRAILER) { + DEBUG_PRINT_LINE("Invalid decryption length", DEBUG_LEVEL_3); + g_free(try_data); + return DOT11DECRYPT_RET_UNSUCCESS; + } + ret = Dot11DecryptGcmpDecrypt(try_data, mac_header_len, (INT)*decrypt_len, + DOT11DECRYPT_GET_TK(sa->wpa.ptk, sa->wpa.akm), + Dot11DecryptGetTkLen(sa->wpa.cipher) / 8); + if (ret) { + continue; + } + DEBUG_PRINT_LINE("GCMP DECRYPTED!!!", DEBUG_LEVEL_3); + /* remove MIC from the end of packet */ + *decrypt_len -= DOT11DECRYPT_GCMP_TRAILER; + break; } else { /* AES-CCMP -> HMAC-SHA1-128 is the EAPOL-Key MIC, AES wep_key wrap is the EAPOL-Key encryption algorithm */ DEBUG_PRINT_LINE("CCMP", DEBUG_LEVEL_3); diff --git a/epan/crypt/dot11decrypt_gcmp.c b/epan/crypt/dot11decrypt_gcmp.c new file mode 100644 index 0000000000..fb823b5f43 --- /dev/null +++ b/epan/crypt/dot11decrypt_gcmp.c @@ -0,0 +1,111 @@ +/* dot11decrypt_gcmp.c + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/****************************************************************************/ +/* File includes */ +#include "config.h" + +#include "dot11decrypt_debug.h" +#include "dot11decrypt_int.h" +#include "dot11decrypt_system.h" +#include "dot11decrypt_util.h" + +#include <glib.h> +#include <wsutil/wsgcrypt.h> + +/****************************************************************************/ +/* Internal definitions */ + +/****************************************************************************/ +/* Internal macros */ + +#define READ_6(b0, b1, b2, b3, b4, b5) \ + ((((guint64)((guint16)((b4 << 0) | (b5 << 8)))) << 32) | \ + ((guint32)((b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24)))) + +/****************************************************************************/ +/* Internal function prototypes declarations */ + +/****************************************************************************/ +/* Function definitions */ + +/* From IEEE 802.11 2016 Chapter 12.5.5.3.4 Construct GCM nonce */ +static void +gcmp_construct_nonce( + PDOT11DECRYPT_MAC_FRAME wh, + guint64 pn, + guint8 nonce[12]) +{ + /* Nonce: A2 | PN */ + DOT11DECRYPT_ADDR_COPY(nonce, wh->addr2); + nonce[6] = (guint8)(pn >> 40); + nonce[7] = (guint8)(pn >> 32); + nonce[8] = (guint8)(pn >> 24); + nonce[9] = (guint8)(pn >> 16); + nonce[10] = (guint8)(pn >> 8); + nonce[11] = (guint8)(pn >> 0); +} + +int Dot11DecryptGcmpDecrypt( + guint8 *m, + int mac_header_len, + int len, + guint8 *TK1, + int tk_len) +{ + PDOT11DECRYPT_MAC_FRAME wh; + guint8 aad[30]; + guint8 nonce[12]; + guint8 mic[16]; + ssize_t data_len; + size_t aad_len; + int z = mac_header_len; + gcry_cipher_hd_t handle; + guint64 pn; + guint8 *ivp = m + z; + + wh = (PDOT11DECRYPT_MAC_FRAME )m; + data_len = len - (z + DOT11DECRYPT_GCMP_HEADER + sizeof(mic)); + if (data_len < 1) { + return 0; + } + + memcpy(mic, m + len - sizeof(mic), sizeof(mic)); + pn = READ_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]); + gcmp_construct_nonce(wh, pn, nonce); + dot11decrypt_construct_aad(wh, aad, &aad_len); + + if (gcry_cipher_open(&handle, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_GCM, 0)) { + return 1; + } + if (gcry_cipher_setkey(handle, TK1, tk_len)) { + goto err_out; + } + if (gcry_cipher_setiv(handle, nonce, sizeof(nonce))) { + goto err_out; + } + if (gcry_cipher_authenticate(handle, aad, aad_len)) { + goto err_out; + } + if (gcry_cipher_decrypt(handle, m + z + DOT11DECRYPT_GCMP_HEADER, data_len, NULL, 0)) { + goto err_out; + } + if (gcry_cipher_checktag(handle, mic, sizeof(mic))) { + goto err_out; + } + + /* TODO replay check (IEEE 802.11i-2004, pg. 62) */ + /* TODO PN must be incremental (IEEE 802.11i-2004, pg. 62) */ + + gcry_cipher_close(handle); + return 0; +err_out: + gcry_cipher_close(handle); + return 1; +} diff --git a/epan/crypt/dot11decrypt_int.h b/epan/crypt/dot11decrypt_int.h index 366a86aa2e..e582179a8a 100644 --- a/epan/crypt/dot11decrypt_int.h +++ b/epan/crypt/dot11decrypt_int.h @@ -15,6 +15,9 @@ #include "dot11decrypt_interop.h" #include "dot11decrypt_system.h" +#include "ws_attributes.h" +#include <wsutil/wsgcrypt.h> + /****************************************************************************/ /****************************************************************************/ @@ -166,4 +169,38 @@ typedef struct _DOT11DECRYPT_MAC_FRAME_ADDR4_QOS { /******************************************************************************/ +int Dot11DecryptCcmpDecrypt( + guint8 *m, + int mac_header_len, + int len, + guint8 *TK1, + int tk_len, + int mic_len); + +#if GCRYPT_VERSION_NUMBER >= 0x010600 /* 1.6.0 */ +int Dot11DecryptGcmpDecrypt( + guint8 *m, + int mac_header_len, + int len, + guint8 *TK1, + int tk_len); +#else +static inline int Dot11DecryptGcmpDecrypt( + guint8 *m _U_, + int mac_header_len _U_, + int len _U_, + guint8 *TK1 _U_, + int tk_len _U_) +{ + return 1; +} +#endif + +INT Dot11DecryptTkipDecrypt( + UCHAR *tkip_mpdu, + size_t mpdu_len, + UCHAR TA[DOT11DECRYPT_MAC_LEN], + UCHAR TK[DOT11DECRYPT_TK_LEN]) + ; + #endif diff --git a/epan/crypt/dot11decrypt_system.h b/epan/crypt/dot11decrypt_system.h index 2e6af3e6da..c0c082d4ae 100644 --- a/epan/crypt/dot11decrypt_system.h +++ b/epan/crypt/dot11decrypt_system.h @@ -75,6 +75,9 @@ #define DOT11DECRYPT_CCMP_TRAILER 8 /* IEEE 802.11-2016 12.5.3.2 CCMP MPDU format */ #define DOT11DECRYPT_CCMP_256_TRAILER 16 /* IEEE 802.11-2016 12.5.3.2 CCMP MPDU format */ +#define DOT11DECRYPT_GCMP_HEADER 8 /* IEEE 802.11-206 12.5.5.2 GCMP MPDU format */ +#define DOT11DECRYPT_GCMP_TRAILER 16 + #define DOT11DECRYPT_TKIP_HEADER DOT11DECRYPT_RSNA_HEADER #define DOT11DECRYPT_TKIP_TRAILER DOT11DECRYPT_TKIP_MICLEN + DOT11DECRYPT_WEP_ICV @@ -437,21 +440,6 @@ INT Dot11DecryptDestroyContext( PDOT11DECRYPT_CONTEXT ctx) ; -int Dot11DecryptCcmpDecrypt( - guint8 *m, - int mac_header_len, - int len, - guint8 *TK1, - int tk_len, - int mic_len); - -extern INT Dot11DecryptTkipDecrypt( - UCHAR *tkip_mpdu, - size_t mpdu_len, - UCHAR TA[DOT11DECRYPT_MAC_LEN], - UCHAR TK[DOT11DECRYPT_TK_LEN]) - ; - #ifdef __cplusplus } #endif diff --git a/epan/crypt/dot11decrypt_user.h b/epan/crypt/dot11decrypt_user.h index c8fa1f36f7..20f64c5b75 100644 --- a/epan/crypt/dot11decrypt_user.h +++ b/epan/crypt/dot11decrypt_user.h @@ -32,6 +32,8 @@ #define DOT11DECRYPT_KEY_TYPE_TKIP 6 #define DOT11DECRYPT_KEY_TYPE_CCMP 7 #define DOT11DECRYPT_KEY_TYPE_CCMP_256 8 +#define DOT11DECRYPT_KEY_TYPE_GCMP 9 +#define DOT11DECRYPT_KEY_TYPE_GCMP_256 10 #define DOT11DECRYPT_KEY_TYPE_UNKNOWN -1 /* Decryption algorithms fields size definition (bytes) */ diff --git a/epan/dissectors/packet-ieee80211.c b/epan/dissectors/packet-ieee80211.c index 91cdd77f23..dd6067f44d 100644 --- a/epan/dissectors/packet-ieee80211.c +++ b/epan/dissectors/packet-ieee80211.c @@ -24082,6 +24082,8 @@ dissect_ieee80211_common(tvbuff_t *tvb, packet_info *pinfo, #define PROTECTION_ALG_TKIP DOT11DECRYPT_KEY_TYPE_TKIP #define PROTECTION_ALG_CCMP DOT11DECRYPT_KEY_TYPE_CCMP #define PROTECTION_ALG_CCMP_256 DOT11DECRYPT_KEY_TYPE_CCMP_256 +#define PROTECTION_ALG_GCMP DOT11DECRYPT_KEY_TYPE_GCMP +#define PROTECTION_ALG_GCMP_256 DOT11DECRYPT_KEY_TYPE_GCMP_256 #define PROTECTION_ALG_RSNA PROTECTION_ALG_CCMP | PROTECTION_ALG_TKIP #define IS_TKIP(tvb, hdr_len) (tvb_get_guint8(tvb, hdr_len + 1) == \ ((tvb_get_guint8(tvb, hdr_len) | 0x20) & 0x7f)) @@ -25227,6 +25229,9 @@ dissect_ieee80211_common(tvbuff_t *tvb, packet_info *pinfo, else if (algorithm == PROTECTION_ALG_CCMP || algorithm == PROTECTION_ALG_CCMP_256) wep_tree = proto_tree_add_subtree(hdr_tree, tvb, hdr_len, 8, ett_wep_parameters, NULL, "CCMP parameters"); + else if (algorithm == PROTECTION_ALG_GCMP || algorithm == PROTECTION_ALG_GCMP_256) + wep_tree = proto_tree_add_subtree(hdr_tree, tvb, hdr_len, 8, + ett_wep_parameters, NULL, "GCMP parameters"); else { if (IS_TKIP(tvb, hdr_len)) { algorithm=PROTECTION_ALG_TKIP; @@ -25249,7 +25254,8 @@ dissect_ieee80211_common(tvbuff_t *tvb, packet_info *pinfo, tvb_get_guint8(tvb, hdr_len + 2)); proto_tree_add_string(wep_tree, hf_ieee80211_tkip_extiv, tvb, hdr_len, EXTIV_LEN, out_buff); - } else if (algorithm == PROTECTION_ALG_CCMP || algorithm == PROTECTION_ALG_CCMP_256) { + } else if (algorithm == PROTECTION_ALG_CCMP || algorithm == PROTECTION_ALG_CCMP_256 || + algorithm == PROTECTION_ALG_GCMP || algorithm == PROTECTION_ALG_GCMP_256) { g_snprintf(out_buff, SHORT_STR, "0x%08X%02X%02X", tvb_get_letohl(tvb, hdr_len + 4), tvb_get_guint8(tvb, hdr_len + 1), @@ -25395,6 +25401,8 @@ dissect_ieee80211_common(tvbuff_t *tvb, packet_info *pinfo, g_strlcpy(wlan_stats.protection, "TKIP", MAX_PROTECT_LEN); } else if (algorithm == PROTECTION_ALG_CCMP || algorithm == PROTECTION_ALG_CCMP_256) { g_strlcpy(wlan_stats.protection, "CCMP", MAX_PROTECT_LEN); + } else if (algorithm == PROTECTION_ALG_GCMP || algorithm == PROTECTION_ALG_GCMP_256) { + g_strlcpy(wlan_stats.protection, "GCMP", MAX_PROTECT_LEN); } else { g_strlcpy(wlan_stats.protection, "Unknown", MAX_PROTECT_LEN); } @@ -25437,6 +25445,8 @@ dissect_ieee80211_common(tvbuff_t *tvb, packet_info *pinfo, add_new_data_source(pinfo, next_tvb, "Decrypted WEP data"); } else if (algorithm == PROTECTION_ALG_CCMP || algorithm == PROTECTION_ALG_CCMP_256) { add_new_data_source(pinfo, next_tvb, "Decrypted CCMP data"); + } else if (algorithm == PROTECTION_ALG_GCMP || algorithm == PROTECTION_ALG_GCMP_256) { + add_new_data_source(pinfo, next_tvb, "Decrypted GCMP data"); } else if (algorithm==PROTECTION_ALG_TKIP) { add_new_data_source(pinfo, next_tvb, "Decrypted TKIP data"); } @@ -26453,6 +26463,11 @@ try_decrypt(tvbuff_t *tvb, packet_info *pinfo, guint offset, guint len, *sec_header = DOT11DECRYPT_RSNA_HEADER; *sec_trailer = DOT11DECRYPT_CCMP_256_TRAILER; break; + case DOT11DECRYPT_KEY_TYPE_GCMP: + case DOT11DECRYPT_KEY_TYPE_GCMP_256: + *sec_header = DOT11DECRYPT_RSNA_HEADER; + *sec_trailer = DOT11DECRYPT_GCMP_TRAILER; + break; case DOT11DECRYPT_KEY_TYPE_TKIP: *sec_header=DOT11DECRYPT_RSNA_HEADER; *sec_trailer=DOT11DECRYPT_TKIP_TRAILER; diff --git a/test/captures/wpa-gcmp-256.pcapng.gz b/test/captures/wpa-gcmp-256.pcapng.gz Binary files differnew file mode 100644 index 0000000000..83ece8e1f3 --- /dev/null +++ b/test/captures/wpa-gcmp-256.pcapng.gz diff --git a/test/captures/wpa-gcmp.pcapng.gz b/test/captures/wpa-gcmp.pcapng.gz Binary files differnew file mode 100644 index 0000000000..e3b799b7fe --- /dev/null +++ b/test/captures/wpa-gcmp.pcapng.gz diff --git a/test/suite_decryption.py b/test/suite_decryption.py index 3c746a1eae..e8b659da9b 100644 --- a/test/suite_decryption.py +++ b/test/suite_decryption.py @@ -171,6 +171,34 @@ class case_decrypt_80211(subprocesstest.SubprocessTestCase): self.assertTrue(self.grepOutput('DHCP Request')) # Verifies TK is correct self.assertTrue(self.grepOutput('Echo \(ping\) request')) # Verifies TK is correct + def test_80211_wpa_gcmp(self, cmd_tshark, capture_file, features): + '''IEEE 802.11 decode GCMP''' + # Included in git sources test/captures/wpa-gcmp.pcapng.gz + if not features.have_libgcrypt16: + self.skipTest('Requires GCrypt 1.6 or later.') + self.assertRun((cmd_tshark, + '-o', 'wlan.enable_decryption: TRUE', + '-r', capture_file('wpa-gcmp.pcapng.gz'), + '-Y', 'wlan.analysis.tk == 755a9c1c9e605d5ff62849e4a17a935c || wlan.analysis.gtk == 7ff30f7a8dd67950eaaf2f20a869a62d', + )) + self.assertTrue(self.grepOutput('Who has 192.168.5.5')) # Verifies GTK is correct + self.assertTrue(self.grepOutput('DHCP Request')) # Verifies TK is correct + self.assertTrue(self.grepOutput('Echo \(ping\) request')) # Verifies TK is correct + + def test_80211_wpa_gcmp_256(self, cmd_tshark, capture_file, features): + '''IEEE 802.11 decode GCMP-256''' + # Included in git sources test/captures/wpa-gcmp-256.pcapng.gz + if not features.have_libgcrypt16: + self.skipTest('Requires GCrypt 1.6 or later.') + self.assertRun((cmd_tshark, + '-o', 'wlan.enable_decryption: TRUE', + '-r', capture_file('wpa-gcmp-256.pcapng.gz'), + '-Y', 'wlan.analysis.tk == b3dc2ff2d88d0d34c1ddc421cea17f304af3c46acbbe7b6d808b6ebf1b98ec38 || wlan.analysis.gtk == a745ee2313f86515a155c4cb044bc148ae234b9c72707f772b69c2fede3e4016', + )) + self.assertTrue(self.grepOutput('Who has 192.168.5.5')) # Verifies GTK is correct + self.assertTrue(self.grepOutput('DHCP Request')) # Verifies TK is correct + self.assertTrue(self.grepOutput('Echo \(ping\) request')) # Verifies TK is correct + @fixtures.mark_usefixtures('test_env') @fixtures.uses_fixtures class case_decrypt_dtls(subprocesstest.SubprocessTestCase): |