PASN: Support PASN with FT key derivation

Add support for PASN authentication with FT key derivation:

- As IEEE 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 state machine 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>
This commit is contained in:
Ilan Peer 2020-12-16 13:01:03 +02:00 committed by Jouni Malinen
parent 09a9e9939c
commit 5c65ad6c0b
6 changed files with 194 additions and 7 deletions

View file

@ -3819,6 +3819,11 @@ void wpa_sm_drop_sa(struct wpa_sm *sm)
sm->pmk_r0_len = 0; sm->pmk_r0_len = 0;
os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1)); os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1));
sm->pmk_r1_len = 0; 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 */ #endif /* CONFIG_IEEE80211R */
} }

View file

@ -430,6 +430,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, int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
const u8 *mdie); 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 */ #else /* CONFIG_IEEE80211R */
static inline int static inline int
@ -473,6 +480,16 @@ wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
return -1; 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 */ #endif /* CONFIG_IEEE80211R */

View file

@ -24,6 +24,15 @@
#ifdef 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, int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
const struct wpa_eapol_key *key, struct wpa_ptk *ptk) const struct wpa_eapol_key *key, struct wpa_ptk *ptk)
{ {
@ -56,6 +65,9 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
sm->r1kh_id, sm->own_addr, sm->pmk_r1, sm->r1kh_id, sm->own_addr, sm->pmk_r1,
sm->pmk_r1_name) < 0) sm->pmk_r1_name) < 0)
return -1; 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, 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, sm->own_addr, sm->bssid, sm->pmk_r1_name, ptk,
ptk_name, sm->key_mgmt, sm->pairwise_cipher, ptk_name, sm->key_mgmt, sm->pairwise_cipher,
@ -649,6 +661,9 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
sm->pmk_r1_len = sm->pmk_r0_len; sm->pmk_r1_len = sm->pmk_r0_len;
bssid = target_ap; 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, if (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce,
anonce, sm->own_addr, bssid, anonce, sm->own_addr, bssid,
sm->pmk_r1_name, &sm->ptk, ptk_name, sm->key_mgmt, sm->pmk_r1_name, &sm->ptk, ptk_name, sm->key_mgmt,
@ -1242,4 +1257,88 @@ int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
return 0; 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 */ #endif /* CONFIG_IEEE80211R */

View file

@ -14,6 +14,11 @@
struct wpa_tdls_peer; struct wpa_tdls_peer;
struct wpa_eapol_key; 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 * 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 mdie_ft_capab; /* FT Capability and Policy from target AP MDIE */
u8 *assoc_resp_ies; /* MDIE and FTIE from (Re)Association Response */ u8 *assoc_resp_ies; /* MDIE and FTIE from (Re)Association Response */
size_t assoc_resp_ies_len; 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 */ #endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_P2P #ifdef CONFIG_P2P

View file

@ -569,6 +569,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_PSK:
case WPA_KEY_MGMT_FT_IEEE8021X: case WPA_KEY_MGMT_FT_IEEE8021X:
case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: 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: default:
wpa_printf(MSG_ERROR, wpa_printf(MSG_ERROR,
"PASN: TODO: Wrapped data for akmp=0x%x", "PASN: TODO: Wrapped data for akmp=0x%x",
@ -593,7 +599,12 @@ static u8 wpas_pasn_get_wrapped_data_format(struct wpas_pasn *pasn)
case WPA_KEY_MGMT_FT_PSK: case WPA_KEY_MGMT_FT_PSK:
case WPA_KEY_MGMT_FT_IEEE8021X: case WPA_KEY_MGMT_FT_IEEE8021X:
case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: 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: case WPA_KEY_MGMT_PASN:
default: default:
return WPA_PASN_WRAPPED_DATA_NO; return WPA_PASN_WRAPPED_DATA_NO;
@ -605,7 +616,7 @@ static struct wpabuf * wpas_pasn_build_auth_1(struct wpa_supplicant *wpa_s)
{ {
struct wpas_pasn *pasn = &wpa_s->pasn; struct wpas_pasn *pasn = &wpa_s->pasn;
struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL; struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL;
struct rsn_pmksa_cache_entry *pmksa; const u8 *pmkid;
u8 wrapped_data; u8 wrapped_data;
int ret; int ret;
@ -632,21 +643,36 @@ static struct wpabuf * wpas_pasn_build_auth_1(struct wpa_supplicant *wpa_s)
wpa_s->own_addr, pasn->bssid, wpa_s->own_addr, pasn->bssid,
pasn->trans_seq + 1, WLAN_STATUS_SUCCESS); 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, pmksa = wpa_sm_pmksa_cache_get(wpa_s->wpa, pasn->bssid,
NULL, NULL, pasn->akmp); NULL, NULL, pasn->akmp);
if (pmksa)
pmkid = pmksa->pmkid;
/* /*
* Note: Even when PMKSA is available, also add wrapped data as * Note: Even when PMKSA is available, also add wrapped data as
* it is possible that the PMKID is no longer valid at the AP. * it is possible that the PMKID is no longer valid at the AP.
*/ */
wrapped_data_buf = wpas_pasn_get_wrapped_data(wpa_s); wrapped_data_buf = wpas_pasn_get_wrapped_data(wpa_s);
} else {
pmksa = NULL;
} }
if (wpa_pasn_add_rsne(buf, pmksa ? pmksa->pmkid : NULL, if (wpa_pasn_add_rsne(buf, pmkid, pasn->akmp, pasn->cipher) < 0)
pasn->akmp, pasn->cipher) < 0)
goto fail; goto fail;
if (!wrapped_data_buf) if (!wrapped_data_buf)
@ -791,6 +817,11 @@ static void wpas_pasn_reset(struct wpa_supplicant *wpa_s)
os_memset(&pasn->fils, 0, sizeof(pasn->fils)); os_memset(&pasn->fils, 0, sizeof(pasn->fils));
#endif /* CONFIG_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; pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
} }
@ -815,6 +846,19 @@ static int wpas_pasn_set_pmk(struct wpa_supplicant *wpa_s,
return 0; 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) { if (rsn_data->num_pmkid) {
struct rsn_pmksa_cache_entry *pmksa; struct rsn_pmksa_cache_entry *pmksa;

View file

@ -555,6 +555,12 @@ struct wpas_pasn {
#ifdef CONFIG_FILS #ifdef CONFIG_FILS
struct pasn_fils fils; struct pasn_fils fils;
#endif /* CONFIG_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 */ #endif /* CONFIG_PASN */