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>
This commit is contained in:
Jouni Malinen 2017-03-12 01:26:43 +02:00
parent f9561868ec
commit 09368515d1
9 changed files with 179 additions and 10 deletions

View file

@ -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)",

View file

@ -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));

View file

@ -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);
}

View file

@ -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 */
};

View file

@ -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);

View file

@ -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

View file

@ -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;

View file

@ -1,6 +1,6 @@
/*
* WPA definitions shared between hostapd and wpa_supplicant
* Copyright (c) 2002-2015, 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.
@ -20,6 +20,8 @@
#define WPA_GMK_LEN 32
#define WPA_GTK_MAX_LEN 32
#define OWE_DH_GROUP 19
#define WPA_ALLOWED_PAIRWISE_CIPHERS \
(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE | \
WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256)

View file

@ -458,7 +458,7 @@ static int ibss_rsn_auth_init(struct ibss_rsn *ibss_rsn,
"\x00\x0f\xac\x04"
"\x01\x00\x00\x0f\xac\x04"
"\x01\x00\x00\x0f\xac\x02"
"\x00\x00", 22, NULL, 0) !=
"\x00\x00", 22, NULL, 0, NULL, 0) !=
WPA_IE_OK) {
wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed");
return -1;