]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
OWE: Process Diffie-Hellman Parameter element in STA mode
authorJouni Malinen <j@w1.fi>
Sun, 12 Mar 2017 09:53:21 +0000 (11:53 +0200)
committerJouni Malinen <j@w1.fi>
Sun, 12 Mar 2017 17:24:11 +0000 (19:24 +0200)
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 <j@w1.fi>
src/rsn_supp/wpa.c
src/rsn_supp/wpa.h
src/rsn_supp/wpa_i.h
wpa_supplicant/events.c
wpa_supplicant/sme.c

index a1b647e5a04a4be99e65c78ebcd057dae2384506..2c3c814ae4fab9f44446c1a6bcdf23cfd5ee3299 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - WPA state machine and EAPOL-Key processing
- * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
  * 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 */
index fe0f84a10aff3e4d09a7b8cfa7859f01f6527e56..df774ade82aae2c1e3df7571fe886dd4f3ff3cbb 100644 (file)
@@ -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 */
index 7073bfeda45d7ce13a3d43cc9ddcf56693415e92..fe5311772f048f74f55a43c1aeb6dfdf1f90cb76 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Internal WPA/RSN supplicant state machine definitions
- * 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.
@@ -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 */
 };
 
 
index 053fe15233fda6dbe2f9347e41f68b2235168f33..82a8b11aaa8b219fa5f77c8c23564ca563101238 100644 (file)
@@ -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) {
index 84d02c38c8208c1d0dd5a752889dd40a75dc90ea..76d1acd00b964536d64dc7269cdbf91c2b0f8b5f 100644 (file)
@@ -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;