Ver Fonte

qcacld-3.0: Derive PMK-R0 and PMK-R1NAME for FT-FILS

Generate PMK-R0 and PMK-R1NAME for FT-FILS connection and send it
over assoc request frame.

According to 802.11-2016 standard:
PMK-R0 is derived as:
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)
PMKR0Name = Truncate-128(SHA-256("FT-R0N" || PMK-R0Name-Salt))

PMK-R1 is derived as:
PMK-R1 = KDF-Hash-Length(PMK-R0, "FT-R1", R1KH-ID || S1KH-ID)

PMK-R1Name is derived as:
PMKR1Name = Truncate-128(SHA-256(“FT-R1N” || PMKR0Name ||
			R1KH-ID || S1KH-ID))

Call qdf_get_hash() function to generate a sha-256 or sha-384
hash. Use qdf_get_hash() with hmac(sha-256) or hmac(sha-384)
to generate hmac_hash.

Set PMKID count as 1 in RSN IE and copy the PMKR1-name
generated to the PMKID list. This PMKR1 should be the only
entry in PMKID list in assoc request frame sent by the driver
during FT-FILS initial mobility domain FILS connection.

Change-Id: Ic634aebbe42a58b92f871cf3258c62f7541d161a
CRs-Fixed: 2414719
Pragaspathi Thilagaraj há 6 anos atrás
pai
commit
5f51077b39

+ 49 - 2
core/mac/src/pe/include/lim_fils_defs.h

@@ -25,6 +25,22 @@
 
 /* 12.12.2.5.3 80211-ai draft */
 #define PTK_KEY_LABEL "FILS PTK Derivation"
+#define FT_PMK_R0_KEY_LABEL "FT-R0"
+#define FT_PMK_R0_NAME_KEY_LABEL "FT-R0N"
+#define FT_PMK_R1_NAME_KEY_LABEL "FT-R1N"
+
+#define PMKR0_SCATTER_LIST_ELEM 2
+#define PMKR1_SCATTER_LIST_ELEM 4
+
+#define SCTR_LST_ELEM0 0
+#define SCTR_LST_ELEM1 1
+#define SCTR_LST_ELEM2 2
+#define SCTR_LST_ELEM3 3
+
+/* Length of "FT-R1N" */
+#define SCTR_LST_R0_LABEL_LEN 6
+#define SCTR_LST_R1_LABEL_LEN 6
+
 #define MAX_ICK_LEN 48
 #define MAX_KEK_LEN 64
 #define MAX_TK_LEN 32
@@ -52,8 +68,17 @@
 #define TK_LEN_CCMP 16
 #define TK_LEN_AES_128_CMAC 32
 
-#define FILS_SHA256_PKM_LEN 32
-#define FILS_SHA384_PKM_LEN 48
+#define FILS_SHA256_PMK_LEN 32
+#define FILS_SHA384_PMK_LEN 48
+
+#define FILS_FT_SHA256_LEN 32
+#define FILS_FT_SHA384_LEN 48
+
+#define FILS_FT_MAX_R0_KEY_DATA_LEN 64
+
+/* 12.7.1.7.3 802.11ai */
+#define FILS_SHA256_Q_LEN 32
+#define FILS_SHA384_Q_LEN 48
 
 #define MAX_PRF_INTERATIONS_COUNT 255
 
@@ -243,6 +268,13 @@ struct mac_ft_ie {
 	struct mac_ft_igtk_ie igtk_ie;
 };
 
