[12/14] PASN: Support PASN with FT key derivation
diff mbox series

Message ID 20200224091602.15306-13-ilan.peer@intel.com
State Deferred
Headers show
Series
  • Support PASN with SAE, FILS and FT
Related show

Commit Message

Peer, Ilan Feb. 24, 2020, 9:16 a.m. UTC
Add support for PASN authentication with FT key derivation:

- As Draft P802.11az_D2.0 states that wrapped data is optional and
  is only needed for further validation of the FT security parameters,
  do not include them in the first PASN frame.

- PASN with FT key derivation requires knowledge of the PMK-R1 and
  PMK-R1-Name for the target AP. As the WPA SM stores PMK-R1 etc.
  only for the currently associated AP, store the mapping of
  BSSID to R1KH-ID for each previous association, so the R1KH-ID
  could be used to derive PMK-R1 and PMK-R1-Name. Do so instead
  of storing the PMK-R1 to avoid maintaining keys that might not
  be used.

Signed-off-by: Ilan Peer <ilan.peer@intel.com>
---
 src/rsn_supp/wpa.c                |  5 ++
 src/rsn_supp/wpa.h                | 17 ++++++
 src/rsn_supp/wpa_ft.c             | 97 +++++++++++++++++++++++++++++++
 src/rsn_supp/wpa_i.h              | 16 +++++
 wpa_supplicant/pasn_supplicant.c  | 66 +++++++++++++++++----
 wpa_supplicant/wpa_supplicant_i.h |  6 ++
 6 files changed, 197 insertions(+), 10 deletions(-)

Patch
diff mbox series

diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index b158b37cdb..6736eeb107 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -3613,6 +3613,11 @@  void wpa_sm_drop_sa(struct wpa_sm *sm)
 	sm->pmk_r0_len = 0;
 	os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1));
 	sm->pmk_r1_len = 0;
+#ifdef CONFIG_PASN
+	os_free(sm->pasn_r1kh);
+	sm->pasn_r1kh = NULL;
+	sm->n_pasn_r1kh = 0;
+#endif /* CONFIG_PASN */
 #endif /* CONFIG_IEEE80211R */
 }
 
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index f72725902f..569759c0a0 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -409,6 +409,13 @@  int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
 int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
 			 const u8 *mdie);
 
+#ifdef CONFIG_PASN
+
+int wpa_pasn_ft_derive_pmk_r1(struct wpa_sm *sm, int akmp, const u8 *r1kh_id,
+			      u8 *pmk_r1, size_t *pmk_r1_len, u8 *pmk_r1_name);
+
+#endif /* CONFIG_PASN */
+
 #else /* CONFIG_IEEE80211R */
 
 static inline int
@@ -452,6 +459,16 @@  wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
 	return -1;
 }
 
+#ifdef CONFIG_PASN
+
+int wpa_pasn_ft_derive_pmk_r1(struct wpa_sm *sm, int akmp, const u8 *r1kh_id,
+			      u8 *pmk_r1, size_t *pmk_r1_len, u8 *pmk_r1_name)
+{
+	return -1;
+}
+
+#endif /* CONFIG_PASN */
+
 #endif /* CONFIG_IEEE80211R */
 
 
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index 027a24ffcd..bb466ec3c2 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -23,6 +23,10 @@ 
 
 #ifdef CONFIG_IEEE80211R
 
+#ifdef CONFIG_PASN
+static void wpa_ft_pasn_store_r1kh(struct wpa_sm *sm, const u8 *bssid);
+#endif /* CONFIG_PASN */
+
 int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
 		      const struct wpa_eapol_key *key, struct wpa_ptk *ptk)
 {
@@ -61,6 +65,11 @@  int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
 	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, sm->pmk_r1_len);
 	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name,
 		    WPA_PMK_NAME_LEN);
