[PATCH v2 12/14] PASN: Support PASN with FT key derivation
Ilan Peer
ilan.peer at intel.com
Wed Dec 16 06:01:03 EST 2020
Add support for PASN authentication with FT key derivation:
- As Draft P802.11az_D2.6 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 at 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(-)
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index a18a8fa7f9..ed9aaae1a3 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -3819,6 +3819,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 cb268648a7..2b181653eb 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -432,6 +432,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
@@ -475,6 +482,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 7fa47a1e5a..cbf3caeb01 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -24,6 +24,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)
{
@@ -56,6 +60,11 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
sm->r1kh_id, sm->own_addr, sm->pmk_r1,
sm->pmk_r1_name) < 0)
return -1;
+
+#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,
@@ -649,6 +658,11 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
sm->pmk_r1_len = sm->pmk_r0_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,
@@ -1242,4 +1256,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 d188a33824..72a6023dd3 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
*/
@@ -152,6 +157,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 9ab02192b2..bdf72d51c6 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 244c43f2ed..3114b60dd2 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -565,6 +565,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 */
--
2.17.1
More information about the Hostap
mailing list