+#define FILS_PMK_LEN 32
+#define FILS_PMK_NAME_LEN 16
+#define FILS_FT_MAX_LEN 48
+#define FILS_FT_PMK_R0_SALT_LEN 16
+#define FILS_MAX_KEY_DATA_LEN \
+	(MAX_ICK_LEN + MAX_KEK_LEN + MAX_TK_LEN + FILS_FT_MAX_LEN)
+
 /*
  * struct pe_fils_session: fils session info used in PE session
  * @is_fils_connection: whether connection is fils or not
@@ -262,7 +294,13 @@ struct mac_ft_ie {
  * @fils_nonce: fils snonce
  * @rsn_ie: rsn ie used in auth request
  * @rsn_ie_len: rsn ie length
+ * @group_mgmt_cipher_suite_present: Check if group management cipher suite
+ * is present in the FILS RSN IE
  * @ft_ie: structure to store the parsed FTIE from auth response frame
+ * @pmkr0: PMKR0
+ * @pmkr0_len: length of PMKR0 key
+ * @pmkr0_name: PMK_R0 name derived
+ * @pmkr1_name: PMKR1 Name derived
  * @fils_eap_finish_pkt: pointer to eap finish packet
  * @fils_eap_finish_pkt_len: eap finish packet length
  * @fils_rmsk: rmsk data pointer
@@ -283,6 +321,8 @@ struct mac_ft_ie {
  * @ap_key_auth_len:  ap key data length
  * @gtk_len: gtk key length
  * @gtk: pointer to gtk data
+ * @fils_ft: xx_key data
+ * @fils_ft_len: xx_key length
  * @rsc: rsc value
  * @igtk_len: igtk length
  * @igtk: igtk data pointer
@@ -310,7 +350,12 @@ struct pe_fils_session {
 	uint8_t fils_nonce[SIR_FILS_NONCE_LENGTH];
 	uint8_t rsn_ie[WLAN_MAX_IE_LEN];
 	uint8_t rsn_ie_len;
+	bool group_mgmt_cipher_present;
 	struct mac_ft_ie ft_ie;
+	uint8_t pmkr0[FILS_PMK_LEN];
+	uint8_t pmkr0_len;
+	uint8_t pmkr0_name[FILS_PMK_NAME_LEN];
+	uint8_t pmkr1_name[FILS_PMK_NAME_LEN];
 	uint8_t *fils_eap_finish_pkt;
 	uint8_t fils_eap_finish_pkt_len;
 	uint8_t *fils_rmsk;
@@ -325,6 +370,8 @@ struct pe_fils_session {
 	uint8_t kek_len;
 	uint8_t tk[MAX_TK_LEN];
 	uint8_t tk_len;
+	uint8_t fils_ft[FILS_FT_MAX_LEN];
+	uint8_t fils_ft_len;
 	uint8_t key_auth[MAX_KEY_AUTH_DATA_LEN];
 	uint8_t key_auth_len;
 	uint8_t ap_key_auth_data[MAX_KEY_AUTH_DATA_LEN];

+ 5 - 2
core/mac/src/pe/include/lim_process_fils.h

@@ -83,12 +83,14 @@ QDF_STATUS lim_create_fils_rik(uint8_t *rrk, uint8_t rrk_len,
 /**
  * lim_update_fils_config()- This API updates fils session info to csr config
  * from join request.
+ * @mac_ctx: pointer to mac context
  * @session: PE session
  * @sme_join_req: pointer to join request
  *
  * Return: None
  */