+
+#ifdef CONFIG_PASN
+	wpa_ft_pasn_store_r1kh(sm, src_addr);
+#endif /* CONFIG_PASN*/
+
 	return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce, anonce,
 				 sm->own_addr, sm->bssid, sm->pmk_r1_name, ptk,
 				 ptk_name, sm->key_mgmt, sm->pairwise_cipher,
@@ -633,6 +642,11 @@  int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
 		    sm->pmk_r1_name, WPA_PMK_NAME_LEN);
 
 	bssid = target_ap;
+
+#ifdef CONFIG_PASN
+	wpa_ft_pasn_store_r1kh(sm, bssid);
+#endif /* CONFIG_PASN*/
+
 	if (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce,
 			      anonce, sm->own_addr, bssid,
 			      sm->pmk_r1_name, &sm->ptk, ptk_name, sm->key_mgmt,
@@ -1168,4 +1182,87 @@  int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
 	return 0;
 }
 
+#ifdef CONFIG_PASN
+
+static struct pasn_ft_r1kh *wpa_ft_pasn_get_r1kh(struct wpa_sm *sm,
+						 const u8 *bssid)
+{
+	size_t i;
+
+	for (i = 0; i < sm->n_pasn_r1kh; i++)
+		if (os_memcmp(sm->pasn_r1kh[i].bssid, bssid, ETH_ALEN) == 0)
+			return &sm->pasn_r1kh[i];
+
+	return NULL;
+}
+
+
+static void wpa_ft_pasn_store_r1kh(struct wpa_sm *sm, const u8 *bssid)
+{
+	struct pasn_ft_r1kh *tmp = wpa_ft_pasn_get_r1kh(sm, bssid);
+
+	if (tmp)
+		return;
+
+	tmp = os_realloc_array(sm->pasn_r1kh, sm->n_pasn_r1kh + 1,
+			       sizeof(*tmp));
+	if (!tmp) {
+		wpa_printf(MSG_DEBUG, "PASN: FT: failed to store r1kh");
+		return;
+	}
+
+	sm->pasn_r1kh = tmp;
+	tmp = &sm->pasn_r1kh[sm->n_pasn_r1kh];
+
+	wpa_printf(MSG_DEBUG, "PASN: FT: store R1KH for " MACSTR,
+		   MAC2STR(bssid));
+
+	os_memcpy(tmp->bssid, bssid, ETH_ALEN);
+	os_memcpy(tmp->r1kh_id, sm->r1kh_id, FT_R1KH_ID_LEN);
+
+	sm->n_pasn_r1kh++;
+}
+
+
+int wpa_pasn_ft_derive_pmk_r1(struct wpa_sm *sm, int akmp, const u8 *bssid,
+			      u8 *pmk_r1, size_t *pmk_r1_len, u8 *pmk_r1_name)
+{
+	struct pasn_ft_r1kh *r1kh_entry;
+
+	if (sm->key_mgmt != (unsigned int)akmp) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: FT: key management mismatch: %u != %u",
+			   sm->key_mgmt, akmp);
+		return -1;
+	}
+
+	r1kh_entry = wpa_ft_pasn_get_r1kh(sm, bssid);
+	if (!r1kh_entry) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: FT: Cannot find R1KH-ID for " MACSTR,
+			   MAC2STR(bssid));
+		return -1;
+	}
+
+	/*
+	 * Note: PMK R0 etc. were already derived and are maintained by the SM,
+	 * and as the same key hierarchy is used, there is no need to derive
+	 * them again, so only derive PMK R1 etc.
+	 */
+	if (wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_len, sm->pmk_r0_name,
+			      r1kh_entry->r1kh_id, sm->own_addr, pmk_r1,
+			      pmk_r1_name) < 0)
+		return -1;
+
+	*pmk_r1_len = sm->pmk_r0_len;
+
+	wpa_hexdump_key(MSG_DEBUG, "PASN: FT: PMK-R1", pmk_r1, sm->pmk_r0_len);
+	wpa_hexdump(MSG_DEBUG, "PASN: FT: PMKR1Name", pmk_r1_name,
+		    WPA_PMK_NAME_LEN);
+
+	return 0;
+}
+
+#endif /* CONFIG_PASN */
+
 #endif /* CONFIG_IEEE80211R */
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index b9164fa111..2dc7c9ebd9 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -14,6 +14,11 @@ 
 struct wpa_tdls_peer;
 struct wpa_eapol_key;
 
