summaryrefslogtreecommitdiffstats
path: root/epan/crypt
diff options
context:
space:
mode:
Diffstat (limited to 'epan/crypt')
-rw-r--r--epan/crypt/CMakeLists.txt82
-rw-r--r--epan/crypt/CMakeListsCustom.txt.example13
-rw-r--r--epan/crypt/dot11decrypt.c3094
-rw-r--r--epan/crypt/dot11decrypt_ccmp.c139
-rw-r--r--epan/crypt/dot11decrypt_debug.h31
-rw-r--r--epan/crypt/dot11decrypt_gcmp.c110
-rw-r--r--epan/crypt/dot11decrypt_int.h193
-rw-r--r--epan/crypt/dot11decrypt_system.h463
-rw-r--r--epan/crypt/dot11decrypt_tkip.c224
-rw-r--r--epan/crypt/dot11decrypt_user.h232
-rw-r--r--epan/crypt/dot11decrypt_util.c412
-rw-r--r--epan/crypt/dot11decrypt_util.h73
-rw-r--r--epan/crypt/dot11decrypt_ws.h25
-rw-r--r--epan/crypt/kasumi.h17
-rw-r--r--epan/crypt/wep-wpadefs.h83
15 files changed, 5191 insertions, 0 deletions
diff --git a/epan/crypt/CMakeLists.txt b/epan/crypt/CMakeLists.txt
new file mode 100644
index 00000000..8b55e9b0
--- /dev/null
+++ b/epan/crypt/CMakeLists.txt
@@ -0,0 +1,82 @@
+# CMakeLists.txt
+#
+# Wireshark - Network traffic analyzer
+# By Gerald Combs <gerald@wireshark.org>
+# Copyright 1998 Gerald Combs
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+
+ADD_CUSTOM_CMAKE_INCLUDE()
+
+set(CRYPT_HEADER_FILES
+ dot11decrypt_debug.h
+ dot11decrypt_int.h
+ dot11decrypt_system.h
+ dot11decrypt_user.h
+ dot11decrypt_util.h
+ dot11decrypt_ws.h
+ kasumi.h
+ wep-wpadefs.h
+)
+
+set(CRYPT_FILES
+ dot11decrypt.c
+ dot11decrypt_tkip.c
+ dot11decrypt_util.c
+ ${CUSTOM_CRYPT_SRC}
+)
+
+list(APPEND CRYPT_FILES
+ dot11decrypt_ccmp.c
+ dot11decrypt_gcmp.c
+)
+
+source_group(crypt FILES ${CRYPT_FILES})
+
+set_source_files_properties(
+ ${CRYPT_FILES}
+ PROPERTIES
+ COMPILE_FLAGS "${WERROR_COMMON_FLAGS}"
+)
+
+add_library(crypt OBJECT
+ #Included so that Visual Studio can properly put header files in solution
+ ${CRYPT_HEADER_FILES}
+
+ ${CRYPT_FILES}
+)
+
+target_include_directories(crypt
+ SYSTEM PRIVATE
+ ${GCRYPT_INCLUDE_DIRS}
+ PRIVATE
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}
+)
+
+set_target_properties(crypt PROPERTIES
+ FOLDER "Libs/epan/crypt"
+ COMPILE_DEFINITIONS "WS_BUILD_DLL"
+)
+
+CHECKAPI(
+ NAME
+ crypt
+ SWITCHES
+ SOURCES
+ ${CRYPT_FILES}
+)
+
+#
+# Editor modelines - https://www.wireshark.org/tools/modelines.html
+#
+# Local variables:
+# c-basic-offset: 8
+# tab-width: 8
+# indent-tabs-mode: t
+# End:
+#
+# vi: set shiftwidth=8 tabstop=8 noexpandtab:
+# :indentSize=8:tabSize=8:noTabs=false:
+#
diff --git a/epan/crypt/CMakeListsCustom.txt.example b/epan/crypt/CMakeListsCustom.txt.example
new file mode 100644
index 00000000..bf1683c9
--- /dev/null
+++ b/epan/crypt/CMakeListsCustom.txt.example
@@ -0,0 +1,13 @@
+# CMakeListsCustom.txt
+#
+# Wireshark - Network traffic analyzer
+# By Gerald Combs <gerald@wireshark.org>
+# Copyright 1998 Gerald Combs
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# You can add custom builtin dissectors here by replacing foo with your dissectors name.
+
+set(CUSTOM_CRYPT_SRC
+# foo.c
+)
diff --git a/epan/crypt/dot11decrypt.c b/epan/crypt/dot11decrypt.c
new file mode 100644
index 00000000..e90aaf8e
--- /dev/null
+++ b/epan/crypt/dot11decrypt.c
@@ -0,0 +1,3094 @@
+/* dot11decrypt.c
+ *
+ * Copyright (c) 2006 CACE Technologies, Davis (California)
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
+ */
+
+/****************************************************************************/
+/* File includes */
+
+#include "config.h"
+
+#include <stdint.h>
+#include <glib.h>
+
+#include <wsutil/wsgcrypt.h>
+#include <wsutil/crc32.h>
+#include <wsutil/pint.h>
+
+#include <epan/proto.h> /* for DISSECTOR_ASSERT. */
+#include <epan/tvbuff.h>
+#include <epan/to_str.h>
+#include <epan/strutil.h>
+
+#include "dot11decrypt_util.h"
+#include "dot11decrypt_system.h"
+#include "dot11decrypt_int.h"
+
+#include "dot11decrypt_debug.h"
+
+#include "wep-wpadefs.h"
+
+
+/****************************************************************************/
+static int Dot11DecryptGetKckLen(int akm);
+static int Dot11DecryptGetTkLen(int cipher);
+static int Dot11DecryptGetKekLen(int akm);
+static int Dot11DecryptGetPtkLen(int akm, int cipher);
+static int Dot11DecryptGetHashAlgoFromAkm(int akm);
+
+/****************************************************************************/
+/* Constant definitions */
+
+/* EAPOL definitions */
+/**
+ * Length of the EAPOL-Key key confirmation key (KCK) used to calculate
+ * MIC over EAPOL frame and validate an EAPOL packet (128 bits)
+ */
+#define DOT11DECRYPT_WPA_KCK_LEN 16
+/**
+ *Offset of the Key MIC in the EAPOL packet body
+ */
+#define DOT11DECRYPT_WPA_MICKEY_OFFSET 77
+/**
+ * Maximum length of the EAPOL packet (it depends on the maximum MAC
+ * frame size)
+ */
+#define DOT11DECRYPT_WPA_MAX_EAPOL_LEN 4095
+/**
+ * EAPOL Key Descriptor Version 1, used for all EAPOL-Key frames to and
+ * from a STA when neither the group nor pairwise ciphers are CCMP for
+ * Key Descriptor 1.
+ * @note
+ * Defined in 802.11i-2004, page 78
+ */
+#define DOT11DECRYPT_WPA_KEY_VER_NOT_CCMP 1
+/**
+ * EAPOL Key Descriptor Version 2, used for all EAPOL-Key frames to and
+ * from a STA when either the pairwise or the group cipher is AES-CCMP
+ * for Key Descriptor 2.
+ * /note
+ * Defined in 802.11i-2004, page 78
+ */
+#define DOT11DECRYPT_WPA_KEY_VER_AES_CCMP 2
+
+/** Define EAPOL Key Descriptor type values: use 254 for WPA and 2 for WPA2 **/
+#define DOT11DECRYPT_RSN_WPA_KEY_DESCRIPTOR 254
+#define DOT11DECRYPT_RSN_WPA2_KEY_DESCRIPTOR 2
+
+/* PMK to PTK derive functions */
+#define DOT11DECRYPT_DERIVE_USING_PRF 0
+#define DOT11DECRYPT_DERIVE_USING_KDF 1
+/****************************************************************************/
+
+
+/****************************************************************************/
+/* Macro definitions */
+
+extern const uint32_t crc32_table[256];
+#define CRC(crc, ch) (crc = (crc >> 8) ^ crc32_table[(crc ^ (ch)) & 0xff])
+
+#define KCK_OFFSET(akm) (0)
+#define KEK_OFFSET(akm) ((KCK_OFFSET(akm) + Dot11DecryptGetKckLen(akm) / 8))
+#define TK_OFFSET(akm) ((KEK_OFFSET(akm) + Dot11DecryptGetKekLen(akm) / 8))
+
+#define DOT11DECRYPT_GET_KCK(ptk, akm) (ptk + KCK_OFFSET(akm))
+#define DOT11DECRYPT_GET_KEK(ptk, akm) (ptk + KEK_OFFSET(akm))
+#define DOT11DECRYPT_GET_TK_TKIP(ptk) (ptk + 32)
+#define DOT11DECRYPT_GET_TK(ptk, akm) (ptk + TK_OFFSET(akm))
+
+#define DOT11DECRYPT_IEEE80211_OUI(oui) (pntoh24(oui) == 0x000fac)
+
+/****************************************************************************/
+
+/****************************************************************************/
+/* Type definitions */
+
+/* Internal function prototype declarations */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * It is a step of the PBKDF2 (specifically the PKCS #5 v2.0) defined in
+ * the RFC 2898 to derive a key (used as PMK in WPA)
+ * @param ppbytes [IN] pointer to a password (sequence of between 8 and
+ * 63 ASCII encoded characters)
+ * @param ssid [IN] pointer to the SSID string encoded in max 32 ASCII
+ * encoded characters
+ * @param iterations [IN] times to hash the password (4096 for WPA)
+ * @param count [IN] ???
+ * @param output [OUT] pointer to a preallocated buffer of
+ * SHA1_DIGEST_LEN characters that will contain a part of the key
+ */
+static int Dot11DecryptRsnaPwd2PskStep(
+ const uint8_t *ppbytes,
+ const unsigned passLength,
+ const char *ssid,
+ const size_t ssidLength,
+ const int iterations,
+ const int count,
+ unsigned char *output)
+ ;
+
+/**
+ * It calculates the passphrase-to-PSK mapping reccomanded for use with
+ * RSNAs. This implementation uses the PBKDF2 method defined in the RFC
+ * 2898.
+ * @param passphrase [IN] pointer to a password (sequence of between 8 and
+ * 63 ASCII encoded characters)
+ * @param ssid [IN] pointer to the SSID string encoded in max 32 ASCII
+ * encoded characters
+ * @param output [OUT] calculated PSK (to use as PMK in WPA)
+ * @note
+ * Described in 802.11i-2004, page 165
+ */
+static int Dot11DecryptRsnaPwd2Psk(
+ const char *passphrase,
+ const char *ssid,
+ const size_t ssidLength,
+ unsigned char *output)
+ ;
+
+static int Dot11DecryptRsnaMng(
+ unsigned char *decrypt_data,
+ unsigned mac_header_len,
+ unsigned *decrypt_len,
+ PDOT11DECRYPT_KEY_ITEM key,
+ DOT11DECRYPT_SEC_ASSOCIATION *sa)
+ ;
+
+static int Dot11DecryptWepMng(
+ PDOT11DECRYPT_CONTEXT ctx,
+ unsigned char *decrypt_data,
+ unsigned mac_header_len,
+ unsigned *decrypt_len,
+ PDOT11DECRYPT_KEY_ITEM key,
+ DOT11DECRYPT_SEC_ASSOCIATION_ID *id)
+ ;
+
+static int Dot11DecryptRsna4WHandshake(
+ PDOT11DECRYPT_CONTEXT ctx,
+ PDOT11DECRYPT_EAPOL_PARSED eapol_parsed,
+ const uint8_t *eapol_raw,
+ DOT11DECRYPT_SEC_ASSOCIATION_ID *id,
+ const unsigned tot_len);
+
+/**
+ * It checks whether the specified key is corrected or not.
+ * @note
+ * For a standard WEP key the length will be changed to the standard
+ * length, and the type changed in a generic WEP key.
+ * @param key [IN] pointer to the key to validate
+ * @return
+ * - true: the key contains valid fields and values
+ * - false: the key has some invalid field or value
+ */
+static int Dot11DecryptValidateKey(
+ PDOT11DECRYPT_KEY_ITEM key)
+ ;
+
+static int Dot11DecryptRsnaMicCheck(
+ PDOT11DECRYPT_EAPOL_PARSED eapol_parsed,
+ unsigned char *eapol,
+ unsigned short eapol_len,
+ unsigned char *KCK,
+ unsigned short key_ver,
+ int akm)
+ ;
+
+static int
+Dot11DecryptFtMicCheck(
+ const PDOT11DECRYPT_ASSOC_PARSED assoc_parsed,
+ const uint8_t *kck,
+ size_t kck_len);
+
+static PDOT11DECRYPT_SEC_ASSOCIATION
+Dot11DecryptGetSa(
+ PDOT11DECRYPT_CONTEXT ctx,
+ const DOT11DECRYPT_SEC_ASSOCIATION_ID *id)
+ ;
+
+static int Dot11DecryptGetSaAddress(
+ const DOT11DECRYPT_MAC_FRAME_ADDR4 *frame,
+ DOT11DECRYPT_SEC_ASSOCIATION_ID *id)
+ ;
+
+static const unsigned char * Dot11DecryptGetStaAddress(
+ const DOT11DECRYPT_MAC_FRAME_ADDR4 *frame)
+ ;
+
+static const unsigned char * Dot11DecryptGetBssidAddress(
+ const DOT11DECRYPT_MAC_FRAME_ADDR4 *frame)
+ ;
+
+static uint8_t
+Dot11DecryptDerivePtk(
+ const DOT11DECRYPT_SEC_ASSOCIATION *sa,
+ const unsigned char *pmk,
+ size_t pmk_len,
+ const unsigned char snonce[32],
+ int key_version,
+ int akm,
+ int cipher,
+ uint8_t *ptk, size_t *ptk_len);
+
+static uint8_t
+Dot11DecryptFtDerivePtk(
+ const PDOT11DECRYPT_CONTEXT ctx,
+ const DOT11DECRYPT_SEC_ASSOCIATION *sa,
+ const PDOT11DECRYPT_KEY_ITEM key,
+ const uint8_t mdid[2],
+ const uint8_t *snonce,
+ const uint8_t *r0kh_id, size_t r0kh_id_len,
+ const uint8_t *r1kh_id, size_t r1kh_id_len _U_,
+ int akm, int cipher,
+ uint8_t *ptk, size_t *ptk_len);
+
+/**
+ * @param sa [IN/OUT] pointer to SA that will hold the key
+ * @param data [IN] Frame
+ * @param offset_rsne [IN] RSNE IE offset in the frame
+ * @param offset_fte [IN] Fast BSS Transition IE offset in the frame
+ * @param offset_timeout [IN] Timeout Interval IE offset in the frame
+ * @param offset_link [IN] Link Identifier IE offset in the frame
+ * @param action [IN] Tdls Action code (response or confirm)
+ *
+ * @return
+ * DOT11DECRYPT_RET_SUCCESS if Key has been sucessfully derived (and MIC verified)
+ * DOT11DECRYPT_RET_UNSUCCESS otherwise
+ */
+static int
+Dot11DecryptTDLSDeriveKey(
+ PDOT11DECRYPT_SEC_ASSOCIATION sa,
+ const uint8_t *data,
+ unsigned offset_rsne,
+ unsigned offset_fte,
+ unsigned offset_timeout,
+ unsigned offset_link,
+ uint8_t action)
+ ;
+#ifdef __cplusplus
+}
+#endif
+
+/****************************************************************************/
+
+/****************************************************************************/
+/* Exported function definitions */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+const uint8_t broadcast_mac[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+
+#define TKIP_GROUP_KEY_LEN 32
+#define CCMP_GROUP_KEY_LEN 16
+
+#define EAPOL_RSN_KEY_LEN 95
+
+/* Minimum possible key data size (at least one GTK KDE with CCMP key) */
+#define GROUP_KEY_MIN_LEN 8 + CCMP_GROUP_KEY_LEN
+/* Minimum possible group key msg size (group key msg using CCMP as cipher)*/
+#define GROUP_KEY_PAYLOAD_LEN_MIN \
+ (EAPOL_RSN_KEY_LEN + GROUP_KEY_MIN_LEN)
+
+static void
+Dot11DecryptCopyKey(PDOT11DECRYPT_SEC_ASSOCIATION sa, PDOT11DECRYPT_KEY_ITEM key)
+{
+ if (key!=NULL) {
+ if (sa->key!=NULL)
+ memcpy(key, sa->key, sizeof(DOT11DECRYPT_KEY_ITEM));
+ else
+ memset(key, 0, sizeof(DOT11DECRYPT_KEY_ITEM));
+ key->KeyData.Wpa.PtkLen = sa->wpa.ptk_len;
+ memcpy(key->KeyData.Wpa.Ptk, sa->wpa.ptk, sa->wpa.ptk_len);
+ key->KeyData.Wpa.Akm = sa->wpa.akm;
+ key->KeyData.Wpa.Cipher = sa->wpa.cipher;
+ if (sa->wpa.key_ver==DOT11DECRYPT_WPA_KEY_VER_NOT_CCMP)
+ key->KeyType=DOT11DECRYPT_KEY_TYPE_TKIP;
+ else if (sa->wpa.key_ver == 0 || sa->wpa.key_ver == 3 ||
+ sa->wpa.key_ver == DOT11DECRYPT_WPA_KEY_VER_AES_CCMP)
+ {
+ switch (sa->wpa.cipher) {
+ case 1:
+ key->KeyType = DOT11DECRYPT_KEY_TYPE_WEP_40;
+ break;
+ case 2:
+ key->KeyType = DOT11DECRYPT_KEY_TYPE_TKIP;
+ break;
+ case 4:
+ key->KeyType = DOT11DECRYPT_KEY_TYPE_CCMP;
+ break;
+ 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;
+ default:
+ key->KeyType = DOT11DECRYPT_KEY_TYPE_UNKNOWN;
+ break;
+ /* NOT SUPPORTED YET
+ case 3: Reserved
+ case 6: BIP-CMAC-128
+ case 7: Group addressed traffic not allowed
+ case 11: BIP-GMAC-128
+ case 12: BIP-GMAC-256
+ case 13: BIP-CMAC-256 */
+ }
+ }
+ }
+}
+
+static uint8_t*
+Dot11DecryptRc4KeyData(const uint8_t *decryption_key, unsigned decryption_key_len,
+ const uint8_t *encrypted_keydata, unsigned encrypted_keydata_len)
+{
+ gcry_cipher_hd_t rc4_handle;
+ uint8_t dummy[256] = { 0 };
+ uint8_t *decrypted_key = NULL;
+
+ if (gcry_cipher_open (&rc4_handle, GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM, 0)) {
+ return NULL;
+ }
+ if (gcry_cipher_setkey(rc4_handle, decryption_key, decryption_key_len)) {
+ gcry_cipher_close(rc4_handle);
+ return NULL;
+ }
+ decrypted_key = (uint8_t *)g_memdup2(encrypted_keydata, encrypted_keydata_len);
+ if (!decrypted_key) {
+ gcry_cipher_close(rc4_handle);
+ return NULL;
+ }
+
+ /* Do dummy 256 iterations of the RC4 algorithm (per 802.11i, Draft 3.0, p. 97 line 6) */
+ gcry_cipher_decrypt(rc4_handle, dummy, 256, NULL, 0);
+ gcry_cipher_decrypt(rc4_handle, decrypted_key, encrypted_keydata_len, NULL, 0);
+ gcry_cipher_close(rc4_handle);
+ return decrypted_key;
+}
+
+static int
+AES_unwrap(
+ const uint8_t *kek,
+ uint16_t kek_len,
+ const uint8_t *cipher_text,
+ uint16_t cipher_len,
+ uint8_t *output,
+ uint16_t *output_len)
+{
+ gcry_cipher_hd_t handle;
+
+ if (kek == NULL || cipher_len < 16 || cipher_text == NULL) {
+ return 1; /* "should not happen" */
+ }
+ if (gcry_cipher_open(&handle, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_AESWRAP, 0)) {
+ return 1;
+ }
+ if (gcry_cipher_setkey(handle, kek, kek_len)) {
+ gcry_cipher_close(handle);
+ return 1;
+ }
+ if (gcry_cipher_decrypt(handle, output, cipher_len - 8, cipher_text, cipher_len)) {
+ gcry_cipher_close(handle);
+ return 1;
+ }
+ *output_len = cipher_len - 8;
+ gcry_cipher_close(handle);
+ return 0;
+}
+
+int
+Dot11DecryptDecryptKeyData(PDOT11DECRYPT_CONTEXT ctx,
+ PDOT11DECRYPT_EAPOL_PARSED eapol_parsed,
+ const unsigned char bssid[DOT11DECRYPT_MAC_LEN],
+ const unsigned char sta[DOT11DECRYPT_MAC_LEN],
+ unsigned char *decrypted_data, unsigned *decrypted_len,
+ PDOT11DECRYPT_KEY_ITEM key)
+{
+ uint8_t key_version;
+ const uint8_t *key_data;
+ uint16_t key_bytes_len = 0; /* Length of the total key data field */
+ DOT11DECRYPT_SEC_ASSOCIATION_ID id;
+ PDOT11DECRYPT_SEC_ASSOCIATION sa;
+
+ /* search for a cached Security Association for current BSSID and AP */
+ memcpy(id.bssid, bssid, DOT11DECRYPT_MAC_LEN);
+ memcpy(id.sta, sta, DOT11DECRYPT_MAC_LEN);
+ sa = Dot11DecryptGetSa(ctx, &id);
+ if (sa == NULL || !sa->validKey) {
+ ws_debug("No valid SA for BSSID found");
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+
+ /* Decrypt GTK using KEK portion of PTK */
+ uint8_t *decryption_key = DOT11DECRYPT_GET_KEK(sa->wpa.ptk, sa->wpa.akm);
+ unsigned decryption_key_len = Dot11DecryptGetKekLen(sa->wpa.akm) / 8;
+
+ /* We skip verifying the MIC of the key. If we were implementing a WPA supplicant we'd want to verify, but for a sniffer it's not needed. */
+
+ /* Preparation for decrypting the group key - determine group key data length */
+ /* depending on whether the pairwise key is TKIP or AES encryption key */
+ key_version = eapol_parsed->key_version;
+ if (key_version == DOT11DECRYPT_WPA_KEY_VER_NOT_CCMP){
+ /* TKIP */
+ key_bytes_len = eapol_parsed->key_len;
+ }else if (key_version == DOT11DECRYPT_WPA_KEY_VER_AES_CCMP){
+ /* AES */
+ key_bytes_len = eapol_parsed->key_data_len;
+
+ /* AES keys must be at least 128 bits = 16 bytes. */
+ if (key_bytes_len < 16) {
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+ } else {
+ /* XXX Ideally group cipher suite type from EAPOL message 2 of 4 should be used to */
+ /* determine key size. As we currently have no way to do this lookup check that key */
+ /* is at least 16 bytes (IEEE802.11-2016 Table 12-4 Cipher suite key lengths) */
+ key_bytes_len = eapol_parsed->key_data_len;
+
+ if (key_bytes_len < 16) {
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+ }
+
+ if ((key_bytes_len < GROUP_KEY_MIN_LEN) ||
+ (eapol_parsed->len < EAPOL_RSN_KEY_LEN) ||
+ (key_bytes_len > eapol_parsed->len - EAPOL_RSN_KEY_LEN)) {
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+
+ /* Encrypted key is in the information element field of the EAPOL key packet */
+ key_data = eapol_parsed->key_data;
+
+ DEBUG_DUMP("Encrypted Broadcast key", key_data, key_bytes_len, LOG_LEVEL_DEBUG);
+ DEBUG_DUMP("KeyIV", eapol_parsed->key_iv, 16, LOG_LEVEL_DEBUG);
+ DEBUG_DUMP("decryption_key", decryption_key, decryption_key_len, LOG_LEVEL_DEBUG);
+
+ /* As we have no concept of the prior association request at this point, we need to deduce the */
+ /* group key cipher from the length of the key bytes. In WPA this is straightforward as the */
+ /* keybytes just contain the GTK, and the GTK is only in the group handshake, NOT the M3. */
+ /* In WPA2 its a little more tricky as the M3 keybytes contain an RSN_IE, but the group handshake */
+ /* does not. Also there are other (variable length) items in the keybytes which we need to account */
+ /* for to determine the true key length, and thus the group cipher. */
+
+ if (key_version == DOT11DECRYPT_WPA_KEY_VER_NOT_CCMP){
+ /* TKIP key */
+ /* Per 802.11i, Draft 3.0 spec, section 8.5.2, p. 97, line 4-8, */
+ /* group key is decrypted using RC4. Concatenate the IV with the 16 byte EK (PTK+16) to get the decryption key */
+ uint8_t new_key[32];
+ uint8_t *data;
+
+ /* The WPA group key just contains the GTK bytes so deducing the type is straightforward */
+ /* Note - WPA M3 doesn't contain a group key so we'll only be here for the group handshake */
+ sa->wpa.key_ver = (key_bytes_len >=TKIP_GROUP_KEY_LEN)?DOT11DECRYPT_WPA_KEY_VER_NOT_CCMP:DOT11DECRYPT_WPA_KEY_VER_AES_CCMP;
+
+ /* Build the full decryption key based on the IV and part of the pairwise key */
+ memcpy(new_key, eapol_parsed->key_iv, 16);
+ memcpy(new_key+16, decryption_key, 16);
+ DEBUG_DUMP("FullDecrKey", new_key, 32, LOG_LEVEL_DEBUG);
+ data = Dot11DecryptRc4KeyData(new_key, 32, key_data, key_bytes_len);
+ if (!data) {
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+ memcpy(decrypted_data, data, key_bytes_len);
+ g_free(data);
+ } else {
+ /* Ideally AKM from EAPOL message 2 of 4 should be used to determine Key-wrap algoritm to use */
+ /* Though fortunately IEEE802.11-2016 Table 12-8 state that all AKMs use "NIST AES Key Wrap" */
+ /* algorithm so no AKM lookup is needed. */
+
+ /* Unwrap the key; the result is key_bytes_len in length */
+ if (AES_unwrap(decryption_key, decryption_key_len, key_data, key_bytes_len,
+ decrypted_data, &key_bytes_len)) {
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+ }
+
+ Dot11DecryptCopyKey(sa, key);
+ *decrypted_len = key_bytes_len;
+ return DOT11DECRYPT_RET_SUCCESS;
+}
+
+/**
+ * @param ctx [IN] pointer to the current context
+ * @param id [IN] id of the association (composed by BSSID and MAC of
+ * the station)
+ * @return a pointer of the requested SA. NULL if it doesn't exist.
+ */
+static PDOT11DECRYPT_SEC_ASSOCIATION
+Dot11DecryptGetSa(
+ PDOT11DECRYPT_CONTEXT ctx,
+ const DOT11DECRYPT_SEC_ASSOCIATION_ID *id)
+{
+ return (DOT11DECRYPT_SEC_ASSOCIATION *)g_hash_table_lookup(ctx->sa_hash, id);
+}
+
+static PDOT11DECRYPT_SEC_ASSOCIATION
+Dot11DecryptNewSa(const DOT11DECRYPT_SEC_ASSOCIATION_ID *id)
+{
+ PDOT11DECRYPT_SEC_ASSOCIATION sa = g_new0(DOT11DECRYPT_SEC_ASSOCIATION, 1);
+ if (sa != NULL) {
+ sa->saId = *id;
+ }
+ return sa;
+}
+
+static DOT11DECRYPT_SEC_ASSOCIATION *
+Dot11DecryptPrependSa(
+ DOT11DECRYPT_SEC_ASSOCIATION *existing_sa,
+ DOT11DECRYPT_SEC_ASSOCIATION *new_sa)
+{
+ DOT11DECRYPT_SEC_ASSOCIATION tmp_sa;
+
+ /* Add new SA first in list, but copy by value into existing record
+ * so that sa_hash need not be updated with new value */
+ tmp_sa = *existing_sa;
+ *existing_sa = *new_sa;
+ *new_sa = tmp_sa;
+ existing_sa->next = new_sa;
+ return existing_sa;
+}
+
+/* Add SA, keep existing (if any). Return pointer to newly inserted (first) SA */
+static PDOT11DECRYPT_SEC_ASSOCIATION
+Dot11DecryptAddSa(
+ PDOT11DECRYPT_CONTEXT ctx,
+ const DOT11DECRYPT_SEC_ASSOCIATION_ID *id,
+ DOT11DECRYPT_SEC_ASSOCIATION *sa)
+{
+ DOT11DECRYPT_SEC_ASSOCIATION *existing_sa = Dot11DecryptGetSa(ctx, id);
+ if (existing_sa != NULL) {
+ sa = Dot11DecryptPrependSa(existing_sa, sa);
+ } else {
+ void *key = g_memdup2(id, sizeof(DOT11DECRYPT_SEC_ASSOCIATION_ID));
+ g_hash_table_insert(ctx->sa_hash, key, sa);
+ }
+ return sa;
+}
+
+int
+Dot11DecryptGetKCK(const PDOT11DECRYPT_KEY_ITEM key, const uint8_t **kck)
+{
+ if (!key || !kck) {
+ return 0;
+ }
+ *kck = DOT11DECRYPT_GET_KCK(key->KeyData.Wpa.Ptk, key->KeyData.Wpa.Akm);
+ return Dot11DecryptGetKckLen(key->KeyData.Wpa.Akm) / 8;
+}
+
+int
+Dot11DecryptGetKEK(const PDOT11DECRYPT_KEY_ITEM key, const uint8_t **kek)
+{
+ if (!key || !kek) {
+ return 0;
+ }
+ *kek = DOT11DECRYPT_GET_KEK(key->KeyData.Wpa.Ptk, key->KeyData.Wpa.Akm);
+ return Dot11DecryptGetKekLen(key->KeyData.Wpa.Akm) / 8;
+}
+
+int
+Dot11DecryptGetTK(const PDOT11DECRYPT_KEY_ITEM key, const uint8_t **tk)
+{
+ int len;
+ if (!key || !tk) {
+ return 0;
+ }
+ if (key->KeyType == DOT11DECRYPT_KEY_TYPE_TKIP) {
+ *tk = DOT11DECRYPT_GET_TK_TKIP(key->KeyData.Wpa.Ptk);
+ len = 16;
+ } else {
+ *tk = DOT11DECRYPT_GET_TK(key->KeyData.Wpa.Ptk, key->KeyData.Wpa.Akm);
+ len = Dot11DecryptGetTkLen(key->KeyData.Wpa.Cipher) / 8;
+ }
+ return len;
+}
+
+int
+Dot11DecryptGetGTK(const PDOT11DECRYPT_KEY_ITEM key, const uint8_t **gtk)
+{
+ int len;
+ if (!key || !gtk) {
+ return 0;
+ }
+
+ /* GTK is stored in PTK at offset 32. See comment in Dot11DecryptCopyBroadcastKey */
+ *gtk = key->KeyData.Wpa.Ptk + 32;
+ if (key->KeyType == DOT11DECRYPT_KEY_TYPE_TKIP) {
+ len = 16;
+ } else {
+ len = Dot11DecryptGetTkLen(key->KeyData.Wpa.Cipher) / 8;
+ }
+ return len;
+}
+
+int Dot11DecryptScanTdlsForKeys(
+ PDOT11DECRYPT_CONTEXT ctx,
+ const uint8_t *data,
+ const unsigned tot_len)
+{
+ unsigned offset = 0;
+ unsigned tot_len_left = tot_len;
+ DOT11DECRYPT_SEC_ASSOCIATION_ID id;
+ PDOT11DECRYPT_SEC_ASSOCIATION sa;
+ const uint8_t *initiator, *responder;
+ uint8_t action;
+ unsigned status, offset_rsne = 0, offset_fte = 0, offset_link = 0, offset_timeout = 0;
+ ws_debug("Authentication: TDLS Action Frame");
+
+ /* TDLS payload contains a TDLS Action field (802.11-2016 9.6.13) */
+
+ /* check if the packet is a TDLS response or confirm */
+ if (tot_len_left < 1) {
+ ws_debug("Not EAPOL-Key");
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+ action = data[offset];
+ if (action != 1 && action != 2) {
+ ws_debug("Not Response nor confirm");
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+ offset++;
+ tot_len_left--;
+
+ /* Check for SUCCESS (0) or SUCCESS_POWER_SAVE_MODE (85) Status Code */
+ if (tot_len_left < 5) {
+ ws_debug("Not EAPOL-Key");
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+ status=pntoh16(data + offset);
+ if (status != 0 && status != 85) {
+ ws_debug("TDLS setup not successful");
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+
+ /* skip Token + capabilities */
+ offset += 5;
+
+ /* search for RSN, Fast BSS Transition, Link Identifier and Timeout Interval IEs */
+
+ while(offset < (tot_len - 2)) {
+ uint8_t element_id = data[offset];
+ uint8_t length = data[offset + 1];
+ unsigned min_length = length;
+ switch (element_id) {
+ case 48: /* RSN (802.11-2016 9.4.2.35) */
+ offset_rsne = offset;
+ min_length = 1;
+ break;
+ case 55: /* FTE (802.11-2016 9.4.2.48) */
+ offset_fte = offset;
+ /* Plus variable length optional parameter(s) */
+ min_length = 2 + 16 + 32 + 32;
+ break;
+ case 56: /* Timeout Interval (802.11-2016 9.4.2.49) */
+ offset_timeout = offset;
+ min_length = 1 + 4;
+ break;
+ case 101: /* Link Identifier (802.11-2016 9.4.2.62) */
+ offset_link = offset;
+ min_length = 6 + 6 + 6;
+ break;
+ }
+
+ if (length < min_length || tot_len < offset + 2 + length) {
+ ws_debug("Invalid length records in IEs");
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+ offset += 2 + length;
+ }
+
+ if (offset_rsne == 0 || offset_fte == 0 ||
+ offset_timeout == 0 || offset_link == 0)
+ {
+ ws_debug("Cannot Find all necessary IEs");
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+
+ ws_debug("Found RSNE/Fast BSS/Timeout Interval/Link IEs");
+
+ /* Will create a Security Association between 2 STA. Need to get both MAC address */
+ initiator = &data[offset_link + 8];
+ responder = &data[offset_link + 14];
+
+ if (memcmp(initiator, responder, DOT11DECRYPT_MAC_LEN) < 0) {
+ memcpy(id.sta, initiator, DOT11DECRYPT_MAC_LEN);
+ memcpy(id.bssid, responder, DOT11DECRYPT_MAC_LEN);
+ } else {
+ memcpy(id.sta, responder, DOT11DECRYPT_MAC_LEN);
+ memcpy(id.bssid, initiator, DOT11DECRYPT_MAC_LEN);
+ }
+
+ /* Check if already derived this key */
+ sa = Dot11DecryptGetSa(ctx, &id);
+ PDOT11DECRYPT_SEC_ASSOCIATION iter_sa;
+ for (iter_sa = sa; iter_sa != NULL; iter_sa = iter_sa->next) {
+ if (iter_sa->validKey &&
+ memcmp(iter_sa->wpa.nonce, data + offset_fte + 52,
+ DOT11DECRYPT_WPA_NONCE_LEN) == 0)
+ {
+ /* Already have valid key for this SA, no need to redo key derivation */
+ return DOT11DECRYPT_RET_SUCCESS_HANDSHAKE;
+ }
+ }
+ /* We are opening a new session with the same two STA (previous sa will be kept if any) */
+ sa = Dot11DecryptNewSa(&id);
+ if (sa == NULL) {
+ ws_warning("Failed to alloc new SA entry");
+ return DOT11DECRYPT_RET_REQ_DATA;
+ }
+ if (Dot11DecryptTDLSDeriveKey(sa, data, offset_rsne, offset_fte,
+ offset_timeout, offset_link, action) == DOT11DECRYPT_RET_SUCCESS) {
+ Dot11DecryptAddSa(ctx, &id, sa);
+ return DOT11DECRYPT_RET_SUCCESS_HANDSHAKE;
+ }
+ g_free(sa);
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+}
+
+static int
+Dot11DecryptCopyBroadcastKey(
+ PDOT11DECRYPT_CONTEXT ctx,
+ const uint8_t *gtk, size_t gtk_len,
+ const DOT11DECRYPT_SEC_ASSOCIATION_ID *id)
+{
+ DOT11DECRYPT_SEC_ASSOCIATION_ID broadcast_id;
+ DOT11DECRYPT_SEC_ASSOCIATION *sa;
+ DOT11DECRYPT_SEC_ASSOCIATION *broadcast_sa;
+
+ if (!gtk || gtk_len == 0) {
+ ws_debug("No broadcast key found");
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+ if (gtk_len > DOT11DECRYPT_WPA_PTK_MAX_LEN - 32) {
+ ws_debug("Broadcast key too large");
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+
+ sa = Dot11DecryptGetSa(ctx, id);
+ if (sa == NULL) {
+ ws_debug("No SA for BSSID found");
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+
+ /* Broadcast SA for the current BSSID */
+ memcpy(broadcast_id.bssid, id->bssid, DOT11DECRYPT_MAC_LEN);
+ memcpy(broadcast_id.sta, broadcast_mac, DOT11DECRYPT_MAC_LEN);
+
+ broadcast_sa = Dot11DecryptNewSa(&broadcast_id);
+ if (broadcast_sa == NULL) {
+ ws_warning("Failed to alloc broadcast sa");
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+
+ /* Retrieve AKMS / cipher etc from handshake message 2 */
+
+ broadcast_sa->wpa.key_ver = sa->wpa.key_ver;
+ broadcast_sa->wpa.akm = sa->wpa.akm;
+ broadcast_sa->wpa.cipher = sa->wpa.tmp_group_cipher;
+ broadcast_sa->wpa.ptk_len = sa->wpa.ptk_len;
+ broadcast_sa->validKey = true;
+ DEBUG_DUMP("Broadcast key", gtk, gtk_len, LOG_LEVEL_DEBUG);
+
+ /* Since this is a GTK and its size is only 32 bytes (vs. the 64 byte size of a PTK),
+ * we fake it and put it in at a 32-byte offset so the Dot11DecryptRsnaMng() function
+ * will extract the right piece of the GTK for decryption. (The first 16 bytes of the
+ * GTK are used for decryption.) */
+ memset(broadcast_sa->wpa.ptk, 0, sizeof(broadcast_sa->wpa.ptk));
+ memcpy(broadcast_sa->wpa.ptk + 32, gtk, gtk_len);
+ Dot11DecryptAddSa(ctx, &broadcast_id, broadcast_sa);
+ return DOT11DECRYPT_RET_SUCCESS_HANDSHAKE;
+}
+
+static int
+Dot11DecryptGroupHandshake(
+ PDOT11DECRYPT_CONTEXT ctx,
+ PDOT11DECRYPT_EAPOL_PARSED eapol_parsed,
+ const DOT11DECRYPT_SEC_ASSOCIATION_ID *id,
+ const unsigned tot_len)
+{
+
+ if (GROUP_KEY_PAYLOAD_LEN_MIN > tot_len) {
+ ws_debug("Message too short for Group Key");
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+ if (eapol_parsed->msg_type != DOT11DECRYPT_HS_MSG_TYPE_GHS_1){
+ ws_warning("Not Group handshake message 1");
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+ return Dot11DecryptCopyBroadcastKey(ctx, eapol_parsed->gtk, eapol_parsed->gtk_len, id);
+}
+
+int Dot11DecryptScanEapolForKeys(
+ PDOT11DECRYPT_CONTEXT ctx,
+ PDOT11DECRYPT_EAPOL_PARSED eapol_parsed,
+ const uint8_t *eapol_raw,
+ const unsigned tot_len,
+ const unsigned char bssid[DOT11DECRYPT_MAC_LEN],
+ const unsigned char sta[DOT11DECRYPT_MAC_LEN])
+{
+ DOT11DECRYPT_SEC_ASSOCIATION_ID id;
+
+ /* Callers provide these guarantees, so let's make them explicit. */
+ DISSECTOR_ASSERT(tot_len <= DOT11DECRYPT_EAPOL_MAX_LEN);
+
+ ws_debug("Authentication: EAPOL packet");
+
+ /* check if the key descriptor type is valid (IEEE 802.1X-2004, pg. 27) */
+ if (/*eapol_parsed->key_type != 0x1 &&*/ /* RC4 Key Descriptor Type (deprecated) */
+ eapol_parsed->key_type != DOT11DECRYPT_RSN_WPA2_KEY_DESCRIPTOR && /* IEEE 802.11 Key Descriptor Type (WPA2) */
+ eapol_parsed->key_type != DOT11DECRYPT_RSN_WPA_KEY_DESCRIPTOR) /* 254 = RSN_KEY_DESCRIPTOR - WPA, */
+ {
+ ws_debug("Not valid key descriptor type");
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+
+ /* search for a cached Security Association for current BSSID and AP */
+ memcpy(id.bssid, bssid, DOT11DECRYPT_MAC_LEN);
+ memcpy(id.sta, sta, DOT11DECRYPT_MAC_LEN);
+
+ switch (eapol_parsed->msg_type) {
+ case DOT11DECRYPT_HS_MSG_TYPE_4WHS_1:
+ case DOT11DECRYPT_HS_MSG_TYPE_4WHS_2:
+ case DOT11DECRYPT_HS_MSG_TYPE_4WHS_3:
+ case DOT11DECRYPT_HS_MSG_TYPE_4WHS_4:
+ return Dot11DecryptRsna4WHandshake(ctx, eapol_parsed, eapol_raw,
+ &id, tot_len);
+ case DOT11DECRYPT_HS_MSG_TYPE_GHS_1:
+ return Dot11DecryptGroupHandshake(ctx, eapol_parsed, &id, tot_len);
+ case DOT11DECRYPT_HS_MSG_TYPE_GHS_2:
+ break;
+ case DOT11DECRYPT_HS_MSG_TYPE_INVALID:
+ default:
+ ws_warning("Invalid message type");
+ break;
+ }
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+}
+
+static int
+Dot11DecryptGetNbrOfTkKeys(PDOT11DECRYPT_CONTEXT ctx)
+{
+ int nbr = 0;
+ for (size_t i = 0; i < ctx->keys_nr; i++) {
+ if (ctx->keys[i].KeyType == DOT11DECRYPT_KEY_TYPE_TK) {
+ nbr++;
+ }
+ }
+ return nbr;
+}
+
+static int
+Dot11DecryptUsingUserTk(
+ PDOT11DECRYPT_CONTEXT ctx,
+ unsigned char *decrypt_data,
+ unsigned mac_header_len,
+ unsigned *decrypt_len,
+ DOT11DECRYPT_SEC_ASSOCIATION_ID *id,
+ DOT11DECRYPT_KEY_ITEM *used_key)
+{
+ int ret = DOT11DECRYPT_RET_REQ_DATA;
+ DOT11DECRYPT_SEC_ASSOCIATION *sa = Dot11DecryptNewSa(id);
+ DOT11DECRYPT_KEY_ITEM *key;
+ if (sa == NULL) {
+ return ret;
+ }
+
+ sa->wpa.akm = 2;
+ sa->validKey = true;
+
+ /* Try decrypt packet with all user TKs applicable ciphers */
+ for (size_t key_index = 0; key_index < ctx->keys_nr; key_index++) {
+ key = &ctx->keys[key_index];
+ if (key->KeyType != DOT11DECRYPT_KEY_TYPE_TK) {
+ continue;
+ }
+ int ciphers_to_try[4] = { 0 };
+ switch (key->Tk.Len) {
+ case DOT11DECRYPT_WEP_40_KEY_LEN:
+ case DOT11DECRYPT_WEP_104_KEY_LEN:
+ /* TBD implement */
+ continue;
+ case 256 / 8:
+ ciphers_to_try[0] = 9; /* GCMP-256 */
+ ciphers_to_try[1] = 10; /* CCMP-256 */
+ break;
+ case 128 / 8:
+ ciphers_to_try[0] = 4; /* CCMP-128 */
+ ciphers_to_try[1] = 8; /* GCMP-128 */
+ ciphers_to_try[2] = 2; /* TKIP */
+ break;
+ default:
+ continue;
+ }
+
+ sa->key = key;
+
+ for (int i = 0; ciphers_to_try[i] != 0; i++) {
+ sa->wpa.cipher = ciphers_to_try[i];
+ if (sa->wpa.cipher == 2 /* TKIP */) {
+ sa->wpa.key_ver = 1;
+ memcpy(DOT11DECRYPT_GET_TK_TKIP(sa->wpa.ptk),
+ key->Tk.Tk, key->Tk.Len);
+ } else {
+ sa->wpa.key_ver = 2;
+ sa->wpa.akm = 2;
+ memcpy(DOT11DECRYPT_GET_TK(sa->wpa.ptk, sa->wpa.akm),
+ key->Tk.Tk, key->Tk.Len);
+ }
+ sa->wpa.ptk_len = Dot11DecryptGetPtkLen(sa->wpa.akm, sa->wpa.cipher) / 8;
+ ret = Dot11DecryptRsnaMng(decrypt_data, mac_header_len, decrypt_len, used_key, sa);
+ if (ret == DOT11DECRYPT_RET_SUCCESS) {
+ /* Successfully decrypted using user TK. Add SA formed from user TK so that
+ * subsequent frames can be decrypted much faster using normal code path
+ * without trying each and every user TK entered.
+ */
+ Dot11DecryptAddSa(ctx, id, sa);
+ return ret;
+ }
+ }
+ }
+ g_free(sa);
+ return ret;
+}
+
+int Dot11DecryptDecryptPacket(
+ PDOT11DECRYPT_CONTEXT ctx,
+ const uint8_t *data,
+ const unsigned mac_header_len,
+ const unsigned tot_len,
+ unsigned char *decrypt_data,
+ unsigned *decrypt_len,
+ PDOT11DECRYPT_KEY_ITEM key)
+{
+ DOT11DECRYPT_SEC_ASSOCIATION_ID id;
+ DISSECTOR_ASSERT(decrypt_data);
+ DISSECTOR_ASSERT(decrypt_len);
+
+ if (decrypt_len) {
+ *decrypt_len = 0;
+ }
+ if (ctx==NULL) {
+ ws_warning("NULL context");
+ return DOT11DECRYPT_RET_REQ_DATA;
+ }
+ if (data==NULL || tot_len==0) {
+ ws_debug("NULL data or length=0");
+ return DOT11DECRYPT_RET_REQ_DATA;
+ }
+
+ /* check correct packet size, to avoid wrong elaboration of encryption algorithms */
+ if (tot_len < (unsigned)(mac_header_len+DOT11DECRYPT_CRYPTED_DATA_MINLEN)) {
+ ws_debug("minimum length violated");
+ return DOT11DECRYPT_RET_WRONG_DATA_SIZE;
+ }
+
+ /* Assume that the decrypt_data field is no more than this size. */
+ if (tot_len > DOT11DECRYPT_MAX_CAPLEN) {
+ ws_debug("length too large");
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+
+ /* get STA/BSSID address */
+ if (Dot11DecryptGetSaAddress((const DOT11DECRYPT_MAC_FRAME_ADDR4 *)(data), &id) != DOT11DECRYPT_RET_SUCCESS) {
+ ws_noisy("STA/BSSID not found");
+ return DOT11DECRYPT_RET_REQ_DATA;
+ }
+
+ /* check if data is encrypted (use the WEP bit in the Frame Control field) */
+ if (DOT11DECRYPT_WEP(data[1])==0) {
+ return DOT11DECRYPT_RET_NO_DATA_ENCRYPTED;
+ }
+ PDOT11DECRYPT_SEC_ASSOCIATION sa;
+
+ /* create new header and data to modify */
+ *decrypt_len = tot_len;
+ memcpy(decrypt_data, data, *decrypt_len);
+
+ /* encrypted data */
+ ws_noisy("Encrypted data");
+
+ /* check the Extension IV to distinguish between WEP encryption and WPA encryption */
+ /* refer to IEEE 802.11i-2004, 8.2.1.2, pag.35 for WEP, */
+ /* IEEE 802.11i-2004, 8.3.2.2, pag. 45 for TKIP, */
+ /* IEEE 802.11i-2004, 8.3.3.2, pag. 57 for CCMP */
+ if (DOT11DECRYPT_EXTIV(data[mac_header_len + 3]) == 0) {
+ ws_noisy("WEP encryption");
+ return Dot11DecryptWepMng(ctx, decrypt_data, mac_header_len, decrypt_len, key, &id);
+ } else {
+ ws_noisy("TKIP or CCMP encryption");
+
+ /* If the destination is a multicast address use the group key. This will not work if the AP is using
+ more than one group key simultaneously. I've not seen this in practice, however.
+ Usually an AP will rotate between the two key index values of 1 and 2 whenever
+ it needs to change the group key to be used. */
+ if (((const DOT11DECRYPT_MAC_FRAME_ADDR4 *)(data))->addr1[0] & 0x01) {
+ ws_noisy("Broadcast/Multicast address. This is encrypted with a group key.");
+
+ /* force STA address to broadcast MAC so we load the SA for the groupkey */
+ memcpy(id.sta, broadcast_mac, DOT11DECRYPT_MAC_LEN);
+ }
+ /* search for a cached Security Association for current BSSID and STA/broadcast MAC */
+ int ret = DOT11DECRYPT_RET_REQ_DATA;
+ sa = Dot11DecryptGetSa(ctx, &id);
+ if (sa != NULL) {
+ /* Decrypt the packet using the appropriate SA */
+ ret = Dot11DecryptRsnaMng(decrypt_data, mac_header_len, decrypt_len, key, sa);
+ }
+ if (ret != DOT11DECRYPT_RET_SUCCESS && Dot11DecryptGetNbrOfTkKeys(ctx) > 0) {
+ /* Decryption with known SAs failed. Try decrypt with TK user entries */
+ ret = Dot11DecryptUsingUserTk(ctx, decrypt_data, mac_header_len, decrypt_len, &id, key);
+ }
+ return ret;
+ }
+ return DOT11DECRYPT_RET_UNSUCCESS;
+}
+
+int Dot11DecryptSetKeys(
+ PDOT11DECRYPT_CONTEXT ctx,
+ DOT11DECRYPT_KEY_ITEM keys[],
+ const size_t keys_nr)
+{
+ int i;
+ int success;
+
+ if (ctx==NULL || keys==NULL) {
+ ws_warning("NULL context or NULL keys array");
+ return 0;
+ }
+
+ if (keys_nr>DOT11DECRYPT_MAX_KEYS_NR) {
+ ws_warning("Keys number greater than maximum");
+ return 0;
+ }
+
+ /* clean key and SA collections before setting new ones */
+ Dot11DecryptInitContext(ctx);
+
+ /* check and insert keys */
+ for (i=0, success=0; i<(int)keys_nr; i++) {
+ if (Dot11DecryptValidateKey(keys+i)==true) {
+ if (keys[i].KeyType==DOT11DECRYPT_KEY_TYPE_WPA_PWD) {
+ Dot11DecryptRsnaPwd2Psk(keys[i].UserPwd.Passphrase, keys[i].UserPwd.Ssid, keys[i].UserPwd.SsidLen, keys[i].KeyData.Wpa.Psk);
+ keys[i].KeyData.Wpa.PskLen = DOT11DECRYPT_WPA_PWD_PSK_LEN;
+ }
+ memcpy(&ctx->keys[success], &keys[i], sizeof(keys[i]));
+ success++;
+ }
+ }
+
+ ctx->keys_nr=success;
+ return success;
+}
+
+static void
+Dot11DecryptCleanKeys(
+ PDOT11DECRYPT_CONTEXT ctx)
+{
+ if (ctx==NULL) {
+ ws_warning("NULL context");
+ return;
+ }
+
+ memset(ctx->keys, 0, sizeof(DOT11DECRYPT_KEY_ITEM) * DOT11DECRYPT_MAX_KEYS_NR);
+
+ ctx->keys_nr=0;
+ ws_debug("Keys collection cleaned!");
+}
+
+static void
+Dot11DecryptRecurseCleanSA(
+ void * first_sa)
+{
+ DOT11DECRYPT_SEC_ASSOCIATION *sa = (DOT11DECRYPT_SEC_ASSOCIATION *)first_sa;
+ if (sa != NULL) {
+ Dot11DecryptRecurseCleanSA((void *)sa->next);
+ g_free(sa);
+ }
+}
+
+static void
+Dot11DecryptCleanSecAssoc(
+ PDOT11DECRYPT_CONTEXT ctx)
+{
+ if (ctx->sa_hash != NULL) {
+ g_hash_table_destroy(ctx->sa_hash);
+ ctx->sa_hash = NULL;
+ }
+}
+
+/*
+ * XXX - This won't be reliable if a packet containing SSID "B" shows
+ * up in the middle of a 4-way handshake for SSID "A".
+ * We should probably use a small array or hash table to keep multiple
+ * SSIDs.
+ */
+int Dot11DecryptSetLastSSID(
+ PDOT11DECRYPT_CONTEXT ctx,
+ char *pkt_ssid,
+ size_t pkt_ssid_len)
+{
+ if (!ctx || !pkt_ssid || pkt_ssid_len < 1 || pkt_ssid_len > WPA_SSID_MAX_SIZE)
+ return DOT11DECRYPT_RET_UNSUCCESS;
+
+ memcpy(ctx->pkt_ssid, pkt_ssid, pkt_ssid_len);
+ ctx->pkt_ssid_len = pkt_ssid_len;
+
+ return DOT11DECRYPT_RET_SUCCESS;
+}
+
+static unsigned
+Dot11DecryptSaHash(gconstpointer key)
+{
+ GBytes *bytes = g_bytes_new_static(key, sizeof(DOT11DECRYPT_SEC_ASSOCIATION_ID));
+ unsigned hash = g_bytes_hash(bytes);
+ g_bytes_unref(bytes);
+ return hash;
+}
+
+static gboolean
+Dot11DecryptIsSaIdEqual(gconstpointer key1, gconstpointer key2)
+{
+ return memcmp(key1, key2, sizeof(DOT11DECRYPT_SEC_ASSOCIATION_ID)) == 0;
+}
+
+int Dot11DecryptInitContext(
+ PDOT11DECRYPT_CONTEXT ctx)
+{
+ if (ctx==NULL) {
+ ws_warning("NULL context");
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+
+ Dot11DecryptCleanKeys(ctx);
+ Dot11DecryptCleanSecAssoc(ctx);
+
+ ctx->pkt_ssid_len = 0;
+ ctx->sa_hash = g_hash_table_new_full(Dot11DecryptSaHash, Dot11DecryptIsSaIdEqual,
+ g_free, Dot11DecryptRecurseCleanSA);
+ if (ctx->sa_hash == NULL) {
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+
+ ws_debug("Context initialized!");
+ return DOT11DECRYPT_RET_SUCCESS;
+}
+
+int Dot11DecryptDestroyContext(
+ PDOT11DECRYPT_CONTEXT ctx)
+{
+ if (ctx==NULL) {
+ ws_warning("NULL context");
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+
+ Dot11DecryptCleanKeys(ctx);
+ Dot11DecryptCleanSecAssoc(ctx);
+
+ ws_debug("Context destroyed!");
+ return DOT11DECRYPT_RET_SUCCESS;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+/****************************************************************************/
+
+/****************************************************************************/
+/* Internal function definitions */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static int
+Dot11DecryptRsnaMng(
+ unsigned char *decrypt_data,
+ unsigned mac_header_len,
+ unsigned *decrypt_len,
+ PDOT11DECRYPT_KEY_ITEM key,
+ DOT11DECRYPT_SEC_ASSOCIATION *sa)
+{
+ int ret = 1;
+ unsigned char *try_data;
+ unsigned try_data_len = *decrypt_len;
+
+ if (*decrypt_len == 0) {
+ ws_debug("Invalid decryption length");
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+
+ /* allocate a temp buffer for the decryption loop */
+ try_data=(unsigned char *)g_malloc(try_data_len);
+
+ /* start of loop added by GCS */
+ for(/* sa */; sa != NULL ;sa=sa->next) {
+
+ if (sa->validKey==false) {
+ ws_noisy("Key not yet valid");
+ continue;
+ }
+
+ /* copy the encrypted data into a temp buffer */
+ memcpy(try_data, decrypt_data, *decrypt_len);
+
+ if (sa->wpa.key_ver==1) {
+ /* CCMP -> HMAC-MD5 is the EAPOL-Key MIC, RC4 is the EAPOL-Key encryption algorithm */
+ ws_noisy("TKIP");
+ DEBUG_DUMP("ptk", sa->wpa.ptk, 64, LOG_LEVEL_NOISY);
+ DEBUG_DUMP("ptk portion used", DOT11DECRYPT_GET_TK_TKIP(sa->wpa.ptk),
+ 16, LOG_LEVEL_NOISY);
+
+ if (*decrypt_len < (unsigned)mac_header_len) {
+ ws_debug("Invalid decryption length");
+ g_free(try_data);
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+ if (*decrypt_len < DOT11DECRYPT_TKIP_MICLEN + DOT11DECRYPT_WEP_ICV) {
+ ws_debug("Invalid decryption length");
+ g_free(try_data);
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+
+ ret = Dot11DecryptTkipDecrypt(try_data + mac_header_len, *decrypt_len - mac_header_len,
+ try_data + DOT11DECRYPT_TA_OFFSET,
+ DOT11DECRYPT_GET_TK_TKIP(sa->wpa.ptk));
+ if (ret) {
+ ws_noisy("TKIP failed!");
+ continue;
+ }
+
+ ws_noisy("TKIP DECRYPTED!!!");
+ /* 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) {
+ ws_noisy("GCMP");
+
+ if (*decrypt_len < DOT11DECRYPT_GCMP_TRAILER) {
+ ws_debug("Invalid decryption length");
+ 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;
+ }
+ ws_noisy("GCMP DECRYPTED!!!");
+ /* 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 */
+ ws_noisy("CCMP");
+
+ unsigned trailer = sa->wpa.cipher != 10 ? DOT11DECRYPT_CCMP_TRAILER : DOT11DECRYPT_CCMP_256_TRAILER;
+ if (*decrypt_len < trailer) {
+ ws_debug("Invalid decryption length");
+ g_free(try_data);
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+
+ ret = Dot11DecryptCcmpDecrypt(try_data, mac_header_len, (int)*decrypt_len,
+ DOT11DECRYPT_GET_TK(sa->wpa.ptk, sa->wpa.akm),
+ Dot11DecryptGetTkLen(sa->wpa.cipher) / 8,
+ trailer);
+ if (ret) {
+ continue;
+ }
+ ws_noisy("CCMP DECRYPTED!!!");
+ /* remove MIC from the end of packet */
+ *decrypt_len -= trailer;
+ break;
+ }
+ }
+ /* end of loop */
+
+ /* none of the keys worked */
+ if(sa == NULL) {
+ g_free(try_data);
+ return ret;
+ }
+
+ if (*decrypt_len > try_data_len || *decrypt_len < 8) {
+ ws_debug("Invalid decryption length");
+ g_free(try_data);
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+
+ /* remove protection bit */
+ decrypt_data[1]&=0xBF;
+
+ /* remove TKIP/CCMP header */
+ *decrypt_len-=8;
+
+ if (*decrypt_len < mac_header_len) {
+ ws_debug("Invalid decryption length < mac_header_len");
+ g_free(try_data);
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+
+ /* copy the decrypted data into the decrypt buffer GCS*/
+ memcpy(decrypt_data + mac_header_len, try_data + mac_header_len + 8,
+ *decrypt_len - mac_header_len);
+ g_free(try_data);
+
+ Dot11DecryptCopyKey(sa, key);
+ return DOT11DECRYPT_RET_SUCCESS;
+}
+
+static int
+Dot11DecryptWepMng(
+ PDOT11DECRYPT_CONTEXT ctx,
+ unsigned char *decrypt_data,
+ unsigned mac_header_len,
+ unsigned *decrypt_len,
+ PDOT11DECRYPT_KEY_ITEM key,
+ DOT11DECRYPT_SEC_ASSOCIATION_ID *id)
+{
+ unsigned char wep_key[DOT11DECRYPT_WEP_KEY_MAXLEN+DOT11DECRYPT_WEP_IVLEN];
+ size_t keylen;
+ int ret_value=1;
+ int key_index;
+ DOT11DECRYPT_KEY_ITEM *tmp_key;
+ uint8_t useCache=false;
+ unsigned char *try_data;
+ DOT11DECRYPT_SEC_ASSOCIATION *sa;
+ unsigned try_data_len = *decrypt_len;
+
+ try_data = (unsigned char *)g_malloc(try_data_len);
+
+ /* get the Security Association structure for the STA and AP */
+
+ /* For WEP the sa is used only for caching. When no sa exists all user
+ * entered WEP keys are checked and on successful packet decryption an
+ * sa is formed caching the key used for decryption.
+ */
+ sa = Dot11DecryptGetSa(ctx, id);
+ if (sa != NULL && sa->key != NULL) {
+ useCache = true;
+ }
+
+ for (key_index=0; key_index<(int)ctx->keys_nr; key_index++) {
+ /* use the cached one, or try all keys */
+ if (!useCache) {
+ tmp_key=&ctx->keys[key_index];
+ } else {
+ if (sa->key!=NULL && sa->key->KeyType==DOT11DECRYPT_KEY_TYPE_WEP) {
+ ws_noisy("Try cached WEP key...");
+ tmp_key=sa->key;
+ } else {
+ ws_noisy("Cached key is not valid, try another WEP key...");
+ tmp_key=&ctx->keys[key_index];
+ }
+ }
+
+ /* obviously, try only WEP keys... */
+ if (tmp_key->KeyType==DOT11DECRYPT_KEY_TYPE_WEP) {
+ ws_noisy("Try WEP key...");
+
+ memset(wep_key, 0, sizeof(wep_key));
+ memcpy(try_data, decrypt_data, *decrypt_len);
+
+ /* Costruct the WEP seed: copy the IV in first 3 bytes and then the WEP key (refer to 802-11i-2004, 8.2.1.4.3, pag. 36) */
+ memcpy(wep_key, try_data+mac_header_len, DOT11DECRYPT_WEP_IVLEN);
+ keylen=tmp_key->KeyData.Wep.WepKeyLen;
+ memcpy(wep_key+DOT11DECRYPT_WEP_IVLEN, tmp_key->KeyData.Wep.WepKey, keylen);
+
+ ret_value=Dot11DecryptWepDecrypt(wep_key,
+ keylen+DOT11DECRYPT_WEP_IVLEN,
+ try_data + (mac_header_len+DOT11DECRYPT_WEP_IVLEN+DOT11DECRYPT_WEP_KIDLEN),
+ *decrypt_len-(mac_header_len+DOT11DECRYPT_WEP_IVLEN+DOT11DECRYPT_WEP_KIDLEN+DOT11DECRYPT_CRC_LEN));
+
+ if (ret_value == DOT11DECRYPT_RET_SUCCESS)
+ memcpy(decrypt_data, try_data, *decrypt_len);
+ }
+
+ if (!ret_value && tmp_key->KeyType==DOT11DECRYPT_KEY_TYPE_WEP) {
+ /* the tried key is the correct one, cache it in the Security Association */
+
+ /* Form an SA if one does not exist already */
+ if (sa == NULL) {
+ sa = Dot11DecryptNewSa(id);
+ if (sa == NULL) {
+ ws_warning("Failed to alloc sa for WEP");
+ ret_value = DOT11DECRYPT_RET_UNSUCCESS;
+ break;
+ }
+ sa = Dot11DecryptAddSa(ctx, id, sa);
+ }
+ sa->key=tmp_key;
+
+ if (key!=NULL) {
+ memcpy(key, sa->key, sizeof(DOT11DECRYPT_KEY_ITEM));
+ key->KeyType=DOT11DECRYPT_KEY_TYPE_WEP;
+ }
+
+ break;
+ } else {
+ /* the cached key was not valid, try other keys */
+
+ if (useCache==true) {
+ useCache=false;
+ key_index--;
+ }
+ }
+ }
+
+ g_free(try_data);
+ if (ret_value)
+ return DOT11DECRYPT_RET_UNSUCCESS;
+
+ ws_noisy("WEP DECRYPTED!!!");
+
+ /* remove ICV (4bytes) from the end of packet */
+ *decrypt_len-=4;
+
+ if (*decrypt_len < 4) {
+ ws_debug("Decryption length too short");
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+
+ /* remove protection bit */
+ decrypt_data[1]&=0xBF;
+
+ /* remove IC header */
+ *decrypt_len-=4;
+ memmove(decrypt_data + mac_header_len,
+ decrypt_data + mac_header_len + DOT11DECRYPT_WEP_IVLEN + DOT11DECRYPT_WEP_KIDLEN,
+ *decrypt_len - mac_header_len);
+
+ return DOT11DECRYPT_RET_SUCCESS;
+}
+
+/* From IEEE 802.11-2016 Table 9-133—AKM suite selectors */
+static bool Dot11DecryptIsFtAkm(int akm)
+{
+ switch (akm) {
+ case 3:
+ case 4:
+ case 9:
+ case 13:
+ return true;
+ }
+ return false;
+}
+
+/* Get xxkey portion of MSK */
+/* From IEEE 802.11-2016 12.7.1.7.3 PMK-R0 */
+static const uint8_t *
+Dot11DecryptGetXXKeyFromMSK(const uint8_t *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 uint8_t *msk, uint8_t msk_len, int akm,
+ uint8_t *pmk, uint8_t *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 ((uint8_t)(msk_len + *pmk_len) < msk_len) {
+ *pmk_len = 0;
+ return;
+ }
+ // PMK = L(MSK, 0, PMK_bits).
+ memcpy(pmk, msk, *pmk_len);
+}
+
+static bool
+Dot11DecryptIsWpaKeyType(uint8_t 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 bool
+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(
+ PDOT11DECRYPT_CONTEXT ctx,
+ PDOT11DECRYPT_EAPOL_PARSED eapol_parsed,
+ const uint8_t *eapol_raw,
+ DOT11DECRYPT_SEC_ASSOCIATION_ID *id,
+ const unsigned tot_len)
+{
+ DOT11DECRYPT_KEY_ITEM *tmp_key, *tmp_pkt_key, pkt_key;
+ DOT11DECRYPT_SEC_ASSOCIATION *sa;
+ int key_index;
+ int ret = 1;
+ unsigned char useCache=false;
+ unsigned char eapol[DOT11DECRYPT_EAPOL_MAX_LEN];
+
+ if (eapol_parsed->len > DOT11DECRYPT_EAPOL_MAX_LEN ||
+ eapol_parsed->key_len > DOT11DECRYPT_EAPOL_MAX_LEN ||
+ eapol_parsed->key_data_len > DOT11DECRYPT_EAPOL_MAX_LEN) {
+ ws_debug("Too large EAPOL frame and/or key data");
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+
+ /* TODO timeouts? */
+
+ /* TODO consider key-index */
+
+ /* TODO considera Deauthentications */
+
+ ws_debug("4-way handshake...");
+
+ /* manage 4-way handshake packets; this step completes the 802.1X authentication process (IEEE 802.11i-2004, pag. 85) */
+
+ /* message 1: Authenticator->Supplicant (Sec=0, Mic=0, Ack=1, Inst=0, Key=1(pairwise), KeyRSC=0, Nonce=ANonce, MIC=0) */
+ if (eapol_parsed->msg_type == DOT11DECRYPT_HS_MSG_TYPE_4WHS_1) {
+ ws_debug("4-way handshake message 1");
+
+ /* On reception of Message 1, the Supplicant determines whether the Key Replay Counter field value has been */
+ /* used before with the current PMKSA. If the Key Replay Counter field value is less than or equal to the current */
+ /* local value, the Supplicant discards the message. */
+ /* -> not checked, the Authenticator will be send another Message 1 (hopefully!) */
+
+ /* save ANonce (from authenticator) to derive the PTK with the SNonce (from the 2 message) */
+ if (!eapol_parsed->nonce) {
+ ws_debug("ANonce missing");
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+
+ sa = Dot11DecryptGetSa(ctx, id);
+ if (sa == NULL || sa->handshake >= 2) {
+ /* Either no SA exists or one exists but we're reauthenticating */
+ sa = Dot11DecryptNewSa(id);
+ if (sa == NULL) {
+ ws_warning("Failed to alloc broadcast sa");
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+ sa = Dot11DecryptAddSa(ctx, id, sa);
+ }
+ memcpy(sa->wpa.nonce, eapol_parsed->nonce, 32);
+
+ /* get the Key Descriptor Version (to select algorithm used in decryption -CCMP or TKIP-) */
+ sa->wpa.key_ver = eapol_parsed->key_version;
+ sa->handshake=1;
+ return DOT11DECRYPT_RET_SUCCESS_HANDSHAKE;
+ }
+
+ /* message 2|4: Supplicant->Authenticator (Sec=0|1, Mic=1, Ack=0, Inst=0, Key=1(pairwise), KeyRSC=0, Nonce=SNonce|0, MIC=MIC(KCK,EAPOL)) */
+ if (eapol_parsed->msg_type == DOT11DECRYPT_HS_MSG_TYPE_4WHS_2) {
+ ws_debug("4-way handshake message 2");
+
+ /* On reception of Message 2, the Authenticator checks that the key replay counter corresponds to the */
+ /* outstanding Message 1. If not, it silently discards the message. */
+ /* If the calculated MIC does not match the MIC that the Supplicant included in the EAPOL-Key frame, */
+ /* the Authenticator silently discards Message 2. */
+ /* -> not checked; the Supplicant will send another message 2 (hopefully!) */
+
+ sa = Dot11DecryptGetSa(ctx, id);
+ if (sa == NULL) {
+ ws_debug("No SA for BSSID found");
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+ if (!eapol_parsed->nonce) {
+ ws_debug("SNonce missing");
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+ if (sa->key != NULL) {
+ useCache = true;
+ }
+
+ int akm = -1;
+ int cipher = -1;
+ int group_cipher = -1;
+ uint8_t ptk[DOT11DECRYPT_WPA_PTK_MAX_LEN];
+ size_t ptk_len = 0;
+
+ /* 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 && Dot11DecryptIsWpaKeyType(sa->key->KeyType)) {
+ ws_debug("Try cached WPA key...");
+ tmp_key = sa->key;
+ /* Step back loop counter as cached key is used instead */
+ key_index--;
+ } else {
+ ws_debug("Try WPA key...");
+ tmp_key = &ctx->keys[key_index];
+ }
+ useCache = false;
+
+ /* obviously, try only WPA keys... */
+ if (!Dot11DecryptIsWpaKeyType(tmp_key->KeyType)) {
+ continue;
+ }
+ if (tmp_key->KeyType == DOT11DECRYPT_KEY_TYPE_WPA_PWD &&
+ Dot11DecryptIsPwdWildcardSsid(ctx, tmp_key))
+ {
+ /* 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 {
+ ws_info("EAPOL key_version not supported");
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+
+ 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);
+ }
+
+ if (Dot11DecryptIsFtAkm(akm)) {
+ ret = Dot11DecryptFtDerivePtk(ctx, sa, tmp_pkt_key,
+ eapol_parsed->mdid,
+ eapol_parsed->nonce,
+ eapol_parsed->fte.r0kh_id,
+ eapol_parsed->fte.r0kh_id_len,
+ eapol_parsed->fte.r1kh_id,
+ eapol_parsed->fte.r1kh_id_len,
+ akm, cipher, ptk, &ptk_len);
+ } else {
+ /* 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,
+ LOG_LEVEL_DEBUG);
+
+ 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;
+ }
+ }
+
+ if (ret) {
+ ws_debug("handshake step failed");
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+ sa->wpa.key_ver = eapol_parsed->key_version;
+ sa->wpa.akm = akm;
+ sa->wpa.cipher = cipher;
+ sa->wpa.tmp_group_cipher = group_cipher;
+ 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 */
+
+ return DOT11DECRYPT_RET_SUCCESS_HANDSHAKE;
+ }
+
+ /* message 3: Authenticator->Supplicant (Sec=1, Mic=1, Ack=1, Inst=0/1, Key=1(pairwise), KeyRSC=???, Nonce=ANonce, MIC=1) */
+ if (eapol_parsed->msg_type == DOT11DECRYPT_HS_MSG_TYPE_4WHS_3) {
+ ws_debug("4-way handshake message 3");
+
+ /* On reception of Message 3, the Supplicant silently discards the message if the Key Replay Counter field */
+ /* value has already been used or if the ANonce value in Message 3 differs from the ANonce value in Message 1. */
+ /* -> not checked, the Authenticator will send another message 3 (hopefully!) */
+
+ /* TODO check page 88 (RNS) */
+
+ /* If using WPA2 PSK, message 3 will contain an RSN for the group key (GTK KDE).
+ In order to properly support decrypting WPA2-PSK packets, we need to parse this to get the group key. */
+ if (eapol_parsed->key_type == DOT11DECRYPT_RSN_WPA2_KEY_DESCRIPTOR) {
+ return Dot11DecryptCopyBroadcastKey(ctx, eapol_parsed->gtk, eapol_parsed->gtk_len, id);
+ }
+ }
+
+ /* message 4 */
+ if (eapol_parsed->msg_type == DOT11DECRYPT_HS_MSG_TYPE_4WHS_4) {
+ /* TODO "Note that when the 4-Way Handshake is first used Message 4 is sent in the clear." */
+
+ /* TODO check MIC and Replay Counter */
+ /* On reception of Message 4, the Authenticator verifies that the Key Replay Counter field value is one */
+ /* that it used on this 4-Way Handshake; if it is not, it silently discards the message. */
+ /* If the calculated MIC does not match the MIC that the Supplicant included in the EAPOL-Key frame, the */
+ /* Authenticator silently discards Message 4. */
+
+ ws_debug("4-way handshake message 4");
+ return DOT11DECRYPT_RET_SUCCESS_HANDSHAKE;
+ }
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+}
+
+/* Refer to IEEE 802.11-2016 Chapeter 13.8 FT authentication sequence */
+int
+Dot11DecryptScanFtAssocForKeys(
+ const PDOT11DECRYPT_CONTEXT ctx,
+ const PDOT11DECRYPT_ASSOC_PARSED assoc_parsed,
+ uint8_t *decrypted_gtk, size_t *decrypted_len,
+ DOT11DECRYPT_KEY_ITEM* used_key)
+{
+ DOT11DECRYPT_SEC_ASSOCIATION_ID id;
+
+ ws_debug("(Re)Association packet");
+
+ if (!ctx || !assoc_parsed) {
+ ws_warning("Invalid input parameters");
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+ if (!Dot11DecryptIsFtAkm(assoc_parsed->akm)) {
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+ if (!assoc_parsed->fte.anonce || !assoc_parsed->fte.snonce) {
+ ws_debug("ANonce or SNonce missing");
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+
+ switch (assoc_parsed->frame_subtype) {
+ case DOT11DECRYPT_SUBTYPE_ASSOC_REQ:
+ case DOT11DECRYPT_SUBTYPE_REASSOC_REQ:
+ memcpy(id.sta, assoc_parsed->sa, DOT11DECRYPT_MAC_LEN);
+ break;
+ case DOT11DECRYPT_SUBTYPE_ASSOC_RESP:
+ case DOT11DECRYPT_SUBTYPE_REASSOC_RESP:
+ memcpy(id.sta, assoc_parsed->da, DOT11DECRYPT_MAC_LEN);
+ break;
+ default:
+ ws_warning("Invalid frame subtype");
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+ memcpy(id.bssid, assoc_parsed->bssid, DOT11DECRYPT_MAC_LEN);
+
+ DOT11DECRYPT_KEY_ITEM *tmp_key, *tmp_pkt_key, pkt_key;
+ DOT11DECRYPT_SEC_ASSOCIATION *sa;
+ size_t key_index;
+ unsigned ret = 1;
+ bool useCache = false;
+
+ sa = Dot11DecryptNewSa(&id);
+ if (sa == NULL) {
+ ws_warning("Failed to alloc sa");
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+
+ memcpy(sa->wpa.nonce, assoc_parsed->fte.anonce, 32);
+
+ if (sa->key != NULL) {
+ useCache = true;
+ }
+
+ uint8_t ptk[DOT11DECRYPT_WPA_PTK_MAX_LEN];
+ size_t ptk_len;
+
+ /* now you can derive the PTK */
+ for (key_index = 0; key_index < ctx->keys_nr || useCache; key_index++) {
+ /* use the cached one, or try all keys */
+ if (useCache && Dot11DecryptIsWpaKeyType(sa->key->KeyType)) {
+ ws_debug("Try cached WPA key...");
+ tmp_key = sa->key;
+ /* Step back loop counter as cached key is used instead */
+ key_index--;
+ } else {
+ ws_debug("Try WPA key...");
+ tmp_key = &ctx->keys[key_index];
+ }
+ useCache = false;
+
+ /* Try only WPA keys... */
+ if (!Dot11DecryptIsWpaKeyType(tmp_key->KeyType)) {
+ continue;
+ }
+ if (tmp_key->KeyType == DOT11DECRYPT_KEY_TYPE_WPA_PWD &&
+ Dot11DecryptIsPwdWildcardSsid(ctx, tmp_key))
+ {
+ /* 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;
+ }
+
+ if (tmp_pkt_key->KeyType == DOT11DECRYPT_KEY_TYPE_MSK) {
+ Dot11DecryptDerivePmkFromMsk(tmp_pkt_key->Msk.Msk, tmp_pkt_key->Msk.Len,
+ assoc_parsed->akm,
+ tmp_pkt_key->KeyData.Wpa.Psk,
+ &tmp_pkt_key->KeyData.Wpa.PskLen);
+ }
+
+ ret = Dot11DecryptFtDerivePtk(ctx, sa, tmp_pkt_key,
+ assoc_parsed->mdid,
+ assoc_parsed->fte.snonce,
+ assoc_parsed->fte.r0kh_id,
+ assoc_parsed->fte.r0kh_id_len,
+ assoc_parsed->fte.r1kh_id,
+ assoc_parsed->fte.r1kh_id_len,
+ assoc_parsed->akm, assoc_parsed->cipher,
+ ptk, &ptk_len);
+ if (ret != DOT11DECRYPT_RET_SUCCESS) {
+ continue;
+ }
+ DEBUG_DUMP("TK", DOT11DECRYPT_GET_TK(ptk, assoc_parsed->akm),
+ Dot11DecryptGetTkLen(assoc_parsed->cipher) / 8,
+ LOG_LEVEL_DEBUG);
+
+ ret = Dot11DecryptFtMicCheck(assoc_parsed,
+ DOT11DECRYPT_GET_KCK(ptk, assoc_parsed->akm),
+ Dot11DecryptGetKckLen(assoc_parsed->akm) / 8);
+ if (ret == DOT11DECRYPT_RET_SUCCESS) {
+ /* the key is the correct one, cache it in the Security Association */
+ sa->key = tmp_key;
+ break;
+ }
+ }
+
+ if (ret) {
+ ws_debug("handshake step failed");
+ g_free(sa);
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+ sa = Dot11DecryptAddSa(ctx, &id, sa);
+
+ sa->wpa.key_ver = 0; /* Determine key type from akms and cipher*/
+ sa->wpa.akm = assoc_parsed->akm;
+ sa->wpa.cipher = assoc_parsed->cipher;
+ sa->wpa.tmp_group_cipher = assoc_parsed->group_cipher;
+ memcpy(sa->wpa.ptk, ptk, ptk_len);
+ sa->wpa.ptk_len = (int)ptk_len;
+ sa->validKey = true;
+
+ if (assoc_parsed->gtk && assoc_parsed->gtk_len - 8 <= DOT11DECRYPT_WPA_PTK_MAX_LEN - 32) {
+ uint8_t decrypted_key[DOT11DECRYPT_WPA_PTK_MAX_LEN - 32];
+ uint16_t decrypted_key_len;
+ if (AES_unwrap(DOT11DECRYPT_GET_KEK(sa->wpa.ptk, sa->wpa.akm),
+ Dot11DecryptGetKekLen(sa->wpa.akm) / 8,
+ assoc_parsed->gtk, assoc_parsed->gtk_len,
+ decrypted_key, &decrypted_key_len)) {
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+ if (decrypted_key_len != assoc_parsed->gtk_subelem_key_len) {
+ ws_debug("Unexpected GTK length");
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+ Dot11DecryptCopyBroadcastKey(ctx, decrypted_key, decrypted_key_len, &id);
+ *decrypted_len = decrypted_key_len;
+ memcpy(decrypted_gtk, decrypted_key, decrypted_key_len);
+ }
+ Dot11DecryptCopyKey(sa, used_key);
+ return DOT11DECRYPT_RET_SUCCESS_HANDSHAKE;
+}
+
+/* From IEEE 802.11-2016 Table 12-8 Integrity and key-wrap algorithms */
+static int
+Dot11DecryptGetIntegrityAlgoFromAkm(int akm, int *algo, bool *hmac)
+{
+ int res = 0;
+ switch (akm) {
+ case 1:
+ case 2:
+ *algo = GCRY_MD_SHA1;
+ *hmac = true;
+ break;
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ *algo = GCRY_MAC_CMAC_AES;
+ *hmac = false;
+ break;
+ case 11:
+ case 18:
+ *algo = GCRY_MD_SHA256;
+ *hmac = true;
+ break;
+ case 12:
+ case 13:
+ *algo = GCRY_MD_SHA384;
+ *hmac = true;
+ break;
+ default:
+ /* Unknown / Not supported yet */
+ res = -1;
+ break;
+ }
+ return res;
+}
+
+static int
+Dot11DecryptRsnaMicCheck(
+ PDOT11DECRYPT_EAPOL_PARSED eapol_parsed,
+ unsigned char *eapol,
+ unsigned short eapol_len,
+ unsigned char *KCK,
+ unsigned short key_ver,
+ int akm)
+{
+ uint8_t *mic = eapol_parsed->mic;
+ uint16_t mic_len = eapol_parsed->mic_len;
+ uint16_t kck_len = Dot11DecryptGetKckLen(akm) / 8;
+ /* MIC 16 or 24 bytes, though HMAC-SHA256 / SHA384 algos need 32 / 48 bytes buffer */
+ unsigned char c_mic[48] = { 0 };
+ int algo = -1;
+ bool hmac = true;
+
+ if (!mic || mic_len > DOT11DECRYPT_WPA_MICKEY_MAX_LEN) {
+ ws_debug("Not a valid mic");
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+
+ /* set to 0 the MIC in the EAPOL packet (to calculate the MIC) */
+ memset(eapol + DOT11DECRYPT_WPA_MICKEY_OFFSET + 4, 0, mic_len);
+
+ if (key_ver==DOT11DECRYPT_WPA_KEY_VER_NOT_CCMP) {
+ /* use HMAC-MD5 for the EAPOL-Key MIC */
+ algo = GCRY_MD_MD5;
+ hmac = true;
+ } else if (key_ver==DOT11DECRYPT_WPA_KEY_VER_AES_CCMP) {
+ /* use HMAC-SHA1-128 for the EAPOL-Key MIC */
+ algo = GCRY_MD_SHA1;
+ hmac = true;
+ } else {
+ /* Mic check algoritm determined by AKM type */
+ if (Dot11DecryptGetIntegrityAlgoFromAkm(akm, &algo, &hmac)) {
+ ws_warning("Unknown Mic check algo");
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ };
+ }
+ if (hmac) {
+ if (ws_hmac_buffer(algo, c_mic, eapol, eapol_len, KCK, kck_len)) {
+ ws_debug("HMAC_BUFFER");
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+ } else {
+ if (ws_cmac_buffer(algo, c_mic, eapol, eapol_len, KCK, kck_len)) {
+ ws_debug("HMAC_BUFFER");
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+ }
+
+ /* compare calculated MIC with the Key MIC and return result (0 means success) */
+ DEBUG_DUMP("mic", mic, mic_len, LOG_LEVEL_DEBUG);
+ DEBUG_DUMP("c_mic", c_mic, mic_len, LOG_LEVEL_DEBUG);
+ return memcmp(mic, c_mic, mic_len);
+}
+
+/* IEEE 802.11-2016 Chapter 13.8.4 FT authentication sequence: contents of third message
+ * IEEE 802.11-2016 Chapter 13.8.5 FT authentication sequence: contents of fourth message
+ * The MIC shall be calculated on the concatenation of the following data, in the order given here:
+ * —
+ * — FTO’s MAC address (6 octets)
+ * — Target AP’s MAC address (6 octets)
+ * If third message:
+ * — Transaction sequence number (1 octet), which shall be set to the value 5 if this is a
+ * Reassociation Request frame and, otherwise, set to the value 3
+ * If fourth message:
+ * — Transaction sequence number (1 octet), which shall be set to the value 6 if this is a
+ * Reassociation Response frame or, otherwise, set to the value 4
+ *
+ * — RSNE
+ * — MDE
+ * — FTE, with the MIC field of the FTE set to 0
+ * — Contents of the RIC-Response (if present)
+ */
+static int
+Dot11DecryptFtMicCheck(
+ const PDOT11DECRYPT_ASSOC_PARSED assoc_parsed,
+ const uint8_t *kck,
+ size_t kck_len)
+{
+ uint8_t *sta;
+ uint8_t seq_num;
+ uint8_t fte_len;
+ uint16_t mic_len;
+ uint8_t zeros[16] = { 0 };
+ gcry_mac_hd_t handle;
+
+ fte_len = assoc_parsed->fte_tag[1] + 2;
+ if (fte_len < 20) {
+ ws_debug("FTE too short");
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+
+ switch (assoc_parsed->frame_subtype) {
+ case DOT11DECRYPT_SUBTYPE_ASSOC_REQ:
+ sta = assoc_parsed->sa;
+ seq_num = 3;
+ break;
+ case DOT11DECRYPT_SUBTYPE_ASSOC_RESP:
+ sta = assoc_parsed->da;
+ seq_num = 4;
+ break;
+ case DOT11DECRYPT_SUBTYPE_REASSOC_REQ:
+ sta = assoc_parsed->sa;
+ seq_num = 5;
+ break;
+ case DOT11DECRYPT_SUBTYPE_REASSOC_RESP:
+ sta = assoc_parsed->da;
+ seq_num = 6;
+ break;
+ default:
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+
+ if (gcry_mac_open(&handle, GCRY_MAC_CMAC_AES, 0, NULL)) {
+ ws_warning("gcry_mac_open failed");
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+ if (gcry_mac_setkey(handle, kck, kck_len)) {
+ ws_warning("gcry_mac_setkey failed");
+ gcry_mac_close(handle);
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+ gcry_mac_write(handle, sta, DOT11DECRYPT_MAC_LEN);
+ gcry_mac_write(handle, assoc_parsed->bssid, DOT11DECRYPT_MAC_LEN);
+
+ gcry_mac_write(handle, &seq_num, 1);
+
+ gcry_mac_write(handle, assoc_parsed->rsne_tag, assoc_parsed->rsne_tag[1] + 2);
+ gcry_mac_write(handle, assoc_parsed->mde_tag, assoc_parsed->mde_tag[1] + 2);
+
+ mic_len = assoc_parsed->fte.mic_len;
+ gcry_mac_write(handle, assoc_parsed->fte_tag, 4);
+ gcry_mac_write(handle, zeros, mic_len); /* MIC zeroed */
+ gcry_mac_write(handle, assoc_parsed->fte_tag + 4 + mic_len, fte_len - 4 - mic_len);
+
+ if (assoc_parsed->rde_tag) {
+ gcry_mac_write(handle, assoc_parsed->rde_tag, assoc_parsed->rde_tag[1] + 2);
+ }
+
+ if (gcry_mac_verify(handle, assoc_parsed->fte.mic, mic_len) != 0) {
+ DEBUG_DUMP("MIC", assoc_parsed->fte.mic, mic_len, LOG_LEVEL_DEBUG);
+ ws_debug("MIC verification failed");
+ gcry_mac_close(handle);
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+ DEBUG_DUMP("MIC", assoc_parsed->fte.mic, mic_len, LOG_LEVEL_DEBUG);
+ gcry_mac_close(handle);
+ return DOT11DECRYPT_RET_SUCCESS;
+}
+
+static int
+Dot11DecryptValidateKey(
+ PDOT11DECRYPT_KEY_ITEM key)
+{
+ size_t len;
+ unsigned char ret=true;
+
+ if (key==NULL) {
+ ws_warning("NULL key");
+ return false;
+ }
+
+ switch (key->KeyType) {
+ case DOT11DECRYPT_KEY_TYPE_WEP:
+ /* check key size limits */
+ len=key->KeyData.Wep.WepKeyLen;
+ if (len<DOT11DECRYPT_WEP_KEY_MINLEN || len>DOT11DECRYPT_WEP_KEY_MAXLEN) {
+ ws_info("WEP key: key length not accepted");
+ ret=false;
+ }
+ break;
+
+ case DOT11DECRYPT_KEY_TYPE_WEP_40:
+ /* set the standard length and use a generic WEP key type */
+ key->KeyData.Wep.WepKeyLen=DOT11DECRYPT_WEP_40_KEY_LEN;
+ key->KeyType=DOT11DECRYPT_KEY_TYPE_WEP;
+ break;
+
+ case DOT11DECRYPT_KEY_TYPE_WEP_104:
+ /* set the standard length and use a generic WEP key type */
+ key->KeyData.Wep.WepKeyLen=DOT11DECRYPT_WEP_104_KEY_LEN;
+ key->KeyType=DOT11DECRYPT_KEY_TYPE_WEP;
+ break;
+
+ case DOT11DECRYPT_KEY_TYPE_WPA_PWD:
+ /* check passphrase and SSID size limits */
+ len=strlen(key->UserPwd.Passphrase);
+ if (len<DOT11DECRYPT_WPA_PASSPHRASE_MIN_LEN || len>DOT11DECRYPT_WPA_PASSPHRASE_MAX_LEN) {
+ ws_info("WPA-PWD key: passphrase length not accepted");
+ ret=false;
+ }
+
+ len=key->UserPwd.SsidLen;
+ if (len>DOT11DECRYPT_WPA_SSID_MAX_LEN) {
+ ws_info("WPA-PWD key: ssid length not accepted");
+ ret=false;
+ }
+
+ break;
+
+ case DOT11DECRYPT_KEY_TYPE_WPA_PSK:
+ break;
+
+ case DOT11DECRYPT_KEY_TYPE_TK:
+ break;
+
+ case DOT11DECRYPT_KEY_TYPE_MSK:
+ break;
+
+ default:
+ ret=false;
+ }
+ return ret;
+}
+
+static int
+Dot11DecryptGetSaAddress(
+ const DOT11DECRYPT_MAC_FRAME_ADDR4 *frame,
+ DOT11DECRYPT_SEC_ASSOCIATION_ID *id)
+{
+ if ((DOT11DECRYPT_TYPE(frame->fc[0])==DOT11DECRYPT_TYPE_DATA) &&
+ (DOT11DECRYPT_DS_BITS(frame->fc[1]) == 0) &&
+ (memcmp(frame->addr2, frame->addr3, DOT11DECRYPT_MAC_LEN) != 0) &&
+ (memcmp(frame->addr1, frame->addr3, DOT11DECRYPT_MAC_LEN) != 0)) {
+ /* DATA frame with fromDS=0 ToDS=0 and neither RA or SA is BSSID
+ => TDLS traffic. Use highest MAC address for bssid */
+ if (memcmp(frame->addr1, frame->addr2, DOT11DECRYPT_MAC_LEN) < 0) {
+ memcpy(id->sta, frame->addr1, DOT11DECRYPT_MAC_LEN);
+ memcpy(id->bssid, frame->addr2, DOT11DECRYPT_MAC_LEN);
+ } else {
+ memcpy(id->sta, frame->addr2, DOT11DECRYPT_MAC_LEN);
+ memcpy(id->bssid, frame->addr1, DOT11DECRYPT_MAC_LEN);
+ }
+ } else {
+ const unsigned char *addr;
+
+ /* Normal Case: SA between STA and AP */
+ if ((addr = Dot11DecryptGetBssidAddress(frame)) != NULL) {
+ memcpy(id->bssid, addr, DOT11DECRYPT_MAC_LEN);
+ } else {
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+
+ if ((addr = Dot11DecryptGetStaAddress(frame)) != NULL) {
+ memcpy(id->sta, addr, DOT11DECRYPT_MAC_LEN);
+ } else {
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+ }
+ ws_noisy("BSSID_MAC: %02X.%02X.%02X.%02X.%02X.%02X\t",
+ id->bssid[0],id->bssid[1],id->bssid[2],id->bssid[3],id->bssid[4],id->bssid[5]);
+ ws_noisy("STA_MAC: %02X.%02X.%02X.%02X.%02X.%02X\t",
+ id->sta[0],id->sta[1],id->sta[2],id->sta[3],id->sta[4],id->sta[5]);
+
+ return DOT11DECRYPT_RET_SUCCESS;
+}
+
+/*
+ * Dot11DecryptGetBssidAddress() and Dot11DecryptGetBssidAddress() are used for
+ * key caching. In each case, it's more important to return a value than
+ * to return a _correct_ value, so we fudge addresses in some cases, e.g.
+ * the BSSID in bridged connections.
+ * FromDS ToDS Sta BSSID
+ * 0 0 addr1/2 addr3
+ * 0 1 addr2 addr1
+ * 1 0 addr1 addr2
+ * 1 1 addr2 addr1
+ */
+
+static const unsigned char *
+Dot11DecryptGetStaAddress(
+ const DOT11DECRYPT_MAC_FRAME_ADDR4 *frame)
+{
+ switch(DOT11DECRYPT_DS_BITS(frame->fc[1])) { /* Bit 1 = FromDS, bit 0 = ToDS */
+ case 0:
+ if (memcmp(frame->addr2, frame->addr3, DOT11DECRYPT_MAC_LEN) == 0)
+ return frame->addr1;
+ else
+ return frame->addr2;
+ case 1:
+ return frame->addr2;
+ case 2:
+ return frame->addr1;
+ case 3:
+ if (memcmp(frame->addr1, frame->addr2, DOT11DECRYPT_MAC_LEN) < 0)
+ return frame->addr1;
+ else
+ return frame->addr2;
+
+ default:
+ return NULL;
+ }
+}
+
+static const unsigned char *
+Dot11DecryptGetBssidAddress(
+ const DOT11DECRYPT_MAC_FRAME_ADDR4 *frame)
+{
+ switch(DOT11DECRYPT_DS_BITS(frame->fc[1])) { /* Bit 1 = FromDS, bit 0 = ToDS */
+ case 0:
+ return frame->addr3;
+ case 1:
+ return frame->addr1;
+ case 2:
+ return frame->addr2;
+ case 3:
+ if (memcmp(frame->addr1, frame->addr2, DOT11DECRYPT_MAC_LEN) > 0)
+ return frame->addr1;
+ else
+ return frame->addr2;
+
+ default:
+ return NULL;
+ }
+}
+
+/* From IEEE 802.11-2016 Table 9-131 Cipher suite selectors and
+ * Table 12-4 Cipher suite key lengths */
+static int Dot11DecryptGetTkLen(int cipher)
+{
+ switch (cipher) {
+ case 1: return 40; /* WEP-40 */
+ case 2: return 256; /* TKIP */
+ case 3: return -1; /* Reserved */
+ case 4: return 128; /* CCMP-128 */
+ case 5: return 104; /* WEP-104 */
+ case 6: return 128; /* BIP-CMAC-128 */
+ case 7: return -1; /* Group addressed traffic not allowed */
+ case 8: return 128; /* GCMP-128 */
+ case 9: return 256; /* GCMP-256 */
+ case 10: return 256; /* CCMP-256 */
+ case 11: return 128; /* BIP-GMAC-128 */
+ case 12: return 256; /* BIP-GMAC-256 */
+ case 13: return 256; /* BIP-CMAC-256 */
+ default:
+ ws_warning("Unknown cipher");
+ return -1;
+ }
+}
+
+/* From IEEE 802.11-2016 Table 12-8 Integrity and key-wrap algorithms */
+static int Dot11DecryptGetKckLen(int akm)
+{
+ switch (akm) {
+ case 1: return 128;
+ case 2: return 128;
+ case 3: return 128;
+ case 4: return 128;
+ case 5: return 128;
+ case 6: return 128;
+ case 8: return 128;
+ case 9: return 128;
+ case 11: return 128;
+ case 12: return 192;
+ case 13: return 192;
+ case 18: return 128;
+ default:
+ /* Unknown / Not supported */
+ ws_warning("Unknown akm");
+ return -1;
+ }
+}
+
+/* From IEEE 802.11-2016 Table 12-8 Integrity and key-wrap algorithms */
+static int Dot11DecryptGetKekLen(int akm)
+{
+ switch (akm) {
+ case 1: return 128;
+ case 2: return 128;
+ case 3: return 128;
+ case 4: return 128;
+ case 5: return 128;
+ case 6: return 128;
+ case 8: return 128;
+ case 9: return 128;
+ case 11: return 128;
+ case 12: return 256;
+ case 13: return 256;
+ case 18: return 128;
+ default:
+ /* Unknown / Not supported */
+ ws_warning("Unknown akm");
+ return -1;
+ }
+}
+
+/* From IEEE 802.11-2016 9.4.2.25.3 AKM suites and
+ * Table 12-8 Integrity and key-wrap algorithms */
+static int Dot11DecryptGetPtkLen(int akm, int cipher)
+{
+ int kck_len = Dot11DecryptGetKckLen(akm);
+ int kek_len = Dot11DecryptGetKekLen(akm);
+ int tk_len = Dot11DecryptGetTkLen(cipher);
+
+ if (kck_len == -1 || kek_len == -1 || tk_len == -1) {
+ ws_warning("Invalid PTK len");
+ return -1;
+ }
+ return kck_len + kek_len + tk_len;
+}
+
+/* From IEEE 802.11-2016 12.7.1.2 PRF and Table 9-133 AKM suite selectors */
+static int
+Dot11DecryptGetDeriveFuncFromAkm(int akm)
+{
+ int func = -1;
+ switch (akm) {
+ case 1:
+ case 2:
+ func = DOT11DECRYPT_DERIVE_USING_PRF;
+ break;
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 18:
+ func = DOT11DECRYPT_DERIVE_USING_KDF;
+ break;
+ default:
+ /* Unknown / Not supported yet */
+ break;
+ }
+ return func;
+}
+
+/* From IEEE 802.11-2016 12.7.1.2 PRF and Table 9-133 AKM suite selectors */
+static int
+Dot11DecryptGetHashAlgoFromAkm(int akm)
+{
+ int algo = -1;
+ switch (akm) {
+ case 1:
+ case 2:
+ algo = GCRY_MD_SHA1;
+ break;
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 18:
+ algo = GCRY_MD_SHA256;
+ break;
+ case 12:
+ case 13:
+ algo = GCRY_MD_SHA384;
+ break;
+ default:
+ /* Unknown / Not supported yet */
+ break;
+ }
+ return algo;
+}
+
+/* derive the PTK from the BSSID, STA MAC, PMK, SNonce, ANonce */
+/** From IEEE 802.11-2016 12.7.1.3 Pairwise key hierarchy:
+ * PRF-Length(PMK, "Pairwise key expansion",
+ * Min(AA, SPA) || Max(AA, SPA) ||
+ * Min(ANonce, SNonce) || Max(ANonce, SNonce))
+ */
+static uint8_t
+Dot11DecryptDerivePtk(
+ const DOT11DECRYPT_SEC_ASSOCIATION *sa,
+ const unsigned char *pmk,
+ size_t pmk_len,
+ const unsigned char snonce[32],
+ int key_version,
+ int akm,
+ int cipher,
+ uint8_t *ptk, size_t *ptk_len)
+{
+ int algo = -1;
+ int ptk_len_bits = -1;
+ int derive_func;
+
+ if (!sa || !pmk || !snonce || !ptk || !ptk_len) {
+ ws_warning("Invalid input for PTK derivation");
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+
+ if (key_version == DOT11DECRYPT_WPA_KEY_VER_NOT_CCMP) {
+ /* TKIP */
+ ptk_len_bits = 512;
+ derive_func = DOT11DECRYPT_DERIVE_USING_PRF;
+ algo = GCRY_MD_SHA1;
+ } else {
+ /* From IEEE 802.11-2016 Table 12-8 Integrity and key-wrap algorithms */
+ ptk_len_bits = Dot11DecryptGetPtkLen(akm, cipher);
+ algo = Dot11DecryptGetHashAlgoFromAkm(akm);
+ derive_func = Dot11DecryptGetDeriveFuncFromAkm(akm);
+ ws_debug("ptk_len_bits: %d, algo: %d, cipher: %d", ptk_len_bits, algo, cipher);
+ }
+
+ if (ptk_len_bits == -1 || algo == -1) {
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+ *ptk_len = ptk_len_bits / 8;
+
+ static const char *const label = "Pairwise key expansion";
+ uint8_t context[DOT11DECRYPT_MAC_LEN * 2 + 32 * 2];
+ int offset = 0;
+
+ /* Min(AA, SPA) || Max(AA, SPA) */
+ if (memcmp(sa->saId.sta, sa->saId.bssid, DOT11DECRYPT_MAC_LEN) < 0)
+ {
+ memcpy(context + offset, sa->saId.sta, DOT11DECRYPT_MAC_LEN);
+ offset += DOT11DECRYPT_MAC_LEN;
+ memcpy(context + offset, sa->saId.bssid, DOT11DECRYPT_MAC_LEN);
+ offset += DOT11DECRYPT_MAC_LEN;
+ }
+ else
+ {
+ memcpy(context + offset, sa->saId.bssid, DOT11DECRYPT_MAC_LEN);
+ offset += DOT11DECRYPT_MAC_LEN;
+ memcpy(context + offset, sa->saId.sta, DOT11DECRYPT_MAC_LEN);
+ offset += DOT11DECRYPT_MAC_LEN;
+ }
+
+ /* Min(ANonce, SNonce) || Max(ANonce, SNonce) */
+ if (memcmp(snonce, sa->wpa.nonce, 32) < 0 )
+ {
+ memcpy(context + offset, snonce, 32);
+ offset += 32;
+ memcpy(context + offset, sa->wpa.nonce, 32);
+ offset += 32;
+ }
+ else
+ {
+ memcpy(context + offset, sa->wpa.nonce, 32);
+ offset += 32;
+ memcpy(context + offset, snonce, 32);
+ offset += 32;
+ }
+ if (derive_func == DOT11DECRYPT_DERIVE_USING_PRF) {
+ dot11decrypt_prf(pmk, pmk_len, label, context, offset, algo,
+ ptk, *ptk_len);
+ } else {
+ dot11decrypt_kdf(pmk, pmk_len, label, context, offset, algo,
+ ptk, *ptk_len);
+ }
+ DEBUG_DUMP("PTK", ptk, *ptk_len, LOG_LEVEL_DEBUG);
+ 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 uint8_t
+Dot11DecryptFtDerivePtk(
+ const PDOT11DECRYPT_CONTEXT ctx,
+ const DOT11DECRYPT_SEC_ASSOCIATION *sa,
+ const PDOT11DECRYPT_KEY_ITEM key,
+ const uint8_t mdid[2],
+ const uint8_t *snonce,
+ const uint8_t *r0kh_id, size_t r0kh_id_len,
+ const uint8_t *r1kh_id, size_t r1kh_id_len _U_,
+ int akm, int cipher,
+ uint8_t *ptk, size_t *ptk_len)
+{
+ int hash_algo = Dot11DecryptGetHashAlgoFromAkm(akm);
+ uint8_t pmk_r0[DOT11DECRYPT_WPA_PMK_MAX_LEN];
+ uint8_t pmk_r1[DOT11DECRYPT_WPA_PMK_MAX_LEN];
+ uint8_t pmk_r0_name[16];
+ uint8_t pmk_r1_name[16];
+ uint8_t ptk_name[16];
+ size_t pmk_r0_len;
+ size_t pmk_r1_len;
+ const uint8_t *xxkey = NULL;
+ size_t xxkey_len;
+ int ptk_len_bits;
+
+ if (!sa || !key || !mdid || !snonce || !r0kh_id || !r1kh_id || !ptk || !ptk_len) {
+ ws_warning("Invalid input for FT PTK derivation");
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+ ptk_len_bits = Dot11DecryptGetPtkLen(akm, cipher);
+ if (ptk_len_bits == -1) {
+ ws_warning("Invalid akm or cipher");
+ 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) {
+ ws_debug("no xxkey. Skipping");
+ return DOT11DECRYPT_RET_NO_VALID_HANDSHAKE;
+ }
+ dot11decrypt_derive_pmk_r0(xxkey, xxkey_len,
+ ctx->pkt_ssid, ctx->pkt_ssid_len,
+ mdid,
+ r0kh_id, 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, LOG_LEVEL_DEBUG);
+ DEBUG_DUMP("PMKR0Name", pmk_r0_name, 16, LOG_LEVEL_DEBUG);
+
+ dot11decrypt_derive_pmk_r1(pmk_r0, pmk_r0_len, pmk_r0_name,
+ r1kh_id, sa->saId.sta, hash_algo,
+ pmk_r1, &pmk_r1_len, pmk_r1_name);
+ DEBUG_DUMP("PMK-R1", pmk_r1, pmk_r1_len, LOG_LEVEL_DEBUG);
+ DEBUG_DUMP("PMKR1Name", pmk_r1_name, 16, LOG_LEVEL_DEBUG);
+
+ dot11decrypt_derive_ft_ptk(pmk_r1, pmk_r1_len, pmk_r1_name,
+ snonce, sa->wpa.nonce,
+ sa->saId.bssid, sa->saId.sta, hash_algo,
+ ptk, *ptk_len, ptk_name);
+ DEBUG_DUMP("PTK", ptk, *ptk_len, LOG_LEVEL_DEBUG);
+ return DOT11DECRYPT_RET_SUCCESS;
+}
+
+#define MAX_SSID_LENGTH 32 /* maximum SSID length */
+
+static int
+Dot11DecryptRsnaPwd2PskStep(
+ const uint8_t *ppBytes,
+ const unsigned ppLength,
+ const char *ssid,
+ const size_t ssidLength,
+ const int iterations,
+ const int count,
+ unsigned char *output)
+{
+ unsigned char digest[MAX_SSID_LENGTH+4] = { 0 }; /* SSID plus 4 bytes of count */
+ int i, j;
+
+ if (ssidLength > MAX_SSID_LENGTH) {
+ /* This "should not happen" */
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+
+ /* U1 = PRF(P, S || int(i)) */
+ memcpy(digest, ssid, ssidLength);
+ digest[ssidLength] = (unsigned char)((count>>24) & 0xff);
+ digest[ssidLength+1] = (unsigned char)((count>>16) & 0xff);
+ digest[ssidLength+2] = (unsigned char)((count>>8) & 0xff);
+ digest[ssidLength+3] = (unsigned char)(count & 0xff);
+ if (ws_hmac_buffer(GCRY_MD_SHA1, digest, digest, (uint32_t) ssidLength + 4, ppBytes, ppLength)) {
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+
+ /* output = U1 */
+ memcpy(output, digest, 20);
+ for (i = 1; i < iterations; i++) {
+ /* Un = PRF(P, Un-1) */
+ if (ws_hmac_buffer(GCRY_MD_SHA1, digest, digest, HASH_SHA1_LENGTH, ppBytes, ppLength)) {
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+
+ /* output = output xor Un */
+ for (j = 0; j < 20; j++) {
+ output[j] ^= digest[j];
+ }
+ }
+
+ return DOT11DECRYPT_RET_SUCCESS;
+}
+
+static int
+Dot11DecryptRsnaPwd2Psk(
+ const char *passphrase,
+ const char *ssid,
+ const size_t ssidLength,
+ unsigned char *output)
+{
+ unsigned char m_output[40] = { 0 };
+ GByteArray *pp_ba = g_byte_array_new();
+
+ if (!uri_str_to_bytes(passphrase, pp_ba)) {
+ g_byte_array_free(pp_ba, true);
+ return 0;
+ }
+
+ Dot11DecryptRsnaPwd2PskStep(pp_ba->data, pp_ba->len, ssid, ssidLength, 4096, 1, m_output);
+ Dot11DecryptRsnaPwd2PskStep(pp_ba->data, pp_ba->len, ssid, ssidLength, 4096, 2, &m_output[20]);
+
+ memcpy(output, m_output, DOT11DECRYPT_WPA_PWD_PSK_LEN);
+ g_byte_array_free(pp_ba, true);
+
+ return 0;
+}
+
+/*
+ * Returns the decryption_key_t struct given a string describing the key.
+ * Returns NULL if the input_string cannot be parsed.
+ */
+decryption_key_t*
+parse_key_string(char* input_string, uint8_t key_type)
+{
+ char *key, *tmp_str;
+ char *ssid;
+
+ GString *key_string = NULL;
+ GByteArray *ssid_ba = NULL, *key_ba;
+ bool res;
+
+ char **tokens;
+ unsigned n = 0;
+ decryption_key_t *dk;
+
+ if(input_string == NULL)
+ return NULL;
+
+ /*
+ * Parse the input_string. WEP and WPA will be just a string
+ * of hexadecimal characters (if key is wrong, null will be
+ * returned...).
+ * WPA-PWD should be in the form
+ * <key data>[:<ssid>]
+ */
+
+ switch(key_type)
+ {
+ case DOT11DECRYPT_KEY_TYPE_WEP:
+ case DOT11DECRYPT_KEY_TYPE_WEP_40:
+ case DOT11DECRYPT_KEY_TYPE_WEP_104:
+
+ key_ba = g_byte_array_new();
+ res = hex_str_to_bytes(input_string, key_ba, false);
+
+ if (res && key_ba->len > 0) {
+ /* Key is correct! It was probably an 'old style' WEP key */
+ /* Create the decryption_key_t structure, fill it and return it*/
+ dk = g_new(decryption_key_t, 1);
+
+ dk->type = DOT11DECRYPT_KEY_TYPE_WEP;
+ /* XXX - The current key handling code in the GUI requires
+ * no separators and lower case */
+ tmp_str = bytes_to_str(NULL, key_ba->data, key_ba->len);
+ dk->key = g_string_new(tmp_str);
+ g_string_ascii_down(dk->key);
+ dk->bits = key_ba->len * 8;
+ dk->ssid = NULL;
+
+ wmem_free(NULL, tmp_str);
+ g_byte_array_free(key_ba, true);
+ return dk;
+ }
+
+ /* Key doesn't work */
+ g_byte_array_free(key_ba, true);
+ return NULL;
+
+ case DOT11DECRYPT_KEY_TYPE_WPA_PWD:
+
+ tokens = g_strsplit(input_string,":",0);
+
+ /* Tokens is a null termiated array of strings ... */
+ while(tokens[n] != NULL)
+ n++;
+
+ if(n < 1)
+ {
+ /* Free the array of strings */
+ g_strfreev(tokens);
+ return NULL;
+ }
+
+ /*
+ * The first token is the key
+ */
+ key = g_strdup(tokens[0]);
+
+ ssid = NULL;
+ /* Maybe there is a second token (an ssid, if everything else is ok) */
+ if(n >= 2)
+ {
+ ssid = g_strdup(tokens[1]);
+ }
+
+ /* Create a new string */
+ key_string = g_string_new(key);
+ ssid_ba = NULL;
+
+ /* Two (or more) tokens mean that the user entered a WPA-PWD key ... */
+ if( ((key_string->len) > WPA_KEY_MAX_CHAR_SIZE) || ((key_string->len) < WPA_KEY_MIN_CHAR_SIZE))
+ {
+ g_string_free(key_string, true);
+
+ g_free(key);
+ g_free(ssid);
+
+ /* Free the array of strings */
+ g_strfreev(tokens);
+ return NULL;
+ }
+
+ if(ssid != NULL) /* more than two tokens found, means that the user specified the ssid */
+ {
+ ssid_ba = g_byte_array_new();
+ if (! uri_str_to_bytes(ssid, ssid_ba)) {
+ g_string_free(key_string, true);
+ g_byte_array_free(ssid_ba, true);
+ g_free(key);
+ g_free(ssid);
+ /* Free the array of strings */
+ g_strfreev(tokens);
+ return NULL;
+ }
+
+ if(ssid_ba->len > WPA_SSID_MAX_CHAR_SIZE)
+ {
+ g_string_free(key_string, true);
+ g_byte_array_free(ssid_ba, true);
+
+ g_free(key);
+ g_free(ssid);
+
+ /* Free the array of strings */
+ g_strfreev(tokens);
+ return NULL;
+ }
+ }
+
+ /* Key was correct!!! Create the new decryption_key_t ... */
+ dk = g_new(decryption_key_t, 1);
+
+ dk->type = DOT11DECRYPT_KEY_TYPE_WPA_PWD;
+ dk->key = g_string_new(key);
+ dk->bits = 256; /* This is the length of the array pf bytes that will be generated using key+ssid ...*/
+ dk->ssid = byte_array_dup(ssid_ba); /* NULL if ssid_ba is NULL */
+
+ g_string_free(key_string, true);
+ if (ssid_ba != NULL)
+ g_byte_array_free(ssid_ba, true);
+
+ g_free(key);
+ g_free(ssid);
+
+ /* Free the array of strings */
+ g_strfreev(tokens);
+ return dk;
+
+ case DOT11DECRYPT_KEY_TYPE_WPA_PSK:
+
+ key_ba = g_byte_array_new();
+ res = hex_str_to_bytes(input_string, key_ba, false);
+
+ /* Two tokens means that the user should have entered a WPA-BIN key ... */
+ if(!res || (key_ba->len != DOT11DECRYPT_WPA_PWD_PSK_LEN &&
+ key_ba->len != DOT11DECRYPT_WPA_PMK_MAX_LEN))
+ {
+ g_byte_array_free(key_ba, true);
+
+ /* No ssid has been created ... */
+ return NULL;
+ }
+
+ /* Key was correct!!! Create the new decryption_key_t ... */
+ dk = g_new(decryption_key_t, 1);
+
+ dk->type = DOT11DECRYPT_KEY_TYPE_WPA_PSK;
+ dk->key = g_string_new(input_string);
+ dk->bits = (unsigned) dk->key->len * 4;
+ dk->ssid = NULL;
+
+ g_byte_array_free(key_ba, true);
+ return dk;
+
+ case DOT11DECRYPT_KEY_TYPE_TK:
+ {
+ /* From IEEE 802.11-2016 Table 12-4 Cipher suite key lengths */
+ static const uint8_t allowed_key_lengths[] = {
+// TBD 40 / 8, /* WEP-40 */
+// TBD 104 / 8, /* WEP-104 */
+ 256 / 8, /* TKIP, GCMP-256, CCMP-256 */
+ 128 / 8, /* CCMP-128, GCMP-128 */
+ };
+ bool key_length_ok = false;
+
+ key_ba = g_byte_array_new();
+ res = hex_str_to_bytes(input_string, key_ba, false);
+
+ for (size_t i = 0; i < sizeof(allowed_key_lengths); i++) {
+ if (key_ba->len == allowed_key_lengths[i]) {
+ key_length_ok = true;
+ break;
+ }
+ }
+ if (!res || !key_length_ok) {
+ g_byte_array_free(key_ba, true);
+ return NULL;
+ }
+ dk = g_new(decryption_key_t, 1);
+ dk->type = DOT11DECRYPT_KEY_TYPE_TK;
+ dk->key = g_string_new(input_string);
+ dk->bits = (unsigned) 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 = (unsigned)dk->key->len * 4;
+ dk->ssid = NULL;
+ g_byte_array_free(key_ba, true);
+ return dk;
+ }
+ }
+
+ /* Type not supported */
+ return NULL;
+}
+
+void
+free_key_string(decryption_key_t *dk)
+{
+ if (dk->key)
+ g_string_free(dk->key, true);
+ if (dk->ssid)
+ g_byte_array_free(dk->ssid, true);
+ g_free(dk);
+}
+
+static int
+Dot11DecryptTDLSDeriveKey(
+ PDOT11DECRYPT_SEC_ASSOCIATION sa,
+ const uint8_t *data,
+ unsigned offset_rsne,
+ unsigned offset_fte,
+ unsigned offset_timeout,
+ unsigned offset_link,
+ uint8_t action)
+{
+
+ gcry_md_hd_t sha256_handle;
+ gcry_md_hd_t hmac_handle;
+ const uint8_t *snonce, *anonce, *initiator, *responder, *bssid;
+ uint8_t key_input[32];
+ uint8_t mic[16], seq_num = action + 1;
+ uint8_t zeros[16] = { 0 };
+ gcry_mac_hd_t cmac_handle;
+ size_t cmac_len = 16;
+ size_t cmac_write_len;
+
+ /* Get key input */
+ anonce = &data[offset_fte + 20];
+ snonce = &data[offset_fte + 52];
+
+ gcry_md_open (&sha256_handle, GCRY_MD_SHA256, 0);
+ if (memcmp(anonce, snonce, DOT11DECRYPT_WPA_NONCE_LEN) < 0) {
+ gcry_md_write(sha256_handle, anonce, DOT11DECRYPT_WPA_NONCE_LEN);
+ gcry_md_write(sha256_handle, snonce, DOT11DECRYPT_WPA_NONCE_LEN);
+ } else {
+ gcry_md_write(sha256_handle, snonce, DOT11DECRYPT_WPA_NONCE_LEN);
+ gcry_md_write(sha256_handle, anonce, DOT11DECRYPT_WPA_NONCE_LEN);
+ }
+ memcpy(key_input, gcry_md_read(sha256_handle, 0), 32);
+ gcry_md_close(sha256_handle);
+
+ /* Derive key */
+ bssid = &data[offset_link + 2];
+ initiator = &data[offset_link + 8];
+ responder = &data[offset_link + 14];
+ if (gcry_md_open(&hmac_handle, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC)) {
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+ if (gcry_md_setkey(hmac_handle, key_input, 32)) {
+ gcry_md_close(hmac_handle);
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+ gcry_md_putc(hmac_handle, 1);
+ gcry_md_putc(hmac_handle, 0);
+ gcry_md_write(hmac_handle, "TDLS PMK", 8);
+ if (memcmp(initiator, responder, DOT11DECRYPT_MAC_LEN) < 0) {
+ gcry_md_write(hmac_handle, initiator, DOT11DECRYPT_MAC_LEN);
+ gcry_md_write(hmac_handle, responder, DOT11DECRYPT_MAC_LEN);
+ } else {
+ gcry_md_write(hmac_handle, responder, DOT11DECRYPT_MAC_LEN);
+ gcry_md_write(hmac_handle, initiator, DOT11DECRYPT_MAC_LEN);
+ }
+ gcry_md_write(hmac_handle, bssid, DOT11DECRYPT_MAC_LEN);
+ gcry_md_putc(hmac_handle, 0);
+ gcry_md_putc(hmac_handle, 1);
+ memcpy(key_input, gcry_md_read(hmac_handle, 0), 32);
+ gcry_md_close(hmac_handle);
+
+ /* Check MIC */
+ if (gcry_mac_open(&cmac_handle, GCRY_MAC_CMAC_AES, 0, NULL)) {
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+ if (gcry_mac_setkey(cmac_handle, key_input, 16)) {
+ gcry_mac_close(cmac_handle);
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+ gcry_mac_write(cmac_handle, initiator, DOT11DECRYPT_MAC_LEN);
+ gcry_mac_write(cmac_handle, responder, DOT11DECRYPT_MAC_LEN);
+ gcry_mac_write(cmac_handle, &seq_num, 1);
+ gcry_mac_write(cmac_handle, &data[offset_link], data[offset_link + 1] + 2);
+ gcry_mac_write(cmac_handle, &data[offset_rsne], data[offset_rsne + 1] + 2);
+ gcry_mac_write(cmac_handle, &data[offset_timeout], data[offset_timeout + 1] + 2);
+ gcry_mac_write(cmac_handle, &data[offset_fte], 4);
+ gcry_mac_write(cmac_handle, zeros, 16);
+ cmac_write_len = data[offset_fte + 1] + 2;
+ if (cmac_write_len < 20) {
+ ws_warning("Bad MAC len");
+ gcry_mac_close(cmac_handle);
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+ gcry_mac_write(cmac_handle, &data[offset_fte + 20], cmac_write_len - 20);
+ if (gcry_mac_read(cmac_handle, mic, &cmac_len) != GPG_ERR_NO_ERROR) {
+ ws_warning("MAC read error");
+ gcry_mac_close(cmac_handle);
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+ if (memcmp(mic, &data[offset_fte + 4], 16)) {
+ ws_debug("MIC verification failed");
+ gcry_mac_close(cmac_handle);
+ return DOT11DECRYPT_RET_UNSUCCESS;
+ }
+ gcry_mac_close(cmac_handle);
+ /* TODO support other akm and ciphers? */
+ sa->wpa.akm = 2;
+ sa->wpa.cipher = 4;
+ sa->wpa.ptk_len = Dot11DecryptGetPtkLen(sa->wpa.akm, sa->wpa.cipher) / 8;
+ memcpy(DOT11DECRYPT_GET_TK(sa->wpa.ptk, sa->wpa.akm),
+ key_input + 16, Dot11DecryptGetTkLen(sa->wpa.cipher) / 8);
+ memcpy(sa->wpa.nonce, snonce, DOT11DECRYPT_WPA_NONCE_LEN);
+ sa->validKey = true;
+ sa->wpa.key_ver = DOT11DECRYPT_WPA_KEY_VER_AES_CCMP;
+ ws_debug("MIC verified");
+ return DOT11DECRYPT_RET_SUCCESS;
+}
+
+
+#ifdef __cplusplus
+}
+#endif
+
+/****************************************************************************/
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/epan/crypt/dot11decrypt_ccmp.c b/epan/crypt/dot11decrypt_ccmp.c
new file mode 100644
index 00000000..eebc947f
--- /dev/null
+++ b/epan/crypt/dot11decrypt_ccmp.c
@@ -0,0 +1,139 @@
+/* dot11decrypt_ccmp.c
+ *
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2006 CACE Technologies, Davis (California)
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
+ */
+
+/*
+ * Note: This file was derived from the FreeBSD source code, RELENG 6,
+ * sys/net80211/ieee80211_crypto_ccmp.c
+ */
+
+/****************************************************************************/
+/* File includes */
+#include "config.h"
+#include "dot11decrypt_system.h"
+#include "dot11decrypt_util.h"
+#include "dot11decrypt_int.h"
+
+#include "dot11decrypt_debug.h"
+#include <wsutil/wsgcrypt.h>
+
+/****************************************************************************/
+/* Internal definitions */
+
+/****************************************************************************/
+/* Internal macros */
+
+#define READ_6(b0, b1, b2, b3, b4, b5) \
+ ((((uint64_t)((uint16_t)((b4 << 0) | (b5 << 8)))) << 32) | \
+ ((uint32_t)((b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24))))
+
+/****************************************************************************/
+/* Internal function prototypes declarations */
+
+/****************************************************************************/
+/* Function definitions */
+
+/* From IEEE 802.11 2016 Chapter 12.5.3.3.4 Construct CCM nonce */
+/* Nonce: Flags | A2 | PN */
+static void ccmp_construct_nonce(
+ PDOT11DECRYPT_MAC_FRAME wh,
+ uint64_t pn,
+ uint8_t nonce[13])
+{
+ uint8_t mgmt = (DOT11DECRYPT_TYPE(wh->fc[0]) == DOT11DECRYPT_TYPE_MANAGEMENT);
+
+ if (DOT11DECRYPT_IS_4ADDRESS(wh) && DOT11DECRYPT_IS_QOS_DATA(wh)) {
+ PDOT11DECRYPT_MAC_FRAME_ADDR4_QOS qwh4 =
+ (PDOT11DECRYPT_MAC_FRAME_ADDR4_QOS) wh;
+ nonce[0] = (uint8_t)(qwh4->qos[0] & 0x0f);/* just priority bits */
+ } else if (DOT11DECRYPT_IS_QOS_DATA(wh)) {
+ PDOT11DECRYPT_MAC_FRAME_QOS qwh =
+ (PDOT11DECRYPT_MAC_FRAME_QOS) wh;
+ nonce[0] = (uint8_t)(qwh->qos[0] & 0x0f); /* just priority bits */
+ } else {
+ nonce[0] = 0;
+ }
+ if (mgmt) {
+ nonce[0] |= 0x10; /* set MGMT flag */
+ }
+
+ DOT11DECRYPT_ADDR_COPY(nonce + 1, wh->addr2);
+ nonce[7] = (uint8_t)(pn >> 40);
+ nonce[8] = (uint8_t)(pn >> 32);
+ nonce[9] = (uint8_t)(pn >> 24);
+ nonce[10] = (uint8_t)(pn >> 16);
+ nonce[11] = (uint8_t)(pn >> 8);
+ nonce[12] = (uint8_t)(pn >> 0);
+}
+
+int Dot11DecryptCcmpDecrypt(
+ uint8_t *m,
+ int mac_header_len,
+ int len,
+ uint8_t *TK1,
+ int tk_len,
+ int mic_len)
+{
+ PDOT11DECRYPT_MAC_FRAME wh;
+ uint8_t aad[30]; /* Max aad_len. See Table 12-1 IEEE 802.11 2016 */
+ uint8_t nonce[13];
+ uint8_t mic[16]; /* Big enough for CCMP-256 */
+ ssize_t data_len;
+ size_t aad_len;
+ int z = mac_header_len;
+ gcry_cipher_hd_t handle;
+ uint64_t pn;
+ uint8_t *ivp = m + z;
+
+ wh = (PDOT11DECRYPT_MAC_FRAME )m;
+ data_len = len - (z + DOT11DECRYPT_CCMP_HEADER + mic_len);
+ if (data_len < 1) {
+ return 0;
+ }
+
+ memcpy(mic, m + len - mic_len, mic_len);
+ pn = READ_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]);
+ ccmp_construct_nonce(wh, pn, nonce);
+ dot11decrypt_construct_aad(wh, aad, &aad_len);
+
+ if (gcry_cipher_open(&handle, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CCM, 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;
+ }
+
+ uint64_t ccm_lengths[3];
+ ccm_lengths[0] = data_len;
+ ccm_lengths[1] = aad_len;
+ ccm_lengths[2] = mic_len;
+ if (gcry_cipher_ctl(handle, GCRYCTL_SET_CCM_LENGTHS, ccm_lengths, sizeof(ccm_lengths))) {
+ goto err_out;
+ }
+ if (gcry_cipher_authenticate(handle, aad, aad_len)) {
+ goto err_out;
+ }
+ if (gcry_cipher_decrypt(handle, m + z + DOT11DECRYPT_CCMP_HEADER, data_len, NULL, 0)) {
+ goto err_out;
+ }
+ if (gcry_cipher_checktag(handle, mic, mic_len)) {
+ 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_debug.h b/epan/crypt/dot11decrypt_debug.h
new file mode 100644
index 00000000..42c55568
--- /dev/null
+++ b/epan/crypt/dot11decrypt_debug.h
@@ -0,0 +1,31 @@
+/** @file
+ *
+ * Copyright (c) 2006 CACE Technologies, Davis (California)
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
+ */
+
+#ifndef _DOT11DECRYPT_DEBUG_H
+#define _DOT11DECRYPT_DEBUG_H
+
+#define WS_LOG_DOMAIN "dot11decrypt"
+
+#include <wsutil/wslog.h>
+
+/******************************************************************************/
+/* Debug section: internal function to print debug information */
+/* */
+#ifdef WS_DEBUG
+
+#define DEBUG_DUMP(name, ptr, size, level) \
+ ws_log_buffer_full(WS_LOG_DOMAIN, level, __FILE__, __LINE__, G_STRFUNC, ptr, size, 72, name);
+
+#else /* defined WS_DEBUG */
+
+#define DEBUG_DUMP(name, ptr, size, level)
+
+#endif /* ?defined WS_DEBUG */
+
+
+#endif /* ?defined _DOT11DECRYPT_DEBUG_H */
diff --git a/epan/crypt/dot11decrypt_gcmp.c b/epan/crypt/dot11decrypt_gcmp.c
new file mode 100644
index 00000000..7e7ae3be
--- /dev/null
+++ b/epan/crypt/dot11decrypt_gcmp.c
@@ -0,0 +1,110 @@
+/* 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 <wsutil/wsgcrypt.h>
+
+/****************************************************************************/
+/* Internal definitions */
+
+/****************************************************************************/
+/* Internal macros */
+
+#define READ_6(b0, b1, b2, b3, b4, b5) \
+ ((((uint64_t)((uint16_t)((b4 << 0) | (b5 << 8)))) << 32) | \
+ ((uint32_t)((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,
+ uint64_t pn,
+ uint8_t nonce[12])
+{
+ /* Nonce: A2 | PN */
+ DOT11DECRYPT_ADDR_COPY(nonce, wh->addr2);
+ nonce[6] = (uint8_t)(pn >> 40);
+ nonce[7] = (uint8_t)(pn >> 32);
+ nonce[8] = (uint8_t)(pn >> 24);
+ nonce[9] = (uint8_t)(pn >> 16);
+ nonce[10] = (uint8_t)(pn >> 8);
+ nonce[11] = (uint8_t)(pn >> 0);
+}
+
+int Dot11DecryptGcmpDecrypt(
+ uint8_t *m,
+ int mac_header_len,
+ int len,
+ uint8_t *TK1,
+ int tk_len)
+{
+ PDOT11DECRYPT_MAC_FRAME wh;
+ uint8_t aad[30];
+ uint8_t nonce[12];
+ uint8_t mic[16];
+ ssize_t data_len;
+ size_t aad_len;
+ int z = mac_header_len;
+ gcry_cipher_hd_t handle;
+ uint64_t pn;
+ uint8_t *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
new file mode 100644
index 00000000..0ca8d5c4
--- /dev/null
+++ b/epan/crypt/dot11decrypt_int.h
@@ -0,0 +1,193 @@
+/** @file
+ *
+ * Copyright (c) 2006 CACE Technologies, Davis (California)
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
+ */
+
+#ifndef _DOT11DECRYPT_INT_H
+#define _DOT11DECRYPT_INT_H
+
+/****************************************************************************/
+/* File includes */
+
+#include "dot11decrypt_system.h"
+
+#include "ws_attributes.h"
+#include <wsutil/wsgcrypt.h>
+
+/****************************************************************************/
+
+/****************************************************************************/
+/* Definitions */
+
+/* IEEE 802.11 packet type values */
+#define DOT11DECRYPT_TYPE_MANAGEMENT 0
+#define DOT11DECRYPT_TYPE_CONTROL 1
+#define DOT11DECRYPT_TYPE_DATA 2
+
+/* IEEE 802.11 packet subtype values */
+#define DOT11DECRYPT_SUBTYPE_ASSOC_REQ 0
+#define DOT11DECRYPT_SUBTYPE_ASSOC_RESP 1
+#define DOT11DECRYPT_SUBTYPE_REASSOC_REQ 2
+#define DOT11DECRYPT_SUBTYPE_REASSOC_RESP 3
+#define DOT11DECRYPT_SUBTYPE_PROBE_REQ 4
+#define DOT11DECRYPT_SUBTYPE_PROBE_RESP 5
+#define DOT11DECRYPT_SUBTYPE_MEASUREMENT_PILOT 6
+#define DOT11DECRYPT_SUBTYPE_BEACON 8
+#define DOT11DECRYPT_SUBTYPE_ATIM 9
+#define DOT11DECRYPT_SUBTYPE_DISASS 10
+#define DOT11DECRYPT_SUBTYPE_AUTHENTICATION 11
+#define DOT11DECRYPT_SUBTYPE_DEAUTHENTICATION 12
+#define DOT11DECRYPT_SUBTYPE_ACTION 13
+#define DOT11DECRYPT_SUBTYPE_ACTION_NO_ACK 14
+
+/*
+ * Min length of encrypted data (TKIP=21bytes, CCMP=17bytes)
+ * CCMP = 8 octets of CCMP header, 1 octet of data, 8 octets of MIC.
+ * TKIP = 4 octets of IV/Key ID, 4 octets of Extended IV, 1 octet of data,
+ * 8 octets of MIC, 4 octets of ICV
+ */
+#define DOT11DECRYPT_CRYPTED_DATA_MINLEN 17
+
+#define DOT11DECRYPT_TA_OFFSET 10
+
+/* */
+/****************************************************************************/
+
+/****************************************************************************/
+/* Macro definitions */
+
+/**
+ * Macros to get various bits of a 802.11 control frame
+ */
+#define DOT11DECRYPT_TYPE(FrameControl_0) (uint8_t)((FrameControl_0 >> 2) & 0x3)
+#define DOT11DECRYPT_SUBTYPE(FrameControl_0) (uint8_t)((FrameControl_0 >> 4) & 0xF)
+#define DOT11DECRYPT_DS_BITS(FrameControl_1) (uint8_t)(FrameControl_1 & 0x3)
+#define DOT11DECRYPT_TO_DS(FrameControl_1) (uint8_t)(FrameControl_1 & 0x1)
+#define DOT11DECRYPT_FROM_DS(FrameControl_1) (uint8_t)((FrameControl_1 >> 1) & 0x1)
+#define DOT11DECRYPT_WEP(FrameControl_1) (uint8_t)((FrameControl_1 >> 6) & 0x1)
+
+/**
+ * Get the Key ID from the Initialization Vector (last byte)
+ */
+#define DOT11DECRYPT_EXTIV(KeyID) ((KeyID >> 5) & 0x1)
+
+#define DOT11DECRYPT_KEY_INDEX(KeyID) ((KeyID >> 6) & 0x3) /** Used to determine TKIP group key from unicast (group = 1, unicast = 0) */
+
+/* Macros to get various bits of an EAPOL frame */
+#define DOT11DECRYPT_EAP_KEY_DESCR_VER(KeyInfo_1) ((unsigned char)(KeyInfo_1 & 0x3))
+#define DOT11DECRYPT_EAP_KEY(KeyInfo_1) ((KeyInfo_1 >> 3) & 0x1)
+#define DOT11DECRYPT_EAP_INST(KeyInfo_1) ((KeyInfo_1 >> 6) & 0x1)
+#define DOT11DECRYPT_EAP_ACK(KeyInfo_1) ((KeyInfo_1 >> 7) & 0x1)
+#define DOT11DECRYPT_EAP_MIC(KeyInfo_0) (KeyInfo_0 & 0x1)
+#define DOT11DECRYPT_EAP_SEC(KeyInfo_0) ((KeyInfo_0 >> 1) & 0x1)
+
+/* Note: copied from net80211/ieee80211.h */
+#define DOT11DECRYPT_FC1_DIR_MASK 0x03
+#define DOT11DECRYPT_FC1_DIR_DSTODS 0x03 /* AP ->AP */
+#define DOT11DECRYPT_FC0_SUBTYPE_QOS 0x80
+#define DOT11DECRYPT_FC0_TYPE_DATA 0x08
+#define DOT11DECRYPT_FC0_TYPE_MASK 0x0c
+#define DOT11DECRYPT_SEQ_FRAG_MASK 0x000f
+#define DOT11DECRYPT_QOS_HAS_SEQ(wh) \
+ (((wh)->fc[0] & \
+ (DOT11DECRYPT_FC0_TYPE_MASK | DOT11DECRYPT_FC0_SUBTYPE_QOS)) == \
+ (DOT11DECRYPT_FC0_TYPE_DATA | DOT11DECRYPT_FC0_SUBTYPE_QOS))
+
+#define DOT11DECRYPT_ADDR_COPY(dst,src) memcpy(dst, src, DOT11DECRYPT_MAC_LEN)
+
+#define DOT11DECRYPT_IS_4ADDRESS(wh) \
+ ((wh->fc[1] & DOT11DECRYPT_FC1_DIR_MASK) == DOT11DECRYPT_FC1_DIR_DSTODS)
+#define DOT11DECRYPT_IS_QOS_DATA(wh) DOT11DECRYPT_QOS_HAS_SEQ(wh)
+
+/****************************************************************************/
+
+/****************************************************************************/
+/* Structure definitions */
+
+/*
+ * XXX - According to the thread at
+ * https://www.wireshark.org/lists/wireshark-dev/200612/msg00384.html we
+ * shouldn't have to worry about packing our structs, since the largest
+ * elements are 8 bits wide.
+ */
+#ifdef _MSC_VER /* MS Visual C++ */
+#pragma pack(push)
+#pragma pack(1)
+#endif
+
+/* Definition of IEEE 802.11 frame (without the address 4) */
+typedef struct _DOT11DECRYPT_MAC_FRAME {
+ unsigned char fc[2];
+ unsigned char dur[2];
+ unsigned char addr1[DOT11DECRYPT_MAC_LEN];
+ unsigned char addr2[DOT11DECRYPT_MAC_LEN];
+ unsigned char addr3[DOT11DECRYPT_MAC_LEN];
+ unsigned char seq[2];
+} DOT11DECRYPT_MAC_FRAME, *PDOT11DECRYPT_MAC_FRAME;
+
+/* Definition of IEEE 802.11 frame (with the address 4) */
+typedef struct _DOT11DECRYPT_MAC_FRAME_ADDR4 {
+ unsigned char fc[2];
+ unsigned char dur[2];
+ unsigned char addr1[DOT11DECRYPT_MAC_LEN];
+ unsigned char addr2[DOT11DECRYPT_MAC_LEN];
+ unsigned char addr3[DOT11DECRYPT_MAC_LEN];
+ unsigned char seq[2];
+ unsigned char addr4[DOT11DECRYPT_MAC_LEN];
+} DOT11DECRYPT_MAC_FRAME_ADDR4, *PDOT11DECRYPT_MAC_FRAME_ADDR4;
+
+/* Definition of IEEE 802.11 frame (without the address 4, with QOS) */
+typedef struct _DOT11DECRYPT_MAC_FRAME_QOS {
+ unsigned char fc[2];
+ unsigned char dur[2];
+ unsigned char addr1[DOT11DECRYPT_MAC_LEN];
+ unsigned char addr2[DOT11DECRYPT_MAC_LEN];
+ unsigned char addr3[DOT11DECRYPT_MAC_LEN];
+ unsigned char seq[2];
+ unsigned char qos[2];
+} DOT11DECRYPT_MAC_FRAME_QOS, *PDOT11DECRYPT_MAC_FRAME_QOS;
+
+/* Definition of IEEE 802.11 frame (with the address 4 and QOS) */
+typedef struct _DOT11DECRYPT_MAC_FRAME_ADDR4_QOS {
+ unsigned char fc[2];
+ unsigned char dur[2];
+ unsigned char addr1[DOT11DECRYPT_MAC_LEN];
+ unsigned char addr2[DOT11DECRYPT_MAC_LEN];
+ unsigned char addr3[DOT11DECRYPT_MAC_LEN];
+ unsigned char seq[2];
+ unsigned char addr4[DOT11DECRYPT_MAC_LEN];
+ unsigned char qos[2];
+} DOT11DECRYPT_MAC_FRAME_ADDR4_QOS, *PDOT11DECRYPT_MAC_FRAME_ADDR4_QOS;
+
+#ifdef _MSC_VER /* MS Visual C++ */
+#pragma pack(pop)
+#endif
+
+/******************************************************************************/
+
+int Dot11DecryptCcmpDecrypt(
+ uint8_t *m,
+ int mac_header_len,
+ int len,
+ uint8_t *TK1,
+ int tk_len,
+ int mic_len);
+
+int Dot11DecryptGcmpDecrypt(
+ uint8_t *m,
+ int mac_header_len,
+ int len,
+ uint8_t *TK1,
+ int tk_len);
+
+int Dot11DecryptTkipDecrypt(
+ unsigned char *tkip_mpdu,
+ size_t mpdu_len,
+ unsigned char TA[DOT11DECRYPT_MAC_LEN],
+ unsigned char TK[DOT11DECRYPT_TK_LEN])
+ ;
+
+#endif
diff --git a/epan/crypt/dot11decrypt_system.h b/epan/crypt/dot11decrypt_system.h
new file mode 100644
index 00000000..19ecb297
--- /dev/null
+++ b/epan/crypt/dot11decrypt_system.h
@@ -0,0 +1,463 @@
+/** @file
+ *
+ * Copyright (c) 2006 CACE Technologies, Davis (California)
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
+ */
+
+#ifndef _DOT11DECRYPT_SYSTEM_H
+#define _DOT11DECRYPT_SYSTEM_H
+
+/************************************************************************/
+/* Constant definitions */
+
+/* General definitions */
+#define DOT11DECRYPT_RET_SUCCESS 0
+#define DOT11DECRYPT_RET_UNSUCCESS 1
+
+#define DOT11DECRYPT_RET_NO_DATA 1
+#define DOT11DECRYPT_RET_WRONG_DATA_SIZE 2
+#define DOT11DECRYPT_RET_REQ_DATA 3
+#define DOT11DECRYPT_RET_NO_VALID_HANDSHAKE 4
+#define DOT11DECRYPT_RET_NO_DATA_ENCRYPTED 5
+
+#define DOT11DECRYPT_RET_SUCCESS_HANDSHAKE -1
+
+#define DOT11DECRYPT_MAX_KEYS_NR 64
+
+/* Decryption algorithms fields size definition (bytes) */
+#define DOT11DECRYPT_WPA_NONCE_LEN 32
+#define DOT11DECRYPT_WPA_PTK_MAX_LEN 88 /* TKIP 48, CCMP 64, GCMP-256 88 bytes */
+#define DOT11DECRYPT_WPA_MICKEY_MAX_LEN 24
+
+#define DOT11DECRYPT_WEP_128_KEY_LEN 16 /* 128 bits */
+
+/* General 802.11 constants */
+#define DOT11DECRYPT_MAC_LEN 6
+#define DOT11DECRYPT_RADIOTAP_HEADER_LEN 24
+
+#define DOT11DECRYPT_EAPOL_MAX_LEN 1024U
+
+#define DOT11DECRYPT_TK_LEN 16
+
+/* Max length of capture data */
+#define DOT11DECRYPT_MAX_CAPLEN 8192
+
+#define DOT11DECRYPT_WEP_IVLEN 3 /* 24bit */
+#define DOT11DECRYPT_WEP_KIDLEN 1 /* 1 octet */
+#define DOT11DECRYPT_WEP_ICV 4
+#define DOT11DECRYPT_WEP_HEADER DOT11DECRYPT_WEP_IVLEN + DOT11DECRYPT_WEP_KIDLEN
+#define DOT11DECRYPT_WEP_TRAILER DOT11DECRYPT_WEP_ICV
+
+/*
+ * 802.11i defines an extended IV for use with non-WEP ciphers.
+ * When the EXTIV bit is set in the key id byte an additional
+ * 4 bytes immediately follow the IV for TKIP. For CCMP the
+ * EXTIV bit is likewise set but the 8 bytes represent the
+ * CCMP header rather than IV+extended-IV.
+ */
+#define DOT11DECRYPT_RSNA_EXTIV 0x20
+#define DOT11DECRYPT_RSNA_EXTIVLEN 4 /* extended IV length */
+#define DOT11DECRYPT_TKIP_MICLEN 8 /* trailing MIC */
+
+#define DOT11DECRYPT_RSNA_HEADER DOT11DECRYPT_WEP_HEADER + DOT11DECRYPT_RSNA_EXTIVLEN
+
+#define DOT11DECRYPT_CCMP_HEADER DOT11DECRYPT_RSNA_HEADER
+#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
+
+#define DOT11DECRYPT_CRC_LEN 4
+
+/************************************************************************/
+/* File includes */
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "dot11decrypt_user.h"
+#include "ws_symbol_export.h"
+
+/************************************************************************/
+/* Macro definitions */
+
+/************************************************************************/
+/* Type definitions */
+
+typedef struct _DOT11DECRYPT_SEC_ASSOCIATION_ID {
+ unsigned char bssid[DOT11DECRYPT_MAC_LEN];
+ unsigned char sta[DOT11DECRYPT_MAC_LEN];
+} DOT11DECRYPT_SEC_ASSOCIATION_ID, *PDOT11DECRYPT_SEC_ASSOCIATION_ID;
+
+typedef struct _DOT11DECRYPT_SEC_ASSOCIATION {
+ /* This is for reassociations. A linked list of old security
+ * associations is kept. GCS
+ */
+ struct _DOT11DECRYPT_SEC_ASSOCIATION* next;
+
+ DOT11DECRYPT_SEC_ASSOCIATION_ID saId;
+ DOT11DECRYPT_KEY_ITEM *key;
+ uint8_t handshake;
+ uint8_t validKey;
+
+ struct {
+ uint8_t key_ver; /* Key descriptor version */
+ unsigned char nonce[DOT11DECRYPT_WPA_NONCE_LEN];
+ /* used to derive PTK, ANonce stored, SNonce taken */
+ /* the 2nd packet of the 4W handshake */
+ int akm;
+ int cipher;
+ int tmp_group_cipher; /* Keep between HS msg 2 and 3 */
+ unsigned char ptk[DOT11DECRYPT_WPA_PTK_MAX_LEN]; /* session key used in decryption algorithm */
+ int ptk_len;
+ } wpa;
+
+
+} DOT11DECRYPT_SEC_ASSOCIATION, *PDOT11DECRYPT_SEC_ASSOCIATION;
+
+typedef struct _DOT11DECRYPT_CONTEXT {
+ GHashTable *sa_hash;
+ DOT11DECRYPT_KEY_ITEM keys[DOT11DECRYPT_MAX_KEYS_NR];
+ size_t keys_nr;
+ char pkt_ssid[DOT11DECRYPT_WPA_SSID_MAX_LEN];
+ size_t pkt_ssid_len;
+} DOT11DECRYPT_CONTEXT, *PDOT11DECRYPT_CONTEXT;
+
+typedef enum _DOT11DECRYPT_HS_MSG_TYPE {
+ DOT11DECRYPT_HS_MSG_TYPE_INVALID = 0,
+ DOT11DECRYPT_HS_MSG_TYPE_4WHS_1,
+ DOT11DECRYPT_HS_MSG_TYPE_4WHS_2,
+ DOT11DECRYPT_HS_MSG_TYPE_4WHS_3,
+ DOT11DECRYPT_HS_MSG_TYPE_4WHS_4,
+ DOT11DECRYPT_HS_MSG_TYPE_GHS_1,
+ DOT11DECRYPT_HS_MSG_TYPE_GHS_2
+} DOT11DECRYPT_HS_MSG_TYPE;
+
+typedef struct _DOT11DECRYPT_FTE {
+ uint8_t *mic;
+ uint8_t mic_len;
+ uint8_t *anonce;
+ uint8_t *snonce;
+ uint8_t *r0kh_id;
+ uint8_t r0kh_id_len;
+ uint8_t *r1kh_id;
+ uint8_t r1kh_id_len;
+} DOT11DECRYPT_FTE, *PDOT11DECRYPT_FTE;
+
+typedef struct _DOT11DECRYPT_EAPOL_PARSED {
+ DOT11DECRYPT_HS_MSG_TYPE msg_type;
+ uint16_t len;
+ uint8_t key_type;
+ uint8_t key_version;
+ uint16_t key_len;
+ uint8_t *key_iv;
+ uint8_t *key_data;
+ uint16_t key_data_len;
+ uint8_t group_cipher;
+ uint8_t cipher;
+ uint8_t akm;
+ uint8_t *nonce;
+ uint8_t *mic;
+ uint16_t mic_len;
+ uint8_t *gtk;
+ uint16_t gtk_len;
+
+ /* For fast bss transition akms */
+ uint8_t *mdid;
+ DOT11DECRYPT_FTE fte;
+} DOT11DECRYPT_EAPOL_PARSED, *PDOT11DECRYPT_EAPOL_PARSED;
+
+typedef struct _DOT11DECRYPT_ASSOC_PARSED
+{
+ uint8_t frame_subtype;
+ uint8_t group_cipher;
+ uint8_t cipher;
+ uint8_t akm;
+ uint8_t *mdid;
+ DOT11DECRYPT_FTE fte;
+ uint8_t* rsne_tag;
+ uint8_t* mde_tag;
+ uint8_t* fte_tag;
+ uint8_t* rde_tag;
+ uint8_t *gtk;
+ uint16_t gtk_len;
+ uint16_t gtk_subelem_key_len;
+ uint8_t bssid[DOT11DECRYPT_MAC_LEN];
+ uint8_t sa[DOT11DECRYPT_MAC_LEN];
+ uint8_t da[DOT11DECRYPT_MAC_LEN];
+} DOT11DECRYPT_ASSOC_PARSED, *PDOT11DECRYPT_ASSOC_PARSED;
+
+/************************************************************************/
+/* Function prototype declarations */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * This will try to decrypt a 802.11 frame.
+ * @param ctx [IN] Pointer to the current context
+ * @param data [IN] Pointer to a buffer with an 802.11 frame, including MAC
+ * header and payload
+ * @param data_off [IN] Payload offset (aka the MAC header length)
+ * @param data_len [IN] Total length of the MAC header and the payload
+ * @param decrypt_data [OUT] Pointer to a buffer that will contain
+ * decrypted data. Must have room for at least DOT11DECRYPT_MAX_CAPLEN bytes.
+ * @param decrypt_len [OUT] Length of decrypted data.
+ * @param key [OUT] Pointer to a preallocated key structure containing
+ * the key used during the decryption process (if done). If this parameter
+ * is set to NULL, the key will be not returned.
+ * @return
+ * - DOT11DECRYPT_RET_SUCCESS: Decryption has been done (decrypt_data and
+ * decrypt_length will contain the packet data decrypted and the length of
+ * the new packet)
+ * - DOT11DECRYPT_RET_NO_DATA: The packet is not a data packet
+ * - DOT11DECRYPT_RET_WRONG_DATA_SIZE: The size of the packet is below the
+ * accepted minimum
+ * - DOT11DECRYPT_RET_REQ_DATA: Required data is not available and the
+ * processing must be interrupted
+ * - DOT11DECRYPT_RET_NO_DATA_ENCRYPTED: Not encrypted
+ * - DOT11DECRYPT_RET_UNSUCCESS: Generic unspecified error (decrypt_data
+ * and decrypt_length will be not modified).
+ * @note
+ * The decrypted buffer should be allocated for a size equal or greater
+ * than the packet data buffer size. Before decryption process original
+ * data is copied in the buffer pointed by decrypt_data not to modify the
+ * original packet.
+ * @note
+ * The length of decrypted data will consider the entire 802.11 frame
+ * (thus the MAC header, the frame body and the recalculated FCS -if
+ * initially present-)
+ * @note
+ * This function is not thread-safe when used in parallel with context
+ * management functions on the same context.
+ */
+
+extern int Dot11DecryptDecryptPacket(
+ PDOT11DECRYPT_CONTEXT ctx,
+ const uint8_t *data,
+ const unsigned data_off,
+ const unsigned data_len,
+ unsigned char *decrypt_data,
+ uint32_t *decrypt_len,
+ PDOT11DECRYPT_KEY_ITEM key)
+ ;
+
+/**
+ * This will try to decrypt the encrypted keydata field of an EAPOL KEY frame.
+ * @param ctx [IN] Pointer to the current context
+ * @param eapol_parsed [IN] Extracted/Parsed pieces of eapol frame
+ * @param bssid [IN] bssid of AP
+ * @param sta [IN] sta MAC address
+ * @param decrypted_data [OUT] Pointer to a buffer that will contain
+ * decrypted data. Must have room for at least DOT11DECRYPT_EAPOL_MAX_LEN bytes.
+ * @param decrypted_len [OUT] Length of decrypted data.
+ * @param key [OUT] Pointer to a preallocated key structure containing
+ * the key used during the decryption process (if done). If this parameter
+ * is set to NULL, the key will be not returned.
+ * @return
+ * - DOT11DECRYPT_RET_SUCCESS: Decryption has been done (decrypt_data and
+ * decrypt_length will contain the packet data decrypted and the length of
+ * the new packet)
+ * - DOT11DECRYPT_RET_UNSUCCESS: Generic unspecified error (decrypt_data
+ * and decrypt_length will be not modified).
+ */
+extern int
+Dot11DecryptDecryptKeyData(PDOT11DECRYPT_CONTEXT ctx,
+ PDOT11DECRYPT_EAPOL_PARSED eapol_parsed,
+ const unsigned char bssid[DOT11DECRYPT_MAC_LEN],
+ const unsigned char sta[DOT11DECRYPT_MAC_LEN],
+ unsigned char *decrypted_data, unsigned *decrypted_len,
+ PDOT11DECRYPT_KEY_ITEM key)
+ ;
+
+/**
+ * This will try to extract keys from an EAPOL frame and add corresponding
+ * SAs to current context. eapol_parsed must contain the already parsed EAPOL
+ * key frame and for frames that contain encrypted EAPOL keydata the keydata
+ * must first be decrypted with Dot11DecryptDecryptKeyData before calling this
+ * function.
+ * @param ctx [IN] Pointer to the current context
+ * @param eapol_parsed [IN] Extracted/Parsed pieces of eapol frame
+ * @param eapol_raw [IN] Pointer to a buffer with an EAPOL frame
+ * @param tot_len [IN] Total length of the EAPOL frame
+ * @param bssid [IN] bssid of AP
+ * @param sta [IN] sta MAC address
+ * @return
+ * - DOT11DECRYPT_RET_REQ_DATA: Required data is not available and the
+ * processing must be interrupted
+ * - DOT11DECRYPT_RET_UNSUCCESS: Generic unspecified error (decrypt_data
+ * and decrypt_length will be not modified).
+ * - DOT11DECRYPT_RET_SUCCESS_HANDSHAKE: An eapol handshake packet was successfuly parsed
+ * and key information extracted.
+ * - DOT11DECRYPT_RET_NO_VALID_HANDSHAKE: The handshake is invalid or was not used
+ * for some reason. For encrypted packets decryption was still successful.
+ * @note
+ * This function is not thread-safe when used in parallel with context
+ * management functions on the same context.
+ */
+extern int Dot11DecryptScanEapolForKeys(
+ PDOT11DECRYPT_CONTEXT ctx,
+ PDOT11DECRYPT_EAPOL_PARSED eapol_parsed,
+ const uint8_t *eapol_raw,
+ const unsigned tot_len,
+ const unsigned char bssid[DOT11DECRYPT_MAC_LEN],
+ const unsigned char sta[DOT11DECRYPT_MAC_LEN])
+ ;
+
+/**
+ * This will try to extract keys from an FT (re)association frame and add
+ * corresponding SAs to current context. assoc_parsed must contain the already
+ * parsed association frame content. If the FT BSS Transition IE contains an
+ * encrypted GTK subelem and decryption is successful the decrypted GTK will
+ * be returned in decrypted_gtk.
+ * @param ctx [IN] Pointer to the current context
+ * @param assoc_parsed [IN] Extracted/Parsed pieces of association frame
+ * @param decrypted_gtk [OUT] Buffer for decrypted GTK subelem
+ * @param decrypted_len [OUT] Decrypted GTK subelem key length
+ * @param used_key [OUT] Buffer to hold the key used during the decryption process.
+ * @return
+ * - DOT11DECRYPT_RET_UNSUCCESS: Generic unspecified error (decrypted_gtk
+ * and decrypted_len will be not modified).
+ * - DOT11DECRYPT_RET_SUCCESS_HANDSHAKE: An association frame was successfuly parsed
+ * and key information extracted.
+ * - DOT11DECRYPT_RET_NO_VALID_HANDSHAKE: The association is invalid or no matching
+ * key for decryption was found.
+ */
+int
+Dot11DecryptScanFtAssocForKeys(
+ const PDOT11DECRYPT_CONTEXT ctx,
+ const PDOT11DECRYPT_ASSOC_PARSED assoc_parsed,
+ uint8_t *decrypted_gtk, size_t *decrypted_len,
+ DOT11DECRYPT_KEY_ITEM* used_key);
+
+/**
+ * This will try to extract keys from a TDLS action frame (without MAC headers)
+ * and add corresponding SAs to current context.
+ * @param ctx [IN] Pointer to the current context
+ * @param data [IN] Pointer to a buffer with a TDLS action frame
+ * @param tot_len [IN] Total length of the TDLS action frame
+ * @return
+ * - DOT11DECRYPT_RET_REQ_DATA: Required data is not available and the
+ * processing must be interrupted
+ * - DOT11DECRYPT_RET_SUCCESS_HANDSHAKE: The TDLS action frame was successfuly parsed
+ * and key information extracted.
+ * - DOT11DECRYPT_RET_NO_VALID_HANDSHAKE: No keys extracted
+ */
+extern int Dot11DecryptScanTdlsForKeys(
+ PDOT11DECRYPT_CONTEXT ctx,
+ const uint8_t *data,
+ const unsigned tot_len)
+ ;
+
+/**
+ * These are helper functions to retrieve KCK, KEK, TK portion of PTK
+ * for a certain "key"
+ * @param key [IN] Pointer to a key structure containing the key retrieved
+ * from functions Dot11DecryptDecryptPacket, Dot11DecryptKeydata
+ * @param kck [OUT] Pointer to the KCK/KEK/TK portion of PTK.
+ * @return length in bytes of KCK/KEK/TK
+ */
+int
+Dot11DecryptGetKCK(const PDOT11DECRYPT_KEY_ITEM key, const uint8_t **kck);
+
+int
+Dot11DecryptGetKEK(const PDOT11DECRYPT_KEY_ITEM key, const uint8_t **kek);
+
+int
+Dot11DecryptGetTK(const PDOT11DECRYPT_KEY_ITEM key, const uint8_t **tk);
+
+int
+Dot11DecryptGetGTK(const PDOT11DECRYPT_KEY_ITEM key, const uint8_t **gtk);
+
+/**
+ * It sets a new keys collection to use during packet processing.
+ * Any key should be well-formed, thus: it should have a defined key
+ * type and the specified length should be conforming WEP or WPA/WPA2
+ * standards. A general WEP keys could be of any length (in the range
+ * defined in DOT11DECRYPT_KEY_ITEM), if a specific WEP key is used, the
+ * length of the key will be the one specified in 802.11i-2004 (40 bits or
+ * 104 bits).
+ * For WPA/WPA2 the password (passphrase and SSID), the PSK and the PMK
+ * are in alternative, as explain in the DOT11DECRYPT_KEY_ITEM structure
+ * description.
+ * @param ctx [IN] pointer to the current context
+ * @param keys [IN] an array of keys to set.
+ * @param keys_nr [IN] the size of the keys array
+ * @return The number of keys correctly inserted in the current database.
+ * @note Before inserting new keys, the current database will be cleaned.
+ * @note
+ * This function is not thread-safe when used in parallel with context
+ * management functions and the packet process function on the same
+ * context.
+ */
+extern int Dot11DecryptSetKeys(
+ PDOT11DECRYPT_CONTEXT ctx,
+ DOT11DECRYPT_KEY_ITEM keys[],
+ const size_t keys_nr)
+ ;
+
+/**
+ * Sets the "last seen" SSID. This allows us to pick up previous
+ * SSIDs and use them when "wildcard" passphrases are specified
+ * in the preferences.
+ * @param ctx [IN|OUT] pointer to a preallocated context structure
+ * @param pkt_ssid [IN] pointer to the packet's SSID
+ * @param pkt_ssid_len [IN] length of the packet's SSID
+ * @return
+ * DOT11DECRYPT_RET_SUCCESS: The key has been set.
+ * DOT11DECRYPT_RET_UNSUCCESS: The has not been set, e.g. the length was
+ * too long.
+ */
+int Dot11DecryptSetLastSSID(
+ PDOT11DECRYPT_CONTEXT ctx,
+ char *pkt_ssid,
+ size_t pkt_ssid_len)
+ ;
+
+/**
+ * Initialize a context used to manage decryption and keys collection.
+ * @param ctx [IN|OUT] pointer to a preallocated context structure
+ * @return
+ * DOT11DECRYPT_RET_SUCCESS: the context has been successfully initialized
+ * DOT11DECRYPT_RET_UNSUCCESS: the context has not been initialized
+ * @note
+ * Only a correctly initialized context can be used to manage decryption
+ * processes and keys.
+ * @note
+ * This function is not thread-safe when used in parallel with context
+ * management functions and the packet process function on the same context.
+ */
+WS_DLL_PUBLIC
+int Dot11DecryptInitContext(
+ PDOT11DECRYPT_CONTEXT ctx)
+ ;
+
+/**
+ * Clean up the specified context. After the cleanup the pointer should
+ * not be used anymore.
+ * @param ctx [IN|OUT] pointer to the current context structure
+ * @return
+ * DOT11DECRYPT_RET_SUCCESS: the context has been successfully initialized
+ * DOT11DECRYPT_RET_UNSUCCESS: the context has not been initialized
+ * @note
+ * This function is not thread-safe when used in parallel with context
+ * management functions and the packet process function on the same
+ * context.
+ */
+WS_DLL_PUBLIC
+int Dot11DecryptDestroyContext(
+ PDOT11DECRYPT_CONTEXT ctx)
+ ;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DOT11DECRYPT_SYSTEM_H */
diff --git a/epan/crypt/dot11decrypt_tkip.c b/epan/crypt/dot11decrypt_tkip.c
new file mode 100644
index 00000000..6fd1bc6b
--- /dev/null
+++ b/epan/crypt/dot11decrypt_tkip.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2006 CACE Technologies, Davis (California)
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
+ */
+
+/******************************************************************************/
+/* File includes */
+/* */
+#include <wsutil/pint.h>
+#include <wsutil/crc32.h>
+#include "dot11decrypt_system.h"
+#include "dot11decrypt_int.h"
+
+#include "dot11decrypt_debug.h"
+/* */
+/******************************************************************************/
+
+/******************************************************************************/
+/* Internal definitions */
+/* */
+#define PHASE1_LOOP_COUNT 8
+
+#define DOT11DECRYPT_TTAK_LEN 6
+/* */
+/******************************************************************************/
+
+/******************************************************************************/
+/* Internal function prototypes declarations */
+/* */
+static void Dot11DecryptTkipMixingPhase1(
+ uint16_t *TTAK,
+ const uint8_t *TK,
+ const uint8_t *TA,
+ uint32_t TSC)
+ ;
+
+static void Dot11DecryptTkipMixingPhase2(
+ uint8_t *wep_seed,
+ const uint8_t *TK,
+ uint16_t *PPK,
+ uint16_t TSC16)
+ ;
+
+/* */
+/******************************************************************************/
+
+/******************************************************************************/
+/* Global variables */
+/* */
+/* Note: copied from FreeBSD source code, RELENG 6, */
+/* sys/net80211/ieee80211_crypto_tkip.c, 471 */
+static const uint16_t Sbox[256] = {
+ 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
+ 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A,
+ 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B,
+ 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B,
+ 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F,
+ 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F,
+ 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5,
+ 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F,
+ 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB,
+ 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397,
+ 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED,
+ 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A,
+ 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194,
+ 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3,
+ 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104,
+ 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D,
+ 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39,
+ 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695,
+ 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83,
+ 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76,
+ 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4,
+ 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B,
+ 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0,
+ 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018,
+ 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751,
+ 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85,
+ 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12,
+ 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9,
+ 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7,
+ 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A,
+ 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8,
+ 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A,
+};
+/* */
+/******************************************************************************/
+
+/* TODO: check for little-endian, big-endian */
+
+/* */
+/* Note: any functions were copied from FreeBSD source code, RELENG 6, */
+/* sys/net80211/ieee80211_crypto_tkip.c */
+/* Converted to macros to avoid using __inline, as not all compilers support it */
+#define RotR1(val) ((uint16_t)(((val) >> 1) | ((val) << 15)))
+
+#define Lo8(val) ((uint8_t)((val) & 0xff))
+
+#define Hi8(val) ((uint8_t)((val) >> 8))
+
+#define Lo16(val) ((uint16_t)((val) & 0xffff))
+
+#define Hi16(val) ((uint16_t)((val) >> 16))
+
+#define Mk16(hi, lo) \
+ ((uint16_t)((lo) | (((uint16_t) (hi)) << 8)))
+
+#define Mk16_le(v) ((uint16_t)pletoh16(v))
+
+#define _S_(v) \
+ ((uint16_t)(Sbox[Lo8(v)] ^ ((Sbox[Hi8(v)] << 8) | (Sbox[Hi8(v)] >> 8))))
+
+#define READ_6(b0, b1, b2, b3, b4, b5) \
+ ((((uint64_t)((uint16_t)((b4 << 0) | (b5 << 8)))) << 32) | \
+ ((uint32_t)((b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24))))
+
+/******************************************************************************/
+/* Function definitions */
+
+static void Dot11DecryptTkipMixingPhase1(
+ uint16_t *TTAK,
+ const uint8_t *TK,
+ const uint8_t *TA,
+ uint32_t TSC)
+{
+ uint16_t i, j;
+
+ /* Initialize the 80-bit TTAK from TSC (TSC) and TA[0..5] */
+ TTAK[0] = Lo16(TSC);
+ TTAK[1] = Hi16(TSC);
+ TTAK[2] = Mk16(TA[1], TA[0]);
+ TTAK[3] = Mk16(TA[3], TA[2]);
+ TTAK[4] = Mk16(TA[5], TA[4]);
+
+ for (i = 0; i < PHASE1_LOOP_COUNT; i++) {
+ j = (uint16_t)(2 * (i & 1));
+ TTAK[0] = (uint16_t)(TTAK[0] + _S_((uint16_t)(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j]))));
+ TTAK[1] = (uint16_t)(TTAK[1] + _S_((uint16_t)(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j]))));
+ TTAK[2] = (uint16_t)(TTAK[2] + _S_((uint16_t)(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j]))));
+ TTAK[3] = (uint16_t)(TTAK[3] + _S_((uint16_t)(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j]))));
+ TTAK[4] = (uint16_t)(TTAK[4] + _S_((uint16_t)(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j]))) + i);
+ }
+}
+
+static void Dot11DecryptTkipMixingPhase2(
+ uint8_t *wep_seed,
+ const uint8_t *TK,
+ uint16_t *TTAK,
+ uint16_t TSC16)
+{
+ int i;
+ TTAK[5] = (uint16_t)(TTAK[4] + TSC16);
+
+ /* Step 2 - 96-bit bijective mixing using S-box */
+ TTAK[0] = (uint16_t)(TTAK[0] + _S_((uint16_t)(TTAK[5] ^ Mk16_le(&TK[0]))));
+ TTAK[1] = (uint16_t)(TTAK[1] + _S_((uint16_t)(TTAK[0] ^ Mk16_le(&TK[2]))));
+ TTAK[2] = (uint16_t)(TTAK[2] + _S_((uint16_t)(TTAK[1] ^ Mk16_le(&TK[4]))));
+ TTAK[3] = (uint16_t)(TTAK[3] + _S_((uint16_t)(TTAK[2] ^ Mk16_le(&TK[6]))));
+ TTAK[4] = (uint16_t)(TTAK[4] + _S_((uint16_t)(TTAK[3] ^ Mk16_le(&TK[8]))));
+ TTAK[5] = (uint16_t)(TTAK[5] + _S_((uint16_t)(TTAK[4] ^ Mk16_le(&TK[10]))));
+
+ TTAK[0] = (uint16_t)(TTAK[0] + RotR1((uint16_t)(TTAK[5] ^ Mk16_le(&TK[12]))));
+ TTAK[1] = (uint16_t)(TTAK[1] + RotR1((uint16_t)(TTAK[0] ^ Mk16_le(&TK[14]))));
+ TTAK[2] = (uint16_t)(TTAK[2] + RotR1(TTAK[1]));
+ TTAK[3] = (uint16_t)(TTAK[3] + RotR1(TTAK[2]));
+ TTAK[4] = (uint16_t)(TTAK[4] + RotR1(TTAK[3]));
+ TTAK[5] = (uint16_t)(TTAK[5] + RotR1(TTAK[4]));
+
+ /* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value
+ * wep_seed[0..2] is transmitted as WEP IV */
+ wep_seed[0] = Hi8(TSC16);
+ wep_seed[1] = (uint8_t)((Hi8(TSC16) | 0x20) & 0x7F);
+ wep_seed[2] = Lo8(TSC16);
+ wep_seed[3] = Lo8((uint16_t)((TTAK[5] ^ Mk16_le(&TK[0])) >> 1));
+
+ for (i = 0; i < 6; i++)
+ {
+ wep_seed[4 + ( 2 * i)] = Lo8( TTAK[i] );
+ wep_seed[5 + ( 2 * i)] = Hi8( TTAK[i] );
+ }
+}
+
+/* Note: taken from FreeBSD source code, RELENG 6, */
+/* sys/net80211/ieee80211_crypto_tkip.c, 936 */
+int Dot11DecryptTkipDecrypt(
+ unsigned char *tkip_mpdu,
+ size_t mpdu_len,
+ unsigned char TA[DOT11DECRYPT_MAC_LEN],
+ unsigned char TK[DOT11DECRYPT_TK_LEN])
+{
+ uint64_t TSC64;
+ uint32_t TSC;
+ uint16_t TSC16;
+ uint8_t *IV;
+ uint16_t TTAK[DOT11DECRYPT_TTAK_LEN];
+ uint8_t wep_seed[DOT11DECRYPT_WEP_128_KEY_LEN];
+
+ IV = tkip_mpdu;
+
+ TSC64 = READ_6(IV[2], IV[0], IV[4], IV[5], IV[6], IV[7]);
+ TSC16 = (uint16_t)TSC64;
+
+ /* The original code made no sense!! We were shifting a 16-bit number 16 bits to the right. */
+ /* We instead have to have READ_6() be returned to a uint64_t and shift *that* value. */
+ TSC = (uint32_t)(TSC64 >> 16);
+
+ Dot11DecryptTkipMixingPhase1(TTAK, TK, TA, TSC);
+
+ Dot11DecryptTkipMixingPhase2(wep_seed, TK, TTAK, TSC16);
+
+ return Dot11DecryptWepDecrypt(
+ wep_seed,
+ DOT11DECRYPT_WEP_128_KEY_LEN,
+ tkip_mpdu + DOT11DECRYPT_TKIP_HEADER,
+ mpdu_len-(DOT11DECRYPT_TKIP_HEADER+DOT11DECRYPT_WEP_ICV)); /* MPDU - TKIP_HEADER - MIC */
+
+ /* TODO check (IEEE 802.11i-2004, pg. 44) */
+
+}
+/* */
+/******************************************************************************/
diff --git a/epan/crypt/dot11decrypt_user.h b/epan/crypt/dot11decrypt_user.h
new file mode 100644
index 00000000..defded6c
--- /dev/null
+++ b/epan/crypt/dot11decrypt_user.h
@@ -0,0 +1,232 @@
+/** @file
+ *
+ * Copyright (c) 2006 CACE Technologies, Davis (California)
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
+ */
+
+#ifndef _DOT11DECRYPT_USER_H
+#define _DOT11DECRYPT_USER_H
+
+/******************************************************************************/
+/* File includes */
+/* */
+#include <glib.h>
+
+#include "ws_symbol_export.h"
+
+/* */
+/* */
+/******************************************************************************/
+
+/******************************************************************************/
+/* Constant definitions */
+/* */
+/* Decryption key types */
+#define DOT11DECRYPT_KEY_TYPE_WEP 0
+#define DOT11DECRYPT_KEY_TYPE_WEP_40 1
+#define DOT11DECRYPT_KEY_TYPE_WEP_104 2
+#define DOT11DECRYPT_KEY_TYPE_WPA_PWD 3
+#define DOT11DECRYPT_KEY_TYPE_WPA_PSK 4
+#define DOT11DECRYPT_KEY_TYPE_WPA_PMK 5
+#define DOT11DECRYPT_KEY_TYPE_TK 6
+#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) */
+#define DOT11DECRYPT_WEP_KEY_MINLEN 1
+#define DOT11DECRYPT_WEP_KEY_MAXLEN 32
+#define DOT11DECRYPT_WEP_40_KEY_LEN 5
+#define DOT11DECRYPT_WEP_104_KEY_LEN 13
+
+#define DOT11DECRYPT_WPA_PASSPHRASE_MIN_LEN 8
+#define DOT11DECRYPT_WPA_PASSPHRASE_MAX_LEN 63 /* null-terminated string, the actual length of the storage is 64 */
+#define DOT11DECRYPT_WPA_SSID_MIN_LEN 0
+#define DOT11DECRYPT_WPA_SSID_MAX_LEN 32
+#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
+/* */
+/* */
+/******************************************************************************/
+
+/******************************************************************************/
+/* Macro definitions */
+/* */
+/* */
+/******************************************************************************/
+
+/******************************************************************************/
+/* Type definitions */
+/* */
+/**
+ * Struct to store info about a specific decryption key.
+ */
+typedef struct {
+ GString *key;
+ GByteArray *ssid;
+ unsigned bits;
+ unsigned type;
+} decryption_key_t;
+
+/**
+ * Key item used during the decryption process.
+ */
+typedef struct _DOT11DECRYPT_KEY_ITEM {
+ /**
+ * Type of key. The type will remain unchanged during the
+ * processing, even if some fields could be changed (e.g., WPA
+ * fields).
+ * @note
+ * You can use constants DOT11DECRYPT_KEY_TYPE_xxx to indicate the
+ * key type.
+ */
+ uint8_t KeyType;
+
+ /**
+ * Key data.
+ * This field can be used for the following decryptographic
+ * algorithms: WEP-40, with a key of 40 bits (10 hex-digits);
+ * WEP-104, with a key of 104 bits (or 26 hex-digits); WPA or
+ * WPA2.
+ * @note
+ * For WPA/WPA2, the PMK is calculated from the PSK, and the PSK
+ * is calculated from the passphrase-SSID pair. You can enter one
+ * of these 3 values and subsequent fields will be automatically
+ * calculated.
+ * @note
+ * For WPA and WPA2 this implementation will use standards as
+ * defined in 802.11i (2004) and 802.1X (2004).
+ */
+ union DOT11DECRYPT_KEY_ITEMDATA {
+ struct DOT11DECRYPT_KEY_ITEMDATA_WEP {
+ /**
+ * The binary value of the WEP key.
+ * @note
+ * It is accepted a key of length between
+ * DOT11DECRYPT_WEP_KEY_MINLEN and
+ * DOT11DECRYPT_WEP_KEY_MAXLEN. A WEP key
+ * standard-compliante should be either 40 bits
+ * (10 hex-digits, 5 bytes) for WEP-40 or 104 bits
+ * (26 hex-digits, 13 bytes) for WEP-104.
+ */
+ unsigned char WepKey[DOT11DECRYPT_WEP_KEY_MAXLEN];
+ /**
+ * The length of the WEP key. Acceptable range
+ * is [DOT11DECRYPT_WEP_KEY_MINLEN;DOT11DECRYPT_WEP_KEY_MAXLEN].
+ */
+ size_t WepKeyLen;
+ } Wep;
+
+ /**
+ * WPA/WPA2 key data. Note that the decryption process
+ * will use the PMK (equal to PSK), that is calculated
+ * from passphrase-SSID pair. You can define one of these
+ * three fields and necessary fields will be automatically
+ * calculated.
+ */
+ struct DOT11DECRYPT_KEY_ITEMDATA_WPA {
+ unsigned char Psk[DOT11DECRYPT_WPA_PMK_MAX_LEN];
+ unsigned char Ptk[DOT11DECRYPT_WPA_PTK_MAX_LEN];
+ uint8_t PskLen;
+ uint8_t PtkLen;
+ uint8_t Akm;
+ uint8_t Cipher;
+ } Wpa;
+
+ } KeyData;
+
+ struct DOT11DECRYPT_KEY_ITEMDATA_TK {
+ uint8_t Tk[DOT11DECRYPT_TK_MAX_LEN];
+ uint8_t Len;
+ } Tk;
+
+ struct DOT11DECRYPT_KEY_ITEMDATA_MSK {
+ uint8_t Msk[DOT11DECRYPT_MSK_MAX_LEN];
+ uint8_t Len;
+ } Msk;
+
+ struct DOT11DECRYPT_KEY_ITEMDATA_PWD {
+ /**
+ * The string (null-terminated) value of
+ * the passphrase.
+ */
+ char Passphrase[DOT11DECRYPT_WPA_PASSPHRASE_MAX_LEN+1];
+ /**
+ * The value of the SSID (up to
+ * DOT11DECRYPT_WPA_SSID_MAX_LEN octets).
+ * @note
+ * A zero-length SSID indicates broadcast.
+ */
+ char Ssid[DOT11DECRYPT_WPA_SSID_MAX_LEN];
+ /**
+ *The length of the SSID
+ */
+ size_t SsidLen;
+ } UserPwd;
+} DOT11DECRYPT_KEY_ITEM, *PDOT11DECRYPT_KEY_ITEM;
+
+/**
+ * Collection of keys to use to decrypt packets
+ */
+typedef struct _DOT11DECRYPT_KEYS_COLLECTION {
+ /**
+ * Number of stored keys
+ */
+ size_t nKeys;
+
+ /**
+ * Array of nKeys keys
+ */
+ DOT11DECRYPT_KEY_ITEM Keys[256];
+} DOT11DECRYPT_KEYS_COLLECTION, *PDOT11DECRYPT_KEYS_COLLECTION;
+/* */
+/******************************************************************************/
+
+/******************************************************************************/
+/* Function prototype declarations */
+
+/**
+ * Returns the decryption_key_t struct given a string describing the key.
+ * @param key_string [IN] Key string in one of the following formats:
+ * - 0102030405 (40/64-bit WEP)
+ * - 01:02:03:04:05 (40/64-bit WEP)
+ * - 0102030405060708090a0b0c0d (104/128-bit WEP)
+ * - 01:02:03:04:05:06:07:08:09:0a:0b:0c:0d (104/128-bit WEP)
+ * - MyPassword (WPA + plaintext password + "wildcard" SSID)
+ * - MyPassword:MySSID (WPA + plaintext password + specific SSID)
+ * - 01020304... (WPA + 256-bit raw key)
+ * @param key_type [IN] Type of key used for string. Possibilities include:
+ * - DOT11DECRYPT_KEY_TYPE_WEP (40/64-bit and 104/128-bit WEP)
+ * - DOT11DECRYPT_KEY_TYPE_WPA_PWD (WPA + plaintext password + "wildcard" SSID or
+ * WPA + plaintext password + specific SSID)
+ * - DOT11DECRYPT_KEY_TYPE_WPA_PSK (WPA + 256-bit raw key)
+ * @return A pointer to a freshly-g_malloc()ed decryption_key_t struct on
+ * success, or NULL on failure.
+ * @see free_key_string()
+ */
+WS_DLL_PUBLIC
+decryption_key_t*
+parse_key_string(char* key_string, uint8_t key_type);
+
+/**
+ * Releases memory associated with a given decryption_key_t struct.
+ * @param dk [IN] Pointer to the key to be freed
+ * @see parse_key_string()
+ */
+WS_DLL_PUBLIC
+void
+free_key_string(decryption_key_t *dk);
+
+/******************************************************************************/
+
+#endif /* _DOT11DECRYPT_USER_H */
diff --git a/epan/crypt/dot11decrypt_util.c b/epan/crypt/dot11decrypt_util.c
new file mode 100644
index 00000000..9de2ba49
--- /dev/null
+++ b/epan/crypt/dot11decrypt_util.c
@@ -0,0 +1,412 @@
+/* dot11decrypt_util.c
+ *
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2006 CACE Technologies, Davis (California)
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
+ */
+
+/****************************************************************************/
+/* File includes */
+#include "config.h"
+#include "dot11decrypt_int.h"
+
+#include "dot11decrypt_debug.h"
+#include "dot11decrypt_util.h"
+
+/****************************************************************************/
+/* Internal definitions */
+
+#define FC0_AAD_MASK 0x8f
+#define FC1_AAD_MASK 0xc7
+#define FC1_AAD_QOS_MASK 0x47
+
+/****************************************************************************/
+/* Internal macros */
+
+/****************************************************************************/
+/* Internal function prototypes declarations */
+
+/****************************************************************************/
+/* Function definitions */
+
+/* From IEEE 802.11 2016 Chapter 12.5.3.3.3 and 12.5.5.3.3 Construct AAD */
+void dot11decrypt_construct_aad(
+ PDOT11DECRYPT_MAC_FRAME wh,
+ uint8_t *aad,
+ size_t *aad_len)
+{
+ uint8_t mgmt = (DOT11DECRYPT_TYPE(wh->fc[0]) == DOT11DECRYPT_TYPE_MANAGEMENT);
+ int alen = 22;
+
+ /* AAD:
+ * FC with bits 4..6 and 11..13 masked to zero; 14 is always one; 15 zero when QoS Control field present
+ * A1 | A2 | A3
+ * SC with bits 4..15 (seq#) masked to zero
+ * A4 (if present)
+ * QC (if present)
+ */
+
+ /* NB: aad[1] set below */
+ if (!mgmt) {
+ aad[0] = (uint8_t)(wh->fc[0] & FC0_AAD_MASK);
+ } else {
+ aad[0] = wh->fc[0];
+ }
+ if (DOT11DECRYPT_IS_QOS_DATA(wh)) {
+ aad[1] = (uint8_t)((wh->fc[1] & FC1_AAD_QOS_MASK) | 0x40);
+ } else {
+ aad[1] = (uint8_t)((wh->fc[1] & FC1_AAD_MASK) | 0x40);
+ }
+ memcpy(aad + 2, (uint8_t *)wh->addr1, DOT11DECRYPT_MAC_LEN);
+ memcpy(aad + 8, (uint8_t *)wh->addr2, DOT11DECRYPT_MAC_LEN);
+ memcpy(aad + 14, (uint8_t *)wh->addr3, DOT11DECRYPT_MAC_LEN);
+ aad[20] = (uint8_t)(wh->seq[0] & DOT11DECRYPT_SEQ_FRAG_MASK);
+ aad[21] = 0; /* all bits masked */
+
+ /*
+ * Construct variable-length portion of AAD based
+ * on whether this is a 4-address frame/QOS frame.
+ */
+ if (DOT11DECRYPT_IS_4ADDRESS(wh)) {
+ alen += 6;
+ DOT11DECRYPT_ADDR_COPY(aad + 22,
+ ((PDOT11DECRYPT_MAC_FRAME_ADDR4)wh)->addr4);
+ if (DOT11DECRYPT_IS_QOS_DATA(wh)) {
+ PDOT11DECRYPT_MAC_FRAME_ADDR4_QOS qwh4 =
+ (PDOT11DECRYPT_MAC_FRAME_ADDR4_QOS) wh;
+ aad[28] = (uint8_t)(qwh4->qos[0] & 0x0f);/* just priority bits */
+ aad[29] = 0;
+ alen += 2;
+ }
+ } else {
+ if (DOT11DECRYPT_IS_QOS_DATA(wh)) {
+ PDOT11DECRYPT_MAC_FRAME_QOS qwh =
+ (PDOT11DECRYPT_MAC_FRAME_QOS) wh;
+ aad[22] = (uint8_t)(qwh->qos[0] & 0x0f); /* just priority bits */
+ aad[23] = 0;
+ alen += 2;
+ }
+ }
+ *aad_len = alen;
+}
+
+/**
+ * IEEE 802.11-2016 12.7.1.2 PRF (Pseudo Random Function)
+ *
+ * @param key Derivation input key.
+ * @param key_len Length of the key in bytes.
+ * @param label Unique label for each different purpose of the PRF (named 'A' in the standard).
+ * @param context Provides context to identify the derived key (named 'B' in the standard).
+ * @param context_len Length of context in bytes.
+ * @param hash_algo Hash algorithm to use for the PRF.
+ * See gcrypt available hash algorithms:
+ * https://gnupg.org/documentation/manuals/gcrypt/Available-hash-algorithms.html
+ * @param[out] output Derived key.
+ * @param output_len Length of derived key in bytes.
+ * @return false on error
+ */
+#define MAX_R_LEN 256
+#define MAX_TMP_LEN 1024
+#define MAX_CONTEXT_LEN 256
+
+bool
+dot11decrypt_prf(const uint8_t *key, size_t key_len,
+ const char *label,
+ const uint8_t *context, size_t context_len,
+ int hash_algo,
+ uint8_t *output, size_t output_len)
+{
+ uint8_t R[MAX_R_LEN]; /* Will hold "label || 0 || context || i" */
+ size_t label_len = strlen(label);
+ uint8_t tmp[MAX_TMP_LEN];
+ uint16_t hash_len = gcry_md_get_algo_dlen(hash_algo);
+ size_t offset = 0;
+ uint8_t i;
+
+ if (!key || !label || !context || !output) {
+ return false;
+ }
+ if (label_len + 1 + context_len + 1 > MAX_R_LEN ||
+ output_len > 64) {
+ ws_warning("Invalid input or output sizes");
+ return false;
+ }
+
+ /* Fill R with "label || 0 || context || i" */
+ memcpy(R + offset, label, label_len);
+ offset += label_len;
+ R[offset++] = 0;
+ memcpy(R + offset, context, context_len);
+ offset += context_len;
+
+ for (i = 0; i <= output_len * 8 / 160; i++)
+ {
+ R[offset] = i;
+ if (ws_hmac_buffer(hash_algo, tmp + hash_len * i, R, offset + 1, key, key_len)) {
+ return false;
+ }
+ }
+ memcpy(output, tmp, output_len);
+ return true;
+}
+
+/**
+ * 12.7.1.7.2 Key derivation function (KDF)
+ *
+ * @param key Derivation input key.
+ * @param key_len Length of the key in bytes.
+ * @param label A string identifying the purpose of the keys derived using this KDF.
+ * @param context Provides context to identify the derived key.
+ * @param context_len Length of context in bytes.
+ * @param hash_algo Hash algorithm to use for the KDF.
+ * See gcrypt available hash algorithms:
+ * https://gnupg.org/documentation/manuals/gcrypt/Available-hash-algorithms.html
+ * @param[out] output Derived key.
+ * @param output_len Length of derived key in bytes.
+ * @return false on error
+ */
+bool
+dot11decrypt_kdf(const uint8_t *key, size_t key_len,
+ const char *label,
+ const uint8_t *context, size_t context_len,
+ int hash_algo,
+ uint8_t *output, size_t output_len)
+{
+ uint8_t R[MAX_R_LEN]; /* Will hold "i || Label || Context || Length" */
+ uint8_t tmp[MAX_TMP_LEN];
+ size_t label_len = strlen(label);
+ uint16_t hash_len = gcry_md_get_algo_dlen(hash_algo);
+ unsigned iterations = (unsigned)output_len * 8 / hash_len;
+ uint16_t len_le = GUINT16_TO_LE(output_len * 8);
+ size_t offset = 0;
+ uint16_t i;
+
+ if (!key || !label || !context || !output) {
+ return false;
+ }
+ if (2 + label_len + context_len + 2 > MAX_R_LEN ||
+ iterations * hash_len > MAX_TMP_LEN) {
+ ws_warning("Invalid input sizes");
+ return false;
+ }
+
+ /* Fill tmp with "i || Label || Context || Length" */
+ offset += 2; /* Skip "i" (will be copied in for loop below) */
+ memcpy(R + offset, label, label_len);
+ offset += label_len;
+ memcpy(R + offset, context, context_len);
+ offset += context_len;
+ memcpy(R + offset, &len_le, 2);
+ offset += 2;
+
+ for (i = 0; i < iterations; i++)
+ {
+ uint16_t count_le = GUINT16_TO_LE(i + 1);
+ memcpy(R, &count_le, 2);
+
+ if (ws_hmac_buffer(hash_algo, tmp + hash_len * i, R, offset, key, key_len)) {
+ return false;
+ }
+ }
+ memcpy(output, tmp, output_len);
+ return true;
+}
+
+static bool sha256(const uint8_t *data, size_t len, uint8_t output[32])
+{
+ gcry_md_hd_t ctx;
+ gcry_error_t result = gcry_md_open(&ctx, GCRY_MD_SHA256, 0);
+ uint8_t *digest;
+
+ if (result) {
+ return false;
+ }
+ gcry_md_write(ctx, data, len);
+ digest = gcry_md_read(ctx, GCRY_MD_SHA256);
+ if (!digest) {
+ return false;
+ }
+ memcpy(output, digest, 32);
+ gcry_md_close(ctx);
+ return true;
+}
+
+/**
+ * Derive PMK-R0 and PMKR0Name. See IEEE 802.11-2016 12.7.1.7.3 PMK-R0
+ *
+ * @param xxkey PSK / MPMK or certain part of MSK.
+ * @param xxkey_len Length of xxkey in bytes.
+ * @param ssid SSID
+ * @param ssid_len Length of SSID in bytes.
+ * @param mdid MDID (Mobility Domain Identifier).
+ * @param r0kh_id PMK-R0 key holder identifier in the Authenticator.
+ * @param r0kh_id_len Length of r0kh_id in bytes.
+ * @param s0kh_id PMK-R0 key holder in the Supplicant (STA mac address)
+ * @param hash_algo Hash algorithm to use for the KDF.
+ * See gcrypt available hash algorithms:
+ * https://gnupg.org/documentation/manuals/gcrypt/Available-hash-algorithms.html
+ * @param[out] pmk_r0 Pairwise master key, first level
+ * @param pmk_r0_len Length of pmk_r0 in bytes.
+ * @param[out] pmk_r0_name Pairwise master key (PMK) R0 name.
+ */
+bool
+dot11decrypt_derive_pmk_r0(const uint8_t *xxkey, size_t xxkey_len,
+ const uint8_t *ssid, size_t ssid_len,
+ const uint8_t mdid[2],
+ const uint8_t *r0kh_id, size_t r0kh_id_len,
+ const uint8_t s0kh_id[DOT11DECRYPT_MAC_LEN],
+ int hash_algo,
+ uint8_t *pmk_r0,
+ size_t *pmk_r0_len,
+ uint8_t pmk_r0_name[16])
+{
+ const char *ft_r0n = "FT-R0N";
+ const size_t ft_r0n_len = strlen(ft_r0n);
+ uint8_t context[MAX_CONTEXT_LEN];
+ uint8_t r0_key_data[DOT11DECRYPT_WPA_PMK_MAX_LEN + 16];
+ uint8_t sha256_res[32];
+ size_t offset = 0;
+ unsigned q = gcry_md_get_algo_dlen(hash_algo);
+ uint16_t mdid_le = GUINT16_TO_LE(*(uint16_t*)mdid);
+
+ if (!xxkey || !ssid || !mdid || !r0kh_id || !s0kh_id ||
+ !pmk_r0 || !pmk_r0_len || !pmk_r0_name)
+ {
+ return false;
+ }
+ if (1 + ssid_len + 2 + 1 + r0kh_id_len + DOT11DECRYPT_MAC_LEN > MAX_CONTEXT_LEN)
+ {
+ ws_warning("Invalid input sizes");
+ return false;
+ }
+
+ // R0-Key-Data =
+ // KDF-Hash-Length(XXKey, "FT-R0",
+ // SSIDlength || SSID || MDID || R0KHlength || R0KH-ID || S0KH-ID)
+ // PMK-R0 = L(R0-Key-Data, 0, Q) * PMK-R0Name-Salt = L(R0-Key-Data, Q, 128)
+ context[offset++] = (uint8_t)ssid_len;
+ memcpy(context + offset, ssid, ssid_len);
+ offset += ssid_len;
+ memcpy(context + offset, &mdid_le, 2);
+ offset += 2;
+ context[offset++] = (uint8_t)r0kh_id_len;
+ memcpy(context + offset, r0kh_id, r0kh_id_len);
+ offset += r0kh_id_len;
+ memcpy(context + offset, s0kh_id, DOT11DECRYPT_MAC_LEN);
+ offset += DOT11DECRYPT_MAC_LEN;
+ dot11decrypt_kdf(xxkey, xxkey_len, "FT-R0", context, offset, hash_algo,
+ r0_key_data, q + 16);
+ memcpy(pmk_r0, r0_key_data, q);
+ *pmk_r0_len = q;
+
+ // PMK-R0Name-Salt = L(R0-Key-Data, Q, 128)
+ // PMKR0Name = Truncate-128(SHA-256("FT-R0N" || PMK-R0Name-Salt))
+ offset = 0;
+ memcpy(context + offset, ft_r0n, ft_r0n_len);
+ offset += ft_r0n_len;
+ memcpy(context + offset, r0_key_data + q, 16);
+ offset += 16;
+ if(!sha256(context, offset, sha256_res))
+ return false;
+ memcpy(pmk_r0_name, sha256_res, 16);
+ return true;
+}
+
+/**
+ * Derive PMK-R1 and PMKR1Name. See IEEE 802.11-2016 12.7.1.7.4 PMK-R1
+ *
+ */
+bool
+dot11decrypt_derive_pmk_r1(const uint8_t *pmk_r0, size_t pmk_r0_len,
+ const uint8_t *pmk_r0_name,
+ const uint8_t *r1kh_id, const uint8_t *s1kh_id,
+ int hash_algo,
+ uint8_t *pmk_r1, size_t *pmk_r1_len,
+ uint8_t *pmk_r1_name)
+{
+ const char *ft_r1n = "FT-R1N";
+ const size_t ft_r1n_len = strlen(ft_r1n);
+ // context len = MAX(R1KH-ID || S1KH-ID, "FT-R1N" || PMKR0Name || R1KH-ID || S1KH-ID)
+ uint8_t context[6 + 16 + 6 + 6];
+ uint8_t sha256_res[32];
+ size_t offset = 0;
+
+ if (!pmk_r0 || !pmk_r0_name || !r1kh_id || !s1kh_id ||
+ !pmk_r1 || !pmk_r1_len || !pmk_r1_name)
+ {
+ return false;
+ }
+ *pmk_r1_len = gcry_md_get_algo_dlen(hash_algo);
+
+ // PMK-R1 = KDF-Hash-Length(PMK-R0, "FT-R1", R1KH-ID || S1KH-ID)
+ memcpy(context + offset, r1kh_id, DOT11DECRYPT_MAC_LEN);
+ offset += DOT11DECRYPT_MAC_LEN;
+ memcpy(context + offset, s1kh_id, DOT11DECRYPT_MAC_LEN);
+ offset += DOT11DECRYPT_MAC_LEN;
+ dot11decrypt_kdf(pmk_r0, pmk_r0_len, "FT-R1", context, offset, hash_algo,
+ pmk_r1, *pmk_r1_len);
+
+ // PMKR1Name = Truncate-128(SHA-256("FT-R1N" || PMKR0Name || R1KH-ID || S1KH-ID))
+ offset = 0;
+ memcpy(context + offset, ft_r1n, ft_r1n_len);
+ offset += ft_r1n_len;
+ memcpy(context + offset, pmk_r0_name, 16);
+ offset += 16;
+ memcpy(context + offset, r1kh_id, DOT11DECRYPT_MAC_LEN);
+ offset += DOT11DECRYPT_MAC_LEN;
+ memcpy(context + offset, s1kh_id, DOT11DECRYPT_MAC_LEN);
+ offset += DOT11DECRYPT_MAC_LEN;
+ if(!sha256(context, offset, sha256_res))
+ return false;
+ memcpy(pmk_r1_name, sha256_res, 16);
+ return true;
+}
+
+/**
+ * Derive PTK for FT AKMS. See IEE 802.11-2016 12.7.1.7.5 PTK
+ *
+ * PTK = KDF-Hash-Length(PMK-R1, "FT-PTK", SNonce || ANonce || BSSID || STA-ADDR)
+ * PTKName = Truncate-128(
+ * SHA-256(PMKR1Name || "FT-PTKN" || SNonce || ANonce || BSSID || STA-ADDR))
+ */
+bool
+dot11decrypt_derive_ft_ptk(const uint8_t *pmk_r1, size_t pmk_r1_len,
+ const uint8_t *pmk_r1_name _U_,
+ const uint8_t *snonce, const uint8_t *anonce,
+ const uint8_t *bssid, const uint8_t *sta_addr,
+ int hash_algo,
+ uint8_t *ptk, const size_t ptk_len, uint8_t *ptk_name _U_)
+{
+ uint8_t context[32 + 32 + 6 + 6];
+ unsigned offset = 0;
+
+ // PTK = KDF-Hash-Length(PMK-R1, "FT-PTK", SNonce || ANonce || BSSID || STA-ADDR)
+ memcpy(context + offset, snonce, 32);
+ offset += 32;
+ memcpy(context + offset, anonce, 32);
+ offset += 32;
+ memcpy(context + offset, bssid, DOT11DECRYPT_MAC_LEN);
+ offset += DOT11DECRYPT_MAC_LEN;
+ memcpy(context + offset, sta_addr, DOT11DECRYPT_MAC_LEN);
+ offset += DOT11DECRYPT_MAC_LEN;
+ dot11decrypt_kdf(pmk_r1, pmk_r1_len, "FT-PTK", context, offset, hash_algo,
+ ptk, ptk_len);
+
+ // TODO derive PTKName
+ return true;
+}
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/epan/crypt/dot11decrypt_util.h b/epan/crypt/dot11decrypt_util.h
new file mode 100644
index 00000000..2c29e105
--- /dev/null
+++ b/epan/crypt/dot11decrypt_util.h
@@ -0,0 +1,73 @@
+/** @file
+ *
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2006 CACE Technologies, Davis (California)
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
+ */
+
+#ifndef _DOT11DECRYPT_UTIL_H
+#define _DOT11DECRYPT_UTIL_H
+
+#include "dot11decrypt_int.h"
+
+void dot11decrypt_construct_aad(
+ PDOT11DECRYPT_MAC_FRAME wh,
+ uint8_t *aad,
+ size_t *aad_len);
+
+bool
+dot11decrypt_prf(const uint8_t *key, size_t key_len,
+ const char *label,
+ const uint8_t *context, size_t context_len,
+ int hash_algo,
+ uint8_t *output, size_t output_len);
+
+bool
+dot11decrypt_kdf(const uint8_t *key, size_t key_len,
+ const char *label,
+ const uint8_t *context, size_t context_len,
+ int hash_algo,
+ uint8_t *output, size_t output_len);
+
+bool
+dot11decrypt_derive_pmk_r0(const uint8_t *xxkey, size_t xxkey_len,
+ const uint8_t *ssid, size_t ssid_len,
+ const uint8_t mdid[2],
+ const uint8_t *r0kh_id, size_t r0kh_id_len,
+ const uint8_t s0kh_id[DOT11DECRYPT_MAC_LEN],
+ int hash_algo,
+ uint8_t *pmk_r0,
+ size_t *pmk_r0_len,
+ uint8_t pmk_r0_name[16]);
+
+bool
+dot11decrypt_derive_pmk_r1(const uint8_t *pmk_r0, size_t pmk_r0_len,
+ const uint8_t *pmk_r0_name,
+ const uint8_t *r1kh_id, const uint8_t *s1kh_id,
+ int hash_algo,
+ uint8_t *pmk_r1, size_t *pmk_r1_len,
+ uint8_t *pmk_r1_name);
+
+bool
+dot11decrypt_derive_ft_ptk(const uint8_t *pmk_r1, size_t pmk_r1_len,
+ const uint8_t *pmk_r1_name,
+ const uint8_t *snonce, const uint8_t *anonce,
+ const uint8_t *bssid, const uint8_t *sta_addr,
+ int hash_algo,
+ uint8_t *ptk, const size_t ptk_len, uint8_t *ptk_name);
+#endif /* _DOT11DECRYPT_UTIL_H */
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/epan/crypt/dot11decrypt_ws.h b/epan/crypt/dot11decrypt_ws.h
new file mode 100644
index 00000000..b22a5db1
--- /dev/null
+++ b/epan/crypt/dot11decrypt_ws.h
@@ -0,0 +1,25 @@
+/** @file
+ *
+ * Copyright (c) 2006 CACE Technologies, Davis (California)
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
+ */
+
+#ifndef _DOT11DECRYPT_WS_H
+#define _DOT11DECRYPT_WS_H
+
+#include "dot11decrypt_system.h"
+#include "ws_symbol_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+WS_DLL_PUBLIC DOT11DECRYPT_CONTEXT dot11decrypt_ctx;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _DOT11DECRYPT_WS_H */
diff --git a/epan/crypt/kasumi.h b/epan/crypt/kasumi.h
new file mode 100644
index 00000000..e3fcfd7c
--- /dev/null
+++ b/epan/crypt/kasumi.h
@@ -0,0 +1,17 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef KASUMI_H
+#define KASUMI_H
+
+/*This needs to be set in order to indicate presence of KASUMI implementation!*/
+/*#define HAVE_UMTS_KASUMI*/
+
+
+#endif /* KASUMI_H */
diff --git a/epan/crypt/wep-wpadefs.h b/epan/crypt/wep-wpadefs.h
new file mode 100644
index 00000000..ae6aa988
--- /dev/null
+++ b/epan/crypt/wep-wpadefs.h
@@ -0,0 +1,83 @@
+/* wap-wpadefs.h
+ *
+ * Copyright (c) 2006 CACE Technologies, Davis (California)
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
+ */
+
+#ifndef __WEP_WPADEFS_H__
+#define __WEP_WPADEFS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @file
+ * WEP and WPA definitions
+ *
+ * Copied from airpcap.h.
+ */
+
+/**
+ * Maximum number of encryption keys. This determines the size of
+ * structures in packet-ieee80211.c, as well as the number of keys
+ * in the IEEE 802.11 preferences.
+ */
+#define MAX_ENCRYPTION_KEYS 64
+
+/**
+ * Maximum size of a WEP key, in bytes. This is the size of an entry in the
+ * AirpcapWepKeysCollection structure.
+ */
+#define WEP_KEY_MAX_SIZE 32
+
+/**
+ * WEP_KEY_MAX_SIZE is in bytes, but each byte is represented as a
+ * hexadecimal string.
+ */
+#define WEP_KEY_MAX_CHAR_SIZE (WEP_KEY_MAX_SIZE*2)
+
+/**
+ * WEP_KEY_MAX_SIZE is in bytes, this is in bits...
+ */
+#define WEP_KEY_MAX_BIT_SIZE (WEP_KEY_MAX_SIZE*8)
+
+#define WEP_KEY_MIN_CHAR_SIZE 2
+#define WEP_KEY_MIN_BIT_SIZE 8
+
+/**
+ * WPA key sizes.
+ */
+#define WPA_KEY_MAX_SIZE 63 /* 63 chars followed by a '\0' */
+
+#define WPA_KEY_MAX_CHAR_SIZE (WPA_KEY_MAX_SIZE*1)
+#define WPA_KEY_MAX_BIT_SIZE (WPA_KEY_MAX_SIZE*8)
+#define WPA_KEY_MIN_CHAR_SIZE 8
+#define WPA_KEY_MIN_BIT_SIZE (WPA_KEY_MIN_CHAR_SIZE*8)
+
+/**
+ * SSID sizes
+ */
+#define WPA_SSID_MAX_SIZE 32
+
+#define WPA_SSID_MAX_CHAR_SIZE (WPA_SSID_MAX_SIZE*1)
+#define WPA_SSID_MAX_BIT_SIZE (WPA_SSID_MAX_SIZE*8)
+#define WPA_SSID_MIN_CHAR_SIZE 0
+#define WPA_SSID_MIN_BIT_SIZE (WPA_SSID_MIN_CHAR_SIZE*8)
+
+/**
+ * Prefix definitions for preferences
+ */
+#define STRING_KEY_TYPE_WEP "wep"
+#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
+}
+#endif
+
+#endif /* __WEP_WPADEFS_H__ */