-void lim_update_fils_config(struct pe_session *session,
+void lim_update_fils_config(struct mac_context *mac_ctx,
+			    struct pe_session *session,
 			    struct join_req *sme_join_req);
 
 /**
@@ -249,7 +251,8 @@ static inline bool lim_is_valid_fils_auth_frame(struct mac_context *mac_ctx,
 }
 
 static inline
-void lim_update_fils_config(struct pe_session *session,
+void lim_update_fils_config(struct mac_context *mac_ctx,
+			    struct pe_session *session,
 			    struct join_req *sme_join_req)
 { }
 

+ 12 - 0
core/mac/src/pe/lim/lim_api.c

@@ -2199,6 +2199,18 @@ pe_roam_synch_callback(struct mac_context *mac_ctx,
 
 	/* Next routine may update nss based on dot11Mode */
 	lim_ft_prepare_add_bss_req(mac_ctx, false, ft_session_ptr, bss_desc);
+	if (session_ptr->is11Rconnection) {
+		ft_session_ptr->is11Rconnection = session_ptr->is11Rconnection;
+		if (session_ptr->fils_info &&
+		    session_ptr->fils_info->fils_ft_len) {
+			ft_session_ptr->fils_info->fils_ft_len =
+			       session_ptr->fils_info->fils_ft_len;
+			qdf_mem_copy(ft_session_ptr->fils_info->fils_ft,
+				     session_ptr->fils_info->fils_ft,
+				     session_ptr->fils_info->fils_ft_len);
+		}
+	}
+
 	roam_sync_ind_ptr->add_bss_params =
 		(tpAddBssParams) ft_session_ptr->ftPEContext.pAddBssReq;
 	add_bss_params = ft_session_ptr->ftPEContext.pAddBssReq;

+ 403 - 32
core/mac/src/pe/lim/lim_process_fils.c

@@ -92,7 +92,32 @@ static uint8_t lim_get_auth_tag_len(enum fils_erp_cryptosuite crypto_suite)
  *
  * Return: Crypto type
  */
-static uint8_t *lim_get_crypto_type(uint8_t akm)
+static uint8_t *lim_get_hash_crypto_type(uint8_t akm)
+{
+	switch (akm) {
+	case eCSR_AUTH_TYPE_FILS_SHA384:
+	case eCSR_AUTH_TYPE_FT_FILS_SHA384:
+		return SHA386_CRYPTO_TYPE;
+	case eCSR_AUTH_TYPE_FILS_SHA256:
+	case eCSR_AUTH_TYPE_FT_FILS_SHA256:
+	default:
+		return SHA256_CRYPTO_TYPE;
+	}
+}
+
+/**
+ * lim_get_hmac_crypto_type()- This API returns crypto type based on akm suite
+ * used.
+ * @akm: akm used for authentication
+ *
+ * This API is used to get the crypto type when HMAC-hash() needs to
+ * be generated.
+ * Eg: PMK = HMAC-Hash(SNonce || ANonce, rMSK [ || DHss ])
+ *     Here HMAC-Hash will be either hmac(sha256) or hmac(sha384)
+ *
+ * Return: Crypto type
+ */
+static uint8_t *lim_get_hmac_crypto_type(uint8_t akm)
 {
 	switch (akm) {
 	case eCSR_AUTH_TYPE_FILS_SHA384:
@@ -105,6 +130,32 @@ static uint8_t *lim_get_crypto_type(uint8_t akm)
 	}
 }
 
+/**
+ * lim_get_Q_length()- This API returns pmk length based on akm used
+ * @akm: akm used for authentication
+ *
+ * [IEEE 802.11ai - 12.7.1.7.3 PMK-R0]
+ * PMK-R0 = L(R0-Key-Data, 0, Q)
+ * where Q is 32 if AKM negotiated is 00-0F-AC:16
+ *       Q is 48 if AKM negotiated is 00-0F-AC:17
+ *
+ * PMK-R0 Derivation is for FT protocol akm only.
+ * So Value of Q is not applicable for non-FT akm.
+ *
+ * Return: Q length
+ */
+static uint8_t lim_get_Q_length(int akm_type)
+{
+	switch (akm_type) {
+	case eCSR_AUTH_TYPE_FT_FILS_SHA256:
+		return FILS_SHA256_Q_LEN;
+	case eCSR_AUTH_TYPE_FT_FILS_SHA384:
+		return FILS_SHA384_Q_LEN;
+	default:
+		return 0;
+	}
+}
+
 /**
  * lim_get_pmk_length()- This API returns pmk length based on akm used
  * @akm: akm used for authentication
@@ -116,12 +167,32 @@ static uint8_t lim_get_pmk_length(int akm_type)
 	switch (akm_type) {
 	case eCSR_AUTH_TYPE_FILS_SHA256:
 	case eCSR_AUTH_TYPE_FT_FILS_SHA256:
-		return FILS_SHA256_PKM_LEN;
+		return FILS_SHA256_PMK_LEN;
 	case eCSR_AUTH_TYPE_FILS_SHA384:
 	case eCSR_AUTH_TYPE_FT_FILS_SHA384:
-		return FILS_SHA384_PKM_LEN;
+		return FILS_SHA384_PMK_LEN;
+	default:
+		return FILS_SHA256_PMK_LEN;
+	}
+}
+
+/**
+ * lim_get_fils_ft_length()- This API returns fils_ft length based on akm used
+ * @akm: akm used for authentication
+ *
+ * FILS_FT is the xx key used in derivation of the PMKR0.
+ *
+ * Return: PMK length
+ */
+static uint8_t lim_get_fils_ft_length(int akm_type)
+{
+	switch (akm_type) {
+	case eCSR_AUTH_TYPE_FT_FILS_SHA256:
+		return FILS_FT_SHA256_LEN;
+	case eCSR_AUTH_TYPE_FT_FILS_SHA384:
+		return FILS_FT_SHA384_LEN;
 	default:
-		return FILS_SHA256_PKM_LEN;
+		return 0;
 	}
 }
 
@@ -413,21 +484,23 @@ static uint32_t lim_process_fils_eap_tlv(struct pe_session *pe_session,
  *
  * Return: QDF_STATUS
  */
-static QDF_STATUS lim_generate_key_data(struct pe_fils_session *fils_info,
-			uint8_t *key_label, uint8_t *data, uint32_t data_len,
-			uint8_t *key_data, uint32_t key_data_len)
+static QDF_STATUS
+lim_generate_key_data(struct pe_fils_session *fils_info,
+		      uint8_t *key_label, uint8_t *data, uint32_t data_len,
+		      uint8_t *key_data, uint32_t key_data_len)
 {
 	QDF_STATUS status;
 
 	if (!fils_info)
 		return QDF_STATUS_E_FAILURE;
 
-	status = lim_get_key_from_prf(lim_get_crypto_type(fils_info->akm),
-			fils_info->fils_pmk,
-			fils_info->fils_pmk_len,
-			key_label, data, data_len, key_data, key_data_len);
+	status = lim_get_key_from_prf(lim_get_hmac_crypto_type(fils_info->akm),
+				      fils_info->fils_pmk,
+				      fils_info->fils_pmk_len, key_label, data,
+				      data_len, key_data, key_data_len);
 	if (status != QDF_STATUS_SUCCESS)
 		pe_err("failed to generate keydata");
+
 	return status;
 }
 
@@ -463,12 +536,12 @@ static void lim_generate_ap_key_auth(struct pe_session *pe_session)
 	qdf_mem_copy(buf, pe_session->selfMacAddr, QDF_MAC_ADDR_SIZE);
 	buf += QDF_MAC_ADDR_SIZE;
 
-	if (qdf_get_hmac_hash(lim_get_crypto_type(fils_info->akm),
-				fils_info->ick, fils_info->ick_len, 1, &addr[0],
-				&len, fils_info->ap_key_auth_data) < 0)
+	if (qdf_get_hmac_hash(lim_get_hmac_crypto_type(fils_info->akm),
+			      fils_info->ick, fils_info->ick_len, 1, &addr[0],
+			      &len, fils_info->ap_key_auth_data) < 0)
 		pe_err("failed to generate PMK id");
 	fils_info->ap_key_auth_len = lim_get_crypto_digest_len(
-					lim_get_crypto_type(fils_info->akm));
+				lim_get_hmac_crypto_type(fils_info->akm));
 	lim_fils_data_dump("AP Key Auth", fils_info->ap_key_auth_data,
 		fils_info->ap_key_auth_len);
 }
