From 0a6147991ebd3d0b52ab3e503838cc8eca32e3b3 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sun, 12 Mar 2017 11:53:21 +0200 Subject: [PATCH] OWE: Process Diffie-Hellman Parameter element in STA mode This adds STA side addition of OWE Diffie-Hellman Parameter element into (Re)Association Request frame and processing it in (Re)Association Response frame. Signed-off-by: Jouni Malinen --- src/rsn_supp/wpa.c | 142 +++++++++++++++++++++++++++++++++++++++- src/rsn_supp/wpa.h | 4 ++ src/rsn_supp/wpa_i.h | 6 +- wpa_supplicant/events.c | 10 +++ wpa_supplicant/sme.c | 25 +++++++ 5 files changed, 185 insertions(+), 2 deletions(-) diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index a1b647e5a..2c3c814ae 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - WPA state machine and EAPOL-Key processing - * Copyright (c) 2003-2015, Jouni Malinen + * Copyright (c) 2003-2017, Jouni Malinen * Copyright(c) 2015 Intel Deutschland GmbH * * This software may be distributed under the terms of the BSD license. @@ -15,6 +15,7 @@ #include "crypto/crypto.h" #include "crypto/random.h" #include "crypto/aes_siv.h" +#include "crypto/sha256.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "eap_common/eap_defs.h" @@ -2447,6 +2448,9 @@ void wpa_sm_deinit(struct wpa_sm *sm) #ifdef CONFIG_TESTING_OPTIONS wpabuf_free(sm->test_assoc_ie); #endif /* CONFIG_TESTING_OPTIONS */ +#ifdef CONFIG_OWE + crypto_ecdh_deinit(sm->owe_ecdh); +#endif /* CONFIG_OWE */ os_free(sm); } @@ -3814,3 +3818,139 @@ int wpa_fils_is_completed(struct wpa_sm *sm) return 0; #endif /* CONFIG_FILS */ } + + +#ifdef CONFIG_OWE + +struct wpabuf * owe_build_assoc_req(struct wpa_sm *sm) +{ + struct wpabuf *ie = NULL, *pub = NULL; + + crypto_ecdh_deinit(sm->owe_ecdh); + sm->owe_ecdh = crypto_ecdh_init(OWE_DH_GROUP); + if (!sm->owe_ecdh) + goto fail; + pub = crypto_ecdh_get_pubkey(sm->owe_ecdh, 0); + if (!pub) + goto fail; + + ie = wpabuf_alloc(5 + wpabuf_len(pub)); + if (!ie) + goto fail; + wpabuf_put_u8(ie, WLAN_EID_EXTENSION); + wpabuf_put_u8(ie, 1 + 2 + wpabuf_len(pub)); + wpabuf_put_u8(ie, WLAN_EID_EXT_OWE_DH_PARAM); + wpabuf_put_le16(ie, OWE_DH_GROUP); + wpabuf_put_buf(ie, pub); + wpabuf_free(pub); + wpa_hexdump_buf(MSG_DEBUG, "OWE: Diffie-Hellman Parameter element", + ie); + + return ie; +fail: + wpabuf_free(pub); + crypto_ecdh_deinit(sm->owe_ecdh); + sm->owe_ecdh = NULL; + return NULL; +} + + +int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *resp_ies, + size_t resp_ies_len) +{ + struct ieee802_11_elems elems; + u16 group; + 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 (!resp_ies || + ieee802_11_parse_elems(resp_ies, resp_ies_len, &elems, 1) == + ParseFailed || + !elems.owe_dh) { + wpa_printf(MSG_INFO, + "OWE: No Diffie-Hellman Parameter element found in Association Response frame"); + return -1; + } + + group = WPA_GET_LE16(elems.owe_dh); + if (group != OWE_DH_GROUP) { + wpa_printf(MSG_INFO, + "OWE: Unexpected Diffie-Hellman group in response: %u", + group); + return -1; + } + + if (!sm->owe_ecdh) { + wpa_printf(MSG_INFO, "OWE: No ECDH state available"); + return -1; + } + + secret = crypto_ecdh_set_peerkey(sm->owe_ecdh, 0, + elems.owe_dh + 2, + elems.owe_dh_len - 2); + if (!secret) { + wpa_printf(MSG_DEBUG, "OWE: Invalid peer DH public key"); + return -1; + } + wpa_hexdump_buf_key(MSG_DEBUG, "OWE: DH shared secret", secret); + + /* prk = HKDF-extract(C | A | group, z) */ + + pub = crypto_ecdh_get_pubkey(sm->owe_ecdh, 0); + if (!pub) { + wpabuf_clear_free(secret); + return -1; + } + + /* PMKID = Truncate-128(Hash(C | A)) */ + addr[0] = wpabuf_head(pub); + len[0] = wpabuf_len(pub); + addr[1] = elems.owe_dh + 2; + len[1] = elems.owe_dh_len - 2; + res = sha256_vector(2, addr, len, pmkid); + if (res < 0) { + wpabuf_free(pub); + wpabuf_clear_free(secret); + return -1; + } + + hkey = wpabuf_alloc(wpabuf_len(pub) + elems.owe_dh_len - 2 + 2); + if (!hkey) { + wpabuf_free(pub); + wpabuf_clear_free(secret); + return -1; + } + + wpabuf_put_buf(hkey, pub); /* C */ + wpabuf_free(pub); + wpabuf_put_data(hkey, elems.owe_dh + 2, elems.owe_dh_len - 2); /* A */ + 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 -1; + + wpa_hexdump_key(MSG_DEBUG, "OWE: prk", prk, SHA256_MAC_LEN); + + /* PMK = HKDF-expand(prk, "OWE Key Generation", n) */ + + res = hmac_sha256_kdf(prk, SHA256_MAC_LEN, NULL, (const u8 *) info, + os_strlen(info), sm->pmk, PMK_LEN); + os_memset(prk, 0, SHA256_MAC_LEN); + if (res < 0) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sm->pmk, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN); + /* TODO: Add PMKSA cache entry */ + + return 0; +} + +#endif /* CONFIG_OWE */ diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index fe0f84a10..df774ade8 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -446,4 +446,8 @@ struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek, int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len); int wpa_fils_is_completed(struct wpa_sm *sm); +struct wpabuf * owe_build_assoc_req(struct wpa_sm *sm); +int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *resp_ies, + size_t resp_ies_len); + #endif /* WPA_H */ diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 7073bfeda..fe5311772 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -1,6 +1,6 @@ /* * Internal WPA/RSN supplicant state machine definitions - * Copyright (c) 2004-2015, Jouni Malinen + * Copyright (c) 2004-2017, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -152,6 +152,10 @@ struct wpa_sm { u8 fils_erp_pmkid[PMKID_LEN]; u8 fils_cache_id[FILS_CACHE_ID_LEN]; #endif /* CONFIG_FILS */ + +#ifdef CONFIG_OWE + struct crypto_ecdh *owe_ecdh; +#endif /* CONFIG_OWE */ }; diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 053fe1523..82a8b11aa 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -2225,6 +2225,16 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, #endif /* CONFIG_SME */ #endif /* CONFIG_FILS */ +#ifdef CONFIG_OWE + if (wpa_s->key_mgmt == WPA_KEY_MGMT_OWE && + owe_process_assoc_resp(wpa_s->wpa, + data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len) < 0) { + wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_UNSPECIFIED); + return -1; + } +#endif /* CONFIG_OWE */ + #ifdef CONFIG_IEEE80211R #ifdef CONFIG_SME if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FT) { diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index 84d02c38c..76d1acd00 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -1064,6 +1064,31 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, } #endif /* CONFIG_FILS */ +#ifdef CONFIG_OWE + if (auth_type == WLAN_AUTH_OPEN && + wpa_s->key_mgmt == WPA_KEY_MGMT_OWE) { + struct wpabuf *owe_ie; + + owe_ie = owe_build_assoc_req(wpa_s->wpa); + if (!owe_ie) { + wpa_printf(MSG_ERROR, + "OWE: Failed to build IE for Association Request frame"); + return; + } + if (wpa_s->sme.assoc_req_ie_len + wpabuf_len(owe_ie) > + sizeof(wpa_s->sme.assoc_req_ie)) { + wpa_printf(MSG_ERROR, + "OWE: Not enough buffer room for own Association Request frame elements"); + wpabuf_free(owe_ie); + return; + } + os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len, + wpabuf_head(owe_ie), wpabuf_len(owe_ie)); + wpa_s->sme.assoc_req_ie_len += wpabuf_len(owe_ie); + wpabuf_free(owe_ie); + } +#endif /* CONFIG_OWE */ + params.bssid = bssid; params.ssid = wpa_s->sme.ssid; params.ssid_len = wpa_s->sme.ssid_len;