]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
OWE: Process Diffie-Hellman Parameter element in AP mode
authorJouni Malinen <j@w1.fi>
Sat, 11 Mar 2017 23:26:43 +0000 (01:26 +0200)
committerJouni Malinen <j@w1.fi>
Sun, 12 Mar 2017 17:24:11 +0000 (19:24 +0200)
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>
src/ap/drv_callbacks.c
src/ap/ieee802_11.c
src/ap/sta_info.c
src/ap/sta_info.h
src/ap/wpa_auth.h
src/ap/wpa_auth_glue.c
src/ap/wpa_auth_ie.c
src/common/wpa_common.h
wpa_supplicant/ibss_rsn.c

index 46b2d4c8a9ea84f52444ed59a67ed65029f80359..331bf9e09fb91cdf474bd1b6d749518dd2f91d5a 100644 (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)",
index dc8cedce7c3d78e80f3a7c2ee1eb060437d0f6d1..5bbd585fb78c061443ddd8a5e2a5c613471300f6 100644 (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));
index af8c754d9e9b16b0b77f0ff423ad90c5b7b5cf1a..f233d753addf673eec25faf7b341d345197b9543 100644 (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);
 }
 
index 6f55403b00c775ef67e634ae72cf623c55a4cd75..b5d1d33155848631b0f9f21ec37f3388a15b6073 100644 (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 */
 };
 
 
index bfca7e5c1c9bcd1f6fb16db946c4d0ce7c34fb8d..20d8eeccf91e0aeb7dde8a1a26af2bd7d2ddd7b9 100644 (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);
index 969ede28ad4f13276f7e518536d3556813407a83..d4e0140793be00358d623759b908c377e5b6c86e 100644 (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
index 5a7691fd227a14e9e2b3f639d92bd6b1838df0dc..b1444d922817e91d7141fa4f9973a1fb3bb73f70 100644 (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;
index c37f8175f086b807eb592a0e6915ea000d2a41ac..6303510fefc4b78eebc438c07c89a16ec58d39e1 100644 (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)
index e5e68626dbafe44f9b11bf182a566d191646703c..9b6c41698fca96101bdfdb3ec55a6f0841ec82ba 100644 (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;