+struct pasn_ft_r1kh {
+	u8 bssid[ETH_ALEN];
+	u8 r1kh_id[FT_R1KH_ID_LEN];
+};
+
 /**
  * struct wpa_sm - Internal WPA state machine data
  */
@@ -147,6 +152,17 @@  struct wpa_sm {
 	u8 mdie_ft_capab; /* FT Capability and Policy from target AP MDIE */
 	u8 *assoc_resp_ies; /* MDIE and FTIE from (Re)Association Response */
 	size_t assoc_resp_ies_len;
+#ifdef CONFIG_PASN
+	/*
+	 * Currently, the WPA SM stores the PMK-R1, PMK-R1-Name and R1KH-ID only
+	 * for the current association. As PMK-R1 is required in order to
+	 * perform PASN authentication with FT, store the R1KH-ID for previous
+	 * associations, which would later be used to derive the PMK-R1 as part
+	 * of the PASN authentication flow.
+	 */
+	struct pasn_ft_r1kh *pasn_r1kh;
+	size_t n_pasn_r1kh;
+#endif /* CONFIG_PASN*/
 #endif /* CONFIG_IEEE80211R */
 
 #ifdef CONFIG_P2P
diff --git a/wpa_supplicant/pasn_supplicant.c b/wpa_supplicant/pasn_supplicant.c
index 3d7971fc9e..c348a1341d 100644
--- a/wpa_supplicant/pasn_supplicant.c
+++ b/wpa_supplicant/pasn_supplicant.c
@@ -592,6 +592,12 @@  static struct wpabuf *wpas_pasn_get_wrapped_data(struct wpa_supplicant *wpa_s)
 	case WPA_KEY_MGMT_FT_PSK:
 	case WPA_KEY_MGMT_FT_IEEE8021X:
 	case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+		/*
+		 * wrapped data with these AKMs is optional and is only needed
+		 * for further validation of FT security parameters. For now do
+		 * not use them.
+		 */
+		return NULL;
 	default:
 		wpa_printf(MSG_ERROR,
 			   "PASN: TODO: Wrapped data for akmp=0x%x",
@@ -616,7 +622,12 @@  static u8 wpas_pasn_get_wrapped_data_format(struct wpas_pasn *pasn)
 	case WPA_KEY_MGMT_FT_PSK:
 	case WPA_KEY_MGMT_FT_IEEE8021X:
 	case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
-		return WPA_PASN_WRAPPED_DATA_FT;
+		/*
+		 * wrapped data with these AKMs is optional and is only needed
+		 * for further validation of FT security parameters. For now do
+		 * not use them.
+		 */
+		return WPA_PASN_NO_WRAPPED_DATA;
 	case WPA_KEY_MGMT_PASN:
 	default:
 		return WPA_PASN_NO_WRAPPED_DATA;
@@ -629,7 +640,7 @@  static struct wpabuf *wpas_pasn_build_auth_1(struct wpa_supplicant *wpa_s,
 {
 	struct wpas_pasn *pasn = &wpa_s->pasn;
 	struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL;
-	struct rsn_pmksa_cache_entry *pmksa;
+	const u8 *pmkid;
 	u8 wrapped_data;
 	int ret;
 
@@ -657,22 +668,37 @@  static struct wpabuf *wpas_pasn_build_auth_1(struct wpa_supplicant *wpa_s,
 				   wpa_s->own_addr, pasn->bssid,
 				   pasn->trans_seq + 1, WLAN_STATUS_SUCCESS);
 
-	if (wrapped_data != WPA_PASN_NO_WRAPPED_DATA) {
-		pmksa = wpa_sm_pmksa_cache_get(wpa_s->wpa, pasn->bssid,
+	pmkid = NULL;
+	if (wpa_key_mgmt_ft(pasn->akmp)) {
+		ret = wpa_pasn_ft_derive_pmk_r1(wpa_s->wpa, pasn->akmp,
+						pasn->bssid,
+						pasn->pmk_r1,
+						&pasn->pmk_r1_len,
+						pasn->pmk_r1_name);
+		if (ret) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: FT: failed to derive keys");
+			goto fail;
+		}
+
+		pmkid = pasn->pmk_r1_name;
+	} else if (wrapped_data != WPA_PASN_NO_WRAPPED_DATA) {
+		struct rsn_pmksa_cache_entry *pmksa =
+			wpa_sm_pmksa_cache_get(wpa_s->wpa, pasn->bssid,
 					       NULL, NULL,
 					       pasn->akmp);
+		if (pmksa)
+			pmkid = pmksa->pmkid;
 
 		/*
-		 * Note: even when PMKSA is available, also add wrapped data as
-		 * it is possible that the PMKID is no longer valid at the AP.
+		 * Note: even when PMKSA is available, also add wrapped
+		 * data as it is possible that the PMKID is no longer
+		 * valid at the AP.
 		 */
 		wrapped_data_buf = wpas_pasn_get_wrapped_data(wpa_s);
-	} else {
-		pmksa = NULL;
 	}
 
-	wpa_pasn_add_rsne(buf, pmksa ? pmksa->pmkid : NULL,
-			  pasn->akmp, pasn->cipher);
+	wpa_pasn_add_rsne(buf, pmkid, pasn->akmp, pasn->cipher);
 
 	if (!wrapped_data_buf)
 		wrapped_data = WPA_PASN_NO_WRAPPED_DATA;
@@ -818,6 +844,11 @@  static void wpas_pasn_reset(struct wpa_supplicant *wpa_s)
 	os_memset(&pasn->fils, 0, sizeof(pasn->fils));
 #endif /* CONFIG_FILS*/
 
+#ifdef CONFIG_IEEE80211R
+	os_memset(pasn->pmk_r1, 0, sizeof(pasn->pmk_r1));
+	os_memset(pasn->pmk_r1_name, 0, sizeof(pasn->pmk_r1_name));
+	pasn->pmk_r1_len = 0;
+#endif /* CONFIG_IEEE80211R */
 	pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
 }
 
@@ -843,6 +874,21 @@  static int wpas_pasn_set_pmk(struct wpa_supplicant *wpa_s,
 		return 0;
 	}
 
+	if (wpa_key_mgmt_ft(pasn->akmp)) {
+#ifdef CONFIG_IEEE80211R
+		wpa_printf(MSG_DEBUG, "PASN: FT: using PMK R1");
+
+		pasn->pmk_len = pasn->pmk_r1_len;
+		os_memcpy(pasn->pmk, pasn->pmk_r1, pasn->pmk_r1_len);
+		pasn->using_pmksa = 1;
+		return 0;
+
+#endif /* CONFIG_IEEE80211R */
+
+		wpa_printf(MSG_DEBUG, "PASN: FT: not supported");
+		return -1;
+	}
+
 	if (rsn_data->num_pmkid) {
 		struct rsn_pmksa_cache_entry *pmksa =
 			wpa_sm_pmksa_cache_get(wpa_s->wpa, pasn->bssid,
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index f5951a8496..6d49eea6c7 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -542,6 +542,12 @@  struct wpas_pasn {
 #ifdef CONFIG_FILS
 	struct pasn_fils fils;
 #endif /* CONFIG_FILS */
+
+#ifdef CONFIG_IEEE80211R
+	u8 pmk_r1[PMK_LEN_MAX];
+	size_t pmk_r1_len;
+	u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+#endif /* CONFIG_IEEE80211R */
 };
 
 #endif /* CONFIG_PASN */