aboutsummaryrefslogtreecommitdiffstats
path: root/src/ap
diff options
context:
space:
mode:
authorJouni Malinen <j@w1.fi>2017-03-11 23:26:43 (GMT)
committerJouni Malinen <j@w1.fi>2017-03-12 17:24:11 (GMT)
commit09368515d1308b64ebb34826eedf3b3e55994c92 (patch)
tree75bbcb29f3f8cf021ce6e2dde239abded1e25a9f /src/ap
parentf9561868ecbad5acdd992fe2d1ba264071133542 (diff)
downloadhostap-09368515d1308b64ebb34826eedf3b3e55994c92.zip
hostap-09368515d1308b64ebb34826eedf3b3e55994c92.tar.gz
hostap-09368515d1308b64ebb34826eedf3b3e55994c92.tar.bz2
OWE: Process Diffie-Hellman Parameter element in AP mode
This adds AP side processing for OWE Diffie-Hellman Parameter element in (Re)Association Request frame and adding it in (Re)Association Response frame. Signed-off-by: Jouni Malinen <j@w1.fi>
Diffstat (limited to 'src/ap')
-rw-r--r--src/ap/drv_callbacks.c3
-rw-r--r--src/ap/ieee802_11.c138
-rw-r--r--src/ap/sta_info.c8
-rw-r--r--src/ap/sta_info.h7
-rw-r--r--src/ap/wpa_auth.h5
-rw-r--r--src/ap/wpa_auth_glue.c6
-rw-r--r--src/ap/wpa_auth_ie.c16
7 files changed, 175 insertions, 8 deletions
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 46b2d4c..331bf9e 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -231,7 +231,8 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
}
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
ie, ielen,
- elems.mdie, elems.mdie_len);
+ elems.mdie, elems.mdie_len,
+ elems.owe_dh, elems.owe_dh_len);
if (res != WPA_IE_OK) {
wpa_printf(MSG_DEBUG,
"WPA/RSN information element rejected? (res %u)",
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index dc8cedc..5bbd585 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -1066,7 +1066,7 @@ static void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
elems.rsn_ie - 2, elems.rsn_ie_len + 2,
- elems.mdie, elems.mdie_len);
+ elems.mdie, elems.mdie_len, NULL, 0);
resp = wpa_res_to_status_code(res);
if (resp != WLAN_STATUS_SUCCESS)
goto fail;
@@ -1893,6 +1893,100 @@ static u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
}
+#ifdef CONFIG_OWE
+static u16 owe_process_assoc_req(struct sta_info *sta, const u8 *owe_dh,
+ u8 owe_dh_len)
+{
+ struct wpabuf *secret, *pub, *hkey;
+ int res;
+ u8 prk[SHA256_MAC_LEN], pmkid[SHA256_MAC_LEN];
+ const char *info = "OWE Key Generation";
+ const u8 *addr[2];
+ size_t len[2];
+
+ if (WPA_GET_LE16(owe_dh) != OWE_DH_GROUP)
+ return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+
+ crypto_ecdh_deinit(sta->owe_ecdh);
+ sta->owe_ecdh = crypto_ecdh_init(OWE_DH_GROUP);
+ if (!sta->owe_ecdh)
+ return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+
+ secret = crypto_ecdh_set_peerkey(sta->owe_ecdh, 0, owe_dh + 2,
+ owe_dh_len - 2);
+ if (!secret) {
+ wpa_printf(MSG_DEBUG, "OWE: Invalid peer DH public key");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ wpa_hexdump_buf_key(MSG_DEBUG, "OWE: DH shared secret", secret);
+
+ /* prk = HKDF-extract(C | A | group, z) */
+
+ pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
+ if (!pub) {
+ wpabuf_clear_free(secret);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ /* PMKID = Truncate-128(Hash(C | A)) */
+ addr[0] = owe_dh + 2;
+ len[0] = owe_dh_len - 2;
+ addr[1] = wpabuf_head(pub);
+ len[1] = wpabuf_len(pub);
+ res = sha256_vector(2, addr, len, pmkid);
+ if (res < 0) {
+ wpabuf_free(pub);
+ wpabuf_clear_free(secret);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ hkey = wpabuf_alloc(owe_dh_len - 2 + wpabuf_len(pub) + 2);
+ if (!hkey) {
+ wpabuf_free(pub);
+ wpabuf_clear_free(secret);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ wpabuf_put_data(hkey, owe_dh + 2, owe_dh_len - 2); /* C */
+ wpabuf_put_buf(hkey, pub); /* A */
+ wpabuf_free(pub);
+ wpabuf_put_le16(hkey, OWE_DH_GROUP); /* group */
+ res = hmac_sha256(wpabuf_head(hkey), wpabuf_len(hkey),
+ wpabuf_head(secret), wpabuf_len(secret), prk);
+ wpabuf_clear_free(hkey);
+ wpabuf_clear_free(secret);
+ if (res < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ wpa_hexdump_key(MSG_DEBUG, "OWE: prk", prk, SHA256_MAC_LEN);
+
+ /* PMK = HKDF-expand(prk, "OWE Key Generation", n) */
+
+ os_free(sta->owe_pmk);
+ sta->owe_pmk = os_malloc(PMK_LEN);
+ if (!sta->owe_pmk) {
+ os_memset(prk, 0, SHA256_MAC_LEN);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ res = hmac_sha256_kdf(prk, SHA256_MAC_LEN, NULL, (const u8 *) info,
+ os_strlen(info), sta->owe_pmk, PMK_LEN);
+ os_memset(prk, 0, SHA256_MAC_LEN);
+ if (res < 0) {
+ os_free(sta->owe_pmk);
+ sta->owe_pmk = NULL;
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sta->owe_pmk, PMK_LEN);
+ wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN);
+ /* TODO: Add PMKSA cache entry */
+
+ return WLAN_STATUS_SUCCESS;
+}
+#endif /* CONFIG_OWE */
+
+
static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ies, size_t ies_len, int reassoc)
{
@@ -2034,7 +2128,8 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
}
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
wpa_ie, wpa_ie_len,
- elems.mdie, elems.mdie_len);
+ elems.mdie, elems.mdie_len,
+ elems.owe_dh, elems.owe_dh_len);
resp = wpa_res_to_status_code(res);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
@@ -2104,6 +2199,17 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
}
#endif /* CONFIG_SAE */
+#ifdef CONFIG_OWE
+ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE &&
+ elems.owe_dh) {
+ resp = owe_process_assoc_req(sta, elems.owe_dh,
+ elems.owe_dh_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+ }
+#endif /* CONFIG_OWE */
+
#ifdef CONFIG_IEEE80211N
if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) &&
wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) {
@@ -2282,6 +2388,10 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
if (sta && sta->fils_hlp_resp)
buflen += wpabuf_len(sta->fils_hlp_resp);
#endif /* CONFIG_FILS */
+#ifdef CONFIG_OWE
+ if (sta && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE))
+ buflen += 50;
+#endif /* CONFIG_OWE */
buf = os_zalloc(buflen);
if (!buf) {
res = WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -2458,6 +2568,30 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
}
#endif /* CONFIG_FILS */
+#ifdef CONFIG_OWE
+ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+ sta && sta->owe_ecdh &&
+ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE) {
+ struct wpabuf *pub;
+
+ pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
+ if (!pub) {
+ res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto done;
+ }
+ /* OWE Diffie-Hellman Parameter element */
+ *p++ = WLAN_EID_EXTENSION; /* Element ID */
+ *p++ = 1 + 2 + wpabuf_len(pub); /* Length */
+ *p++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension */
+ WPA_PUT_LE16(p, OWE_DH_GROUP);
+ p += 2;
+ os_memcpy(p, wpabuf_head(pub), wpabuf_len(pub));
+ p += wpabuf_len(pub);
+ send_len += 3 + 2 + wpabuf_len(pub);
+ wpabuf_free(pub);
+ }
+#endif /* CONFIG_OWE */
+
if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) {
wpa_printf(MSG_INFO, "Failed to send assoc resp: %s",
strerror(errno));
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index af8c754..f233d75 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -1,6 +1,6 @@
/*
* hostapd / Station table
- * Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -17,6 +17,7 @@
#include "radius/radius_client.h"
#include "p2p/p2p.h"
#include "fst/fst.h"
+#include "crypto/crypto.h"
#include "hostapd.h"
#include "accounting.h"
#include "ieee802_1x.h"
@@ -346,6 +347,11 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
#endif /* CONFIG_FILS */
+#ifdef CONFIG_OWE
+ bin_clear_free(sta->owe_pmk, PMK_LEN);
+ crypto_ecdh_deinit(sta->owe_ecdh);
+#endif /* CONFIG_OWE */
+
os_free(sta);
}
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 6f55403..b5d1d33 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -1,6 +1,6 @@
/*
* hostapd / Station table
- * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -232,6 +232,11 @@ struct sta_info {
struct wpabuf *fils_hlp_resp;
struct wpabuf *hlp_dhcp_discover;
#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_OWE
+ u8 *owe_pmk;
+ struct crypto_ecdh *owe_ecdh;
+#endif /* CONFIG_OWE */
};
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index bfca7e5..20d8eec 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -1,6 +1,6 @@
/*
* hostapd - IEEE 802.11i-2004 / WPA Authenticator
- * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -257,7 +257,8 @@ enum {
int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
const u8 *wpa_ie, size_t wpa_ie_len,
- const u8 *mdie, size_t mdie_len);
+ const u8 *mdie, size_t mdie_len,
+ const u8 *owe_dh, size_t owe_dh_len);
int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
const u8 *osen_ie, size_t osen_ie_len);
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 969ede2..d4e0140 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -245,6 +245,12 @@ static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr,
}
#endif /* CONFIG_SAE */
+#ifdef CONFIG_OWE
+ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+ sta && sta->owe_pmk)
+ return sta->owe_pmk;
+#endif /* CONFIG_OWE */
+
psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk);
/*
* This is about to iterate over all psks, prev_psk gives the last
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 5a7691f..b1444d9 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -505,7 +505,8 @@ static int wpa_auth_okc_iter(struct wpa_authenticator *a, void *ctx)
int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
const u8 *wpa_ie, size_t wpa_ie_len,
- const u8 *mdie, size_t mdie_len)
+ const u8 *mdie, size_t mdie_len,
+ const u8 *owe_dh, size_t owe_dh_len)
{
struct wpa_ie_data data;
int ciphers, key_mgmt, res, version;
@@ -738,6 +739,19 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
}
#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_OWE
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && !owe_dh) {
+ wpa_printf(MSG_DEBUG,
+ "OWE: No Diffie-Hellman Parameter element");
+ return WPA_INVALID_AKMP;
+ }
+ if (sm->wpa_key_mgmt != WPA_KEY_MGMT_OWE && owe_dh) {
+ wpa_printf(MSG_DEBUG,
+ "OWE: Unexpected Diffie-Hellman Parameter element with non-OWE AKM");
+ return WPA_INVALID_AKMP;
+ }
+#endif /* CONFIG_OWE */
+
sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0);
if (sm->pairwise < 0)
return WPA_INVALID_PAIRWISE;