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 */
}
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
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 */
#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_PASN
+static void wpa_ft_pasn_store_r1kh(struct wpa_sm *sm, const u8 *bssid);
+#else /* 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)
{
sm->r1kh_id, sm->own_addr, sm->pmk_r1,
sm->pmk_r1_name) < 0)
return -1;
+
+ wpa_ft_pasn_store_r1kh(sm, src_addr);
+
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,
sm->pmk_r1_len = sm->pmk_r0_len;
bssid = target_ap;
+
+ wpa_ft_pasn_store_r1kh(sm, bssid);
+
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,
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
+ * state machine, 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 */
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
*/
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 state machine stores the PMK-R1, PMK-R1-Name and
+ * R1KH-ID only for the current association. As PMK-R1 is required 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;
+ unsigned int n_pasn_r1kh;
+#endif /* CONFIG_PASN */
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_P2P
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",
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_WRAPPED_DATA_NO;
case WPA_KEY_MGMT_PASN:
default:
return WPA_PASN_WRAPPED_DATA_NO;
{
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;
wpa_s->own_addr, pasn->bssid,
pasn->trans_seq + 1, WLAN_STATUS_SUCCESS);
- if (wrapped_data != WPA_PASN_WRAPPED_DATA_NO) {
+ 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_WRAPPED_DATA_NO) {
+ struct rsn_pmksa_cache_entry *pmksa;
+
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.
*/
wrapped_data_buf = wpas_pasn_get_wrapped_data(wpa_s);
- } else {
- pmksa = NULL;
}
- if (wpa_pasn_add_rsne(buf, pmksa ? pmksa->pmkid : NULL,
- pasn->akmp, pasn->cipher) < 0)
+ if (wpa_pasn_add_rsne(buf, pmkid, pasn->akmp, pasn->cipher) < 0)
goto fail;
if (!wrapped_data_buf)
os_memset(&pasn->fils, 0, sizeof(pasn->fils));
#endif /* CONFIG_FILS*/
+#ifdef CONFIG_IEEE80211R
+ forced_memzero(pasn->pmk_r1, sizeof(pasn->pmk_r1));
+ pasn->pmk_r1_len = 0;
+ os_memset(pasn->pmk_r1_name, 0, sizeof(pasn->pmk_r1_name));
+#endif /* CONFIG_IEEE80211R */
pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
}
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 = true;
+ return 0;
+#else /* CONFIG_IEEE80211R */
+ wpa_printf(MSG_DEBUG, "PASN: FT: Not supported");
+ return -1;
+#endif /* CONFIG_IEEE80211R */
+ }
+
if (rsn_data->num_pmkid) {
struct rsn_pmksa_cache_entry *pmksa;
#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 */