@@ -506,12 +579,12 @@ static void lim_generate_key_auth(struct pe_session *pe_session)
 	qdf_mem_copy(buf, pe_session->bssId, QDF_MAC_ADDR_SIZE);
 	buf += QDF_MAC_ADDR_SIZE;
 
-	if (qdf_get_hmac_hash(lim_get_crypto_type(fils_info->akm),
-				fils_info->ick, fils_info->ick_len, 1,
-				&addr[0], &len, fils_info->key_auth) < 0)
+	if (qdf_get_hmac_hash(lim_get_hmac_crypto_type(fils_info->akm),
+			      fils_info->ick, fils_info->ick_len, 1,
+			      &addr[0], &len, fils_info->key_auth) < 0)
 		pe_err("failed to generate key auth");
 	fils_info->key_auth_len = lim_get_crypto_digest_len(
-				lim_get_crypto_type(fils_info->akm));
+				lim_get_hmac_crypto_type(fils_info->akm));
 	lim_fils_data_dump("STA Key Auth",
 			fils_info->key_auth, fils_info->key_auth_len);
 }
@@ -533,10 +606,11 @@ static void lim_get_keys(struct pe_session *pe_session)
 	uint8_t *data;
 	uint8_t data_len;
 	struct pe_fils_session *fils_info = pe_session->fils_info;
-	uint8_t key_data[MAX_ICK_LEN + MAX_KEK_LEN + MAX_TK_LEN] = {0};
+	uint8_t key_data[FILS_MAX_KEY_DATA_LEN] = {0};
 	uint8_t key_data_len;
 	uint8_t ick_len;
 	uint8_t kek_len;
+	uint8_t fils_ft_len = 0;
 	uint8_t tk_len = lim_get_tk_len(pe_session->encryptType);
 	uint8_t *buf;
 
@@ -546,34 +620,69 @@ static void lim_get_keys(struct pe_session *pe_session)
 	ick_len = lim_get_ick_len(fils_info->akm);
 	kek_len = lim_get_kek_len(fils_info->akm);
 
-	key_data_len = ick_len + kek_len + tk_len;
+	if (pe_session->is11Rconnection)
+		fils_ft_len = lim_get_fils_ft_length(fils_info->akm);
+
+	/*
+	 * [IEEE 802.11ai - 12.12.2.5.3]
+	 * FILS-Key-Data = PRF-X(PMK, “FILS PTK Derivation”, SPA || AA ||
+	 *                                              SNonce || ANonce)
+	 * ICK = L(FILS-Key-Data, 0, ICK_bits)
+	 * KEK = L(FILS-Key-Data, ICK_bits, KEK_bits)
+	 * TK = L(FILS-Key-Data, ICK_bits + KEK_bits, TK_bits)
+	 * When doing FT initial mobility domain association using
+	 * FILS authentication,
+	 * FILS-FT = L(FILS-Key-Data, ICK_bits + KEK_bits +
+	 *             TK_bits, FILS-FT_bits)
+	 */
+	key_data_len = ick_len + kek_len + tk_len + fils_ft_len;
 
 	data_len = 2 * SIR_FILS_NONCE_LENGTH + 2 * QDF_MAC_ADDR_SIZE;
 	data = qdf_mem_malloc(data_len);
 	if (!data)
 		return;
 
-	/* Update data */
+	/* data is  SPA || AA ||SNonce || ANonce */
 	buf = data;
 	qdf_mem_copy(buf, pe_session->selfMacAddr, QDF_MAC_ADDR_SIZE);
 	buf += QDF_MAC_ADDR_SIZE;
+
 	qdf_mem_copy(buf, pe_session->bssId, QDF_MAC_ADDR_SIZE);
 	buf += QDF_MAC_ADDR_SIZE;
+
 	qdf_mem_copy(buf, fils_info->fils_nonce, SIR_FILS_NONCE_LENGTH);
 	buf += SIR_FILS_NONCE_LENGTH;
+
 	qdf_mem_copy(buf, fils_info->auth_info.fils_nonce,
 			SIR_FILS_NONCE_LENGTH);
