[PATCH v2 02/14] AP: Support PASN with SAE key derivation
Ilan Peer
ilan.peer at intel.com
Wed Dec 16 06:00:53 EST 2020
Signed-off-by: Ilan Peer <ilan.peer at intel.com>
---
src/ap/ieee802_11.c | 299 +++++++++++++++++++++++++++++++++++++++-----
src/ap/sta_info.c | 3 +
src/ap/sta_info.h | 5 +
3 files changed, 276 insertions(+), 31 deletions(-)
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index a1f6aa931e..72e8818906 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -469,34 +469,17 @@ static void sae_set_state(struct sta_info *sta, enum sae_state state,
}
-static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
- struct sta_info *sta, int update,
- int status_code)
+static const char *sae_get_password(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const char *rx_id,
+ struct sae_password_entry **pw_entry,
+ struct sae_pt **s_pt,
+ struct sae_pk **s_pk)
{
- struct wpabuf *buf;
const char *password = NULL;
struct sae_password_entry *pw;
- const char *rx_id = NULL;
- int use_pt = 0;
struct sae_pt *pt = NULL;
- const struct sae_pk *pk = NULL;
-
- if (sta->sae->tmp) {
- rx_id = sta->sae->tmp->pw_id;
- use_pt = sta->sae->h2e;
-#ifdef CONFIG_SAE_PK
- os_memcpy(sta->sae->tmp->own_addr, hapd->own_addr, ETH_ALEN);
- os_memcpy(sta->sae->tmp->peer_addr, sta->addr, ETH_ALEN);
-#endif /* CONFIG_SAE_PK */
- }
-
- if (rx_id && hapd->conf->sae_pwe != 3)
- use_pt = 1;
- else if (status_code == WLAN_STATUS_SUCCESS)
- use_pt = 0;
- else if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
- status_code == WLAN_STATUS_SAE_PK)
- use_pt = 1;
+ struct sae_pk *pk = NULL;
for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
if (!is_broadcast_ether_addr(pw->peer_addr) &&
@@ -513,11 +496,51 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
pk = pw->pk;
break;
}
+
if (!password) {
password = hapd->conf->ssid.wpa_passphrase;
pt = hapd->conf->ssid.pt;
}
- if (!password || (use_pt && !pt)) {
+
+ if (pw_entry)
+ *pw_entry = pw;
+ if (s_pt)
+ *s_pt = pt;
+ if (s_pk)
+ *s_pk = pk;
+
+ return password;
+}
+
+
+static struct wpabuf *auth_build_sae_commit(struct hostapd_data *hapd,
+ struct sta_info *sta, int update,
+ int status_code)
+{
+ struct wpabuf *buf;
+ const char *password = NULL;
+ struct sae_password_entry *pw;
+ const char *rx_id = NULL;
+ int use_pt = 0;
+ struct sae_pt *pt = NULL;
+ struct sae_pk *pk = NULL;
+
+ if (sta->sae->tmp) {
+ rx_id = sta->sae->tmp->pw_id;
+ use_pt = sta->sae->h2e;
+#ifdef CONFIG_SAE_PK
+ os_memcpy(sta->sae->tmp->own_addr, hapd->own_addr, ETH_ALEN);
+ os_memcpy(sta->sae->tmp->peer_addr, sta->addr, ETH_ALEN);
+#endif /* CONFIG_SAE_PK */
+ }
+
+ if (status_code == WLAN_STATUS_SUCCESS)
+ use_pt = 0;
+ else if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT)
+ use_pt = 1;
+
+ password = sae_get_password(hapd, sta, rx_id, &pw, &pt, &pk);
+ if (!password ||(use_pt && !pt)) {
wpa_printf(MSG_DEBUG, "SAE: No password available");
return NULL;
}
@@ -2297,6 +2320,181 @@ ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta,
#ifdef CONFIG_PASN
+#ifdef CONFIG_SAE
+
+static int pasn_wd_handle_sae_commit(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ struct wpabuf *wd)
+{
+ struct pasn_data *pasn = sta->pasn;
+ const char *password = NULL;
+ const u8 *data;
+ size_t buf_len;
+ u16 res, alg, seq, status;
+ int groups[] = { pasn->group, 0 };
+ int ret;
+
+ if (!wd)
+ return -1;
+
+ data = wpabuf_head_u8(wd);
+ buf_len = wpabuf_len(wd);
+
+ if (buf_len < 6) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short. len=%lu",
+ buf_len);
+ return -1;
+ }
+
+ alg = WPA_GET_LE16(data);
+ seq = WPA_GET_LE16(data + 2);
+ status = WPA_GET_LE16(data + 4);
+
+ wpa_printf(MSG_DEBUG, "PASN: SAE: commit: alg=%u, seq=%u, status=%u",
+ alg, seq, status);
+
+ if (alg != WLAN_AUTH_SAE || seq != 1 || status != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE: dropping peer commit");
+ return -1;
+ }
+
+ sae_clear_data(&pasn->sae);
+ pasn->sae.state = SAE_NOTHING;
+
+ ret = sae_set_group(&pasn->sae, pasn->group);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to set SAE group");
+ return -1;
+ }
+
+ password = sae_get_password(hapd, sta, NULL, NULL, NULL, NULL);
+ if (!password) {
+ wpa_printf(MSG_DEBUG, "PASN: No SAE password found");
+ return -1;
+ }
+
+ ret = sae_prepare_commit(hapd->own_addr, sta->addr,
+ (u8 *)password,
+ os_strlen(password), 0,
+ &pasn->sae);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to prepare SAE commit");
+ return -1;
+ }
+
+ res = sae_parse_commit(&pasn->sae, data + 6, buf_len - 6, NULL, 0,
+ groups, 0);
+ if (res != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE failed parsing commit");
+ return -1;
+ }
+
+ /* process the commit message and derive the PMK */
+ ret = sae_process_commit(&pasn->sae);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit");
+ return -1;
+ }
+
+ pasn->sae.state = SAE_COMMITTED;
+
+ return 0;
+}
+
+
+static int pasn_wd_handle_sae_confirm(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ struct wpabuf *wd)
+{
+ struct pasn_data *pasn = sta->pasn;
+ const u8 *data;
+ size_t buf_len;
+ u16 res, alg, seq, status;
+
+ if (!wd)
+ return -1;
+
+ data = wpabuf_head_u8(wd);
+ buf_len = wpabuf_len(wd);
+
+ if (buf_len < 6) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short. len=%lu",
+ buf_len);
+ return -1;
+ }
+
+ alg = WPA_GET_LE16(data);
+ seq = WPA_GET_LE16(data + 2);
+ status = WPA_GET_LE16(data + 4);
+
+ wpa_printf(MSG_DEBUG, "PASN: SAE: commit: alg=%u, seq=%u, status=%u",
+ alg, seq, status);
+
+ if (alg != WLAN_AUTH_SAE || seq != 2 || status != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE: dropping peer commit");
+ return -1;
+ }
+
+ res = sae_check_confirm(&pasn->sae, data + 6, buf_len - 6);
+ if (res != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE failed checking confirm");
+ return -1;
+ }
+
+ pasn->sae.state = SAE_ACCEPTED;
+
+ /*
+ * TODO: Based on on P802.11az_D2.0, the PMKSA derived with PASN/SAE
+ * should only be allowed with future PASN only. For now do not restrict
+ * this only for PASN.
+ */
+ wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
+ pasn->sae.pmk, pasn->sae.pmkid);
+ return 0;
+}
+
+
+static struct wpabuf *pasn_get_sae_wd(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ struct pasn_data *pasn = sta->pasn;
+ struct wpabuf *buf = NULL;
+ u8 *len_ptr;
+ size_t len;
+
+ /* Need to add the entire authentication frame body */
+ buf = wpabuf_alloc(8 + SAE_COMMIT_MAX_LEN + 8 + SAE_CONFIRM_MAX_LEN);
+ if (!buf) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer");
+ return NULL;
+ }
+
+ /* Need to add the entire authentication frame body for the commit */
+ len_ptr = wpabuf_put(buf, 2);
+ wpabuf_put_le16(buf, WLAN_AUTH_SAE);
+ wpabuf_put_le16(buf, 1);
+ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+
+ /* write the actual commit and update the length accordingly */
+ sae_write_commit(&pasn->sae, buf, NULL, 0);
+ len = wpabuf_len(buf);
+ WPA_PUT_LE16(len_ptr, len - 2);
+
+ /* Need to add the entire authentication frame body for the confirm */
+ len_ptr = wpabuf_put(buf, 2);
+ wpabuf_put_le16(buf, WLAN_AUTH_SAE);
+ wpabuf_put_le16(buf, 2);
+ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+
+ sae_write_confirm(&pasn->sae, buf);
+ WPA_PUT_LE16(len_ptr, wpabuf_len(buf) - len - 2);
+
+ pasn->sae.state = SAE_CONFIRMED;
+
+ return buf;
+}
+
+#endif /* CONFIG_SAE */
static struct wpabuf *pasn_get_wrapped_data(struct hostapd_data *hapd,
struct sta_info *sta)
@@ -2306,6 +2504,10 @@ static struct wpabuf *pasn_get_wrapped_data(struct hostapd_data *hapd,
/* no wrapped data */
return NULL;
case WPA_KEY_MGMT_SAE:
+#ifdef CONFIG_SAE
+ return pasn_get_sae_wd(hapd, sta);
+#endif /* CONFIG_SAE */
+ /* fall through */
case WPA_KEY_MGMT_FILS_SHA256:
case WPA_KEY_MGMT_FILS_SHA384:
case WPA_KEY_MGMT_FT_PSK:
@@ -2349,11 +2551,22 @@ pasn_derive_keys(struct hostapd_data *hapd, struct sta_info *sta,
pmk_len = pmksa->pmk_len;
os_memcpy(pmk, pmksa->pmk, pmksa->pmk_len);
} else {
- /* TODO: derive PMK based on wrapped data */
- wpa_printf(MSG_DEBUG,
- "PASN: missing implementation to derive PMK");
-
- return -1;
+ switch (sta->pasn->akmp) {
+#ifdef CONFIG_SAE
+ case WPA_KEY_MGMT_SAE:
+ if (sta->pasn->sae.state == SAE_COMMITTED) {
+ pmk_len = PMK_LEN;
+ os_memcpy(pmk, sta->pasn->sae.pmk, PMK_LEN);
+ break;
+ }
+#endif /* CONFIG_SAE */
+ /* fall through */
+ default:
+ /* TODO: derive PMK based on wrapped data */
+ wpa_printf(MSG_DEBUG,
+ "PASN: missing PMK derivation");
+ return -1;
+ }
}
ret = pasn_pmk_to_ptk(pmk, pmk_len,
@@ -2591,6 +2804,19 @@ static void handle_auth_pasn_1(struct hostapd_data *hapd, struct sta_info *sta,
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto send_resp;
}
+
+#ifdef CONFIG_SAE
+ if (sta->pasn->akmp == WPA_KEY_MGMT_SAE) {
+ ret = pasn_wd_handle_sae_commit(hapd, sta,
+ wrapped_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: failed processing SAE commit");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto send_resp;
+ }
+ }
+#endif /* CONFIG_SAE */
}
sta->pasn->wrapped_data_format = pasn_params.wrapped_data_format;
@@ -2715,7 +2941,18 @@ static void handle_auth_pasn_3(struct hostapd_data *hapd, struct sta_info *sta,
goto fail;
}
- /* TODO: handle wrapped data */
+#ifdef CONFIG_SAE
+ if (sta->pasn->akmp == WPA_KEY_MGMT_SAE) {
+ ret = pasn_wd_handle_sae_confirm(hapd, sta,
+ wrapped_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: failed processing SAE confirm");
+ wpabuf_free(wrapped_data);
+ goto fail;
+ }
+ }
+#endif /* CONFIG_SAE */
wpabuf_free(wrapped_data);
}
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index b28a2415bc..dd2d5cb2a8 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -165,6 +165,9 @@ void ap_free_sta_pasn(struct hostapd_data *hapd, struct sta_info *sta)
if (sta->pasn) {
if (sta->pasn->ecdh)
crypto_ecdh_deinit(sta->pasn->ecdh);
+#ifdef CONFIG_SAE
+ sae_clear_data(&sta->pasn->sae);
+#endif /* CONFIG_SAE */
os_free(sta->pasn);
sta->pasn = NULL;
}
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index d77d52da10..d0ec32ba65 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -15,6 +15,7 @@
#include "common/wpa_common.h"
#include "common/ieee802_11_defs.h"
#include "crypto/sha384.h"
+#include "common/sae.h"
/* STA flags */
#define WLAN_STA_AUTH BIT(0)
@@ -75,6 +76,10 @@ struct pasn_data {
u8 hash[SHA384_MAC_LEN];
struct wpa_ptk ptk;
struct crypto_ecdh *ecdh;
+
+#ifdef CONFIG_SAE
+ struct sae_data sae;
+#endif /* CONFIG_SAE */
};
struct sta_info {
--
2.17.1
More information about the Hostap
mailing list