diff --git a/hostapd/config_file.c b/hostapd/config_file.c index c40983c98..0169b989d 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -4412,6 +4412,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, line, pos); return 1; } + } else if (os_strcmp(buf, "owe_ptk_workaround") == 0) { + bss->owe_ptk_workaround = atoi(pos); #endif /* CONFIG_OWE */ } else if (os_strcmp(buf, "coloc_intf_reporting") == 0) { bss->coloc_intf_reporting = atoi(pos); diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index fbaa42b09..f55925afd 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -1817,6 +1817,19 @@ own_ip_addr=127.0.0.1 # http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-10 #owe_groups=19 20 21 +# OWE PTK derivation workaround +# Initial OWE implementation used SHA256 when deriving the PTK for all OWE +# groups. This was supposed to change to SHA384 for group 20 and SHA512 for +# group 21. This parameter can be used to enable workaround for interoperability +# with stations that use SHA256 with groups 20 and 21. By default (0) only the +# appropriate hash function is accepted. When workaround is enabled (1), the +# appropriate hash function is tried first and if that fails, SHA256-based PTK +# derivation is attempted. This workaround can result in reduced security for +# groups 20 and 21, but is required for interoperability with older +# implementations. There is no impact to group 19 behavior. The workaround is +# disabled by default and can be enabled by uncommenting the following line. +#owe_ptk_workaround=1 + # OWE transition mode configuration # Pointer to the matching open/OWE BSS #owe_transition_bssid= diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 1efdc2b43..017e60aa4 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -733,6 +733,7 @@ struct hostapd_bss_config { size_t owe_transition_ssid_len; char owe_transition_ifname[IFNAMSIZ + 1]; int *owe_groups; + int owe_ptk_workaround; #endif /* CONFIG_OWE */ int coloc_intf_reporting; diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 423528d12..0a807a3c6 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -56,7 +56,7 @@ static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, const u8 *pmk, unsigned int pmk_len, - struct wpa_ptk *ptk); + struct wpa_ptk *ptk, int force_sha256); static void wpa_group_free(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static void wpa_group_get(struct wpa_authenticator *wpa_auth, @@ -926,7 +926,8 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data, pmk_len = sm->pmk_len; } - if (wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK) < 0) + if (wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK, 0) < + 0) break; if (wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK, @@ -2233,10 +2234,11 @@ SM_STATE(WPA_PTK, PTKSTART) static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, const u8 *pmk, unsigned int pmk_len, - struct wpa_ptk *ptk) + struct wpa_ptk *ptk, int force_sha256) { const u8 *z = NULL; size_t z_len = 0; + int akmp; #ifdef CONFIG_IEEE80211R_AP if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { @@ -2262,9 +2264,12 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, } #endif /* CONFIG_DPP2 */ + akmp = sm->wpa_key_mgmt; + if (force_sha256) + akmp = WPA_KEY_MGMT_PSK_SHA256; return wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion", sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce, - ptk, sm->wpa_key_mgmt, sm->pairwise, z, z_len); + ptk, akmp, sm->pairwise, z, z_len); } @@ -2844,6 +2849,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) struct wpa_eapol_key *key; struct wpa_eapol_ie_parse kde; int vlan_id = 0; + int owe_ptk_workaround = !!wpa_auth->conf.owe_ptk_workaround; SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); sm->EAPOLKeyReceived = FALSE; @@ -2881,7 +2887,8 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) pmk_len = sm->pmksa->pmk_len; } - if (wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK) < 0) + if (wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK, + owe_ptk_workaround == 2) < 0) break; if (mic_len && @@ -2905,6 +2912,16 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) } #endif /* CONFIG_FILS */ +#ifdef CONFIG_OWE + if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && pmk_len > 32 && + owe_ptk_workaround == 1) { + wpa_printf(MSG_DEBUG, + "OWE: Try PTK derivation workaround with SHA256"); + owe_ptk_workaround = 2; + continue; + } +#endif /* CONFIG_OWE */ + if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) || wpa_key_mgmt_sae(sm->wpa_key_mgmt)) break; diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index 0b4b7297c..437719b17 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -237,6 +237,7 @@ struct wpa_auth_config { u8 fils_cache_id[FILS_CACHE_ID_LEN]; #endif /* CONFIG_FILS */ int sae_pwe; + int owe_ptk_workaround; }; typedef enum { diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index 4927744ef..066d7c5fe 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -162,6 +162,9 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, wconf->sae_pwe = 1; else if (sae_pw_id == 1 && wconf->sae_pwe == 0) wconf->sae_pwe = 2; +#ifdef CONFIG_OWE + wconf->owe_ptk_workaround = conf->owe_ptk_workaround; +#endif /* CONFIG_OWE */ }