qcacld-3.0: Add Support for FILS Association
Adds the following support:
1) Add FILS IE's to assoc request.
2) AEAD encrypt/decrypt the assoc request/response frames for FILS
3) Process/Verify the Assoc response for the FILS params
4) Plumb the Auth keys to firmware after successful FILS assoc
5) Update the user space with Auth key details
Change-Id: I57f6319cecdf10e08047b510321b885c3cd711b5
CRs-Fixed: 2030038
diff --git a/core/mac/src/pe/include/lim_fils_defs.h b/core/mac/src/pe/include/lim_fils_defs.h
index c3619cb..7e0286d 100644
--- a/core/mac/src/pe/include/lim_fils_defs.h
+++ b/core/mac/src/pe/include/lim_fils_defs.h
@@ -34,6 +34,9 @@
#define MAX_KEY_AUTH_DATA_LEN 48
#define MAX_GTK_LEN 255
#define MAX_IGTK_LEN 255
+#define SIR_FILS_SESSION_IE_LEN 11
+#define FILS_KEY_RSC_LEN 8
+#define FILS_MAX_KEY_AUTH_LEN (MAX_ICK_LEN + MAX_KEK_LEN + MAX_TK_LEN)
#define IPN_LEN 6
#define FILS_SESSION_LENGTH 8
@@ -90,6 +93,18 @@
#define SIR_FILS_EAP_TLV_CRYPTO_LIST 5
#define SIR_FILS_EAP_TLV_AUTH_INDICATION 6
+#define DATA_TYPE_GTK 1
+#define DATA_TYPE_IGTK 9
+#define KEY_RSC_LEN 8
+#define KDE_IE_DATA_OFFSET 4
+#define KDE_DATA_TYPE_OFFSET 3
+#define GTK_OFFSET 2
+#define IPN_OFFSET 2
+#define IGTK_OFFSET 8
+
+#define KDE_OUI_TYPE "\x00\x0F\xAC"
+#define KDE_OUI_TYPE_SIZE 3
+
/*
* struct eap_auth_reserved: this structure defines flags format in eap packets
* as defined in RFC 6696 5.3.1
diff --git a/core/mac/src/pe/include/lim_process_fils.h b/core/mac/src/pe/include/lim_process_fils.h
index 5070bb2..a214f92 100644
--- a/core/mac/src/pe/include/lim_process_fils.h
+++ b/core/mac/src/pe/include/lim_process_fils.h
@@ -99,6 +99,84 @@
session_entry->fils_info->sequence_number++;
}
+/**
+ * populate_fils_connect_params() - Populate FILS connect params to join rsp
+ * @mac_ctx: Mac context
+ * @session: PE session
+ * @sme_join_rsp: SME join rsp
+ *
+ * This API copies the FILS connect params from PE session to SME join rsp
+ *
+ * Return: None
+ */
+void populate_fils_connect_params(tpAniSirGlobal mac_ctx,
+ tpPESession session,
+ tpSirSmeJoinRsp sme_join_rsp);
+
+/**
+ * aead_encrypt_assoc_req() - Encrypt FILS IE's in assoc request
+ * @mac_ctx: mac context
+ * @pe_session: PE session
+ * @frame: packed frame buffer
+ * @payload: length of @frame
+ *
+ * This API is used to encrypt the all the IE present after FILS session IE
+ * in Association request frame
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS aead_encrypt_assoc_req(tpAniSirGlobal mac_ctx,
+ tpPESession pe_session,
+ uint8_t *frame, uint32_t *payload);
+
+/**
+ * aead_decrypt_assoc_rsp() - API for AEAD decryption in FILS connection
+ * @mac_ctx: MAC context
+ * @session: PE session
+ * @ar: Assoc response frame structure
+ * @p_frame: frame buffer received
+ * @n_frame: length of @p_frame
+ *
+ * This API is used to decrypt the AEAD encrypted part of FILS assoc response
+ * and populate the decrypted FILS IE's to Assoc response frame structure(ar)
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS aead_decrypt_assoc_rsp(tpAniSirGlobal mac_ctx,
+ tpPESession session,
+ tDot11fAssocResponse *ar,
+ uint8_t *p_frame, uint32_t *n_frame);
+/**
+ * lim_is_fils_connection() - Check if it is FILS connection
+ * @pe_session: PE session
+ *
+ * This API is used to check if current PE session is FILS connection
+ *
+ * Return: True if FILS connection, false if not
+ */
+static inline bool lim_is_fils_connection(tpPESession pe_session)
+{
+ if (pe_session->fils_info->is_fils_connection)
+ return true;
+ return false;
+}
+
+/**
+ * lim_verify_fils_params_assoc_rsp() - Verify FILS params in assoc rsp
+ * @mac_ctx: Mac context
+ * @session_entry: PE session
+ * @assoc_rsp: Assoc response received
+ * @assoc_cnf: Assoc cnf msg to be sent to MLME
+ *
+ * This API is used to match FILS params received in Assoc response
+ * with Assoc params received/derived at the Authentication stage
+ *
+ * Return: True, if successfully matches. False, otherwise
+ */
+bool lim_verify_fils_params_assoc_rsp(tpAniSirGlobal mac_ctx,
+ tpPESession session_entry,
+ tpSirAssocRsp assoc_rsp,
+ tLimMlmAssocCnf * assoc_cnf);
#else
static inline bool lim_process_fils_auth_frame2(tpAniSirGlobal mac_ctx,
tpPESession pe_session, tSirMacAuthFrameBody *rx_auth_frm_body)
@@ -130,4 +208,39 @@
{
return 0;
}
+
+static inline bool lim_is_fils_connection(tpPESession pe_session)
+{
+ return false;
+}
+
+static inline void populate_fils_connect_params(tpAniSirGlobal mac_ctx,
+ tpPESession session,
+ tpSirSmeJoinRsp sme_join_rsp)
+{ }
+
+static inline QDF_STATUS aead_encrypt_assoc_req(tpAniSirGlobal mac_ctx,
+ tpPESession pe_session,
+ uint8_t *frame,
+ uint32_t *payload)
+{
+ return QDF_STATUS_SUCCESS;
+}
+
+static inline QDF_STATUS aead_decrypt_assoc_rsp(tpAniSirGlobal mac_ctx,
+ tpPESession session,
+ tDot11fAssocResponse *ar,
+ uint8_t *p_frame, uint32_t *n_frame)
+{
+ return QDF_STATUS_SUCCESS;
+}
+
+static inline bool lim_verify_fils_params_assoc_rsp(tpAniSirGlobal mac_ctx,
+ tpPESession session_entry,
+ tpSirAssocRsp assoc_rsp,
+ tLimMlmAssocCnf *assoc_cnf)
+
+{
+ return true;
+}
#endif
diff --git a/core/mac/src/pe/include/lim_session.h b/core/mac/src/pe/include/lim_session.h
index cb0de7b..54cbe04 100644
--- a/core/mac/src/pe/include/lim_session.h
+++ b/core/mac/src/pe/include/lim_session.h
@@ -646,4 +646,13 @@
tpPESession pe_find_session_by_sme_session_id(tpAniSirGlobal mac_ctx,
uint8_t sme_session_id);
uint8_t pe_get_active_session_count(tpAniSirGlobal mac_ctx);
+#ifdef WLAN_FEATURE_FILS_SK
+/**
+ * pe_delete_fils_info: API to delete fils session info
+ * @session: pe session
+ *
+ * Return: void
+ */
+void pe_delete_fils_info(tpPESession session);
+#endif
#endif /* #if !defined( __LIM_SESSION_H ) */
diff --git a/core/mac/src/pe/lim/lim_assoc_utils.c b/core/mac/src/pe/lim/lim_assoc_utils.c
index f33dd7b..4b58c22 100644
--- a/core/mac/src/pe/lim/lim_assoc_utils.c
+++ b/core/mac/src/pe/lim/lim_assoc_utils.c
@@ -767,16 +767,15 @@
mlmStaContext.protStatusCode,
psessionEntry->peSessionId);
- if (mlmStaContext.resultCode != eSIR_SME_SUCCESS) {
- pe_delete_session(pMac, psessionEntry);
- psessionEntry = NULL;
- }
-
lim_send_sme_join_reassoc_rsp(pMac, eWNI_SME_REASSOC_RSP,
mlmStaContext.resultCode,
mlmStaContext.protStatusCode,
psessionEntry, smesessionId,
smetransactionId);
+ if (mlmStaContext.resultCode != eSIR_SME_SUCCESS) {
+ pe_delete_session(pMac, psessionEntry);
+ psessionEntry = NULL;
+ }
} else {
qdf_mem_free(psessionEntry->pLimJoinReq);
psessionEntry->pLimJoinReq = NULL;
@@ -788,16 +787,16 @@
mlmStaContext.protStatusCode,
psessionEntry->peSessionId);
- if (mlmStaContext.resultCode != eSIR_SME_SUCCESS) {
- pe_delete_session(pMac, psessionEntry);
- psessionEntry = NULL;
- }
-
lim_send_sme_join_reassoc_rsp(pMac, eWNI_SME_JOIN_RSP,
mlmStaContext.resultCode,
mlmStaContext.protStatusCode,
psessionEntry, smesessionId,
smetransactionId);
+
+ if (mlmStaContext.resultCode != eSIR_SME_SUCCESS) {
+ pe_delete_session(pMac, psessionEntry);
+ psessionEntry = NULL;
+ }
}
} else if (mlmStaContext.cleanupTrigger == eLIM_DUPLICATE_ENTRY) {
diff --git a/core/mac/src/pe/lim/lim_process_assoc_rsp_frame.c b/core/mac/src/pe/lim/lim_process_assoc_rsp_frame.c
index f9d6fe6..2bdcea0 100644
--- a/core/mac/src/pe/lim/lim_process_assoc_rsp_frame.c
+++ b/core/mac/src/pe/lim/lim_process_assoc_rsp_frame.c
@@ -51,7 +51,7 @@
#include "lim_ser_des_utils.h"
#include "lim_sta_hash_api.h"
#include "lim_send_messages.h"
-
+#include "lim_process_fils.h"
extern tSirRetStatus sch_beacon_edca_process(tpAniSirGlobal pMac,
tSirMacEdcaParamSetIE *edca, tpPESession psessionEntry);
@@ -626,7 +626,7 @@
else
body = WMA_GET_RX_MPDU_DATA(rx_pkt_info);
/* parse Re/Association Response frame. */
- if (sir_convert_assoc_resp_frame2_struct(mac_ctx, body,
+ if (sir_convert_assoc_resp_frame2_struct(mac_ctx, session_entry, body,
frame_len, assoc_rsp) == eSIR_FAILURE) {
qdf_mem_free(assoc_rsp);
pe_err("Parse error Assoc resp subtype: %d" "length: %d",
@@ -759,6 +759,23 @@
hdr->sa, session_entry, false);
goto assocReject;
}
+
+ /*
+ * If it is FILS connection, check is FILS params are matching
+ * with Authentication stage.
+ */
+ if (!lim_verify_fils_params_assoc_rsp(mac_ctx, session_entry,
+ assoc_rsp, &assoc_cnf)) {
+ pe_err("FILS params doesnot match");
+ assoc_cnf.resultCode = eSIR_SME_INVALID_ASSOC_RSP_RXED;
+ assoc_cnf.protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS;
+ /* Send advisory Disassociation frame to AP */
+ lim_send_disassoc_mgmt_frame(mac_ctx,
+ eSIR_MAC_UNSPEC_FAILURE_REASON,
+ hdr->sa, session_entry, false);
+ goto assocReject;
+ }
+
/*
* Association Response received with success code
* Set the link state to POSTASSOC now that we have received
diff --git a/core/mac/src/pe/lim/lim_process_fils.c b/core/mac/src/pe/lim/lim_process_fils.c
index 79e8df3..01b49fb 100644
--- a/core/mac/src/pe/lim/lim_process_fils.c
+++ b/core/mac/src/pe/lim/lim_process_fils.c
@@ -1235,4 +1235,555 @@
}
return frame_len;
}
+
+void populate_fils_connect_params(tpAniSirGlobal mac_ctx,
+ tpPESession session,
+ tpSirSmeJoinRsp sme_join_rsp)
+{
+ struct fils_join_rsp_params *fils_join_rsp;
+ struct pe_fils_session *fils_info = session->fils_info;
+
+ if (!lim_is_fils_connection(session))
+ return;
+
+ if (!fils_info->fils_pmk_len ||
+ !fils_info->tk_len || !fils_info->gtk_len ||
+ !fils_info->fils_pmk || !fils_info->kek_len) {
+ pe_err("Invalid FILS info pmk len %d kek len %d tk len %d gtk len %d",
+ fils_info->fils_pmk_len,
+ fils_info->kek_len,
+ fils_info->tk_len,
+ fils_info->gtk_len);
+ return;
+ }
+
+ sme_join_rsp->fils_join_rsp = qdf_mem_malloc(sizeof(*fils_join_rsp));
+ if (!sme_join_rsp->fils_join_rsp) {
+ pe_err("fils_join_rsp malloc fails!");
+ pe_delete_fils_info(session);
+ return;
+ }
+
+ fils_join_rsp = sme_join_rsp->fils_join_rsp;
+ fils_join_rsp->fils_pmk = qdf_mem_malloc(fils_info->fils_pmk_len);
+ if (!fils_join_rsp->fils_pmk) {
+ pe_err("fils_pmk malloc fails!");
+ qdf_mem_free(fils_join_rsp);
+ pe_delete_fils_info(session);
+ return;
+ }
+
+ fils_join_rsp->fils_pmk_len = fils_info->fils_pmk_len;
+ qdf_mem_copy(fils_join_rsp->fils_pmk, fils_info->fils_pmk,
+ fils_info->fils_pmk_len);
+
+ qdf_mem_copy(fils_join_rsp->fils_pmkid, fils_info->fils_pmkid,
+ IEEE80211_PMKID_LEN);
+
+ fils_join_rsp->kek_len = fils_info->kek_len;
+ qdf_mem_copy(fils_join_rsp->kek, fils_info->kek, fils_info->kek_len);
+
+ fils_join_rsp->tk_len = fils_info->tk_len;
+ qdf_mem_copy(fils_join_rsp->tk, fils_info->tk, fils_info->tk_len);
+
+ fils_join_rsp->gtk_len = fils_info->gtk_len;
+ qdf_mem_copy(fils_join_rsp->gtk, fils_info->gtk, fils_info->gtk_len);
+
+ pe_debug("FILS connect params copied lim");
+ pe_delete_fils_info(session);
+}
+
+/**
+ * lim_parse_kde_elements() - Parse Key Delivery Elements
+ * @mac_ctx: mac context
+ * @fils_info: FILS info
+ * @kde_list: KDE list buffer
+ * @kde_list_len: Length of @kde_list
+ *
+ * This API is used to parse the Key Delivery Elements from buffer
+ * and populate them in PE FILS session struct i.e @fils_info
+ *
+ * Key Delivery Element[KDE] format
+ * +----------+--------+-----------+------------+----------+
+ * | ID(0xDD) | length | KDE OUI | data type | IE data |
+ * |----------|--------|-----------|------------|----------|
+ * | 1 byte | 1 byte | 3 bytes | 1 byte | variable |
+ * +----------+--------+-----------+------------+----------+
+ *
+ * there can be multiple KDE present inside KDE list.
+ * the IE data could be GTK, IGTK etc based on the data type
+ *
+ * Return: QDF_STATUS_SUCCESS if we parse GTK successfully,
+ * QDF_STATUS_E_FAILURE otherwise
+ */
+static QDF_STATUS lim_parse_kde_elements(tpAniSirGlobal mac_ctx,
+ struct pe_fils_session *fils_info,
+ uint8_t *kde_list,
+ uint8_t kde_list_len)
+{
+ uint8_t rem_len = kde_list_len;
+ uint8_t *temp_ie = kde_list;
+ uint8_t elem_id, data_type, data_len, *ie_data = NULL, *current_ie;
+ uint16_t elem_len;
+
+ if (!kde_list_len || !kde_list) {
+ pe_err("kde_list NULL or kde_list_len %d", kde_list_len);
+ return QDF_STATUS_E_FAILURE;
+ }
+
+ while (rem_len >= 2) {
+ current_ie = temp_ie;
+ elem_id = *temp_ie++;
+ elem_len = *temp_ie++;
+ rem_len -= 2;
+
+ if (lim_check_if_vendor_oui_match(mac_ctx, KDE_OUI_TYPE,
+ KDE_OUI_TYPE_SIZE, current_ie, elem_len)) {
+
+ data_type = *(temp_ie + KDE_DATA_TYPE_OFFSET);
+ ie_data = (temp_ie + KDE_IE_DATA_OFFSET);
+ data_len = (elem_len - KDE_IE_DATA_OFFSET);
+
+ switch (data_type) {
+ case DATA_TYPE_GTK:
+ qdf_mem_copy(fils_info->gtk, (ie_data +
+ GTK_OFFSET), (data_len -
+ GTK_OFFSET));
+ fils_info->gtk_len = (data_len - GTK_OFFSET);
+ lim_fils_data_dump("GTK: ", fils_info->gtk,
+ fils_info->gtk_len);
+ break;
+
+ case DATA_TYPE_IGTK:
+ fils_info->igtk_len = (data_len - IGTK_OFFSET);
+ qdf_mem_copy(fils_info->igtk, (ie_data +
+ IGTK_OFFSET), (data_len -
+ IGTK_OFFSET));
+ qdf_mem_copy(fils_info->ipn, (ie_data +
+ IPN_OFFSET), IPN_LEN);
+ lim_fils_data_dump("IGTK: ", fils_info->igtk,
+ fils_info->igtk_len);
+ break;
+ default:
+ pe_err("Unknown KDE data type %x", data_type);
+ break;
+ }
+ }
+
+ temp_ie += elem_len;
+ rem_len -= elem_len;
+ ie_data = NULL;
+ }
+
+ /* Expecting GTK in KDE */
+ if (!fils_info->gtk_len) {
+ pe_err("GTK not found in KDE");
+ return QDF_STATUS_E_FAILURE;
+ }
+
+ return QDF_STATUS_SUCCESS;
+}
+
+bool lim_verify_fils_params_assoc_rsp(tpAniSirGlobal mac_ctx,
+ tpPESession session_entry,
+ tpSirAssocRsp assoc_rsp,
+ tLimMlmAssocCnf *assoc_cnf)
+{
+ struct pe_fils_session *fils_info = session_entry->fils_info;
+ tDot11fIEfils_session fils_session = assoc_rsp->fils_session;
+ tDot11fIEfils_key_confirmation fils_key_auth = assoc_rsp->fils_key_auth;
+ tDot11fIEfils_kde fils_kde = assoc_rsp->fils_kde;
+ QDF_STATUS status;
+
+ if (!lim_is_fils_connection(session_entry))
+ return true;
+
+ if (!assoc_rsp->fils_session.present) {
+ pe_err("FILS IE not present");
+ goto verify_fils_params_fails;
+ }
+
+ /* Compare FILS session */
+ if (qdf_mem_cmp(fils_info->fils_session,
+ fils_session.session, DOT11F_IE_FILS_SESSION_MAX_LEN)) {
+ pe_err("FILS session mismatch");
+ goto verify_fils_params_fails;
+ }
+
+ /* Compare FILS key auth */
+ if ((fils_key_auth.num_key_auth != fils_info->key_auth_len) ||
+ qdf_mem_cmp(fils_info->ap_key_auth_data, fils_key_auth.key_auth,
+ fils_info->ap_key_auth_len)) {
+ lim_fils_data_dump("session keyauth",
+ fils_info->ap_key_auth_data,
+ fils_info->ap_key_auth_len);
+ lim_fils_data_dump("Pkt keyauth",
+ fils_key_auth.key_auth,
+ fils_key_auth.num_key_auth);
+ goto verify_fils_params_fails;
+ }
+
+ /* Verify the Key Delivery Element presence */
+ if (!fils_kde.num_kde_list) {
+ pe_err("FILS KDE list absent");
+ goto verify_fils_params_fails;
+ }
+
+ /* Derive KDE elements */
+ status = lim_parse_kde_elements(mac_ctx, fils_info, fils_kde.kde_list,
+ fils_kde.num_kde_list);
+ if (!QDF_IS_STATUS_SUCCESS(status)) {
+ pe_err("KDE parsing fails");
+ goto verify_fils_params_fails;
+ }
+ return true;
+
+verify_fils_params_fails:
+ assoc_cnf->resultCode = eSIR_SME_ASSOC_REFUSED;
+ assoc_cnf->protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS;
+ return false;
+}
+
+/**
+ * find_ie_data_after_fils_session_ie() - Find IE pointer after FILS Session IE
+ * @mac_ctx: MAC context
+ * @buf: IE buffer
+ * @buf_len: Length of @buf
+ * @ie: Pointer to update the found IE pointer after FILS session IE
+ * @ie_len: length of the IE data after FILS session IE
+ *
+ * This API is used to find the IE data ptr and length after FILS session IE
+ *
+ * Return: QDF_STATUS_SUCCESS if found, else QDF_STATUS_E_FAILURE
+ */
+static QDF_STATUS find_ie_data_after_fils_session_ie(tpAniSirGlobal mac_ctx,
+ uint8_t *buf,
+ uint32_t buf_len,
+ uint8_t **ie,
+ uint32_t *ie_len)
+{
+ uint32_t left = buf_len;
+ uint8_t *ptr = buf;
+ uint8_t elem_id, elem_len;
+
+ if (NULL == buf || 0 == buf_len)
+ return QDF_STATUS_E_FAILURE;
+
+ while (left >= 2) {
+ elem_id = ptr[0];
+ elem_len = ptr[1];
+ left -= 2;
+ if (elem_len > left)
+ return QDF_STATUS_E_FAILURE;
+
+ if (elem_id == SIR_MAC_REQUEST_EID_MAX &&
+ ptr[2] == SIR_FILS_SESSION_EXT_EID) {
+ (*ie) = ((&ptr[1]) + ptr[1] + 1);
+ (*ie_len) = (left - elem_len);
+ return QDF_STATUS_SUCCESS;
+ }
+ left -= elem_len;
+ ptr += (elem_len + 2);
+ }
+ return QDF_STATUS_E_FAILURE;
+}
+
+/**
+ * fils_aead_encrypt() - API to do FILS AEAD encryption
+ *
+ * @kek: Pointer to KEK
+ * @kek_len: KEK length
+ * @own_mac: Pointer to own MAC address
+ * @bssid: Bssid
+ * @snonce: Supplicant Nonce
+ * @anonce: Authenticator Nonce
+ * @data: Pointer to data after MAC header
+ * @data_len: length of @data
+ * @plain_text: Pointer to data after FILS Session IE
+ * @plain_text_len: length of @plain_text
+ * @out: Pointer to the encrypted data
+ *
+ * length of AEAD encryption @out is @plain_text_len + AES_BLOCK_SIZE[16 bytes]
+ *
+ * Return: zero on success, error otherwise
+ */
+static int fils_aead_encrypt(const u8 *kek, unsigned int kek_len,
+ const u8 *own_mac, const u8 *bssid,
+ const u8 *snonce, const u8 *anonce,
+ const u8 *data, size_t data_len, u8 *plain_text,
+ size_t plain_text_len, u8 *out)
+{
+ u8 v[AES_BLOCK_SIZE];
+ const u8 *aad[6];
+ size_t aad_len[6];
+ u8 *buf;
+ int ret;
+
+ /* SIV Encrypt/Decrypt takes input key of length 256, 384 or 512 bits */
+ if (kek_len != 32 && kek_len != 48 && kek_len != 64) {
+ QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
+ FL("Invalid key length: %u"), kek_len);
+ return -EINVAL;
+ }
+
+ if (own_mac == NULL || bssid == NULL || snonce == NULL ||
+ anonce == NULL || data_len == 0 || plain_text_len == 0 ||
+ out == NULL) {
+ QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
+ FL("Error missing params mac:%p bssid:%p snonce:%p anonce:%p data_len:%lu plain_text_len:%lu out:%p"),
+ own_mac, bssid, snonce, anonce, data_len,
+ plain_text_len, out);
+ return -EINVAL;
+ }
+
+ if (plain_text == out) {
+ buf = qdf_mem_malloc(plain_text_len);
+ if (buf == NULL) {
+ QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
+ FL("Failed to allocate memory"));
+ return -ENOMEM;
+ }
+ qdf_mem_copy(buf, plain_text, plain_text_len);
+ } else {
+ buf = plain_text;
+ }
+
+ aad[0] = own_mac;
+ aad_len[0] = QDF_MAC_ADDR_SIZE;
+ aad[1] = bssid;
+ aad_len[1] = QDF_MAC_ADDR_SIZE;
+ aad[2] = snonce;
+ aad_len[2] = SIR_FILS_NONCE_LENGTH;
+ aad[3] = anonce;
+ aad_len[3] = SIR_FILS_NONCE_LENGTH;
+ aad[4] = data;
+ aad_len[4] = data_len;
+ /* Plain text, P, is Sn in AES-SIV */
+ aad[5] = buf;
+ aad_len[5] = plain_text_len;
+
+ /* AES-SIV S2V */
+ /* K1 = leftmost(K, len(K)/2) */
+ ret = qdf_aes_s2v(kek, kek_len/2, aad, aad_len, 6, v);
+ if (ret)
+ goto error;
+
+ /* out = SIV || C (Synthetic Initialization Vector || Ciphered text) */
+ qdf_mem_copy(out, v, AES_BLOCK_SIZE);
+
+ /* AES-SIV CTR */
+ /* K2 = rightmost(K, len(K)/2) */
+ /* Clear 31st and 63rd bits in counter synthetic iv */
+ v[12] &= 0x7F;
+ v[8] &= 0x7F;
+
+ ret = qdf_aes_ctr(kek + kek_len/2, kek_len/2, v, buf, plain_text_len,
+ out + AES_BLOCK_SIZE, true);
+
+error:
+ if (plain_text == out)
+ qdf_mem_free(buf);
+ return ret;
+}
+
+QDF_STATUS aead_encrypt_assoc_req(tpAniSirGlobal mac_ctx,
+ tpPESession pe_session,
+ uint8_t *frm, uint32_t *frm_len)
+{
+ uint8_t *plain_text = NULL, *data;
+ uint32_t plain_text_len = 0, data_len;
+ QDF_STATUS status;
+ struct pe_fils_session *fils_info = pe_session->fils_info;
+
+ /*
+ * data is the packet data after MAC header till
+ * FILS session IE(inclusive)
+ */
+ data = frm + sizeof(tSirMacMgmtHdr);
+
+ /*
+ * plain_text is the packet data after FILS session IE
+ * which needs to be encrypted. Get plain_text ptr and
+ * plain_text_len values using find_ptr_aft_fils_session_ie()
+ */
+ status = find_ie_data_after_fils_session_ie(mac_ctx, data +
+ FIXED_PARAM_OFFSET_ASSOC_REQ,
+ (*frm_len -
+ FIXED_PARAM_OFFSET_ASSOC_REQ),
+ &plain_text, &plain_text_len);
+ if (QDF_IS_STATUS_ERROR(status)) {
+ pe_err("Could not find FILS session IE");
+ return QDF_STATUS_E_FAILURE;
+ }
+ data_len = ((*frm_len) - plain_text_len);
+
+ lim_fils_data_dump("Plain text: ", plain_text, plain_text_len);
+
+ /* Overwrite the AEAD encrypted output @ plain_text */
+ if (fils_aead_encrypt(fils_info->kek, fils_info->kek_len,
+ pe_session->selfMacAddr, pe_session->bssId,
+ fils_info->fils_nonce,
+ fils_info->auth_info.fils_nonce,
+ data, data_len, plain_text, plain_text_len,
+ plain_text)) {
+ pe_err("AEAD Encryption fails!");
+ return QDF_STATUS_E_FAILURE;
+ }
+
+ /*
+ * AEAD encrypted output(cipher_text) will have length equals to
+ * plain_text_len + AES_BLOCK_SIZE(AEAD encryption header info).
+ * Add this to frm_len
+ */
+ (*frm_len) += (AES_BLOCK_SIZE);
+
+ return QDF_STATUS_SUCCESS;
+}
+
+/**
+ * fils_aead_decrypt() - API to do AEAD decryption
+ *
+ * @kek: Pointer to KEK
+ * @kek_len: KEK length
+ * @own_mac: Pointer to own MAC address
+ * @bssid: Bssid
+ * @snonce: Supplicant Nonce
+ * @anonce: Authenticator Nonce
+ * @data: Pointer to data after MAC header
+ * @data_len: length of @data
+ * @plain_text: Pointer to data after FILS Session IE
+ * @plain_text_len: length of @plain_text
+ * @out: Pointer to the encrypted data
+ *
+ * Return: zero on success, error otherwise
+ */
+static int fils_aead_decrypt(const u8 *kek, unsigned int kek_len,
+ const u8 *own_mac, const u8 *bssid,
+ const u8 *snonce, const u8 *anonce,
+ const u8 *data, size_t data_len, u8 *ciphered_text,
+ size_t ciphered_text_len, u8 *plain_text)
+{
+ const u8 *aad[6];
+ size_t aad_len[6];
+ u8 *buf;
+ size_t buf_len;
+ u8 v[AES_BLOCK_SIZE];
+ u8 siv[AES_BLOCK_SIZE];
+ int ret;
+
+ /* SIV Encrypt/Decrypt takes input key of length 256, 384 or 512 bits */
+ if (kek_len != 32 && kek_len != 48 && kek_len != 64) {
+ QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
+ FL("Invalid key length: %u"), kek_len);
+ return -EINVAL;
+ }
+
+ if (own_mac == NULL || bssid == NULL || snonce == NULL ||
+ anonce == NULL || data_len == 0 || ciphered_text_len == 0 ||
+ plain_text == NULL) {
+ QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
+ FL("Error missing params mac:%p bssid:%p snonce:%p anonce:%p data_len:%lu ciphered_text_len:%lu plain_text:%p"),
+ own_mac, bssid, snonce, anonce, data_len,
+ ciphered_text_len, plain_text);
+ return -EINVAL;
+ }
+
+ qdf_mem_copy(v, ciphered_text, AES_BLOCK_SIZE);
+ qdf_mem_copy(siv, ciphered_text, AES_BLOCK_SIZE);
+ v[12] &= 0x7F;
+ v[8] &= 0x7F;
+
+ buf_len = ciphered_text_len - AES_BLOCK_SIZE;
+ if (ciphered_text == plain_text) {
+ /* in place decryption */
+ buf = qdf_mem_malloc(buf_len);
+ if (buf == NULL) {
+ QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
+ FL("Failed to allocate memory"));
+ return -ENOMEM;
+ }
+ qdf_mem_copy(buf, ciphered_text + AES_BLOCK_SIZE, buf_len);
+ } else {
+ buf = ciphered_text + AES_BLOCK_SIZE;
+ }
+
+ /* AES-SIV CTR */
+ /* K2 = rightmost(K, len(K)/2) */
+ ret = qdf_aes_ctr(kek + kek_len/2, kek_len/2, v, buf, buf_len,
+ plain_text, false);
+ if (ret)
+ goto error;
+
+ aad[0] = bssid;
+ aad_len[0] = QDF_MAC_ADDR_SIZE;
+ aad[1] = own_mac;
+ aad_len[1] = QDF_MAC_ADDR_SIZE;
+ aad[2] = anonce;
+ aad_len[2] = SIR_FILS_NONCE_LENGTH;
+ aad[3] = snonce;
+ aad_len[3] = SIR_FILS_NONCE_LENGTH;
+ aad[4] = data;
+ aad_len[4] = data_len;
+ aad[5] = plain_text;
+ aad_len[5] = buf_len;
+
+ /* AES-SIV S2V */
+ /* K1 = leftmost(K, len(K)/2) */
+ ret = qdf_aes_s2v(kek, kek_len/2, aad, aad_len, 6, v);
+ if (ret)
+ goto error;
+
+ /* compare the iv generated against the one sent by AP */
+ if (memcmp(v, siv, AES_BLOCK_SIZE) != 0) {
+ QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
+ FL("siv not same as frame siv"));
+ ret = -EINVAL;
+ }
+
+error:
+ if (ciphered_text == plain_text)
+ qdf_mem_free(buf);
+ return ret;
+}
+
+QDF_STATUS aead_decrypt_assoc_rsp(tpAniSirGlobal mac_ctx,
+ tpPESession session,
+ tDot11fAssocResponse *ar,
+ uint8_t *p_frame, uint32_t *n_frame)
+{
+ QDF_STATUS status;
+ uint32_t data_len, fils_ies_len;
+ uint8_t *fils_ies;
+ struct pe_fils_session *fils_info = session->fils_info;
+
+ lim_fils_data_dump("Assoc Rsp :", p_frame, *n_frame);
+ status = find_ie_data_after_fils_session_ie(mac_ctx, p_frame +
+ FIXED_PARAM_OFFSET_ASSOC_RSP,
+ ((*n_frame) -
+ FIXED_PARAM_OFFSET_ASSOC_RSP),
+ &fils_ies, &fils_ies_len);
+ if (!QDF_IS_STATUS_SUCCESS(status)) {
+ pe_err("FILS session IE not present");
+ return status;
+ }
+
+ data_len = (*n_frame) - fils_ies_len;
+
+ if (fils_aead_decrypt(fils_info->kek, fils_info->kek_len,
+ session->selfMacAddr, session->bssId,
+ fils_info->fils_nonce,
+ fils_info->auth_info.fils_nonce,
+ p_frame, data_len,
+ fils_ies, fils_ies_len, fils_ies)){
+ pe_err("AEAD decryption fails");
+ return QDF_STATUS_E_FAILURE;
+ }
+
+ /* Dump the output of AEAD decrypt */
+ lim_fils_data_dump("Plain text: ", fils_ies,
+ fils_ies_len - AES_BLOCK_SIZE);
+
+ (*n_frame) -= AES_BLOCK_SIZE;
+ return status;
+}
#endif
diff --git a/core/mac/src/pe/lim/lim_process_mlm_rsp_messages.c b/core/mac/src/pe/lim/lim_process_mlm_rsp_messages.c
index 274c3d9..361d787 100644
--- a/core/mac/src/pe/lim/lim_process_mlm_rsp_messages.c
+++ b/core/mac/src/pe/lim/lim_process_mlm_rsp_messages.c
@@ -1289,7 +1289,7 @@
lim_send_sme_join_reassoc_rsp(mac, eWNI_SME_JOIN_RSP,
link_state_params->result_code,
link_state_params->prot_status_code,
- NULL, sme_session_id, sme_trans_id);
+ session, sme_session_id, sme_trans_id);
pe_delete_session(mac, session);
qdf_mem_free(link_state_params);
}
diff --git a/core/mac/src/pe/lim/lim_send_management_frames.c b/core/mac/src/pe/lim/lim_send_management_frames.c
index f9cbf7a..889a561 100644
--- a/core/mac/src/pe/lim/lim_send_management_frames.c
+++ b/core/mac/src/pe/lim/lim_send_management_frames.c
@@ -59,6 +59,7 @@
#include "cds_utils.h"
#include "sme_trace.h"
#include "rrm_api.h"
+#include "qdf_crypto.h"
#include "wma_types.h"
#include <cdp_txrx_cmn.h>
@@ -1637,6 +1638,7 @@
tDot11fIEExtCap bcn_ext_cap;
uint8_t *bcn_ie = NULL;
uint32_t bcn_ie_len = 0;
+ uint32_t aes_block_size_len = 0;
if (NULL == pe_session) {
pe_err("pe_session is NULL");
@@ -1921,6 +1923,20 @@
add_ie, &add_ie_len, &frm->SuppOperatingClasses))
pe_debug("Unable to Stripoff supp op classes IE from Assoc Req");
+ if (lim_is_fils_connection(pe_session)) {
+ populate_dot11f_fils_params(mac_ctx, frm, pe_session);
+ aes_block_size_len = AES_BLOCK_SIZE;
+ }
+
+ /*
+ * Do unpack to populate the add_ie buffer to frm structure
+ * before packing the frm structure. In this way, the IE ordering
+ * which the latest 802.11 spec mandates is maintained.
+ */
+ if (add_ie_len)
+ dot11f_unpack_assoc_request(mac_ctx, add_ie,
+ add_ie_len, frm, true);
+
status = dot11f_get_packed_assoc_request_size(mac_ctx, frm, &payload);
if (DOT11F_FAILED(status)) {
pe_err("Association Request packet size failure(0x%08x)",
@@ -1932,7 +1948,8 @@
status);
}
- bytes = payload + sizeof(tSirMacMgmtHdr) + add_ie_len;
+ bytes = payload + sizeof(tSirMacMgmtHdr) +
+ aes_block_size_len;
qdf_status = cds_packet_alloc((uint16_t) bytes, (void **)&frame,
(void **)&packet);
@@ -1974,17 +1991,20 @@
pe_warn("Assoc request pack warning (0x%08x)", status);
}
- pe_debug("Sending Association Request length %d to ", bytes);
if (pe_session->assocReq != NULL) {
qdf_mem_free(pe_session->assocReq);
pe_session->assocReq = NULL;
pe_session->assocReqLen = 0;
}
- if (add_ie_len) {
- qdf_mem_copy(frame + sizeof(tSirMacMgmtHdr) + payload,
- add_ie, add_ie_len);
- payload += add_ie_len;
+ if (lim_is_fils_connection(pe_session)) {
+ qdf_status = aead_encrypt_assoc_req(mac_ctx, pe_session,
+ frame, &payload);
+ if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
+ cds_packet_free((void *)packet);
+ qdf_mem_free(frm);
+ return;
+ }
}
pe_session->assocReq = qdf_mem_malloc(payload);
@@ -2017,6 +2037,8 @@
mac_hdr = (tpSirMacMgmtHdr) frame;
MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_MGMT,
pe_session->peSessionId, mac_hdr->fc.subType));
+
+ pe_debug("Sending Association Request length %d to ", bytes);
qdf_status =
wma_tx_frame(mac_ctx, packet,
(uint16_t) (sizeof(tSirMacMgmtHdr) + payload),
diff --git a/core/mac/src/pe/lim/lim_send_sme_rsp_messages.c b/core/mac/src/pe/lim/lim_send_sme_rsp_messages.c
index 330974a..67728c6 100644
--- a/core/mac/src/pe/lim/lim_send_sme_rsp_messages.c
+++ b/core/mac/src/pe/lim/lim_send_sme_rsp_messages.c
@@ -64,6 +64,7 @@
#ifdef CONVERGED_TDLS_ENABLE
#include "wlan_tdls_tgt_api.h"
#endif
+#include "lim_process_fils.h"
static void lim_handle_join_rsp_status(tpAniSirGlobal mac_ctx,
tpPESession session_entry, tSirResultCodes result_code,
@@ -453,6 +454,18 @@
sme_join_rsp->vht_operation = parsed_ies->vht_operation;
}
+#ifdef WLAN_FEATURE_FILS_SK
+static void lim_update_fils_seq_num(tpSirSmeJoinRsp sme_join_rsp,
+ tpPESession session_entry)
+{
+ sme_join_rsp->fils_seq_num =
+ session_entry->fils_info->sequence_number;
+}
+#else
+static inline void lim_update_fils_seq_num(tpSirSmeJoinRsp sme_join_rsp,
+ tpPESession session_entry)
+{}
+#endif
/**
* lim_send_sme_join_reassoc_rsp() - Send Response to Upper Layers
* @mac_ctx: Pointer to Global MAC structure
@@ -516,6 +529,13 @@
pe_err("MemAlloc fail - JOIN/REASSOC_RSP");
return;
}
+
+ if (lim_is_fils_connection(session_entry)) {
+ sme_join_rsp->is_fils_connection = true;
+ lim_update_fils_seq_num(sme_join_rsp,
+ session_entry);
+ }
+
if (result_code == eSIR_SME_SUCCESS) {
sta_ds = dph_get_hash_entry(mac_ctx,
DPH_STA_HASH_INDEX_PEER,
@@ -541,8 +561,13 @@
sme_join_rsp->max_rate_flags =
lim_get_max_rate_flags(mac_ctx, sta_ds);
lim_add_bss_info(sta_ds, sme_join_rsp);
+
+ /* Copy FILS params only for Successful join */
+ populate_fils_connect_params(mac_ctx,
+ session_entry, sme_join_rsp);
}
}
+
sme_join_rsp->beaconLength = 0;
sme_join_rsp->assocReqLength = 0;
sme_join_rsp->assocRspLength = 0;
diff --git a/core/mac/src/pe/lim/lim_session.c b/core/mac/src/pe/lim/lim_session.c
index 747981c..09d88ed 100644
--- a/core/mac/src/pe/lim/lim_session.c
+++ b/core/mac/src/pe/lim/lim_session.c
@@ -274,7 +274,7 @@
*
* Return: void
*/
-static void pe_delete_fils_info(tpPESession session)
+void pe_delete_fils_info(tpPESession session)
{
struct pe_fils_session *fils_info;
diff --git a/core/mac/src/pe/lim/lim_utils.c b/core/mac/src/pe/lim/lim_utils.c
index ba38611..79087d7 100644
--- a/core/mac/src/pe/lim/lim_utils.c
+++ b/core/mac/src/pe/lim/lim_utils.c
@@ -7988,3 +7988,22 @@
return NULL;
}
+
+bool lim_check_if_vendor_oui_match(tpAniSirGlobal mac_ctx,
+ uint8_t *oui, uint8_t oui_len,
+ uint8_t *ie, uint8_t ie_len)
+{
+ uint8_t *ptr = ie;
+ uint8_t elem_id = *ie;
+
+ if (NULL == ie || 0 == ie_len) {
+ pe_err("IE Null or ie len zero %d", ie_len);
+ return false;
+ }
+
+ if (elem_id == IE_EID_VENDOR &&
+ !qdf_mem_cmp(&ptr[2], oui, oui_len))
+ return true;
+ else
+ return false;
+}
diff --git a/core/mac/src/pe/lim/lim_utils.h b/core/mac/src/pe/lim/lim_utils.h
index 9736827..d4630b0 100644
--- a/core/mac/src/pe/lim/lim_utils.h
+++ b/core/mac/src/pe/lim/lim_utils.h
@@ -1127,4 +1127,19 @@
* Return: None
*/
void lim_decrement_pending_mgmt_count(tpAniSirGlobal mac_ctx);
+
+/**
+ * lim_check_if_vendor_oui_match() - Check if the given OUI match in IE buffer
+ * @mac_ctx: MAC context
+ * @ie: IE buffer
+ * @ie_len: length of @ie
+ *
+ * This API is used to check if given vendor OUI
+ * matches in given IE buffer
+ *
+ * Return: True, if mataches. False otherwise
+ */
+bool lim_check_if_vendor_oui_match(tpAniSirGlobal mac_ctx,
+ uint8_t *oui, uint8_t oui_len,
+ uint8_t *ie, uint8_t ie_len);
#endif /* __LIM_UTILS_H */