+
+	/* Derive FILS-Key-Data */
 	lim_generate_key_data(fils_info, key_label, data, data_len,
 				key_data, key_data_len);
 	buf = key_data;
+
 	qdf_mem_copy(fils_info->ick, buf, ick_len);
 	fils_info->ick_len = ick_len;
 	buf += ick_len;
+
 	qdf_mem_copy(fils_info->kek, buf, kek_len);
 	fils_info->kek_len = kek_len;
 	buf += kek_len;
+
 	qdf_mem_copy(fils_info->tk, buf, tk_len);
 	fils_info->tk_len = tk_len;
+	buf += tk_len;
+
+	/*
+	 * Derive FILS-FT:
+	 * FILS-FT =
+	 *     L(FILS-Key-Data, ICK_bits + KEK_bits + TK_bits, FILS-FT_bits)
+	 */
+	if (pe_session->is11Rconnection && fils_ft_len) {
+		qdf_mem_copy(fils_info->fils_ft, buf, fils_ft_len);
+		fils_info->fils_ft_len = fils_ft_len;
+	}
+	qdf_mem_zero(data, data_len);
 	qdf_mem_free(data);
 }
 
@@ -593,9 +702,9 @@ static void lim_generate_pmkid(struct pe_session *pe_session)
 	if (!fils_info)
 		return;
 
-	qdf_get_hash(lim_get_crypto_type(fils_info->akm), 1,
-		&fils_info->fils_erp_reauth_pkt,
-		&fils_info->fils_erp_reauth_pkt_len, hash);
+	qdf_get_hash(lim_get_hash_crypto_type(fils_info->akm), 1,
+		     &fils_info->fils_erp_reauth_pkt,
+		     &fils_info->fils_erp_reauth_pkt_len, hash);
 	qdf_mem_copy(fils_info->fils_pmkid, hash, PMKID_LEN);
 	lim_fils_data_dump("PMKID", fils_info->fils_pmkid, PMKID_LEN);
 }
@@ -638,9 +747,9 @@ static void lim_generate_pmk(struct pe_session *pe_session)
 	addr[0] = fils_info->fils_rmsk;
 	len[0] = fils_info->fils_rmsk_len;
 	lim_fils_data_dump("Nonce", nounce, nounce_len);
-	if (qdf_get_hmac_hash(lim_get_crypto_type(fils_info->akm), nounce,
-				nounce_len, 1,
-				&addr[0], &len[0], fils_info->fils_pmk) < 0)
+	if (qdf_get_hmac_hash(lim_get_hmac_crypto_type(fils_info->akm), nounce,
+			      nounce_len, 1, &addr[0], &len[0],
+			      fils_info->fils_pmk) < 0)
 		pe_err("failed to generate PMK");
 }
 
@@ -996,7 +1105,7 @@ void lim_add_fils_data_to_auth_frame(struct pe_session *session,
 	lim_fils_data_dump("FILS RSN", fils_info->rsn_ie,
 			fils_info->rsn_ie_len);
 
-	/**
+	/*
 	 * FT-FILS IEEE-802.11ai specification mandates
 	 * MDIE to be sent in auth frame during initial
 	 * mobility domain association
@@ -1078,6 +1187,231 @@ void lim_add_fils_data_to_auth_frame(struct pe_session *session,
 	body = body + fils_info->fils_erp_reauth_pkt_len;
 }
 
+/**
+ * lim_generate_fils_pmkr0() - Derive PMKR0 and PMKR0-Name from FT-FILS
+ * key data
+ * @pe_session: pointer to pe_session
+ *
+ * Return: QDF_STATUS
+ */
+static QDF_STATUS lim_generate_fils_pmkr0(struct pe_session *pe_session)
+{
+	uint8_t *key_data, *data_buf;
+	uint8_t key_label[] = FT_PMK_R0_KEY_LABEL;
+	uint8_t key_data_len, ssid_length, r0kh_len, mdid_len;
+	uint8_t *r0_key_data;
+	uint8_t *hash;
+	uint8_t r0_key_data_len;
+	uint8_t pmkr0_len;
+	uint8_t *buf, *pmkr0_name_salt;
+	uint8_t *scatter_list[PMKR0_SCATTER_LIST_ELEM];
+	uint32_t len[PMKR0_SCATTER_LIST_ELEM];
+	uint16_t data_buf_len;
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+	struct pe_fils_session *fils_info = pe_session->fils_info;
+	struct bss_description *bss_desc =
+			&pe_session->pLimJoinReq->bssDescription;
+
+	if (!fils_info)
+		return QDF_STATUS_E_FAILURE;
+
+	ssid_length = pe_session->ssId.length;
+	r0kh_len = fils_info->ft_ie.r0kh_id_len;
+	mdid_len = (SIR_MDIE_SIZE - 1);
+	pmkr0_len = lim_get_Q_length(fils_info->akm);
+	r0_key_data_len = pmkr0_len + FILS_FT_PMK_R0_SALT_LEN;
+
+	/*
+	 * [IEEE 802.11ai 12.7.1.7.3]
+	 * 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)
+	 * Length = Q + 128
+	 */
+	key_data_len = (1 + ssid_length + mdid_len + 1 + r0kh_len +
+			QDF_MAC_ADDR_SIZE);
+	pe_debug("FT-FILS: ssid_length:%d MDID len:%d R0KH len:%d key_data len:%d",
+		 ssid_length, mdid_len, r0kh_len, key_data_len);
+
+	data_buf_len = (key_data_len + FILS_FT_MAX_R0_KEY_DATA_LEN +
+			SHA384_DIGEST_SIZE);
+	data_buf = qdf_mem_malloc(data_buf_len);
+	if (!data_buf)
+		return QDF_STATUS_E_NOMEM;
+
+	key_data = &data_buf[0];
+	r0_key_data = &data_buf[key_data_len];
+	hash = &data_buf[key_data_len + FILS_FT_MAX_R0_KEY_DATA_LEN];
+
+	/*
+	 * key_data is (SSIDlength || SSID || MDID || R0KHlength || R0KH-ID ||
+	 *              S0KH-ID)
+	 */
+	buf = key_data;
+
+	*key_data = pe_session->ssId.length;
+	key_data += 1;
+
+	qdf_mem_copy(key_data, pe_session->ssId.ssId, ssid_length);
+	key_data += ssid_length;
+
+	qdf_mem_copy(key_data, bss_desc->mdie, mdid_len);
+	key_data += mdid_len;
+
+	*key_data = r0kh_len;
+	key_data += 1;
+
+	qdf_mem_copy(key_data, fils_info->ft_ie.r0kh_id, r0kh_len);
+	key_data += r0kh_len;
+
+	qdf_mem_copy(key_data, pe_session->selfMacAddr, QDF_MAC_ADDR_SIZE);
+
+	pe_debug("FT-FILS: Derive R0-Key-Data");
+	status = lim_get_key_from_prf(lim_get_hmac_crypto_type(fils_info->akm),
+				      fils_info->fils_ft,
+				      fils_info->fils_ft_len, key_label,
+				      buf, key_data_len, r0_key_data,
+				      r0_key_data_len);
+	if (QDF_IS_STATUS_ERROR(status))
+		goto free_buf;
+
+	/* PMK-R0 is the first Q bytes of R0-Key-Data */
+	qdf_mem_copy(fils_info->pmkr0, r0_key_data, pmkr0_len);
+	fils_info->pmkr0_len = pmkr0_len;
+
+	/* PMK-R0Name-Salt = L(R0-Key-Data, Q, 128) */
+	pmkr0_name_salt = r0_key_data + pmkr0_len;
+
+	/*
+	 * [IEEE 802.11-2016 12.7.1.7.3]
+	 * PMKR0Name = Truncate-128(Hash("FT-R0N" || PMK-R0Name-Salt)
+	 * The Hash function requires the crypto type, number of scatterlist
+	 * parameters, scatterlist, lengths of scatterlist arguments, pointer
+	 * to output hash.
+	 *
+	 * The scatterlist has two arguments - label "FT-R0N" and
+	 * PMK-R0Name-Salt
+	 *
+	 */
+	scatter_list[SCTR_LST_ELEM0] = FT_PMK_R0_NAME_KEY_LABEL;
+	len[SCTR_LST_ELEM0] = SCTR_LST_R0_LABEL_LEN;
+	scatter_list[SCTR_LST_ELEM1] = pmkr0_name_salt;
+	len[SCTR_LST_ELEM1] = FILS_FT_PMK_R0_SALT_LEN;
+
+	pe_debug("FT-FILS: Derive PMK-R0 Name");
+	if (qdf_get_hash(lim_get_hash_crypto_type(fils_info->akm),
+			 PMKR0_SCATTER_LIST_ELEM, scatter_list, len,
+			 hash) < 0) {
+		pe_err("FT-FILS: PMK-R0Name derivation failed");
+		status = QDF_STATUS_E_FAILURE;
+		goto free_buf;
+	}
+	qdf_mem_copy(fils_info->pmkr0_name, hash, FILS_PMK_NAME_LEN);
+
+free_buf:
+	qdf_mem_zero(data_buf, data_buf_len);
+	qdf_mem_free(data_buf);
+
+	return status;
+}
+
+/**
+ * lim_generate_fils_pmkr1_name() - Derive PMKR1 and PMKR1-Name from FT-FILS
+ * key data
+ * @pe_session: pointer to pe_session
+ *
+ * Return: None
+ */
+static QDF_STATUS lim_generate_fils_pmkr1_name(struct pe_session *pe_session)
+{
+	uint8_t *hash;
+	uint8_t *scatter_list[PMKR1_SCATTER_LIST_ELEM];
+	uint32_t len[PMKR1_SCATTER_LIST_ELEM];
+	uint8_t *buf;
+	uint8_t gp_mgmt_cipher_suite[4];
+	struct pe_fils_session *fils_info = pe_session->fils_info;
+
+	if (!fils_info)
+		return QDF_STATUS_E_FAILURE;
+
+	hash = qdf_mem_malloc(SHA384_DIGEST_SIZE);
+	if (!hash)
+		return QDF_STATUS_E_NOMEM;
+
+	/*
+	 * [IEEE 802.11-2016 12.7.1.7.4]
+	 * PMKR1Name = Truncate-128(Hash("FT-R1N" || PMKR0Name ||
+	 *                               R1KH-ID || S1KH-ID))
+	 */
+	scatter_list[SCTR_LST_ELEM0] = FT_PMK_R1_NAME_KEY_LABEL;
+	len[SCTR_LST_ELEM0] = SCTR_LST_R1_LABEL_LEN;
+	scatter_list[SCTR_LST_ELEM1] = fils_info->pmkr0_name;
+	len[SCTR_LST_ELEM1] = FILS_PMK_NAME_LEN;
+	scatter_list[SCTR_LST_ELEM2] = fils_info->ft_ie.r1kh_id;
+	len[SCTR_LST_ELEM2] = FT_R1KH_ID_LEN;
+	scatter_list[SCTR_LST_ELEM3] = pe_session->selfMacAddr;
+	len[SCTR_LST_ELEM3] = QDF_MAC_ADDR_SIZE;
+
+	if (qdf_get_hash(lim_get_hash_crypto_type(fils_info->akm),
+			 PMKR1_SCATTER_LIST_ELEM, scatter_list, len,
+			 hash) < 0) {
+		qdf_mem_zero(hash, SHA384_DIGEST_SIZE);
+		qdf_mem_free(hash);
+		return QDF_STATUS_E_FAILURE;
+	}
+	qdf_mem_copy(fils_info->pmkr1_name, hash, FILS_PMK_NAME_LEN);
+
+	if (fils_info->rsn_ie_len) {
+		if (fils_info->group_mgmt_cipher_present) {
+			/*
+			 * If 802.11w is enabled, group management cipher
+			 * suite is added at the end of RSN IE after
+			 * PMKID. Since the driver has opaque RSN IE
+			 * saved in fils_session, strip the last 4 bytes
+			 * of the RSN IE to get group mgmt cipher suite.
+			 * Then copy the PMKID followed by the grp mgmt cipher
+			 * suite.
+			 */
+			buf = fils_info->rsn_ie + fils_info->rsn_ie_len - 4;
+			qdf_mem_copy(gp_mgmt_cipher_suite, buf, 4);
+			buf -= 2;
+		} else {
+			buf = fils_info->rsn_ie + fils_info->rsn_ie_len;
+		}
+
+		/*
+		 * Add PMKID count as 1. PMKID count field is 2 bytes long.
+		 * Copy the PMKR1-Name in the PMKID list at the end of the
+		 * RSN IE.
+		 */
+		*buf = 1;
+		buf += 2;
+		qdf_mem_copy(buf, fils_info->pmkr1_name, FILS_PMK_NAME_LEN);
+		if (fils_info->group_mgmt_cipher_present) {
+			fils_info->rsn_ie_len += FILS_PMK_NAME_LEN;
+			fils_info->rsn_ie[1] += (FILS_PMK_NAME_LEN);
+		} else {
+			fils_info->rsn_ie_len += (2 + FILS_PMK_NAME_LEN);
+			fils_info->rsn_ie[1] += (2 + FILS_PMK_NAME_LEN);
+		}
+
+		if (fils_info->group_mgmt_cipher_present) {
+			buf += FILS_PMK_NAME_LEN;
+			qdf_mem_copy(buf, gp_mgmt_cipher_suite, 4);
+		}
+	} else {
+		pe_err("FT-FILS: RSN IE not present");
+		qdf_mem_zero(hash, SHA384_DIGEST_SIZE);
+		qdf_mem_free(hash);
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	qdf_mem_zero(hash, SHA384_DIGEST_SIZE);
+	qdf_mem_free(hash);
+	return QDF_STATUS_SUCCESS;
+}
+
 /**
  * lim_process_fils_auth_frame2()- This API process fils data from auth response
  * @mac_ctx: mac context
@@ -1094,6 +1428,7 @@ bool lim_process_fils_auth_frame2(struct mac_context *mac_ctx,
 	uint32_t ret;
 	bool pmkid_found = false;
 	tDot11fIERSN dot11f_ie_rsn = {0};
+	QDF_STATUS status;
 
 	if (!pe_session->fils_info)
 		return false;
@@ -1138,16 +1473,28 @@ bool lim_process_fils_auth_frame2(struct mac_context *mac_ctx,
 			return false;
 	}
 	lim_get_keys(pe_session);
+	if (pe_session->is11Rconnection) {
+		status = lim_generate_fils_pmkr0(pe_session);
+		if (QDF_IS_STATUS_ERROR(status))
+			return false;
+
+		status = lim_generate_fils_pmkr1_name(pe_session);
+		if (QDF_IS_STATUS_ERROR(status))
+			return false;
+	}
 	lim_generate_key_auth(pe_session);
 	lim_generate_ap_key_auth(pe_session);
 	return true;
 }
 
-void lim_update_fils_config(struct pe_session *session,
+void lim_update_fils_config(struct mac_context *mac_ctx,
+			    struct pe_session *session,
 			    struct join_req *sme_join_req)
 {
 	struct pe_fils_session *csr_fils_info;
 	struct cds_fils_connection_info *fils_config_info;
+	tDot11fIERSN dot11f_ie_rsn = {0};
+	uint32_t ret;
 
 	fils_config_info = &sme_join_req->fils_con_info;
 	csr_fils_info = session->fils_info;
@@ -1199,6 +1546,26 @@ void lim_update_fils_config(struct pe_session *session,
 	qdf_mem_copy(csr_fils_info->rsn_ie,
 			sme_join_req->rsnIE.rsnIEdata,
 			sme_join_req->rsnIE.length);
+	/*
+	 * When AP is MFP capable and STA is also MFP capable,
+	 * the supplicant fills the RSN IE with PMKID count as 0
+	 * and PMKID as 0, then appends the group management cipher
+	 * suite. This opaque RSN IE is copied into fils_info in pe
+	 * session. For FT-FILS association, STA has to fill the
+	 * PMKR0 derived after authentication response is received from
+	 * the AP. So unpack the RSN IE to find if group management cipher
+	 * suite is present and based on this RSN IE will be constructed in
+	 * lim_generate_fils_pmkr1_name() for FT-FILS connection.
+	 */
+	ret = dot11f_unpack_ie_rsn(mac_ctx, csr_fils_info->rsn_ie + 2,
+				   csr_fils_info->rsn_ie_len - 2,
+				   &dot11f_ie_rsn, 0);
+	if (DOT11F_SUCCEEDED(ret)) {
+		csr_fils_info->group_mgmt_cipher_present =
+			dot11f_ie_rsn.gp_mgmt_cipher_suite_present;
+	} else {
+		pe_err("FT-FILS: Invalid RSN IE");
+	}
 
 	csr_fils_info->fils_pmk_len = fils_config_info->pmk_len;
 	if (fils_config_info->pmk_len) {
@@ -1918,7 +2285,11 @@ void lim_update_fils_rik(struct pe_session *pe_session,
 
 	roam_fils_params->rik_length = pe_fils_info->fils_rik_len;
 	qdf_mem_copy(roam_fils_params->rik, pe_fils_info->fils_rik,
-			roam_fils_params->rik_length);
-	pe_debug("fils rik len %d", roam_fils_params->rik_length);
+		     roam_fils_params->rik_length);
+	qdf_mem_copy(roam_fils_params->fils_ft, pe_fils_info->fils_ft,
+		     pe_fils_info->fils_ft_len);
+	roam_fils_params->fils_ft_len = pe_fils_info->fils_ft_len;
+	pe_debug("fils rik len %d ft-len:%d", roam_fils_params->rik_length,
+		 pe_fils_info->fils_ft_len);
 }
 #endif

+ 1 - 1
core/mac/src/pe/lim/lim_process_sme_req_messages.c

@@ -1516,7 +1516,7 @@ __lim_process_sme_join_req(struct mac_context *mac_ctx, void *msg_buf)
 		session->txLdpcIniFeatureEnabled =
 			sme_join_req->txLdpcIniFeatureEnabled;
 
-		lim_update_fils_config(session, sme_join_req);
+		lim_update_fils_config(mac_ctx, session, sme_join_req);
 		lim_update_sae_config(session, sme_join_req);
 		if (session->bssType == eSIR_INFRASTRUCTURE_MODE) {
 			session->limSystemRole = eLIM_STA_ROLE;

+ 4 - 0
core/wma/src/wma_scan_roam.c

@@ -250,6 +250,10 @@ static void wma_roam_scan_fill_fils_params(tp_wma_handle wma_handle,
 	qdf_mem_copy(dst_fils_params->rik, src_fils_params->rik,
 		     dst_fils_params->rik_length);
 
+	dst_fils_params->fils_ft_len = src_fils_params->fils_ft_len;
+	qdf_mem_copy(dst_fils_params->fils_ft, src_fils_params->fils_ft,
+		     dst_fils_params->fils_ft_len);
+
 	dst_fils_params->realm_len = src_fils_params->realm_len;
 	qdf_mem_copy(dst_fils_params->realm, src_fils_params->realm,
 		     dst_fils_params->realm_len);