Accumulative patch from commit dc013f1e37df3462085cf01a13f0c432f146ad7a

Author: Jouni Malinen <jouni@qca.qualcomm.com>
Date:   Tue Jan 15 12:03:29 2013 +0200
    eapol_test: Remove unnecessary header file inclusion

 - P2P: Send P2P-FIND-STOPPED event in the new continue-search states
 - P2P: Add some more details on Service Query TLV format
 - P2P: Use the same Dialog Token value for every GO Negotiation retry
 - P2P: Publish more connected clients info in Probe Response frames
 - P2P: Fix some memory leaks in p2p_add_device()
 - P2P: Use the same Dialog Token value for every PD retry
 - P2P: Document operating channel selection functions
 - P2P: Always re-select operating channel if not hard coded
 - P2P: Do not allow re-selection of GO channel if forced_freq in use
 - P2P: Set FORCE_FREQ flag as part of p2p_prepare_channel()
 - P2P: Share a single function for GO channel selection
 - P2P: Prefer operating channels where HT40 is possible
 - P2P: Be more careful with wpa_config_update_psk() call
 - P2P: Allow PSK to be used instead of passphrase for persistent GO
 - P2P: Consider age for the P2P scan results
 - Move some P2P offchannel operations to offchannel.c
 - P2P: Add more complete description of p2p_cancel
 - P2P: Allow p2p_cancel to be used to stop p2p_connect-join operation
 - Interworking changes
 - WNM changes
 - WPS changes
 - SAE changes

Change-Id: I38b847d3460066cc58aecbcf67266bfcff1d344e
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/ap/accounting.c b/src/ap/accounting.c
index 7563b52..9540531 100644
--- a/src/ap/accounting.c
+++ b/src/ap/accounting.c
@@ -26,8 +26,6 @@
  * input/output octets and updates Acct-{Input,Output}-Gigawords. */
 #define ACCT_DEFAULT_UPDATE_INTERVAL 300
 
-static void accounting_sta_get_id(struct hostapd_data *hapd,
-				  struct sta_info *sta);
 static void accounting_sta_interim(struct hostapd_data *hapd,
 				   struct sta_info *sta);
 
@@ -210,7 +208,6 @@
 	if (sta->acct_session_started)
 		return;
 
-	accounting_sta_get_id(hapd, sta);
 	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
 		       HOSTAPD_LEVEL_INFO,
 		       "starting accounting session %08X-%08X",
@@ -377,7 +374,7 @@
 }
 
 
-static void accounting_sta_get_id(struct hostapd_data *hapd,
+void accounting_sta_get_id(struct hostapd_data *hapd,
 				  struct sta_info *sta)
 {
 	sta->acct_session_id_lo = hapd->acct_session_id_lo++;
diff --git a/src/ap/accounting.h b/src/ap/accounting.h
index 9d13d01..dcc54ee 100644
--- a/src/ap/accounting.h
+++ b/src/ap/accounting.h
@@ -10,6 +10,11 @@
 #define ACCOUNTING_H
 
 #ifdef CONFIG_NO_ACCOUNTING
+static inline void accounting_sta_get_id(struct hostapd_data *hapd,
+					 struct sta_info *sta)
+{
+}
+
 static inline void accounting_sta_start(struct hostapd_data *hapd,
 					struct sta_info *sta)
 {
@@ -29,6 +34,7 @@
 {
 }
 #else /* CONFIG_NO_ACCOUNTING */
+void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta);
 void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta);
 void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta);
 int accounting_init(struct hostapd_data *hapd);
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 3c699f7..922f564 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -89,6 +89,8 @@
 #endif /* CONFIG_IEEE80211R */
 
 	bss->radius_das_time_window = 300;
+
+	bss->sae_anti_clogging_threshold = 5;
 }
 
 
@@ -158,6 +160,9 @@
 
 	conf->ht_capab = HT_CAP_INFO_SMPS_DISABLED;
 
+	conf->ap_table_max_size = 255;
+	conf->ap_table_expiration_time = 60;
+
 	return conf;
 }
 
@@ -516,6 +521,8 @@
 #endif /* CONFIG_HS20 */
 
 	wpabuf_free(conf->vendor_elements);
+
+	os_free(conf->sae_groups);
 }
 
 
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 71313c0..4742107 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -51,7 +51,8 @@
 struct hostapd_ssid {
 	u8 ssid[HOSTAPD_MAX_SSID_LEN];
 	size_t ssid_len;
-	int ssid_set;
+	unsigned int ssid_set:1;
+	unsigned int utf8_ssid:1;
 
 	char vlan[IFNAMSIZ + 1];
 	secpolicy security_policy;
@@ -390,6 +391,8 @@
 	/* IEEE 802.11v */
 	int time_advertisement;
 	char *time_zone;
+	int wnm_sleep_mode;
+	int bss_transition;
 
 	/* IEEE 802.11u - Interworking */
 	int interworking;
@@ -452,6 +455,9 @@
 #endif /* CONFIG_RADIUS_TEST */
 
 	struct wpabuf *vendor_elements;
+
+	unsigned int sae_anti_clogging_threshold;
+	int *sae_groups;
 };
 
 
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 02da25b..b71d51d 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -338,6 +338,7 @@
 		    const u8 *supp_rates, size_t supp_rates_len,
 		    u16 listen_interval,
 		    const struct ieee80211_ht_capabilities *ht_capab,
+		    const struct ieee80211_vht_capabilities *vht_capab,
 		    u32 flags, u8 qosinfo)
 {
 	struct hostapd_sta_add_params params;
@@ -355,6 +356,7 @@
 	params.supp_rates_len = supp_rates_len;
 	params.listen_interval = listen_interval;
 	params.ht_capabilities = ht_capab;
+	params.vht_capabilities = vht_capab;
 	params.flags = hostapd_sta_flags_to_drv(flags);
 	params.qosinfo = qosinfo;
 	return hapd->driver->sta_add(hapd->drv_priv, &params);
@@ -454,19 +456,76 @@
 
 
 int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq,
-		     int channel, int ht_enabled, int sec_channel_offset)
+		     int channel, int ht_enabled, int vht_enabled,
+		     int sec_channel_offset, int vht_oper_chwidth,
+		     int center_segment0, int center_segment1)
 {
 	struct hostapd_freq_params data;
-	if (hapd->driver == NULL)
-		return 0;
-	if (hapd->driver->set_freq == NULL)
-		return 0;
+	int tmp;
+
 	os_memset(&data, 0, sizeof(data));
 	data.mode = mode;
 	data.freq = freq;
 	data.channel = channel;
 	data.ht_enabled = ht_enabled;
+	data.vht_enabled = vht_enabled;
 	data.sec_channel_offset = sec_channel_offset;
+	data.center_freq1 = freq + sec_channel_offset * 10;
+	data.center_freq2 = 0;
+	data.bandwidth = sec_channel_offset ? 40 : 20;
+
+	/*
+	 * This validation code is probably misplaced, maybe it should be
+	 * in src/ap/hw_features.c and check the hardware support as well.
+	 */
+	if (data.vht_enabled) switch (vht_oper_chwidth) {
+	case VHT_CHANWIDTH_USE_HT:
+		if (center_segment1)
+			return -1;
+		if (5000 + center_segment0 * 5 != data.center_freq1)
+			return -1;
+		break;
+	case VHT_CHANWIDTH_80P80MHZ:
+		if (center_segment1 == center_segment0 + 4 ||
+		    center_segment1 == center_segment0 - 4)
+			return -1;
+		data.center_freq2 = 5000 + center_segment1 * 5;
+		/* fall through */
+	case VHT_CHANWIDTH_80MHZ:
+		data.bandwidth = 80;
+		if (vht_oper_chwidth == 1 && center_segment1)
+			return -1;
+		if (vht_oper_chwidth == 3 && !center_segment1)
+			return -1;
+		if (!sec_channel_offset)
+			return -1;
+		/* primary 40 part must match the HT configuration */
+		tmp = (30 + freq - 5000 - center_segment0 * 5)/20;
+		tmp /= 2;
+		if (data.center_freq1 != 5000 +
+					 center_segment0 * 5 - 20 + 40 * tmp)
+			return -1;
+		data.center_freq1 = 5000 + center_segment0 * 5;
+		break;
+	case VHT_CHANWIDTH_160MHZ:
+		data.bandwidth = 160;
+		if (center_segment1)
+			return -1;
+		if (!sec_channel_offset)
+			return -1;
+		/* primary 40 part must match the HT configuration */
+		tmp = (70 + freq - 5000 - center_segment0 * 5)/20;
+		tmp /= 2;
+		if (data.center_freq1 != 5000 +
+					 center_segment0 * 5 - 60 + 40 * tmp)
+			return -1;
+		data.center_freq1 = 5000 + center_segment0 * 5;
+		break;
+	}
+	if (hapd->driver == NULL)
+		return 0;
+	if (hapd->driver->set_freq == NULL)
+		return 0;
 	return hapd->driver->set_freq(hapd->drv_priv, &data);
 }
 
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 9c53b99..ceb7e68 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -13,6 +13,7 @@
 struct wpa_bss_params;
 struct wpa_driver_scan_params;
 struct ieee80211_ht_capabilities;
+struct ieee80211_vht_capabilities;
 
 u32 hostapd_sta_flags_to_drv(u32 flags);
 int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
@@ -37,6 +38,7 @@
 		    const u8 *supp_rates, size_t supp_rates_len,
 		    u16 listen_interval,
 		    const struct ieee80211_ht_capabilities *ht_capab,
+		    const struct ieee80211_vht_capabilities *vht_capab,
 		    u32 flags, u8 qosinfo);
 int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
 int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
@@ -55,7 +57,9 @@
 		       const u8 *addr, int idx, u8 *seq);
 int hostapd_flush(struct hostapd_data *hapd);
 int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq,
-		     int channel, int ht_enabled, int sec_channel_offset);
+		     int channel, int ht_enabled, int vht_enabled,
+		     int sec_channel_offset, int vht_oper_chwidth,
+		     int center_segment0, int center_segment1);
 int hostapd_set_rts(struct hostapd_data *hapd, int rts);
 int hostapd_set_frag(struct hostapd_data *hapd, int frag);
 int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index f761bf5..4c47c75 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -2,7 +2,7 @@
  * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response
  * Copyright (c) 2002-2004, Instant802 Networks, Inc.
  * Copyright (c) 2005-2006, Devicescape Software, Inc.
- * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -310,6 +310,46 @@
 }
 
 
+enum ssid_match_result {
+	NO_SSID_MATCH,
+	EXACT_SSID_MATCH,
+	WILDCARD_SSID_MATCH
+};
+
+static enum ssid_match_result ssid_match(struct hostapd_data *hapd,
+					 const u8 *ssid, size_t ssid_len,
+					 const u8 *ssid_list,
+					 size_t ssid_list_len)
+{
+	const u8 *pos, *end;
+	int wildcard = 0;
+
+	if (ssid_len == 0)
+		wildcard = 1;
+	if (ssid_len == hapd->conf->ssid.ssid_len &&
+	    os_memcmp(ssid, hapd->conf->ssid.ssid, ssid_len) == 0)
+		return EXACT_SSID_MATCH;
+
+	if (ssid_list == NULL)
+		return wildcard ? WILDCARD_SSID_MATCH : NO_SSID_MATCH;
+
+	pos = ssid_list;
+	end = ssid_list + ssid_list_len;
+	while (pos + 1 <= end) {
+		if (pos + 2 + pos[1] > end)
+			break;
+		if (pos[1] == 0)
+			wildcard = 1;
+		if (pos[1] == hapd->conf->ssid.ssid_len &&
+		    os_memcmp(pos + 2, hapd->conf->ssid.ssid, pos[1]) == 0)
+			return EXACT_SSID_MATCH;
+		pos += 2 + pos[1];
+	}
+
+	return wildcard ? WILDCARD_SSID_MATCH : NO_SSID_MATCH;
+}
+
+
 void handle_probe_req(struct hostapd_data *hapd,
 		      const struct ieee80211_mgmt *mgmt, size_t len,
 		      int ssi_signal)
@@ -321,6 +361,7 @@
 	struct sta_info *sta = NULL;
 	size_t i, resp_len;
 	int noack;
+	enum ssid_match_result res;
 
 	ie = mgmt->u.probe_req.variable;
 	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req))
@@ -376,7 +417,8 @@
 	}
 #endif /* CONFIG_P2P */
 
-	if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0) {
+	if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0 &&
+	    elems.ssid_list_len == 0) {
 		wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for "
 			   "broadcast SSID ignored", MAC2STR(mgmt->sa));
 		return;
@@ -394,10 +436,9 @@
 	}
 #endif /* CONFIG_P2P */
 
-	if (elems.ssid_len == 0 ||
-	    (elems.ssid_len == hapd->conf->ssid.ssid_len &&
-	     os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) ==
-	     0)) {
+	res = ssid_match(hapd, elems.ssid, elems.ssid_len,
+			 elems.ssid_list, elems.ssid_list_len);
+	if (res != NO_SSID_MATCH) {
 		if (sta)
 			sta->ssid_probe = &hapd->conf->ssid;
 	} else {
@@ -406,9 +447,10 @@
 			ieee802_11_print_ssid(ssid_txt, elems.ssid,
 					      elems.ssid_len);
 			wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
-				   " for foreign SSID '%s' (DA " MACSTR ")",
+				   " for foreign SSID '%s' (DA " MACSTR ")%s",
 				   MAC2STR(mgmt->sa), ssid_txt,
-				   MAC2STR(mgmt->da));
+				   MAC2STR(mgmt->da),
+				   elems.ssid_list ? " (SSID list)" : "");
 		}
 		return;
 	}
@@ -455,7 +497,8 @@
 	 * If this is a broadcast probe request, apply no ack policy to avoid
 	 * excessive retries.
 	 */
-	noack = !!(elems.ssid_len == 0 && is_broadcast_ether_addr(mgmt->da));
+	noack = !!(res == WILDCARD_SSID_MATCH &&
+		   is_broadcast_ether_addr(mgmt->da));
 
 	if (hostapd_drv_send_mlme(hapd, resp, resp_len, noack) < 0)
 		perror("handle_probe_req: send");
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index c55d3fe..1cb7e73 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -189,6 +189,7 @@
 	u8 addr[ETH_ALEN];
 	struct sta_info *sta;
 	const char *pos;
+	u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
 
 	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s",
 		txtaddr);
@@ -228,11 +229,14 @@
 	}
 #endif /* CONFIG_P2P_MANAGER */
 
-	hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+	pos = os_strstr(txtaddr, " reason=");
+	if (pos)
+		reason = atoi(pos + 8);
+
+	hostapd_drv_sta_deauth(hapd, addr, reason);
 	sta = ap_get_sta(hapd, addr);
 	if (sta)
-		ap_sta_deauthenticate(hapd, sta,
-				      WLAN_REASON_PREV_AUTH_NOT_VALID);
+		ap_sta_deauthenticate(hapd, sta, reason);
 	else if (addr[0] == 0xff)
 		hostapd_free_stas(hapd);
 
@@ -246,6 +250,7 @@
 	u8 addr[ETH_ALEN];
 	struct sta_info *sta;
 	const char *pos;
+	u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
 
 	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s",
 		txtaddr);
@@ -285,11 +290,14 @@
 	}
 #endif /* CONFIG_P2P_MANAGER */
 
-	hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+	pos = os_strstr(txtaddr, " reason=");
+	if (pos)
+		reason = atoi(pos + 8);
+
+	hostapd_drv_sta_disassoc(hapd, addr, reason);
 	sta = ap_get_sta(hapd, addr);
 	if (sta)
-		ap_sta_disassociate(hapd, sta,
-				    WLAN_REASON_PREV_AUTH_NOT_VALID);
+		ap_sta_disassociate(hapd, sta, reason);
 	else if (addr[0] == 0xff)
 		hostapd_free_stas(hapd);
 
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 8613975..8980bec 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -512,13 +512,13 @@
 					   action->data + 2);
 	}
 #endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_IEEE80211V
+#ifdef CONFIG_WNM
 	if (action->category == WLAN_ACTION_WNM) {
 		wpa_printf(MSG_DEBUG, "%s: WNM_ACTION length %d",
 			   __func__, (int) action->len);
 		ieee802_11_rx_wnm_action_ap(hapd, action);
 	}
-#endif /* CONFIG_IEEE80211V */
+#endif /* CONFIG_WNM */
 }
 
 
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index cef9daf..92fda56 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -894,7 +894,11 @@
 		if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
 				     hapd->iconf->channel,
 				     hapd->iconf->ieee80211n,
-				     hapd->iconf->secondary_channel)) {
+				     hapd->iconf->ieee80211ac,
+				     hapd->iconf->secondary_channel,
+				     hapd->iconf->vht_oper_chwidth,
+				     hapd->iconf->vht_oper_centr_freq_seg0_idx,
+				     hapd->iconf->vht_oper_centr_freq_seg1_idx)) {
 			wpa_printf(MSG_ERROR, "Could not set channel for "
 				   "kernel driver");
 			return -1;
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index f1e7d9f..c9087b3 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -192,6 +192,12 @@
 #ifdef CONFIG_SQLITE
 	struct hostapd_eap_user tmp_eap_user;
 #endif /* CONFIG_SQLITE */
+
+#ifdef CONFIG_SAE
+	/** Key used for generating SAE anti-clogging tokens */
+	u8 sae_token_key[8];
+	os_time_t last_sae_token_key_update;
+#endif /* CONFIG_SAE */
 };
 
 
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 97e1238..923b698 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -122,6 +122,8 @@
 	case HOSTAPD_MODE_IEEE80211G:
 		basic_rates = basic_rates_g;
 		break;
+	case HOSTAPD_MODE_IEEE80211AD:
+		return 0; /* No basic rates for 11ad */
 	default:
 		return -1;
 	}
@@ -756,6 +758,8 @@
 		return "IEEE 802.11b";
 	case HOSTAPD_MODE_IEEE80211G:
 		return "IEEE 802.11g";
+	case HOSTAPD_MODE_IEEE80211AD:
+		return "IEEE 802.11ad";
 	default:
 		return "UNKNOWN";
 	}
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index a13a135..79235df 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -13,10 +13,13 @@
 #include "utils/common.h"
 #include "utils/eloop.h"
 #include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
 #include "drivers/driver.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "common/wpa_ctrl.h"
+#include "common/sae.h"
 #include "radius/radius.h"
 #include "radius/radius_client.h"
 #include "p2p/p2p.h"
@@ -34,6 +37,7 @@
 #include "ap_mlme.h"
 #include "p2p_hostapd.h"
 #include "ap_drv_ops.h"
+#include "wnm_ap.h"
 #include "ieee802_11.h"
 
 
@@ -316,19 +320,33 @@
 
 #ifdef CONFIG_SAE
 
-static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
-					     struct sta_info *sta)
+static struct wpabuf * auth_process_sae_commit(struct hostapd_data *hapd,
+					       struct sta_info *sta)
 {
 	struct wpabuf *buf;
 
-	buf = wpabuf_alloc(2);
+	if (hapd->conf->ssid.wpa_passphrase == NULL) {
+		wpa_printf(MSG_DEBUG, "SAE: No password available");
+		return NULL;
+	}
+
+	if (sae_prepare_commit(hapd->own_addr, sta->addr,
+			       (u8 *) hapd->conf->ssid.wpa_passphrase,
+			       os_strlen(hapd->conf->ssid.wpa_passphrase),
+			       sta->sae) < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
+		return NULL;
+	}
+
+	if (sae_process_commit(sta->sae) < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit");
+		return NULL;
+	}
+
+	buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN);
 	if (buf == NULL)
 		return NULL;
-
-	wpabuf_put_le16(buf, 19); /* Finite Cyclic Group */
-	/* TODO: Anti-Clogging Token (if requested) */
-	/* TODO: Scalar */
-	/* TODO: Element */
+	sae_write_commit(sta->sae, buf, NULL);
 
 	return buf;
 }
@@ -339,49 +357,82 @@
 {
 	struct wpabuf *buf;
 
-	buf = wpabuf_alloc(2);
+	buf = wpabuf_alloc(SAE_CONFIRM_MAX_LEN);
 	if (buf == NULL)
 		return NULL;
 
-	wpabuf_put_le16(buf, sta->sae_send_confirm);
-	sta->sae_send_confirm++;
-	/* TODO: Confirm */
+	sae_write_confirm(sta->sae, buf);
 
 	return buf;
 }
 
 
-static u16 handle_sae_commit(struct hostapd_data *hapd, struct sta_info *sta,
-			     const u8 *data, size_t len)
+static int use_sae_anti_clogging(struct hostapd_data *hapd)
 {
-	wpa_hexdump(MSG_DEBUG, "SAE commit fields", data, len);
+	struct sta_info *sta;
+	unsigned int open = 0;
 
-	/* Check Finite Cyclic Group */
-	if (len < 2)
-		return WLAN_STATUS_UNSPECIFIED_FAILURE;
-	if (WPA_GET_LE16(data) != 19) {
-		wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u",
-			   WPA_GET_LE16(data));
-		return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+	if (hapd->conf->sae_anti_clogging_threshold == 0)
+		return 1;
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		if (!sta->sae)
+			continue;
+		if (sta->sae->state != SAE_COMMITTED &&
+		    sta->sae->state != SAE_CONFIRMED)
+			continue;
+		open++;
+		if (open >= hapd->conf->sae_anti_clogging_threshold)
+			return 1;
 	}
 
-	return WLAN_STATUS_SUCCESS;
+	return 0;
 }
 
 
-static u16 handle_sae_confirm(struct hostapd_data *hapd, struct sta_info *sta,
-			      const u8 *data, size_t len)
+static int check_sae_token(struct hostapd_data *hapd, const u8 *addr,
+			   const u8 *token, size_t token_len)
 {
-	u16 rc;
+	u8 mac[SHA256_MAC_LEN];
 
-	wpa_hexdump(MSG_DEBUG, "SAE confirm fields", data, len);
+	if (token_len != SHA256_MAC_LEN)
+		return -1;
+	if (hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key),
+			addr, ETH_ALEN, mac) < 0 ||
+	    os_memcmp(token, mac, SHA256_MAC_LEN) != 0)
+		return -1;
 
-	if (len < 2)
-		return WLAN_STATUS_UNSPECIFIED_FAILURE;
-	rc = WPA_GET_LE16(data);
-	wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", rc);
+	return 0;
+}
 
-	return WLAN_STATUS_SUCCESS;
+
+static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd,
+					    const u8 *addr)
+{
+	struct wpabuf *buf;
+	u8 *token;
+	struct os_time t;
+
+	os_get_time(&t);
+	if (hapd->last_sae_token_key_update == 0 ||
+	    t.sec > hapd->last_sae_token_key_update + 60) {
+		if (random_get_bytes(hapd->sae_token_key,
+				     sizeof(hapd->sae_token_key)) < 0)
+			return NULL;
+		wpa_hexdump(MSG_DEBUG, "SAE: Updated token key",
+			    hapd->sae_token_key, sizeof(hapd->sae_token_key));
+		hapd->last_sae_token_key_update = t.sec;
+	}
+
+	buf = wpabuf_alloc(SHA256_MAC_LEN);
+	if (buf == NULL)
+		return NULL;
+
+	token = wpabuf_put(buf, SHA256_MAC_LEN);
+	hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key),
+		    addr, ETH_ALEN, token);
+
+	return buf;
 }
 
 
@@ -390,19 +441,52 @@
 			    u8 auth_transaction)
 {
 	u16 resp = WLAN_STATUS_SUCCESS;
-	struct wpabuf *data;
+	struct wpabuf *data = NULL;
+
+	if (!sta->sae) {
+		if (auth_transaction != 1)
+			return;
+		sta->sae = os_zalloc(sizeof(*sta->sae));
+		if (sta->sae == NULL)
+			return;
+		sta->sae->state = SAE_NOTHING;
+	}
 
 	if (auth_transaction == 1) {
+		const u8 *token = NULL;
+		size_t token_len = 0;
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
 			       "start SAE authentication (RX commit)");
-		resp = handle_sae_commit(hapd, sta, mgmt->u.auth.variable,
-					 ((u8 *) mgmt) + len -
-					 mgmt->u.auth.variable);
-		if (resp == WLAN_STATUS_SUCCESS)
-			sta->sae_state = SAE_COMMIT;
+		resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable,
+					((const u8 *) mgmt) + len -
+					mgmt->u.auth.variable, &token,
+					&token_len, hapd->conf->sae_groups);
+		if (token && check_sae_token(hapd, sta->addr, token, token_len)
+		    < 0) {
+			wpa_printf(MSG_DEBUG, "SAE: Drop commit message with "
+				   "incorrect token from " MACSTR,
+				   MAC2STR(sta->addr));
+			return;
+		}
+
+		if (resp == WLAN_STATUS_SUCCESS) {
+			if (!token && use_sae_anti_clogging(hapd)) {
+				wpa_printf(MSG_DEBUG, "SAE: Request anti-"
+					   "clogging token from " MACSTR,
+					   MAC2STR(sta->addr));
+				data = auth_build_token_req(hapd, sta->addr);
+				resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ;
+			} else {
+				data = auth_process_sae_commit(hapd, sta);
+				if (data == NULL)
+					resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+				else
+					sta->sae->state = SAE_COMMITTED;
+			}
+		}
 	} else if (auth_transaction == 2) {
-		if (sta->sae_state != SAE_COMMIT) {
+		if (sta->sae->state != SAE_COMMITTED) {
 			hostapd_logger(hapd, sta->addr,
 				       HOSTAPD_MODULE_IEEE80211,
 				       HOSTAPD_LEVEL_DEBUG,
@@ -412,14 +496,24 @@
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
 			       "SAE authentication (RX confirm)");
-		resp = handle_sae_confirm(hapd, sta, mgmt->u.auth.variable,
-					  ((u8 *) mgmt) + len -
-					  mgmt->u.auth.variable);
-		if (resp == WLAN_STATUS_SUCCESS) {
+		if (sae_check_confirm(sta->sae, mgmt->u.auth.variable,
+				       ((u8 *) mgmt) + len -
+				       mgmt->u.auth.variable) < 0) {
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		} else {
+			resp = WLAN_STATUS_SUCCESS;
 			sta->flags |= WLAN_STA_AUTH;
 			wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
 			sta->auth_alg = WLAN_AUTH_SAE;
 			mlme_authenticate_indication(hapd, sta);
+
+			data = auth_build_sae_confirm(hapd, sta);
+			if (data == NULL)
+				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			else {
+				sta->sae->state = SAE_ACCEPTED;
+				sae_clear_temp_data(sta->sae);
+			}
 		}
 	} else {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
@@ -431,16 +525,6 @@
 
 	sta->auth_alg = WLAN_AUTH_SAE;
 
-	if (resp == WLAN_STATUS_SUCCESS) {
-		if (auth_transaction == 1)
-			data = auth_build_sae_commit(hapd, sta);
-		else
-			data = auth_build_sae_confirm(hapd, sta);
-		if (data == NULL)
-			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
-	} else
-		data = NULL;
-
 	send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
 			auth_transaction, resp,
 			data ? wpabuf_head(data) : (u8 *) "",
@@ -1434,13 +1518,32 @@
 #endif /* CONFIG_IEEE80211W */
 
 
+#ifdef CONFIG_WNM
+static void hostapd_wnm_action(struct hostapd_data *hapd, struct sta_info *sta,
+			       const struct ieee80211_mgmt *mgmt,
+			       size_t len)
+{
+	struct rx_action action;
+	if (len < IEEE80211_HDRLEN + 2)
+		return;
+	os_memset(&action, 0, sizeof(action));
+	action.da = mgmt->da;
+	action.sa = mgmt->sa;
+	action.bssid = mgmt->bssid;
+	action.category = mgmt->u.action.category;
+	action.data = (const u8 *) &mgmt->u.action.u.wnm_sleep_req.action;
+	action.len = len - IEEE80211_HDRLEN - 1;
+	action.freq = hapd->iface->freq;
+	ieee802_11_rx_wnm_action_ap(hapd, &action);
+}
+#endif /* CONFIG_WNM */
+
+
 static void handle_action(struct hostapd_data *hapd,
 			  const struct ieee80211_mgmt *mgmt, size_t len)
 {
-#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R)
 	struct sta_info *sta;
 	sta = ap_get_sta(hapd, mgmt->sa);
-#endif /* CONFIG_IEEE80211W || CONFIG_IEEE80211R */
 
 	if (len < IEEE80211_HDRLEN + 1) {
 		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
@@ -1450,6 +1553,14 @@
 		return;
 	}
 
+	if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
+	    (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) {
+		wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored Action "
+			   "frame (category=%u) from unassociated STA " MACSTR,
+			   MAC2STR(mgmt->sa), mgmt->u.action.category);
+		return;
+	}
+
 #ifdef CONFIG_IEEE80211W
 	if (sta && (sta->flags & WLAN_STA_MFP) &&
 	    !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP) &&
@@ -1465,20 +1576,10 @@
 	switch (mgmt->u.action.category) {
 #ifdef CONFIG_IEEE80211R
 	case WLAN_ACTION_FT:
-	{
-		if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
-			wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored FT Action "
-				   "frame from unassociated STA " MACSTR,
-				   MAC2STR(mgmt->sa));
-			return;
-		}
-
 		if (wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action,
 				     len - IEEE80211_HDRLEN))
 			break;
-
 		return;
-	}
 #endif /* CONFIG_IEEE80211R */
 	case WLAN_ACTION_WMM:
 		hostapd_wmm_action(hapd, mgmt, len);
@@ -1488,6 +1589,11 @@
 		hostapd_sa_query_action(hapd, mgmt, len);
 		return;
 #endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_WNM
+	case WLAN_ACTION_WNM:
+		hostapd_wnm_action(hapd, sta, mgmt, len);
+		return;
+#endif /* CONFIG_WNM */
 	case WLAN_ACTION_PUBLIC:
 		if (hapd->public_action_cb) {
 			hapd->public_action_cb(hapd->public_action_cb_ctx,
@@ -1685,6 +1791,7 @@
 	struct sta_info *sta;
 	int new_assoc = 1;
 	struct ieee80211_ht_capabilities ht_cap;
+	struct ieee80211_vht_capabilities vht_cap;
 
 	if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) :
 				      sizeof(mgmt->u.assoc_resp))) {
@@ -1757,11 +1864,16 @@
 	if (sta->flags & WLAN_STA_HT)
 		hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap);
 #endif /* CONFIG_IEEE80211N */
+#ifdef CONFIG_IEEE80211AC
+	if (sta->flags & WLAN_STA_VHT)
+		hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap);
+#endif /* CONFIG_IEEE80211AC */
 
 	if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability,
 			    sta->supported_rates, sta->supported_rates_len,
 			    sta->listen_interval,
 			    sta->flags & WLAN_STA_HT ? &ht_cap : NULL,
+			    sta->flags & WLAN_STA_VHT ? &vht_cap : NULL,
 			    sta->flags, sta->qosinfo)) {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_NOTICE,
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 1e5800d..2aab56d 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -53,6 +53,9 @@
 void hostapd_get_ht_capab(struct hostapd_data *hapd,
 			  struct ieee80211_ht_capabilities *ht_cap,
 			  struct ieee80211_ht_capabilities *neg_ht_cap);
+void hostapd_get_vht_capab(struct hostapd_data *hapd,
+			   struct ieee80211_vht_capabilities *vht_cap,
+			   struct ieee80211_vht_capabilities *neg_vht_cap);
 u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
 		      const u8 *ht_capab, size_t ht_capab_len);
 void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta);
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index b3fdf3d..76f78a7 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -173,6 +173,14 @@
 		len = 5;
 	if (len < 4 && hapd->conf->interworking)
 		len = 4;
+	if (len < 3 && hapd->conf->wnm_sleep_mode)
+		len = 3;
+	if (len < 7 && hapd->conf->ssid.utf8_ssid)
+		len = 7;
+#ifdef CONFIG_WNM
+	if (len < 4)
+		len = 4;
+#endif /* CONFIG_WNM */
 	if (len == 0)
 		return eid;
 
@@ -180,9 +188,20 @@
 	*pos++ = len;
 	*pos++ = 0x00;
 	*pos++ = 0x00;
-	*pos++ = 0x00;
 
 	*pos = 0x00;
+	if (hapd->conf->wnm_sleep_mode)
+		*pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */
+	if (hapd->conf->bss_transition)
+		*pos |= 0x08; /* Bit 19 - BSS Transition */
+	pos++;
+
+	if (len < 4)
+		return pos;
+	*pos = 0x00;
+#ifdef CONFIG_WNM
+	*pos |= 0x02; /* Bit 25 - SSID List */
+#endif /* CONFIG_WNM */
 	if (hapd->conf->time_advertisement == 2)
 		*pos |= 0x08; /* Bit 27 - UTC TSF Offset */
 	if (hapd->conf->interworking)
@@ -198,6 +217,18 @@
 		*pos |= 0x80; /* Bit 39 - TDLS Channel Switching Prohibited */
 	pos++;
 
+	if (len < 6)
+		return pos;
+	*pos = 0x00;
+	pos++;
+
+	if (len < 7)
+		return pos;
+	*pos = 0x00;
+	if (hapd->conf->ssid.utf8_ssid)
+		*pos |= 0x01; /* Bit 48 - UTF-8 SSID */
+	pos++;
+
 	return pos;
 }
 
diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
index b21c2b7..f6cc5d6 100644
--- a/src/ap/ieee802_11_vht.c
+++ b/src/ap/ieee802_11_vht.c
@@ -108,3 +108,14 @@
 
 	return WLAN_STATUS_SUCCESS;
 }
+
+void hostapd_get_vht_capab(struct hostapd_data *hapd,
+			   struct ieee80211_vht_capabilities *vht_cap,
+			   struct ieee80211_vht_capabilities *neg_vht_cap)
+{
+	if (vht_cap == NULL)
+		return;
+	os_memcpy(neg_vht_cap, vht_cap, sizeof(*neg_vht_cap));
+
+	/* TODO: mask own capabilities, like get_ht_capab() */
+}
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index e1b11ba..a832a73 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -66,8 +66,9 @@
 	if (sta->flags & WLAN_STA_PREAUTH) {
 		rsn_preauth_send(hapd, sta, buf, len);
 	} else {
-		hostapd_drv_hapd_send_eapol(hapd, sta->addr, buf, len,
-					    encrypt, sta->flags);
+		hostapd_drv_hapd_send_eapol(
+			hapd, sta->addr, buf, len,
+			encrypt, hostapd_sta_flags_to_drv(sta->flags));
 	}
 
 	os_free(buf);
@@ -354,6 +355,8 @@
 const char *radius_mode_txt(struct hostapd_data *hapd)
 {
 	switch (hapd->iface->conf->hw_mode) {
+	case HOSTAPD_MODE_IEEE80211AD:
+		return "802.11ad";
 	case HOSTAPD_MODE_IEEE80211A:
 		return "802.11a";
 	case HOSTAPD_MODE_IEEE80211G:
@@ -452,6 +455,16 @@
 		return -1;
 	}
 
+	if (sta->acct_session_id_hi || sta->acct_session_id_lo) {
+		os_snprintf(buf, sizeof(buf), "%08X-%08X",
+			    sta->acct_session_id_hi, sta->acct_session_id_lo);
+		if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
+					 (u8 *) buf, os_strlen(buf))) {
+			wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id");
+			return -1;
+		}
+	}
+
 	return 0;
 }
 
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index 3a9cc7b..d27fd30 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -209,6 +209,8 @@
 	pmksa->pmkid[PMKID_HASH(entry->pmkid)] = entry;
 
 	pmksa->pmksa_count++;
+	if (prev == NULL)
+		pmksa_cache_set_expiration(pmksa);
 	wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
 		   MAC2STR(entry->spa));
 	wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN);
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 6bc43d2..8ada121 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -12,6 +12,7 @@
 #include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
 #include "common/wpa_ctrl.h"
+#include "common/sae.h"
 #include "radius/radius.h"
 #include "radius/radius_client.h"
 #include "drivers/driver.h"
@@ -240,6 +241,11 @@
 	os_free(sta->identity);
 	os_free(sta->radius_cui);
 
+#ifdef CONFIG_SAE
+	sae_clear_data(sta->sae);
+	os_free(sta->sae);
+#endif /* CONFIG_SAE */
+
 	os_free(sta);
 }
 
@@ -493,6 +499,7 @@
 		return NULL;
 	}
 	sta->acct_interim_interval = hapd->conf->acct_interim_interval;
+	accounting_sta_get_id(hapd, sta);
 
 	/* initialize STA info data */
 	wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index d5e92fa..32ea46e 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -127,8 +127,7 @@
 	struct os_time connected_time;
 
 #ifdef CONFIG_SAE
-	enum { SAE_INIT, SAE_COMMIT, SAE_CONFIRM } sae_state;
-	u16 sae_send_confirm;
+	struct sae_data *sae;
 #endif /* CONFIG_SAE */
 };
 
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index 2594404..54a6b85 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -19,7 +19,6 @@
 
 #define MAX_TFS_IE_LEN  1024
 
-#ifdef CONFIG_IEEE80211V
 
 /* get the TFS IE from driver */
 static int ieee80211_11_get_tfs_ie(struct hostapd_data *hapd, const u8 *addr,
@@ -57,8 +56,8 @@
 	u16 wnmtfs_ie_len;
 	u8 *pos;
 	struct sta_info *sta;
-	enum wnm_oper tfs_oper = action_type == 0 ? WNM_SLEEP_TFS_RESP_IE_ADD :
-		WNM_SLEEP_TFS_RESP_IE_NONE;
+	enum wnm_oper tfs_oper = action_type == WNM_SLEEP_MODE_ENTER ?
+		WNM_SLEEP_TFS_RESP_IE_ADD : WNM_SLEEP_TFS_RESP_IE_NONE;
 
 	sta = ap_get_sta(hapd, addr);
 	if (sta == NULL) {
@@ -105,7 +104,8 @@
 	mgmt->u.action.u.wnm_sleep_resp.dialogtoken = dialog_token;
 	pos = (u8 *)mgmt->u.action.u.wnm_sleep_resp.variable;
 	/* add key data if MFP is enabled */
-	if (wpa_auth_uses_mfp(sta->wpa_sm) || action_type != 1){
+	if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
+	    action_type != WNM_SLEEP_MODE_EXIT) {
 		mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0;
 	} else {
 		gtk_elem_len = wpa_wnmsleep_gtk_subelem(sta->wpa_sm, pos);
@@ -132,7 +132,8 @@
 	os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len);
 	/* copy TFS IE here */
 	pos += wnmsleep_ie_len;
-	os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len);
+	if (wnmtfs_ie)
+		os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len);
 
 	len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len +
 		igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len;
@@ -152,7 +153,7 @@
 		 * WNM Sleep
 		 */
 		if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT &&
-		    wnmsleep_ie.action_type == 0) {
+		    wnmsleep_ie.action_type == WNM_SLEEP_MODE_ENTER) {
 			hostapd_drv_wnm_oper(hapd, WNM_SLEEP_ENTER_CONFIRM,
 					     addr, NULL, NULL);
 			wpa_set_wnmsleep(sta->wpa_sm, 1);
@@ -162,12 +163,14 @@
 		 * 2. start GTK/IGTK update if MFP is not used
 		 * 3. unpause the node in driver
 		 */
-		if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT &&
-		    wnmsleep_ie.action_type == 1) {
+		if ((wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT ||
+		     wnmsleep_ie.status ==
+		     WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) &&
+		    wnmsleep_ie.action_type == WNM_SLEEP_MODE_EXIT) {
 			wpa_set_wnmsleep(sta->wpa_sm, 0);
 			hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM,
 					     addr, NULL, NULL);
-			if (wpa_auth_uses_mfp(sta->wpa_sm) && action_type == 1)
+			if (!wpa_auth_uses_mfp(sta->wpa_sm))
 				wpa_wnmsleep_rekey_gtk(sta->wpa_sm);
 		}
 	} else
@@ -184,29 +187,29 @@
 static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
 				       const u8 *addr, const u8 *frm, int len)
 {
-	/*
-	 * Action [1] | Dialog Token [1] | WNM-Sleep Mode IE |
-	 * TFS Response IE
-	 */
-	u8 *pos = (u8 *) frm; /* point to action field */
-	u8 dialog_token = pos[1];
+	/* Dialog Token [1] | WNM-Sleep Mode IE | TFS Response IE */
+	const u8 *pos = frm;
+	u8 dialog_token;
 	struct wnm_sleep_element *wnmsleep_ie = NULL;
 	/* multiple TFS Req IE (assuming consecutive) */
 	u8 *tfsreq_ie_start = NULL;
 	u8 *tfsreq_ie_end = NULL;
 	u16 tfsreq_ie_len = 0;
 
-	pos += 1 + 1;
-	while (pos - frm < len - 1) {
-		u8 ie_len = *(pos+1);
+	dialog_token = *pos++;
+	while (pos + 1 < frm + len) {
+		u8 ie_len = pos[1];
+		if (pos + 2 + ie_len > frm + len)
+			break;
 		if (*pos == WLAN_EID_WNMSLEEP)
-			wnmsleep_ie = (struct wnm_sleep_element *)pos;
+			wnmsleep_ie = (struct wnm_sleep_element *) pos;
 		else if (*pos == WLAN_EID_TFS_REQ) {
 			if (!tfsreq_ie_start)
-				tfsreq_ie_start = pos;
-			tfsreq_ie_end = pos;
+				tfsreq_ie_start = (u8 *) pos;
+			tfsreq_ie_end = (u8 *) pos;
 		} else
-			wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos);
+			wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized",
+				   *pos);
 		pos += ie_len + 2;
 	}
 
@@ -215,8 +218,9 @@
 		return;
 	}
 
-	if (wnmsleep_ie->action_type == 0 && tfsreq_ie_start &&
-	    tfsreq_ie_end && tfsreq_ie_end - tfsreq_ie_start >= 0) {
+	if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER &&
+	    tfsreq_ie_start && tfsreq_ie_end &&
+	    tfsreq_ie_end - tfsreq_ie_start >= 0) {
 		tfsreq_ie_len = (tfsreq_ie_end + tfsreq_ie_end[1] + 2) -
 			tfsreq_ie_start;
 		wpa_printf(MSG_DEBUG, "TFS Req IE(s) found");
@@ -231,7 +235,7 @@
 				      wnmsleep_ie->action_type,
 				      wnmsleep_ie->intval);
 
-	if (wnmsleep_ie->action_type == 1) {
+	if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
 		/* clear the tfs after sending the resp frame */
 		ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start,
 					&tfsreq_ie_len, WNM_SLEEP_TFS_IE_DEL);
@@ -239,20 +243,29 @@
 }
 
 
-void ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
-				 struct rx_action *action)
+int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
+				struct rx_action *action)
 {
-	u8 *pos = (u8 *) action->data + 1; /* point to the action field */
-	u8 act = *pos;
+	if (action->len < 1 || action->data == NULL)
+		return -1;
 
-	switch (act) {
+	switch (action->data[0]) {
+	case WNM_BSS_TRANS_MGMT_QUERY:
+		wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query");
+		/* TODO */
+		return -1;
+	case WNM_BSS_TRANS_MGMT_RESP:
+		wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management "
+			   "Response");
+		/* TODO */
+		return -1;
 	case WNM_SLEEP_MODE_REQ:
 		ieee802_11_rx_wnmsleep_req(hapd, action->sa, action->data + 1,
-					   action->len);
-		break;
-	default:
-		break;
+					   action->len - 1);
+		return 0;
 	}
-}
 
-#endif /* CONFIG_IEEE80211V */
+	wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR,
+		   action->data[0], MAC2STR(action->sa));
+	return -1;
+}
diff --git a/src/ap/wnm_ap.h b/src/ap/wnm_ap.h
index ab7c4f1..f05726e 100644
--- a/src/ap/wnm_ap.h
+++ b/src/ap/wnm_ap.h
@@ -11,7 +11,7 @@
 
 struct rx_action;
 
-void ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
-				 struct rx_action *action);
+int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
+				struct rx_action *action);
 
 #endif /* WNM_AP_H */
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 0816b25..fa4b1cb 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -282,8 +282,9 @@
 static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth,
 					  struct wpa_group *group)
 {
-	u8 buf[ETH_ALEN + 8 + sizeof(group)];
+	u8 buf[ETH_ALEN + 8 + sizeof(unsigned long)];
 	u8 rkey[32];
+	unsigned long ptr;
 
 	if (random_get_bytes(group->GMK, WPA_GMK_LEN) < 0)
 		return -1;
@@ -295,7 +296,8 @@
 	 */
 	os_memcpy(buf, wpa_auth->addr, ETH_ALEN);
 	wpa_get_ntp_timestamp(buf + ETH_ALEN);
-	os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group));
+	ptr = (unsigned long) group;
+	os_memcpy(buf + ETH_ALEN + 8, &ptr, sizeof(ptr));
 	if (random_get_bytes(rkey, sizeof(rkey)) < 0)
 		return -1;
 
@@ -2413,11 +2415,9 @@
 				"marking station for GTK rekeying");
 	}
 
-#ifdef CONFIG_IEEE80211V
-	/* Do not rekey GTK/IGTK when STA is in wnmsleep */
+	/* Do not rekey GTK/IGTK when STA is in WNM-Sleep Mode */
 	if (sm->is_wnmsleep)
 		return 0;
-#endif /* CONFIG_IEEE80211V */
 
 	sm->group->GKeyDoneStations++;
 	sm->GUpdateStationKeys = TRUE;
@@ -2427,8 +2427,8 @@
 }
 
 
-#ifdef CONFIG_IEEE80211V
-/* update GTK when exiting wnmsleep mode */
+#ifdef CONFIG_WNM
+/* update GTK when exiting WNM-Sleep Mode */
 void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm)
 {
 	if (sm->is_wnmsleep)
@@ -2446,111 +2446,65 @@
 
 int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos)
 {
-	u8 *subelem;
 	struct wpa_group *gsm = sm->group;
-	size_t subelem_len, pad_len;
-	const u8 *key;
-	size_t key_len;
-	u8 keybuf[32];
-
-	/* GTK subslement */
-	key_len = gsm->GTK_len;
-	if (key_len > sizeof(keybuf))
-		return 0;
+	u8 *start = pos;
 
 	/*
-	 * Pad key for AES Key Wrap if it is not multiple of 8 bytes or is less
-	 * than 16 bytes.
-	 */
-	pad_len = key_len % 8;
-	if (pad_len)
-		pad_len = 8 - pad_len;
-	if (key_len + pad_len < 16)
-		pad_len += 8;
-	if (pad_len) {
-		os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len);
-		os_memset(keybuf + key_len, 0, pad_len);
-		keybuf[key_len] = 0xdd;
-		key_len += pad_len;
-		key = keybuf;
-	} else
-		key = gsm->GTK[gsm->GN - 1];
-
-	/*
+	 * GTK subelement:
 	 * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] |
-	 * Key[5..32] | 8 padding.
+	 * Key[5..32]
 	 */
-	subelem_len = 13 + key_len + 8;
-	subelem = os_zalloc(subelem_len);
-	if (subelem == NULL)
-		return 0;
-
-	subelem[0] = WNM_SLEEP_SUBELEM_GTK;
-	subelem[1] = 11 + key_len + 8;
+	*pos++ = WNM_SLEEP_SUBELEM_GTK;
+	*pos++ = 11 + gsm->GTK_len;
 	/* Key ID in B0-B1 of Key Info */
-	WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03);
-	subelem[4] = gsm->GTK_len;
-	if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5) != 0)
-	{
-		os_free(subelem);
+	WPA_PUT_LE16(pos, gsm->GN & 0x03);
+	pos += 2;
+	*pos++ = gsm->GTK_len;
+	if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, pos) != 0)
 		return 0;
-	}
-	if (aes_wrap(sm->PTK.kek, key_len / 8, key, subelem + 13)) {
-		os_free(subelem);
-		return 0;
-	}
+	pos += 8;
+	os_memcpy(pos, gsm->GTK[gsm->GN - 1], gsm->GTK_len);
+	pos += gsm->GTK_len;
 
-	os_memcpy(pos, subelem, subelem_len);
-
-	wpa_hexdump_key(MSG_DEBUG, "Plaintext GTK",
+	wpa_printf(MSG_DEBUG, "WNM: GTK Key ID %u in WNM-Sleep Mode exit",
+		   gsm->GN);
+	wpa_hexdump_key(MSG_DEBUG, "WNM: GTK in WNM-Sleep Mode exit",
 			gsm->GTK[gsm->GN - 1], gsm->GTK_len);
-	os_free(subelem);
 
-	return subelem_len;
+	return pos - start;
 }
 
 
 #ifdef CONFIG_IEEE80211W
 int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos)
 {
-	u8 *subelem, *ptr;
 	struct wpa_group *gsm = sm->group;
-	size_t subelem_len;
+	u8 *start = pos;
 
-	/* IGTK subelement
-	 * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] |
-	 * Key[16] | 8 padding */
-	subelem_len = 1 + 1 + 2 + 6 + WPA_IGTK_LEN + 8;
-	subelem = os_zalloc(subelem_len);
-	if (subelem == NULL)
+	/*
+	 * IGTK subelement:
+	 * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16]
+	 */
+	*pos++ = WNM_SLEEP_SUBELEM_IGTK;
+	*pos++ = 2 + 6 + WPA_IGTK_LEN;
+	WPA_PUT_LE16(pos, gsm->GN_igtk);
+	pos += 2;
+	if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos) != 0)
 		return 0;
+	pos += 6;
 
-	ptr = subelem;
-	*ptr++ = WNM_SLEEP_SUBELEM_IGTK;
-	*ptr++ = subelem_len - 2;
-	WPA_PUT_LE16(ptr, gsm->GN_igtk);
-	ptr += 2;
-	if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, ptr) != 0) {
-		os_free(subelem);
-		return 0;
-	}
-	ptr += 6;
-	if (aes_wrap(sm->PTK.kek, WPA_IGTK_LEN / 8,
-		     gsm->IGTK[gsm->GN_igtk - 4], ptr)) {
-		os_free(subelem);
-		return -1;
-	}
+	os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN);
+	pos += WPA_IGTK_LEN;
 
-	os_memcpy(pos, subelem, subelem_len);
-
-	wpa_hexdump_key(MSG_DEBUG, "Plaintext IGTK",
+	wpa_printf(MSG_DEBUG, "WNM: IGTK Key ID %u in WNM-Sleep Mode exit",
+		   gsm->GN_igtk);
+	wpa_hexdump_key(MSG_DEBUG, "WNM: IGTK in WNM-Sleep Mode exit",
 			gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN);
-	os_free(subelem);
 
-	return subelem_len;
+	return pos - start;
 }
 #endif /* CONFIG_IEEE80211W */
-#endif /* CONFIG_IEEE80211V */
+#endif /* CONFIG_WNM */
 
 
 static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 6ab170d..465eec6 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -282,14 +282,10 @@
 void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr);
 #endif /* CONFIG_IEEE80211R */
 
-#ifdef CONFIG_IEEE80211V
 void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm);
 void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag);
 int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos);
-#ifdef CONFIG_IEEE80211W
 int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos);
-#endif /* CONFIG_IEEE80211W */
-#endif /* CONFIG_IEEE80211V */
 
 int wpa_auth_uses_sae(struct wpa_state_machine *sm);
 
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 48bf79b..ccb3f82 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -416,7 +416,7 @@
 		pad_len = 8 - pad_len;
 	if (key_len + pad_len < 16)
 		pad_len += 8;
-	if (pad_len) {
+	if (pad_len && key_len < sizeof(keybuf)) {
 		os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len);
 		os_memset(keybuf + key_len, 0, pad_len);
 		keybuf[key_len] = 0xdd;
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 76c61ea..fdaaaff 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / WPA authenticator glue code
- * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,6 +10,7 @@
 
 #include "utils/common.h"
 #include "common/ieee802_11_defs.h"
+#include "common/sae.h"
 #include "eapol_auth/eapol_auth_sm.h"
 #include "eapol_auth/eapol_auth_sm_i.h"
 #include "eap_server/eap.h"
@@ -184,7 +185,17 @@
 {
 	struct hostapd_data *hapd = ctx;
 	struct sta_info *sta = ap_get_sta(hapd, addr);
-	const u8 *psk = hostapd_get_psk(hapd->conf, addr, prev_psk);
+	const u8 *psk;
+
+#ifdef CONFIG_SAE
+	if (sta && sta->auth_alg == WLAN_AUTH_SAE) {
+		if (!sta->sae || prev_psk)
+			return NULL;
+		return sta->sae->pmk;
+	}
+#endif /* CONFIG_SAE */
+
+	psk = hostapd_get_psk(hapd->conf, addr, prev_psk);
 	/*
 	 * This is about to iterate over all psks, prev_psk gives the last
 	 * returned psk which should not be returned again.
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index d5cf2c5..97489d3 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -87,9 +87,7 @@
 	unsigned int ft_completed:1;
 	unsigned int pmk_r1_name_valid:1;
 #endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_IEEE80211V
 	unsigned int is_wnmsleep:1;
-#endif /* CONFIG_IEEE80211V */
 
 	u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN];
 	int req_replay_counter_used;
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 4fd0135..cdfcca1 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -564,12 +564,9 @@
 	}
 #endif /* CONFIG_IEEE80211R */
 
-	if (ciphers & WPA_CIPHER_CCMP)
-		sm->pairwise = WPA_CIPHER_CCMP;
-	else if (ciphers & WPA_CIPHER_GCMP)
-		sm->pairwise = WPA_CIPHER_GCMP;
-	else
-		sm->pairwise = WPA_CIPHER_TKIP;
+	sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0);
+	if (sm->pairwise < 0)
+		return WPA_INVALID_PAIRWISE;
 
 	/* TODO: clear WPA/WPA2 state if STA changes from one to another */
 	if (wpa_ie[0] == WLAN_EID_RSN)
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index 85633ec..5ce4f1b 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -957,6 +957,9 @@
 	if (conf->ssid.security_policy == SECURITY_STATIC_WEP)
 		cfg.static_wep_only = 1;
 	cfg.dualband = interface_count(hapd->iface) > 1;
+	if ((wps->dev.rf_bands & (WPS_RF_50GHZ | WPS_RF_24GHZ)) ==
+	    (WPS_RF_50GHZ | WPS_RF_24GHZ))
+		cfg.dualband = 1;
 	if (cfg.dualband)
 		wpa_printf(MSG_DEBUG, "WPS: Dualband AP");
 
diff --git a/src/common/defs.h b/src/common/defs.h
index 85e9932..281dd8a 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -297,6 +297,7 @@
 	HOSTAPD_MODE_IEEE80211B,
 	HOSTAPD_MODE_IEEE80211G,
 	HOSTAPD_MODE_IEEE80211A,
+	HOSTAPD_MODE_IEEE80211AD,
 	NUM_HOSTAPD_MODES
 };
 
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index d9d3cd0..98fadda 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -284,6 +284,10 @@
 				break;
 			elems->bss_max_idle_period = pos;
 			break;
+		case WLAN_EID_SSID_LIST:
+			elems->ssid_list = pos;
+			elems->ssid_list_len = elen;
+			break;
 		default:
 			unknown++;
 			if (!show_errors)
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index bfc3eb2..55fa49d 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -1,6 +1,6 @@
 /*
  * IEEE 802.11 Common routines
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -43,6 +43,7 @@
 	const u8 *hs20;
 	const u8 *ext_capab;
 	const u8 *bss_max_idle_period;
+	const u8 *ssid_list;
 
 	u8 ssid_len;
 	u8 supp_rates_len;
@@ -74,6 +75,7 @@
 	u8 interworking_len;
 	u8 hs20_len;
 	u8 ext_capab_len;
+	u8 ssid_list_len;
 };
 
 typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 07c8580..f72c0d4 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -232,6 +232,7 @@
 #define WLAN_EID_20_40_BSS_INTOLERANT 73
 #define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74
 #define WLAN_EID_MMIE 76
+#define WLAN_EID_SSID_LIST 84
 #define WLAN_EID_BSS_MAX_IDLE_PERIOD 90
 #define WLAN_EID_TFS_REQ 91
 #define WLAN_EID_TFS_RESP 92
@@ -537,6 +538,16 @@
 					 * Entries */
 					u8 variable[0];
 				} STRUCT_PACKED bss_tm_req;
+				struct {
+					u8 action; /* 8 */
+					u8 dialog_token;
+					u8 status_code;
+					u8 bss_termination_delay;
+					/* Target BSSID (optional),
+					 * BSS Transition Candidate List
+					 * Entries (optional) */
+					u8 variable[0];
+				} STRUCT_PACKED bss_tm_resp;
 			} u;
 		} STRUCT_PACKED action;
 	} u;
@@ -701,6 +712,12 @@
 #define VHT_CAP_RX_ANTENNA_PATTERN                  ((u32) BIT(28))
 #define VHT_CAP_TX_ANTENNA_PATTERN                  ((u32) BIT(29))
 
+/* VHT channel widths */
+#define VHT_CHANWIDTH_USE_HT	0
+#define VHT_CHANWIDTH_80MHZ	1
+#define VHT_CHANWIDTH_160MHZ	2
+#define VHT_CHANWIDTH_80P80MHZ	3
+
 #define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs)
 				* 00:50:F2 */
 #define WPA_IE_VENDOR_TYPE 0x0050f201
@@ -1048,11 +1065,14 @@
 struct wnm_sleep_element {
 	u8 eid;     /* WLAN_EID_WNMSLEEP */
 	u8 len;
-	u8 action_type; /* WLAN_WNM_SLEEP_ENTER/EXIT */
+	u8 action_type; /* WNM_SLEEP_ENTER/WNM_SLEEP_MODE_EXIT */
 	u8 status;
 	le16 intval;
 } STRUCT_PACKED;
 
+#define WNM_SLEEP_MODE_ENTER 0
+#define WNM_SLEEP_MODE_EXIT 1
+
 enum wnm_sleep_mode_response_status {
 	WNM_STATUS_SLEEP_ACCEPT = 0,
 	WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE = 1,
diff --git a/src/common/sae.c b/src/common/sae.c
new file mode 100644
index 0000000..44ffcd0
--- /dev/null
+++ b/src/common/sae.c
@@ -0,0 +1,1028 @@
+/*
+ * Simultaneous authentication of equals
+ * Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "crypto/dh_groups.h"
+#include "ieee802_11_defs.h"
+#include "sae.h"
+
+
+int sae_set_group(struct sae_data *sae, int group)
+{
+	struct sae_temporary_data *tmp;
+
+	sae_clear_data(sae);
+	tmp = sae->tmp = os_zalloc(sizeof(*tmp));
+	if (tmp == NULL)
+		return -1;
+
+	/* First, check if this is an ECC group */
+	tmp->ec = crypto_ec_init(group);
+	if (tmp->ec) {
+		sae->group = group;
+		tmp->prime_len = crypto_ec_prime_len(tmp->ec);
+		tmp->prime = crypto_ec_get_prime(tmp->ec);
+		tmp->order = crypto_ec_get_order(tmp->ec);
+		return 0;
+	}
+
+	/* Not an ECC group, check FFC */
+	tmp->dh = dh_groups_get(group);
+	if (tmp->dh) {
+		sae->group = group;
+		tmp->prime_len = tmp->dh->prime_len;
+		if (tmp->prime_len > SAE_MAX_PRIME_LEN) {
+			sae_clear_data(sae);
+			return -1;
+		}
+
+		tmp->prime_buf = crypto_bignum_init_set(tmp->dh->prime,
+							tmp->prime_len);
+		if (tmp->prime_buf == NULL) {
+			sae_clear_data(sae);
+			return -1;
+		}
+		tmp->prime = tmp->prime_buf;
+
+		tmp->order_buf = crypto_bignum_init_set(tmp->dh->order,
+							tmp->dh->order_len);
+		if (tmp->order_buf == NULL) {
+			sae_clear_data(sae);
+			return -1;
+		}
+		tmp->order = tmp->order_buf;
+
+		return 0;
+	}
+
+	/* Unsupported group */
+	return -1;
+}
+
+
+void sae_clear_temp_data(struct sae_data *sae)
+{
+	struct sae_temporary_data *tmp;
+	if (sae == NULL || sae->tmp == NULL)
+		return;
+	tmp = sae->tmp;
+	crypto_ec_deinit(tmp->ec);
+	crypto_bignum_deinit(tmp->prime_buf, 0);
+	crypto_bignum_deinit(tmp->order_buf, 0);
+	crypto_bignum_deinit(tmp->sae_rand, 1);
+	crypto_bignum_deinit(tmp->pwe_ffc, 1);
+	crypto_bignum_deinit(tmp->own_commit_scalar, 0);
+	crypto_bignum_deinit(tmp->own_commit_element_ffc, 0);
+	crypto_bignum_deinit(tmp->peer_commit_element_ffc, 0);
+	crypto_ec_point_deinit(tmp->pwe_ecc, 1);
+	crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0);
+	crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0);
+	os_free(sae->tmp);
+	sae->tmp = NULL;
+}
+
+
+void sae_clear_data(struct sae_data *sae)
+{
+	if (sae == NULL)
+		return;
+	sae_clear_temp_data(sae);
+	crypto_bignum_deinit(sae->peer_commit_scalar, 0);
+	os_memset(sae, 0, sizeof(*sae));
+}
+
+
+static void buf_shift_right(u8 *buf, size_t len, size_t bits)
+{
+	size_t i;
+	for (i = len - 1; i > 0; i--)
+		buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits);
+	buf[0] >>= bits;
+}
+
+
+static struct crypto_bignum * sae_get_rand(struct sae_data *sae)
+{
+	u8 val[SAE_MAX_PRIME_LEN];
+	int iter = 0;
+	struct crypto_bignum *bn = NULL;
+	int order_len_bits = crypto_bignum_bits(sae->tmp->order);
+	size_t order_len = (order_len_bits + 7) / 8;
+
+	if (order_len > sizeof(val))
+		return NULL;
+
+	for (;;) {
+		if (iter++ > 100)
+			return NULL;
+		if (random_get_bytes(val, order_len) < 0)
+			return NULL;
+		if (order_len_bits % 8)
+			buf_shift_right(val, order_len, 8 - order_len_bits % 8);
+		bn = crypto_bignum_init_set(val, order_len);
+		if (bn == NULL)
+			return NULL;
+		if (crypto_bignum_is_zero(bn) ||
+		    crypto_bignum_is_one(bn) ||
+		    crypto_bignum_cmp(bn, sae->tmp->order) >= 0)
+			continue;
+		break;
+	}
+
+	os_memset(val, 0, order_len);
+	return bn;
+}
+
+
+static struct crypto_bignum * sae_get_rand_and_mask(struct sae_data *sae)
+{
+	crypto_bignum_deinit(sae->tmp->sae_rand, 1);
+	sae->tmp->sae_rand = sae_get_rand(sae);
+	if (sae->tmp->sae_rand == NULL)
+		return NULL;
+	return sae_get_rand(sae);
+}
+
+
+static void sae_pwd_seed_key(const u8 *addr1, const u8 *addr2, u8 *key)
+{
+	wpa_printf(MSG_DEBUG, "SAE: PWE derivation - addr1=" MACSTR
+		   " addr2=" MACSTR, MAC2STR(addr1), MAC2STR(addr2));
+	if (os_memcmp(addr1, addr2, ETH_ALEN) > 0) {
+		os_memcpy(key, addr1, ETH_ALEN);
+		os_memcpy(key + ETH_ALEN, addr2, ETH_ALEN);
+	} else {
+		os_memcpy(key, addr2, ETH_ALEN);
+		os_memcpy(key + ETH_ALEN, addr1, ETH_ALEN);
+	}
+}
+
+
+static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
+				 struct crypto_ec_point *pwe)
+{
+	u8 pwd_value[SAE_MAX_ECC_PRIME_LEN], prime[SAE_MAX_ECC_PRIME_LEN];
+	struct crypto_bignum *x;
+	int y_bit;
+	size_t bits;
+
+	if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
+				 sae->tmp->prime_len) < 0)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
+
+	/* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */
+	bits = crypto_ec_prime_len_bits(sae->tmp->ec);
+	sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking",
+			prime, sae->tmp->prime_len, pwd_value, bits);
+	if (bits % 8)
+		buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8);
+	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value",
+			pwd_value, sae->tmp->prime_len);
+
+	if (os_memcmp(pwd_value, prime, sae->tmp->prime_len) >= 0)
+		return 0;
+
+	y_bit = pwd_seed[SHA256_MAC_LEN - 1] & 0x01;
+
+	x = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
+	if (x == NULL)
+		return -1;
+	if (crypto_ec_point_solve_y_coord(sae->tmp->ec, pwe, x, y_bit) < 0) {
+		crypto_bignum_deinit(x, 0);
+		wpa_printf(MSG_DEBUG, "SAE: No solution found");
+		return 0;
+	}
+	crypto_bignum_deinit(x, 0);
+
+	wpa_printf(MSG_DEBUG, "SAE: PWE found");
+
+	return 1;
+}
+
+
+static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
+				 struct crypto_bignum *pwe)
+{
+	u8 pwd_value[SAE_MAX_PRIME_LEN];
+	size_t bits = sae->tmp->prime_len * 8;
+	u8 exp[1];
+	struct crypto_bignum *a, *b;
+	int res;
+
+	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
+
+	/* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */
+	sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking",
+			sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value,
+			bits);
+	if (bits % 8)
+		buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8);
+	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value,
+			sae->tmp->prime_len);
+
+	if (os_memcmp(pwd_value, sae->tmp->dh->prime, sae->tmp->prime_len) >= 0)
+	{
+		wpa_printf(MSG_DEBUG, "SAE: pwd-value >= p");
+		return 0;
+	}
+
+	/* PWE = pwd-value^((p-1)/r) modulo p */
+
+	a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
+
+	if (sae->tmp->dh->safe_prime) {
+		/*
+		 * r = (p-1)/2 for the group used here, so this becomes:
+		 * PWE = pwd-value^2 modulo p
+		 */
+		exp[0] = 2;
+		b = crypto_bignum_init_set(exp, sizeof(exp));
+	} else {
+		/* Calculate exponent: (p-1)/r */
+		exp[0] = 1;
+		b = crypto_bignum_init_set(exp, sizeof(exp));
+		if (b == NULL ||
+		    crypto_bignum_sub(sae->tmp->prime, b, b) < 0 ||
+		    crypto_bignum_div(b, sae->tmp->order, b) < 0) {
+			crypto_bignum_deinit(b, 0);
+			b = NULL;
+		}
+	}
+
+	if (a == NULL || b == NULL)
+		res = -1;
+	else
+		res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
+
+	crypto_bignum_deinit(a, 0);
+	crypto_bignum_deinit(b, 0);
+
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Failed to calculate PWE");
+		return -1;
+	}
+
+	/* if (PWE > 1) --> found */
+	if (crypto_bignum_is_zero(pwe) || crypto_bignum_is_one(pwe)) {
+		wpa_printf(MSG_DEBUG, "SAE: PWE <= 1");
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "SAE: PWE found");
+	return 1;
+}
+
+
+static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
+			      const u8 *addr2, const u8 *password,
+			      size_t password_len)
+{
+	u8 counter, k = 4;
+	u8 addrs[2 * ETH_ALEN];
+	const u8 *addr[2];
+	size_t len[2];
+	int found = 0;
+	struct crypto_ec_point *pwe_tmp;
+
+	if (sae->tmp->pwe_ecc == NULL) {
+		sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec);
+		if (sae->tmp->pwe_ecc == NULL)
+			return -1;
+	}
+	pwe_tmp = crypto_ec_point_init(sae->tmp->ec);
+	if (pwe_tmp == NULL)
+		return -1;
+
+	wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
+			      password, password_len);
+
+	/*
+	 * H(salt, ikm) = HMAC-SHA256(salt, ikm)
+	 * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC),
+	 *              password || counter)
+	 */
+	sae_pwd_seed_key(addr1, addr2, addrs);
+
+	addr[0] = password;
+	len[0] = password_len;
+	addr[1] = &counter;
+	len[1] = sizeof(counter);
+
+	/*
+	 * Continue for at least k iterations to protect against side-channel
+	 * attacks that attempt to determine the number of iterations required
+	 * in the loop.
+	 */
+	for (counter = 1; counter < k || !found; counter++) {
+		u8 pwd_seed[SHA256_MAC_LEN];
+		int res;
+
+		if (counter > 200) {
+			/* This should not happen in practice */
+			wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE");
+			break;
+		}
+
+		wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
+		if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len,
+				       pwd_seed) < 0)
+			break;
+		res = sae_test_pwd_seed_ecc(sae, pwd_seed,
+					    found ? pwe_tmp :
+					    sae->tmp->pwe_ecc);
+		if (res < 0)
+			break;
+		if (res == 0)
+			continue;
+		if (found) {
+			wpa_printf(MSG_DEBUG, "SAE: Ignore this PWE (one was "
+				   "already selected)");
+		} else {
+			wpa_printf(MSG_DEBUG, "SAE: Use this PWE");
+			found = 1;
+		}
+	}
+
+	crypto_ec_point_deinit(pwe_tmp, 1);
+
+	return found ? 0 : -1;
+}
+
+
+static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
+			      const u8 *addr2, const u8 *password,
+			      size_t password_len)
+{
+	u8 counter;
+	u8 addrs[2 * ETH_ALEN];
+	const u8 *addr[2];
+	size_t len[2];
+	int found = 0;
+
+	if (sae->tmp->pwe_ffc == NULL) {
+		sae->tmp->pwe_ffc = crypto_bignum_init();
+		if (sae->tmp->pwe_ffc == NULL)
+			return -1;
+	}
+
+	wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
+			      password, password_len);
+
+	/*
+	 * H(salt, ikm) = HMAC-SHA256(salt, ikm)
+	 * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC),
+	 *              password || counter)
+	 */
+	sae_pwd_seed_key(addr1, addr2, addrs);
+
+	addr[0] = password;
+	len[0] = password_len;
+	addr[1] = &counter;
+	len[1] = sizeof(counter);
+
+	for (counter = 1; !found; counter++) {
+		u8 pwd_seed[SHA256_MAC_LEN];
+		int res;
+
+		if (counter > 200) {
+			/* This should not happen in practice */
+			wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE");
+			break;
+		}
+
+		wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
+		if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len,
+				       pwd_seed) < 0)
+			break;
+		res = sae_test_pwd_seed_ffc(sae, pwd_seed, sae->tmp->pwe_ffc);
+		if (res < 0)
+			break;
+		if (res > 0) {
+			wpa_printf(MSG_DEBUG, "SAE: Use this PWE");
+			found = 1;
+		}
+	}
+
+	return found ? 0 : -1;
+}
+
+
+static int sae_derive_commit_element_ecc(struct sae_data *sae,
+					 struct crypto_bignum *mask)
+{
+	/* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */
+	if (!sae->tmp->own_commit_element_ecc) {
+		sae->tmp->own_commit_element_ecc =
+			crypto_ec_point_init(sae->tmp->ec);
+		if (!sae->tmp->own_commit_element_ecc)
+			return -1;
+	}
+
+	if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, mask,
+				sae->tmp->own_commit_element_ecc) < 0 ||
+	    crypto_ec_point_invert(sae->tmp->ec,
+				   sae->tmp->own_commit_element_ecc) < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int sae_derive_commit_element_ffc(struct sae_data *sae,
+					 struct crypto_bignum *mask)
+{
+	/* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */
+	if (!sae->tmp->own_commit_element_ffc) {
+		sae->tmp->own_commit_element_ffc = crypto_bignum_init();
+		if (!sae->tmp->own_commit_element_ffc)
+			return -1;
+	}
+
+	if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, mask, sae->tmp->prime,
+				  sae->tmp->own_commit_element_ffc) < 0 ||
+	    crypto_bignum_inverse(sae->tmp->own_commit_element_ffc,
+				  sae->tmp->prime,
+				  sae->tmp->own_commit_element_ffc) < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int sae_derive_commit(struct sae_data *sae)
+{
+	struct crypto_bignum *mask;
+	int ret = -1;
+
+	mask = sae_get_rand_and_mask(sae);
+	if (mask == NULL) {
+		wpa_printf(MSG_DEBUG, "SAE: Could not get rand/mask");
+		return -1;
+	}
+
+	/* commit-scalar = (rand + mask) modulo r */
+	if (!sae->tmp->own_commit_scalar) {
+		sae->tmp->own_commit_scalar = crypto_bignum_init();
+		if (!sae->tmp->own_commit_scalar)
+			goto fail;
+	}
+	crypto_bignum_add(sae->tmp->sae_rand, mask,
+			  sae->tmp->own_commit_scalar);
+	crypto_bignum_mod(sae->tmp->own_commit_scalar, sae->tmp->order,
+			  sae->tmp->own_commit_scalar);
+
+	if (sae->tmp->ec && sae_derive_commit_element_ecc(sae, mask) < 0)
+		goto fail;
+	if (sae->tmp->dh && sae_derive_commit_element_ffc(sae, mask) < 0)
+		goto fail;
+
+	ret = 0;
+fail:
+	crypto_bignum_deinit(mask, 1);
+	return ret;
+}
+
+
+int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
+		       const u8 *password, size_t password_len,
+		       struct sae_data *sae)
+{
+	if (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password,
+					  password_len) < 0)
+		return -1;
+	if (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password,
+					  password_len) < 0)
+		return -1;
+	if (sae_derive_commit(sae) < 0)
+		return -1;
+	return 0;
+}
+
+
+static int sae_derive_k_ecc(struct sae_data *sae, u8 *k)
+{
+	struct crypto_ec_point *K;
+	int ret = -1;
+
+	K = crypto_ec_point_init(sae->tmp->ec);
+	if (K == NULL)
+		goto fail;
+
+	if (!crypto_ec_point_is_on_curve(sae->tmp->ec,
+					 sae->tmp->peer_commit_element_ecc)) {
+		wpa_printf(MSG_DEBUG, "SAE: Peer element is not on curve");
+		goto fail;
+	}
+
+	/*
+	 * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE),
+	 *                                        PEER-COMMIT-ELEMENT)))
+	 * If K is identity element (point-at-infinity), reject
+	 * k = F(K) (= x coordinate)
+	 */
+
+	if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc,
+				sae->peer_commit_scalar, K) < 0 ||
+	    crypto_ec_point_add(sae->tmp->ec, K,
+				sae->tmp->peer_commit_element_ecc, K) < 0 ||
+	    crypto_ec_point_mul(sae->tmp->ec, K, sae->tmp->sae_rand, K) < 0 ||
+	    crypto_ec_point_is_at_infinity(sae->tmp->ec, K) ||
+	    crypto_ec_point_to_bin(sae->tmp->ec, K, k, NULL) < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k");
+		goto fail;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len);
+
+	ret = 0;
+fail:
+	crypto_ec_point_deinit(K, 1);
+	return ret;
+}
+
+
+static int sae_derive_k_ffc(struct sae_data *sae, u8 *k)
+{
+	struct crypto_bignum *K;
+	int ret = -1;
+
+	K = crypto_bignum_init();
+	if (K == NULL)
+		goto fail;
+
+	/*
+	 * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE),
+	 *                                        PEER-COMMIT-ELEMENT)))
+	 * If K is identity element (one), reject.
+	 * k = F(K) (= x coordinate)
+	 */
+
+	if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, sae->peer_commit_scalar,
+				  sae->tmp->prime, K) < 0 ||
+	    crypto_bignum_mulmod(K, sae->tmp->peer_commit_element_ffc,
+				 sae->tmp->prime, K) < 0 ||
+	    crypto_bignum_exptmod(K, sae->tmp->sae_rand, sae->tmp->prime, K) < 0
+	    ||
+	    crypto_bignum_is_one(K) ||
+	    crypto_bignum_to_bin(K, k, SAE_MAX_PRIME_LEN, sae->tmp->prime_len) <
+	    0) {
+		wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k");
+		goto fail;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len);
+
+	ret = 0;
+fail:
+	crypto_bignum_deinit(K, 1);
+	return ret;
+}
+
+
+static int sae_derive_keys(struct sae_data *sae, const u8 *k)
+{
+	u8 null_key[SAE_KEYSEED_KEY_LEN], val[SAE_MAX_PRIME_LEN];
+	u8 keyseed[SHA256_MAC_LEN];
+	u8 keys[SAE_KCK_LEN + SAE_PMK_LEN];
+	struct crypto_bignum *tmp;
+	int ret = -1;
+
+	tmp = crypto_bignum_init();
+	if (tmp == NULL)
+		goto fail;
+
+	/* keyseed = H(<0>32, k)
+	 * KCK || PMK = KDF-512(keyseed, "SAE KCK and PMK",
+	 *                      (commit-scalar + peer-commit-scalar) modulo r)
+	 * PMKID = L((commit-scalar + peer-commit-scalar) modulo r, 0, 128)
+	 */
+
+	os_memset(null_key, 0, sizeof(null_key));
+	hmac_sha256(null_key, sizeof(null_key), k, sae->tmp->prime_len,
+		    keyseed);
+	wpa_hexdump_key(MSG_DEBUG, "SAE: keyseed", keyseed, sizeof(keyseed));
+
+	crypto_bignum_add(sae->tmp->own_commit_scalar, sae->peer_commit_scalar,
+			  tmp);
+	crypto_bignum_mod(tmp, sae->tmp->order, tmp);
+	crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->prime_len);
+	wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN);
+	sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK",
+		   val, sae->tmp->prime_len, keys, sizeof(keys));
+	os_memcpy(sae->tmp->kck, keys, SAE_KCK_LEN);
+	os_memcpy(sae->pmk, keys + SAE_KCK_LEN, SAE_PMK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->tmp->kck, SAE_KCK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN);
+
+	ret = 0;
+fail:
+	crypto_bignum_deinit(tmp, 0);
+	return ret;
+}
+
+
+int sae_process_commit(struct sae_data *sae)
+{
+	u8 k[SAE_MAX_PRIME_LEN];
+	if ((sae->tmp->ec && sae_derive_k_ecc(sae, k) < 0) ||
+	    (sae->tmp->dh && sae_derive_k_ffc(sae, k) < 0) ||
+	    sae_derive_keys(sae, k) < 0)
+		return -1;
+	return 0;
+}
+
+
+void sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
+		      const struct wpabuf *token)
+{
+	u8 *pos;
+	wpabuf_put_le16(buf, sae->group); /* Finite Cyclic Group */
+	if (token)
+		wpabuf_put_buf(buf, token);
+	pos = wpabuf_put(buf, sae->tmp->prime_len);
+	crypto_bignum_to_bin(sae->tmp->own_commit_scalar, pos,
+			     sae->tmp->prime_len, sae->tmp->prime_len);
+	wpa_hexdump(MSG_DEBUG, "SAE: own commit-scalar",
+		    pos, sae->tmp->prime_len);
+	if (sae->tmp->ec) {
+		pos = wpabuf_put(buf, 2 * sae->tmp->prime_len);
+		crypto_ec_point_to_bin(sae->tmp->ec,
+				       sae->tmp->own_commit_element_ecc,
+				       pos, pos + sae->tmp->prime_len);
+		wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(x)",
+			    pos, sae->tmp->prime_len);
+		wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(y)",
+			    pos + sae->tmp->prime_len, sae->tmp->prime_len);
+	} else {
+		pos = wpabuf_put(buf, sae->tmp->prime_len);
+		crypto_bignum_to_bin(sae->tmp->own_commit_element_ffc, pos,
+				     sae->tmp->prime_len, sae->tmp->prime_len);
+		wpa_hexdump(MSG_DEBUG, "SAE: own commit-element",
+			    pos, sae->tmp->prime_len);
+	}
+}
+
+
+static u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups,
+			     u16 group)
+{
+	if (allowed_groups) {
+		int i;
+		for (i = 0; allowed_groups[i] >= 0; i++) {
+			if (allowed_groups[i] == group)
+				break;
+		}
+		if (allowed_groups[i] != group) {
+			wpa_printf(MSG_DEBUG, "SAE: Proposed group %u not "
+				   "enabled in the current configuration",
+				   group);
+			return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+		}
+	}
+
+	if (sae->state == SAE_COMMITTED && group != sae->group) {
+		wpa_printf(MSG_DEBUG, "SAE: Do not allow group to be changed");
+		return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+	}
+
+	if (group != sae->group && sae_set_group(sae, group) < 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u",
+			   group);
+		return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+	}
+
+	if (sae->tmp->dh && !allowed_groups) {
+		wpa_printf(MSG_DEBUG, "SAE: Do not allow FFC group %u without "
+			   "explicit configuration enabling it", group);
+		return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+	}
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos,
+				   const u8 *end, const u8 **token,
+				   size_t *token_len)
+{
+	if (*pos + (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len < end) {
+		size_t tlen = end - (*pos + (sae->tmp->ec ? 3 : 2) *
+				     sae->tmp->prime_len);
+		wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen);
+		if (token)
+			*token = *pos;
+		if (token_len)
+			*token_len = tlen;
+		*pos += tlen;
+	} else {
+		if (token)
+			*token = NULL;
+		if (token_len)
+			*token_len = 0;
+	}
+}
+
+
+static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos,
+				   const u8 *end)
+{
+	struct crypto_bignum *peer_scalar;
+
+	if (*pos + sae->tmp->prime_len > end) {
+		wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	peer_scalar = crypto_bignum_init_set(*pos, sae->tmp->prime_len);
+	if (peer_scalar == NULL)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	/*
+	 * IEEE Std 802.11-2012, 11.3.8.6.1: If there is a protocol instance for
+	 * the peer and it is in Authenticated state, the new Commit Message
+	 * shall be dropped if the peer-scalar is identical to the one used in
+	 * the existing protocol instance.
+	 */
+	if (sae->state == SAE_ACCEPTED && sae->peer_commit_scalar &&
+	    crypto_bignum_cmp(sae->peer_commit_scalar, peer_scalar) == 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Do not accept re-use of previous "
+			   "peer-commit-scalar");
+		crypto_bignum_deinit(peer_scalar, 0);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	/* 0 < scalar < r */
+	if (crypto_bignum_is_zero(peer_scalar) ||
+	    crypto_bignum_cmp(peer_scalar, sae->tmp->order) >= 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Invalid peer scalar");
+		crypto_bignum_deinit(peer_scalar, 0);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+
+	crypto_bignum_deinit(sae->peer_commit_scalar, 0);
+	sae->peer_commit_scalar = peer_scalar;
+	wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-scalar",
+		    *pos, sae->tmp->prime_len);
+	*pos += sae->tmp->prime_len;
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos,
+					const u8 *end)
+{
+	u8 prime[SAE_MAX_ECC_PRIME_LEN];
+
+	if (pos + 2 * sae->tmp->prime_len > end) {
+		wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
+			   "commit-element");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
+				 sae->tmp->prime_len) < 0)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	/* element x and y coordinates < p */
+	if (os_memcmp(pos, prime, sae->tmp->prime_len) >= 0 ||
+	    os_memcmp(pos + sae->tmp->prime_len + sae->tmp->prime_len, prime,
+		      sae->tmp->prime_len) >= 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer "
+			   "element");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)",
+		    pos, sae->tmp->prime_len);
+	wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)",
+		    pos + sae->tmp->prime_len, sae->tmp->prime_len);
+
+	crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0);
+	sae->tmp->peer_commit_element_ecc =
+		crypto_ec_point_from_bin(sae->tmp->ec, pos);
+	if (sae->tmp->peer_commit_element_ecc == NULL)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos,
+					const u8 *end)
+{
+	if (pos + sae->tmp->prime_len > end) {
+		wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
+			   "commit-element");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", pos,
+		    sae->tmp->prime_len);
+
+	crypto_bignum_deinit(sae->tmp->peer_commit_element_ffc, 0);
+	sae->tmp->peer_commit_element_ffc =
+		crypto_bignum_init_set(pos, sae->tmp->prime_len);
+	if (sae->tmp->peer_commit_element_ffc == NULL)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	if (crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) ||
+	    crypto_bignum_is_one(sae->tmp->peer_commit_element_ffc) ||
+	    crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc,
+			      sae->tmp->prime) >= 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Invalid peer element");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+static u16 sae_parse_commit_element(struct sae_data *sae, const u8 *pos,
+				    const u8 *end)
+{
+	if (sae->tmp->dh)
+		return sae_parse_commit_element_ffc(sae, pos, end);
+	return sae_parse_commit_element_ecc(sae, pos, end);
+}
+
+
+u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
+		     const u8 **token, size_t *token_len, int *allowed_groups)
+{
+	const u8 *pos = data, *end = data + len;
+	u16 res;
+
+	/* Check Finite Cyclic Group */
+	if (pos + 2 > end)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	res = sae_group_allowed(sae, allowed_groups, WPA_GET_LE16(pos));
+	if (res != WLAN_STATUS_SUCCESS)
+		return res;
+	pos += 2;
+
+	/* Optional Anti-Clogging Token */
+	sae_parse_commit_token(sae, &pos, end, token, token_len);
+
+	/* commit-scalar */
+	res = sae_parse_commit_scalar(sae, &pos, end);
+	if (res != WLAN_STATUS_SUCCESS)
+		return res;
+
+	/* commit-element */
+	return sae_parse_commit_element(sae, pos, end);
+}
+
+
+static void sae_cn_confirm(struct sae_data *sae, const u8 *sc,
+			   const struct crypto_bignum *scalar1,
+			   const u8 *element1, size_t element1_len,
+			   const struct crypto_bignum *scalar2,
+			   const u8 *element2, size_t element2_len,
+			   u8 *confirm)
+{
+	const u8 *addr[5];
+	size_t len[5];
+	u8 scalar_b1[SAE_MAX_PRIME_LEN], scalar_b2[SAE_MAX_PRIME_LEN];
+
+	/* Confirm
+	 * CN(key, X, Y, Z, ...) =
+	 *    HMAC-SHA256(key, D2OS(X) || D2OS(Y) || D2OS(Z) | ...)
+	 * confirm = CN(KCK, send-confirm, commit-scalar, COMMIT-ELEMENT,
+	 *              peer-commit-scalar, PEER-COMMIT-ELEMENT)
+	 * verifier = CN(KCK, peer-send-confirm, peer-commit-scalar,
+	 *               PEER-COMMIT-ELEMENT, commit-scalar, COMMIT-ELEMENT)
+	 */
+	addr[0] = sc;
+	len[0] = 2;
+	crypto_bignum_to_bin(scalar1, scalar_b1, sizeof(scalar_b1),
+			     sae->tmp->prime_len);
+	addr[1] = scalar_b1;
+	len[1] = sae->tmp->prime_len;
+	addr[2] = element1;
+	len[2] = element1_len;
+	crypto_bignum_to_bin(scalar2, scalar_b2, sizeof(scalar_b2),
+			     sae->tmp->prime_len);
+	addr[3] = scalar_b2;
+	len[3] = sae->tmp->prime_len;
+	addr[4] = element2;
+	len[4] = element2_len;
+	hmac_sha256_vector(sae->tmp->kck, sizeof(sae->tmp->kck), 5, addr, len,
+			   confirm);
+}
+
+
+static void sae_cn_confirm_ecc(struct sae_data *sae, const u8 *sc,
+			       const struct crypto_bignum *scalar1,
+			       const struct crypto_ec_point *element1,
+			       const struct crypto_bignum *scalar2,
+			       const struct crypto_ec_point *element2,
+			       u8 *confirm)
+{
+	u8 element_b1[2 * SAE_MAX_ECC_PRIME_LEN];
+	u8 element_b2[2 * SAE_MAX_ECC_PRIME_LEN];
+
+	crypto_ec_point_to_bin(sae->tmp->ec, element1, element_b1,
+			       element_b1 + sae->tmp->prime_len);
+	crypto_ec_point_to_bin(sae->tmp->ec, element2, element_b2,
+			       element_b2 + sae->tmp->prime_len);
+
+	sae_cn_confirm(sae, sc, scalar1, element_b1, 2 * sae->tmp->prime_len,
+		       scalar2, element_b2, 2 * sae->tmp->prime_len, confirm);
+}
+
+
+static void sae_cn_confirm_ffc(struct sae_data *sae, const u8 *sc,
+			       const struct crypto_bignum *scalar1,
+			       const struct crypto_bignum *element1,
+			       const struct crypto_bignum *scalar2,
+			       const struct crypto_bignum *element2,
+			       u8 *confirm)
+{
+	u8 element_b1[SAE_MAX_PRIME_LEN];
+	u8 element_b2[SAE_MAX_PRIME_LEN];
+
+	crypto_bignum_to_bin(element1, element_b1, sizeof(element_b1),
+			     sae->tmp->prime_len);
+	crypto_bignum_to_bin(element2, element_b2, sizeof(element_b2),
+			     sae->tmp->prime_len);
+
+	sae_cn_confirm(sae, sc, scalar1, element_b1, sae->tmp->prime_len,
+		       scalar2, element_b2, sae->tmp->prime_len, confirm);
+}
+
+
+void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf)
+{
+	const u8 *sc;
+
+	/* Send-Confirm */
+	sc = wpabuf_put(buf, 0);
+	wpabuf_put_le16(buf, sae->send_confirm);
+	sae->send_confirm++;
+
+	if (sae->tmp->ec)
+		sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar,
+				   sae->tmp->own_commit_element_ecc,
+				   sae->peer_commit_scalar,
+				   sae->tmp->peer_commit_element_ecc,
+				   wpabuf_put(buf, SHA256_MAC_LEN));
+	else
+		sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar,
+				   sae->tmp->own_commit_element_ffc,
+				   sae->peer_commit_scalar,
+				   sae->tmp->peer_commit_element_ffc,
+				   wpabuf_put(buf, SHA256_MAC_LEN));
+}
+
+
+int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len)
+{
+	u8 verifier[SHA256_MAC_LEN];
+
+	if (len < 2 + SHA256_MAC_LEN) {
+		wpa_printf(MSG_DEBUG, "SAE: Too short confirm message");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data));
+
+	if (sae->tmp->ec)
+		sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar,
+				   sae->tmp->peer_commit_element_ecc,
+				   sae->tmp->own_commit_scalar,
+				   sae->tmp->own_commit_element_ecc,
+				   verifier);
+	else
+		sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar,
+				   sae->tmp->peer_commit_element_ffc,
+				   sae->tmp->own_commit_scalar,
+				   sae->tmp->own_commit_element_ffc,
+				   verifier);
+
+	if (os_memcmp(verifier, data + 2, SHA256_MAC_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch");
+		wpa_hexdump(MSG_DEBUG, "SAE: Received confirm",
+			    data + 2, SHA256_MAC_LEN);
+		wpa_hexdump(MSG_DEBUG, "SAE: Calculated verifier",
+			    verifier, SHA256_MAC_LEN);
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/src/common/sae.h b/src/common/sae.h
new file mode 100644
index 0000000..d82a98e
--- /dev/null
+++ b/src/common/sae.h
@@ -0,0 +1,64 @@
+/*
+ * Simultaneous authentication of equals
+ * Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SAE_H
+#define SAE_H
+
+#define SAE_KCK_LEN 32
+#define SAE_PMK_LEN 32
+#define SAE_PMKID_LEN 16
+#define SAE_KEYSEED_KEY_LEN 32
+#define SAE_MAX_PRIME_LEN 512
+#define SAE_MAX_ECC_PRIME_LEN 66
+#define SAE_COMMIT_MAX_LEN (2 + 3 * SAE_MAX_PRIME_LEN)
+#define SAE_CONFIRM_MAX_LEN (2 + SAE_MAX_PRIME_LEN)
+
+struct sae_temporary_data {
+	u8 kck[SAE_KCK_LEN];
+	struct crypto_bignum *own_commit_scalar;
+	struct crypto_bignum *own_commit_element_ffc;
+	struct crypto_ec_point *own_commit_element_ecc;
+	struct crypto_bignum *peer_commit_element_ffc;
+	struct crypto_ec_point *peer_commit_element_ecc;
+	struct crypto_ec_point *pwe_ecc;
+	struct crypto_bignum *pwe_ffc;
+	struct crypto_bignum *sae_rand;
+	struct crypto_ec *ec;
+	int prime_len;
+	const struct dh_group *dh;
+	const struct crypto_bignum *prime;
+	const struct crypto_bignum *order;
+	struct crypto_bignum *prime_buf;
+	struct crypto_bignum *order_buf;
+};
+
+struct sae_data {
+	enum { SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED } state;
+	u16 send_confirm;
+	u8 pmk[SAE_PMK_LEN];
+	struct crypto_bignum *peer_commit_scalar;
+	int group;
+	struct sae_temporary_data *tmp;
+};
+
+int sae_set_group(struct sae_data *sae, int group);
+void sae_clear_temp_data(struct sae_data *sae);
+void sae_clear_data(struct sae_data *sae);
+
+int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
+		       const u8 *password, size_t password_len,
+		       struct sae_data *sae);
+int sae_process_commit(struct sae_data *sae);
+void sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
+		      const struct wpabuf *token);
+u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
+		     const u8 **token, size_t *token_len, int *allowed_groups);
+void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf);
+int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len);
+
+#endif /* SAE_H */
diff --git a/src/common/version.h b/src/common/version.h
index 7afba48..2faa8c7 100644
--- a/src/common/version.h
+++ b/src/common/version.h
@@ -5,6 +5,6 @@
 #define VERSION_STR_POSTFIX ""
 #endif /* VERSION_STR_POSTFIX */
 
-#define VERSION_STR "2.0-devel" VERSION_STR_POSTFIX
+#define VERSION_STR "2.1-devel" VERSION_STR_POSTFIX
 
 #endif /* VERSION_H */
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index 8d7a11c..a8cf6be 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -1,6 +1,6 @@
 /*
  * WPA/RSN - Shared functions for supplicant and authenticator
- * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -1132,6 +1132,26 @@
 }
 
 
+enum wpa_cipher wpa_cipher_to_suite_driver(int cipher)
+{
+	switch (cipher) {
+	case WPA_CIPHER_NONE:
+		return CIPHER_NONE;
+	case WPA_CIPHER_WEP40:
+		return CIPHER_WEP40;
+	case WPA_CIPHER_WEP104:
+		return CIPHER_WEP104;
+	case WPA_CIPHER_CCMP:
+		return CIPHER_CCMP;
+	case WPA_CIPHER_GCMP:
+		return CIPHER_GCMP;
+	case WPA_CIPHER_TKIP:
+	default:
+		return CIPHER_TKIP;
+	}
+}
+
+
 int wpa_cipher_valid_pairwise(int cipher)
 {
 	return cipher == WPA_CIPHER_CCMP ||
@@ -1214,3 +1234,150 @@
 
 	return num_suites;
 }
+
+
+int wpa_pick_pairwise_cipher(int ciphers, int none_allowed)
+{
+	if (ciphers & WPA_CIPHER_CCMP)
+		return WPA_CIPHER_CCMP;
+	if (ciphers & WPA_CIPHER_GCMP)
+		return WPA_CIPHER_GCMP;
+	if (ciphers & WPA_CIPHER_TKIP)
+		return WPA_CIPHER_TKIP;
+	if (none_allowed && (ciphers & WPA_CIPHER_NONE))
+		return WPA_CIPHER_NONE;
+	return -1;
+}
+
+
+int wpa_pick_group_cipher(int ciphers)
+{
+	if (ciphers & WPA_CIPHER_CCMP)
+		return WPA_CIPHER_CCMP;
+	if (ciphers & WPA_CIPHER_GCMP)
+		return WPA_CIPHER_GCMP;
+	if (ciphers & WPA_CIPHER_TKIP)
+		return WPA_CIPHER_TKIP;
+	if (ciphers & WPA_CIPHER_WEP104)
+		return WPA_CIPHER_WEP104;
+	if (ciphers & WPA_CIPHER_WEP40)
+		return WPA_CIPHER_WEP40;
+	return -1;
+}
+
+
+int wpa_parse_cipher(const char *value)
+{
+	int val = 0, last;
+	char *start, *end, *buf;
+
+	buf = os_strdup(value);
+	if (buf == NULL)
+		return -1;
+	start = buf;
+
+	while (*start != '\0') {
+		while (*start == ' ' || *start == '\t')
+			start++;
+		if (*start == '\0')
+			break;
+		end = start;
+		while (*end != ' ' && *end != '\t' && *end != '\0')
+			end++;
+		last = *end == '\0';
+		*end = '\0';
+		if (os_strcmp(start, "CCMP") == 0)
+			val |= WPA_CIPHER_CCMP;
+		else if (os_strcmp(start, "GCMP") == 0)
+			val |= WPA_CIPHER_GCMP;
+		else if (os_strcmp(start, "TKIP") == 0)
+			val |= WPA_CIPHER_TKIP;
+		else if (os_strcmp(start, "WEP104") == 0)
+			val |= WPA_CIPHER_WEP104;
+		else if (os_strcmp(start, "WEP40") == 0)
+			val |= WPA_CIPHER_WEP40;
+		else if (os_strcmp(start, "NONE") == 0)
+			val |= WPA_CIPHER_NONE;
+		else {
+			os_free(buf);
+			return -1;
+		}
+
+		if (last)
+			break;
+		start = end + 1;
+	}
+	os_free(buf);
+
+	return val;
+}
+
+
+int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim)
+{
+	char *pos = start;
+	int ret;
+
+	if (ciphers & WPA_CIPHER_CCMP) {
+		ret = os_snprintf(pos, end - pos, "%sCCMP",
+				  pos == start ? "" : delim);
+		if (ret < 0 || ret >= end - pos)
+			return -1;
+		pos += ret;
+	}
+	if (ciphers & WPA_CIPHER_GCMP) {
+		ret = os_snprintf(pos, end - pos, "%sGCMP",
+				  pos == start ? "" : delim);
+		if (ret < 0 || ret >= end - pos)
+			return -1;
+		pos += ret;
+	}
+	if (ciphers & WPA_CIPHER_TKIP) {
+		ret = os_snprintf(pos, end - pos, "%sTKIP",
+				  pos == start ? "" : delim);
+		if (ret < 0 || ret >= end - pos)
+			return -1;
+		pos += ret;
+	}
+	if (ciphers & WPA_CIPHER_WEP104) {
+		ret = os_snprintf(pos, end - pos, "%sWEP104",
+				  pos == start ? "" : delim);
+		if (ret < 0 || ret >= end - pos)
+			return -1;
+		pos += ret;
+	}
+	if (ciphers & WPA_CIPHER_WEP40) {
+		ret = os_snprintf(pos, end - pos, "%sWEP40",
+				  pos == start ? "" : delim);
+		if (ret < 0 || ret >= end - pos)
+			return -1;
+		pos += ret;
+	}
+	if (ciphers & WPA_CIPHER_NONE) {
+		ret = os_snprintf(pos, end - pos, "%sNONE",
+				  pos == start ? "" : delim);
+		if (ret < 0 || ret >= end - pos)
+			return -1;
+		pos += ret;
+	}
+
+	return pos - start;
+}
+
+
+int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise)
+{
+	int pairwise = 0;
+
+	/* Select group cipher based on the enabled pairwise cipher suites */
+	if (wpa & 1)
+		pairwise |= wpa_pairwise;
+	if (wpa & 2)
+		pairwise |= rsn_pairwise;
+
+	if (pairwise & WPA_CIPHER_TKIP)
+		return WPA_CIPHER_TKIP;
+	if ((pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) == WPA_CIPHER_GCMP)
+		return WPA_CIPHER_GCMP;
+	return WPA_CIPHER_CCMP;
+}
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 20c79d8..2d63662 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -1,6 +1,6 @@
 /*
  * WPA definitions shared between hostapd and wpa_supplicant
- * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2013, 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,12 @@
 #define WPA_GMK_LEN 32
 #define WPA_GTK_MAX_LEN 32
 
+#define WPA_ALLOWED_PAIRWISE_CIPHERS \
+(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE)
+#define WPA_ALLOWED_GROUP_CIPHERS \
+(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_WEP104 | \
+WPA_CIPHER_WEP40)
+
 #define WPA_SELECTOR_LEN 4
 #define WPA_VERSION 1
 #define RSN_SELECTOR_LEN 4
@@ -386,9 +392,15 @@
 int wpa_cipher_key_len(int cipher);
 int wpa_cipher_rsc_len(int cipher);
 int wpa_cipher_to_alg(int cipher);
+enum wpa_cipher wpa_cipher_to_suite_driver(int cipher);
 int wpa_cipher_valid_pairwise(int cipher);
 u32 wpa_cipher_to_suite(int proto, int cipher);
 int rsn_cipher_put_suites(u8 *pos, int ciphers);
 int wpa_cipher_put_suites(u8 *pos, int ciphers);
+int wpa_pick_pairwise_cipher(int ciphers, int none_allowed);
+int wpa_pick_group_cipher(int ciphers);
+int wpa_parse_cipher(const char *value);
+int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim);
+int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise);
 
 #endif /* WPA_COMMON_H */
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index 26b9acf..9bccaaa 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -1,6 +1,6 @@
 /*
- * WPA Supplicant / wrapper functions for crypto libraries
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Wrapper functions for crypto libraries
+ * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -457,4 +457,329 @@
  */
 int crypto_get_random(void *buf, size_t len);
 
+
+/**
+ * struct crypto_bignum - bignum
+ *
+ * Internal data structure for bignum implementation. The contents is specific
+ * to the used crypto library.
+ */
+struct crypto_bignum;
+
+/**
+ * crypto_bignum_init - Allocate memory for bignum
+ * Returns: Pointer to allocated bignum or %NULL on failure
+ */
+struct crypto_bignum * crypto_bignum_init(void);
+
+/**
+ * crypto_bignum_init_set - Allocate memory for bignum and set the value
+ * @buf: Buffer with unsigned binary value
+ * @len: Length of buf in octets
+ * Returns: Pointer to allocated bignum or %NULL on failure
+ */
+struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len);
+
+/**
+ * crypto_bignum_deinit - Free bignum
+ * @n: Bignum from crypto_bignum_init() or crypto_bignum_init_set()
+ * @clear: Whether to clear the value from memory
+ */
+void crypto_bignum_deinit(struct crypto_bignum *n, int clear);
+
+/**
+ * crypto_bignum_to_bin - Set binary buffer to unsigned bignum
+ * @a: Bignum
+ * @buf: Buffer for the binary number
+ * @len: Length of @buf in octets
+ * @padlen: Length in octets to pad the result to or 0 to indicate no padding
+ * Returns: Number of octets written on success, -1 on failure
+ */
+int crypto_bignum_to_bin(const struct crypto_bignum *a,
+			 u8 *buf, size_t buflen, size_t padlen);
+
+/**
+ * crypto_bignum_add - c = a + b
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a + b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_add(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_mod - c = a % b
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a % b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_mod(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_exptmod - Modular exponentiation: d = a^b (mod c)
+ * @a: Bignum; base
+ * @b: Bignum; exponent
+ * @c: Bignum; modulus
+ * @d: Bignum; used to store the result of a^b (mod c)
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_exptmod(const struct crypto_bignum *a,
+			  const struct crypto_bignum *b,
+			  const struct crypto_bignum *c,
+			  struct crypto_bignum *d);
+
+/**
+ * crypto_bignum_rshift - b = a >> n
+ * @a: Bignum
+ * @n: Number of bits to shift
+ * @b: Bignum; used to store the result of a >> n
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
+			 struct crypto_bignum *b);
+
+/**
+ * crypto_bignum_inverse - Inverse a bignum so that a * c = 1 (mod b)
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_inverse(const struct crypto_bignum *a,
+			  const struct crypto_bignum *b,
+			  struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_sub - c = a - b
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a - b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_sub(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_div - c = a / b
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a / b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_div(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c);
+
+/**
+ * crypto_bignum_mulmod - d = a * b (mod c)
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum
+ * @d: Bignum; used to store the result of (a * b) % c
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_mulmod(const struct crypto_bignum *a,
+			 const struct crypto_bignum *b,
+			 const struct crypto_bignum *c,
+			 struct crypto_bignum *d);
+
+/**
+ * crypto_bignum_cmp - Compare two bignums
+ * @a: Bignum
+ * @b: Bignum
+ * Returns: -1 if a < b, 0 if a == b, or 1 if a > b
+ */
+int crypto_bignum_cmp(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b);
+
+/**
+ * crypto_bignum_bits - Get size of a bignum in bits
+ * @a: Bignum
+ * Returns: Number of bits in the bignum
+ */
+int crypto_bignum_bits(const struct crypto_bignum *a);
+
+/**
+ * crypto_bignum_is_zero - Is the given bignum zero
+ * @a: Bignum
+ * Returns: 1 if @a is zero or 0 if not
+ */
+int crypto_bignum_is_zero(const struct crypto_bignum *a);
+
+/**
+ * crypto_bignum_is_one - Is the given bignum one
+ * @a: Bignum
+ * Returns: 1 if @a is one or 0 if not
+ */
+int crypto_bignum_is_one(const struct crypto_bignum *a);
+
+/**
+ * struct crypto_ec - Elliptic curve context
+ *
+ * Internal data structure for EC implementation. The contents is specific
+ * to the used crypto library.
+ */
+struct crypto_ec;
+
+/**
+ * crypto_ec_init - Initialize elliptic curve context
+ * @group: Identifying number for the ECC group (IANA "Group Description"
+ *	attribute registrty for RFC 2409)
+ * Returns: Pointer to EC context or %NULL on failure
+ */
+struct crypto_ec * crypto_ec_init(int group);
+
+/**
+ * crypto_ec_deinit - Deinitialize elliptic curve context
+ * @e: EC context from crypto_ec_init()
+ */
+void crypto_ec_deinit(struct crypto_ec *e);
+
+/**
+ * crypto_ec_prime_len - Get length of the prime in octets
+ * @e: EC context from crypto_ec_init()
+ * Returns: Length of the prime defining the group
+ */
+size_t crypto_ec_prime_len(struct crypto_ec *e);
+
+/**
+ * crypto_ec_prime_len_bits - Get length of the prime in bits
+ * @e: EC context from crypto_ec_init()
+ * Returns: Length of the prime defining the group in bits
+ */
+size_t crypto_ec_prime_len_bits(struct crypto_ec *e);
+
+/**
+ * crypto_ec_get_prime - Get prime defining an EC group
+ * @e: EC context from crypto_ec_init()
+ * Returns: Prime (bignum) defining the group
+ */
+const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e);
+
+/**
+ * crypto_ec_get_order - Get order of an EC group
+ * @e: EC context from crypto_ec_init()
+ * Returns: Order (bignum) of the group
+ */
+const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e);
+
+/**
+ * struct crypto_ec_point - Elliptic curve point
+ *
+ * Internal data structure for EC implementation to represent a point. The
+ * contents is specific to the used crypto library.
+ */
+struct crypto_ec_point;
+
+/**
+ * crypto_ec_point_init - Initialize data for an EC point
+ * @e: EC context from crypto_ec_init()
+ * Returns: Pointer to EC point data or %NULL on failure
+ */
+struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e);
+
+/**
+ * crypto_ec_point_deinit - Deinitialize EC point data
+ * @p: EC point data from crypto_ec_point_init()
+ * @clear: Whether to clear the EC point value from memory
+ */
+void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear);
+
+/**
+ * crypto_ec_point_to_bin - Write EC point value as binary data
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point data from crypto_ec_point_init()
+ * @x: Buffer for writing the binary data for x coordinate or %NULL if not used
+ * @y: Buffer for writing the binary data for y coordinate or %NULL if not used
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to write an EC point as binary data in a format
+ * that has the x and y coordinates in big endian byte order fields padded to
+ * the length of the prime defining the group.
+ */
+int crypto_ec_point_to_bin(struct crypto_ec *e,
+			   const struct crypto_ec_point *point, u8 *x, u8 *y);
+
+/**
+ * crypto_ec_point_from_bin - Create EC point from binary data
+ * @e: EC context from crypto_ec_init()
+ * @val: Binary data to read the EC point from
+ * Returns: Pointer to EC point data or %NULL on failure
+ *
+ * This function readers x and y coordinates of the EC point from the provided
+ * buffer assuming the values are in big endian byte order with fields padded to
+ * the length of the prime defining the group.
+ */
+struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
+						  const u8 *val);
+
+/**
+ * crypto_bignum_add - c = a + b
+ * @e: EC context from crypto_ec_init()
+ * @a: Bignum
+ * @b: Bignum
+ * @c: Bignum; used to store the result of a + b
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
+			const struct crypto_ec_point *b,
+			struct crypto_ec_point *c);
+
+/**
+ * crypto_bignum_mul - res = b * p
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point
+ * @b: Bignum
+ * @res: EC point; used to store the result of b * p
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
+			const struct crypto_bignum *b,
+			struct crypto_ec_point *res);
+
+/**
+ * crypto_ec_point_invert - Compute inverse of an EC point
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point to invert (and result of the operation)
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p);
+
+/**
+ * crypto_ec_point_solve_y_coord - Solve y coordinate for an x coordinate
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point to use for the returning the result
+ * @x: x coordinate
+ * @y_bit: y-bit (0 or 1) for selecting the y value to use
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_solve_y_coord(struct crypto_ec *e,
+				  struct crypto_ec_point *p,
+				  const struct crypto_bignum *x, int y_bit);
+
+/**
+ * crypto_ec_point_is_at_infinity - Check whether EC point is neutral element
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point
+ * Returns: 1 if the specified EC point is the neutral element of the group or
+ *	0 if not
+ */
+int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
+				   const struct crypto_ec_point *p);
+
+/**
+ * crypto_ec_point_is_on_curve - Check whether EC point is on curve
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point
+ * Returns: 1 if the specified EC point is on the curve or 0 if not
+ */
+int crypto_ec_point_is_on_curve(struct crypto_ec *e,
+				const struct crypto_ec_point *p);
+
 #endif /* CRYPTO_H */
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index 711e312..5215c00 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -1,6 +1,6 @@
 /*
- * WPA Supplicant / wrapper functions for libcrypto
- * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ * Wrapper functions for OpenSSL libcrypto
+ * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -19,6 +19,9 @@
 #ifdef CONFIG_OPENSSL_CMAC
 #include <openssl/cmac.h>
 #endif /* CONFIG_OPENSSL_CMAC */
+#ifdef CONFIG_ECC
+#include <openssl/ec.h>
+#endif /* CONFIG_ECC */
 
 #include "common.h"
 #include "wpabuf.h"
@@ -818,3 +821,413 @@
 	return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
 }
 #endif /* CONFIG_OPENSSL_CMAC */
+
+
+struct crypto_bignum * crypto_bignum_init(void)
+{
+	return (struct crypto_bignum *) BN_new();
+}
+
+
+struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len)
+{
+	BIGNUM *bn = BN_bin2bn(buf, len, NULL);
+	return (struct crypto_bignum *) bn;
+}
+
+
+void crypto_bignum_deinit(struct crypto_bignum *n, int clear)
+{
+	if (clear)
+		BN_clear_free((BIGNUM *) n);
+	else
+		BN_free((BIGNUM *) n);
+}
+
+
+int crypto_bignum_to_bin(const struct crypto_bignum *a,
+			 u8 *buf, size_t buflen, size_t padlen)
+{
+	int num_bytes, offset;
+
+	if (padlen > buflen)
+		return -1;
+
+	num_bytes = BN_num_bytes((const BIGNUM *) a);
+	if ((size_t) num_bytes > buflen)
+		return -1;
+	if (padlen > (size_t) num_bytes)
+		offset = padlen - num_bytes;
+	else
+		offset = 0;
+
+	os_memset(buf, 0, offset);
+	BN_bn2bin((const BIGNUM *) a, buf + offset);
+
+	return num_bytes + offset;
+}
+
+
+int crypto_bignum_add(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c)
+{
+	return BN_add((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b) ?
+		0 : -1;
+}
+
+
+int crypto_bignum_mod(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c)
+{
+	int res;
+	BN_CTX *bnctx;
+
+	bnctx = BN_CTX_new();
+	if (bnctx == NULL)
+		return -1;
+	res = BN_mod((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b,
+		     bnctx);
+	BN_CTX_free(bnctx);
+
+	return res ? 0 : -1;
+}
+
+
+int crypto_bignum_exptmod(const struct crypto_bignum *a,
+			  const struct crypto_bignum *b,
+			  const struct crypto_bignum *c,
+			  struct crypto_bignum *d)
+{
+	int res;
+	BN_CTX *bnctx;
+
+	bnctx = BN_CTX_new();
+	if (bnctx == NULL)
+		return -1;
+	res = BN_mod_exp((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b,
+			 (const BIGNUM *) c, bnctx);
+	BN_CTX_free(bnctx);
+
+	return res ? 0 : -1;
+}
+
+
+int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
+			 struct crypto_bignum *b)
+{
+	return BN_rshift((BIGNUM *) b, (const BIGNUM *) a, n) ? 0 : -1;
+}
+
+
+int crypto_bignum_inverse(const struct crypto_bignum *a,
+			  const struct crypto_bignum *b,
+			  struct crypto_bignum *c)
+{
+	BIGNUM *res;
+	BN_CTX *bnctx;
+
+	bnctx = BN_CTX_new();
+	if (bnctx == NULL)
+		return -1;
+	res = BN_mod_inverse((BIGNUM *) c, (const BIGNUM *) a,
+			     (const BIGNUM *) b, bnctx);
+	BN_CTX_free(bnctx);
+
+	return res ? 0 : -1;
+}
+
+
+int crypto_bignum_sub(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c)
+{
+	return BN_sub((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b) ?
+		0 : -1;
+}
+
+
+int crypto_bignum_div(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *c)
+{
+	int res;
+
+	BN_CTX *bnctx;
+
+	bnctx = BN_CTX_new();
+	if (bnctx == NULL)
+		return -1;
+	res = BN_div((BIGNUM *) c, NULL, (const BIGNUM *) a,
+		     (const BIGNUM *) b, bnctx);
+	BN_CTX_free(bnctx);
+
+	return res ? 0 : -1;
+}
+
+
+int crypto_bignum_mulmod(const struct crypto_bignum *a,
+			 const struct crypto_bignum *b,
+			 const struct crypto_bignum *c,
+			 struct crypto_bignum *d)
+{
+	int res;
+
+	BN_CTX *bnctx;
+
+	bnctx = BN_CTX_new();
+	if (bnctx == NULL)
+		return -1;
+	res = BN_mod_mul((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b,
+			 (const BIGNUM *) c, bnctx);
+	BN_CTX_free(bnctx);
+
+	return res ? 0 : -1;
+}
+
+
+int crypto_bignum_cmp(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b)
+{
+	return BN_cmp((const BIGNUM *) a, (const BIGNUM *) b);
+}
+
+
+int crypto_bignum_bits(const struct crypto_bignum *a)
+{
+	return BN_num_bits((const BIGNUM *) a);
+}
+
+
+int crypto_bignum_is_zero(const struct crypto_bignum *a)
+{
+	return BN_is_zero((const BIGNUM *) a);
+}
+
+
+int crypto_bignum_is_one(const struct crypto_bignum *a)
+{
+	return BN_is_one((const BIGNUM *) a);
+}
+
+
+#ifdef CONFIG_ECC
+
+struct crypto_ec {
+	EC_GROUP *group;
+	BN_CTX *bnctx;
+	BIGNUM *prime;
+	BIGNUM *order;
+};
+
+struct crypto_ec * crypto_ec_init(int group)
+{
+	struct crypto_ec *e;
+	int nid;
+
+	/* Map from IANA registry for IKE D-H groups to OpenSSL NID */
+	switch (group) {
+	case 19:
+		nid = NID_X9_62_prime256v1;
+		break;
+	case 20:
+		nid = NID_secp384r1;
+		break;
+	case 21:
+		nid = NID_secp521r1;
+		break;
+	case 25:
+		nid = NID_X9_62_prime192v1;
+		break;
+	case 26:
+		nid = NID_secp224r1;
+		break;
+	default:
+		return NULL;
+	}
+
+	e = os_zalloc(sizeof(*e));
+	if (e == NULL)
+		return NULL;
+
+	e->bnctx = BN_CTX_new();
+	e->group = EC_GROUP_new_by_curve_name(nid);
+	e->prime = BN_new();
+	e->order = BN_new();
+	if (e->group == NULL || e->bnctx == NULL || e->prime == NULL ||
+	    e->order == NULL ||
+	    !EC_GROUP_get_curve_GFp(e->group, e->prime, NULL, NULL, e->bnctx) ||
+	    !EC_GROUP_get_order(e->group, e->order, e->bnctx)) {
+		crypto_ec_deinit(e);
+		e = NULL;
+	}
+
+	return e;
+}
+
+
+void crypto_ec_deinit(struct crypto_ec *e)
+{
+	if (e == NULL)
+		return;
+	BN_free(e->order);
+	EC_GROUP_free(e->group);
+	BN_CTX_free(e->bnctx);
+	os_free(e);
+}
+
+
+struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e)
+{
+	if (e == NULL)
+		return NULL;
+	return (struct crypto_ec_point *) EC_POINT_new(e->group);
+}
+
+
+size_t crypto_ec_prime_len(struct crypto_ec *e)
+{
+	return BN_num_bytes(e->prime);
+}
+
+
+size_t crypto_ec_prime_len_bits(struct crypto_ec *e)
+{
+	return BN_num_bits(e->prime);
+}
+
+
+const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e)
+{
+	return (const struct crypto_bignum *) e->prime;
+}
+
+
+const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e)
+{
+	return (const struct crypto_bignum *) e->order;
+}
+
+
+void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear)
+{
+	if (clear)
+		EC_POINT_clear_free((EC_POINT *) p);
+	else
+		EC_POINT_free((EC_POINT *) p);
+}
+
+
+int crypto_ec_point_to_bin(struct crypto_ec *e,
+			   const struct crypto_ec_point *point, u8 *x, u8 *y)
+{
+	BIGNUM *x_bn, *y_bn;
+	int ret = -1;
+	int len = BN_num_bytes(e->prime);
+
+	x_bn = BN_new();
+	y_bn = BN_new();
+
+	if (x_bn && y_bn &&
+	    EC_POINT_get_affine_coordinates_GFp(e->group, (EC_POINT *) point,
+						x_bn, y_bn, e->bnctx)) {
+		if (x) {
+			crypto_bignum_to_bin((struct crypto_bignum *) x_bn,
+					     x, len, len);
+		}
+		if (y) {
+			crypto_bignum_to_bin((struct crypto_bignum *) y_bn,
+					     y, len, len);
+		}
+		ret = 0;
+	}
+
+	BN_free(x_bn);
+	BN_free(y_bn);
+	return ret;
+}
+
+
+struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
+						  const u8 *val)
+{
+	BIGNUM *x, *y;
+	EC_POINT *elem;
+	int len = BN_num_bytes(e->prime);
+
+	x = BN_bin2bn(val, len, NULL);
+	y = BN_bin2bn(val + len, len, NULL);
+	elem = EC_POINT_new(e->group);
+	if (x == NULL || y == NULL || elem == NULL) {
+		BN_free(x);
+		BN_free(y);
+		EC_POINT_free(elem);
+		return NULL;
+	}
+
+	if (!EC_POINT_set_affine_coordinates_GFp(e->group, elem, x, y,
+						 e->bnctx)) {
+		EC_POINT_free(elem);
+		elem = NULL;
+	}
+
+	BN_free(x);
+	BN_free(y);
+
+	return (struct crypto_ec_point *) elem;
+}
+
+
+int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
+			const struct crypto_ec_point *b,
+			struct crypto_ec_point *c)
+{
+	return EC_POINT_add(e->group, (EC_POINT *) c, (const EC_POINT *) a,
+			    (const EC_POINT *) b, e->bnctx) ? 0 : -1;
+}
+
+
+int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
+			const struct crypto_bignum *b,
+			struct crypto_ec_point *res)
+{
+	return EC_POINT_mul(e->group, (EC_POINT *) res, NULL,
+			    (const EC_POINT *) p, (const BIGNUM *) b, e->bnctx)
+		? 0 : -1;
+}
+
+
+int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
+{
+	return EC_POINT_invert(e->group, (EC_POINT *) p, e->bnctx) ? 0 : -1;
+}
+
+
+int crypto_ec_point_solve_y_coord(struct crypto_ec *e,
+				  struct crypto_ec_point *p,
+				  const struct crypto_bignum *x, int y_bit)
+{
+	if (!EC_POINT_set_compressed_coordinates_GFp(e->group, (EC_POINT *) p,
+						     (const BIGNUM *) x, y_bit,
+						     e->bnctx) ||
+	    !EC_POINT_is_on_curve(e->group, (EC_POINT *) p, e->bnctx))
+		return -1;
+	return 0;
+}
+
+
+int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
+				   const struct crypto_ec_point *p)
+{
+	return EC_POINT_is_at_infinity(e->group, (const EC_POINT *) p);
+}
+
+
+int crypto_ec_point_is_on_curve(struct crypto_ec *e,
+				const struct crypto_ec_point *p)
+{
+	return EC_POINT_is_on_curve(e->group, (const EC_POINT *) p, e->bnctx);
+}
+
+#endif /* CONFIG_ECC */
diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c
index f757b6b..3a675df 100644
--- a/src/crypto/dh_groups.c
+++ b/src/crypto/dh_groups.c
@@ -35,6 +35,20 @@
 	0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x3A, 0x36, 0x20,
 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 };
+static const u8 dh_group1_order[96] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1D, 0x1B, 0x10,
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
 
 /* RFC 4306, B.2. Group 2 - 1024 Bit MODP
  * Generator: 2
@@ -59,6 +73,24 @@
 	0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81,
 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 };
+static const u8 dh_group2_order[128] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x73, 0x29, 0xC0,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
 
 #endif /* ALL_DH_GROUPS */
 
@@ -93,6 +125,32 @@
 	0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x23, 0x73, 0x27,
 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 };
+static const u8 dh_group5_order[192] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x11, 0xB9, 0x93,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
 
 #ifdef ALL_DH_GROUPS
 
@@ -135,6 +193,40 @@
 	0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68,
 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 };
+static const u8 dh_group14_order[256] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
+	0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
+	0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
+	0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
+	0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
+	0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
+	0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
+	0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
+	0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x56, 0x55, 0x34,
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
 
 /* RFC 3526, 4. Group 15 - 3072 Bit MODP
  * Generator: 2
@@ -191,6 +283,56 @@
 	0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x3A, 0xD2, 0xCA,
 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 };
+static const u8 dh_group15_order[384] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
+	0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
+	0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
+	0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
+	0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
+	0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
+	0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
+	0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
+	0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16,
+	0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19,
+	0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32,
+	0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85,
+	0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E,
+	0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63,
+	0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB,
+	0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE,
+	0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35,
+	0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32,
+	0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32,
+	0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06,
+	0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6,
+	0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71,
+	0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98,
+	0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47,
+	0x25, 0xC1, 0x68, 0x90, 0x54, 0x9D, 0x69, 0x65,
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
 
 /* RFC 3526, 5. Group 16 - 4096 Bit MODP
  * Generator: 2
@@ -263,6 +405,72 @@
 	0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99,
 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 };
+static const u8 dh_group16_order[512] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
+	0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
+	0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
+	0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
+	0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
+	0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
+	0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
+	0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
+	0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16,
+	0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19,
+	0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32,
+	0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85,
+	0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E,
+	0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63,
+	0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB,
+	0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE,
+	0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35,
+	0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32,
+	0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32,
+	0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06,
+	0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6,
+	0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71,
+	0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98,
+	0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47,
+	0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00,
+	0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B,
+	0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93,
+	0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E,
+	0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED,
+	0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74,
+	0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C,
+	0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53,
+	0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E,
+	0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1,
+	0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6,
+	0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7,
+	0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E,
+	0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54,
+	0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0,
+	0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47,
+	0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x03, 0x18, 0xCC,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
 
 /* RFC 3526, 6. Group 17 - 6144 Bit MODP
  * Generator: 2
@@ -367,6 +575,104 @@
 	0xE6, 0x94, 0xF9, 0x1E, 0x6D, 0xCC, 0x40, 0x24,
 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 };
+static const u8 dh_group17_order[768] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
+	0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
+	0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
+	0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
+	0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
+	0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
+	0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
+	0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
+	0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16,
+	0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19,
+	0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32,
+	0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85,
+	0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E,
+	0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63,
+	0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB,
+	0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE,
+	0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35,
+	0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32,
+	0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32,
+	0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06,
+	0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6,
+	0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71,
+	0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98,
+	0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47,
+	0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00,
+	0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B,
+	0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93,
+	0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E,
+	0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED,
+	0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74,
+	0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C,
+	0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53,
+	0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E,
+	0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1,
+	0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6,
+	0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7,
+	0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E,
+	0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54,
+	0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0,
+	0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47,
+	0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x01, 0x42, 0x49,
+	0x1B, 0x61, 0xFD, 0x5A, 0x69, 0x3E, 0x38, 0x13,
+	0x60, 0xEA, 0x6E, 0x59, 0x30, 0x13, 0x23, 0x6F,
+	0x64, 0xBA, 0x8F, 0x3B, 0x1E, 0xDD, 0x1B, 0xDE,
+	0xFC, 0x7F, 0xCA, 0x03, 0x56, 0xCF, 0x29, 0x87,
+	0x72, 0xED, 0x9C, 0x17, 0xA0, 0x98, 0x00, 0xD7,
+	0x58, 0x35, 0x29, 0xF6, 0xC8, 0x13, 0xEC, 0x18,
+	0x8B, 0xCB, 0x93, 0xD8, 0x43, 0x2D, 0x44, 0x8C,
+	0x6D, 0x1F, 0x6D, 0xF5, 0xE7, 0xCD, 0x8A, 0x76,
+	0xA2, 0x67, 0x36, 0x5D, 0x67, 0x6A, 0x5D, 0x8D,
+	0xED, 0xBF, 0x8A, 0x23, 0xF3, 0x66, 0x12, 0xA5,
+	0x99, 0x90, 0x28, 0xA8, 0x95, 0xEB, 0xD7, 0xA1,
+	0x37, 0xDC, 0x7A, 0x00, 0x9B, 0xC6, 0x69, 0x5F,
+	0xAC, 0xC1, 0xE5, 0x00, 0xE3, 0x25, 0xC9, 0x76,
+	0x78, 0x19, 0x75, 0x0A, 0xE8, 0xB9, 0x0E, 0x81,
+	0xFA, 0x41, 0x6B, 0xE7, 0x37, 0x3A, 0x7F, 0x7B,
+	0x6A, 0xAF, 0x38, 0x17, 0xA3, 0x4C, 0x06, 0x41,
+	0x5A, 0xD4, 0x20, 0x18, 0xC8, 0x05, 0x8E, 0x4F,
+	0x2C, 0xF3, 0xE4, 0xBF, 0xDF, 0x63, 0xF4, 0x79,
+	0x91, 0xD4, 0xBD, 0x3F, 0x1B, 0x66, 0x44, 0x5F,
+	0x07, 0x8E, 0xA2, 0xDB, 0xFF, 0xAC, 0x2D, 0x62,
+	0xA5, 0xEA, 0x03, 0xD9, 0x15, 0xA0, 0xAA, 0x55,
+	0x66, 0x47, 0xB6, 0xBF, 0x5F, 0xA4, 0x70, 0xEC,
+	0x0A, 0x66, 0x2F, 0x69, 0x07, 0xC0, 0x1B, 0xF0,
+	0x53, 0xCB, 0x8A, 0xF7, 0x79, 0x4D, 0xF1, 0x94,
+	0x03, 0x50, 0xEA, 0xC5, 0xDB, 0xE2, 0xED, 0x3B,
+	0x7A, 0xA8, 0x55, 0x1E, 0xC5, 0x0F, 0xDF, 0xF8,
+	0x75, 0x8C, 0xE6, 0x58, 0xD1, 0x89, 0xEA, 0xAE,
+	0x6D, 0x2B, 0x64, 0xF6, 0x17, 0x79, 0x4B, 0x19,
+	0x1C, 0x3F, 0xF4, 0x6B, 0xB7, 0x1E, 0x02, 0x34,
+	0x02, 0x1F, 0x47, 0xB3, 0x1F, 0xA4, 0x30, 0x77,
+	0x09, 0x5F, 0x96, 0xAD, 0x85, 0xBA, 0x3A, 0x6B,
+	0x73, 0x4A, 0x7C, 0x8F, 0x36, 0xE6, 0x20, 0x12,
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
 
 /* RFC 3526, 7. Group 18 - 8192 Bit MODP
  * Generator: 2
@@ -503,25 +809,363 @@
 	0x60, 0xC9, 0x80, 0xDD, 0x98, 0xED, 0xD3, 0xDF,
 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 };
+static const u8 dh_group18_order[1024] = {
+	0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
+	0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
+	0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
+	0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
+	0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
+	0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
+	0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
+	0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
+	0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
+	0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
+	0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
+	0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
+	0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
+	0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
+	0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
+	0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
+	0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
+	0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
+	0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
+	0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
+	0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
+	0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
+	0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
+	0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
+	0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
+	0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
+	0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
+	0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
+	0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
+	0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16,
+	0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19,
+	0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32,
+	0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85,
+	0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E,
+	0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63,
+	0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB,
+	0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE,
+	0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35,
+	0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32,
+	0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32,
+	0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06,
+	0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6,
+	0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71,
+	0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98,
+	0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47,
+	0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00,
+	0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B,
+	0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93,
+	0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E,
+	0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED,
+	0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74,
+	0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C,
+	0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53,
+	0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E,
+	0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1,
+	0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6,
+	0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7,
+	0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E,
+	0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54,
+	0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0,
+	0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47,
+	0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x01, 0x42, 0x49,
+	0x1B, 0x61, 0xFD, 0x5A, 0x69, 0x3E, 0x38, 0x13,
+	0x60, 0xEA, 0x6E, 0x59, 0x30, 0x13, 0x23, 0x6F,
+	0x64, 0xBA, 0x8F, 0x3B, 0x1E, 0xDD, 0x1B, 0xDE,
+	0xFC, 0x7F, 0xCA, 0x03, 0x56, 0xCF, 0x29, 0x87,
+	0x72, 0xED, 0x9C, 0x17, 0xA0, 0x98, 0x00, 0xD7,
+	0x58, 0x35, 0x29, 0xF6, 0xC8, 0x13, 0xEC, 0x18,
+	0x8B, 0xCB, 0x93, 0xD8, 0x43, 0x2D, 0x44, 0x8C,
+	0x6D, 0x1F, 0x6D, 0xF5, 0xE7, 0xCD, 0x8A, 0x76,
+	0xA2, 0x67, 0x36, 0x5D, 0x67, 0x6A, 0x5D, 0x8D,
+	0xED, 0xBF, 0x8A, 0x23, 0xF3, 0x66, 0x12, 0xA5,
+	0x99, 0x90, 0x28, 0xA8, 0x95, 0xEB, 0xD7, 0xA1,
+	0x37, 0xDC, 0x7A, 0x00, 0x9B, 0xC6, 0x69, 0x5F,
+	0xAC, 0xC1, 0xE5, 0x00, 0xE3, 0x25, 0xC9, 0x76,
+	0x78, 0x19, 0x75, 0x0A, 0xE8, 0xB9, 0x0E, 0x81,
+	0xFA, 0x41, 0x6B, 0xE7, 0x37, 0x3A, 0x7F, 0x7B,
+	0x6A, 0xAF, 0x38, 0x17, 0xA3, 0x4C, 0x06, 0x41,
+	0x5A, 0xD4, 0x20, 0x18, 0xC8, 0x05, 0x8E, 0x4F,
+	0x2C, 0xF3, 0xE4, 0xBF, 0xDF, 0x63, 0xF4, 0x79,
+	0x91, 0xD4, 0xBD, 0x3F, 0x1B, 0x66, 0x44, 0x5F,
+	0x07, 0x8E, 0xA2, 0xDB, 0xFF, 0xAC, 0x2D, 0x62,
+	0xA5, 0xEA, 0x03, 0xD9, 0x15, 0xA0, 0xAA, 0x55,
+	0x66, 0x47, 0xB6, 0xBF, 0x5F, 0xA4, 0x70, 0xEC,
+	0x0A, 0x66, 0x2F, 0x69, 0x07, 0xC0, 0x1B, 0xF0,
+	0x53, 0xCB, 0x8A, 0xF7, 0x79, 0x4D, 0xF1, 0x94,
+	0x03, 0x50, 0xEA, 0xC5, 0xDB, 0xE2, 0xED, 0x3B,
+	0x7A, 0xA8, 0x55, 0x1E, 0xC5, 0x0F, 0xDF, 0xF8,
+	0x75, 0x8C, 0xE6, 0x58, 0xD1, 0x89, 0xEA, 0xAE,
+	0x6D, 0x2B, 0x64, 0xF6, 0x17, 0x79, 0x4B, 0x19,
+	0x1C, 0x3F, 0xF4, 0x6B, 0xB7, 0x1E, 0x02, 0x34,
+	0x02, 0x1F, 0x47, 0xB3, 0x1F, 0xA4, 0x30, 0x77,
+	0x09, 0x5F, 0x96, 0xAD, 0x85, 0xBA, 0x3A, 0x6B,
+	0x73, 0x4A, 0x7C, 0x8F, 0x36, 0xDF, 0x08, 0xAC,
+	0xBA, 0x51, 0xC9, 0x37, 0x89, 0x7F, 0x72, 0xF2,
+	0x1C, 0x3B, 0xBE, 0x5B, 0x54, 0x99, 0x6F, 0xC6,
+	0x6C, 0x5F, 0x62, 0x68, 0x39, 0xDC, 0x98, 0xDD,
+	0x1D, 0xE4, 0x19, 0x5B, 0x46, 0xCE, 0xE9, 0x80,
+	0x3A, 0x0F, 0xD3, 0xDF, 0xC5, 0x7E, 0x23, 0xF6,
+	0x92, 0xBB, 0x7B, 0x49, 0xB5, 0xD2, 0x12, 0x33,
+	0x1D, 0x55, 0xB1, 0xCE, 0x2D, 0x72, 0x7A, 0xB4,
+	0x1A, 0x11, 0xDA, 0x3A, 0x15, 0xF8, 0xE4, 0xBC,
+	0x11, 0xC7, 0x8B, 0x65, 0xF1, 0xCE, 0xB2, 0x96,
+	0xF1, 0xFE, 0xDC, 0x5F, 0x7E, 0x42, 0x45, 0x6C,
+	0x91, 0x11, 0x17, 0x02, 0x52, 0x01, 0xBE, 0x03,
+	0x89, 0xF5, 0xAB, 0xD4, 0x0D, 0x11, 0xF8, 0x63,
+	0x9A, 0x39, 0xFE, 0x32, 0x36, 0x75, 0x18, 0x35,
+	0xA5, 0xE5, 0xE4, 0x43, 0x17, 0xC1, 0xC2, 0xEE,
+	0xFD, 0x4E, 0xA5, 0xBF, 0xD1, 0x60, 0x43, 0xF4,
+	0x3C, 0xB4, 0x19, 0x81, 0xF6, 0xAD, 0xEE, 0x9D,
+	0x03, 0x15, 0x9E, 0x7A, 0xD9, 0xD1, 0x3C, 0x53,
+	0x36, 0x95, 0x09, 0xFC, 0x1F, 0xA2, 0x7C, 0x16,
+	0xEF, 0x98, 0x87, 0x70, 0x3A, 0x55, 0xB5, 0x1B,
+	0x22, 0xCB, 0xF4, 0x4C, 0xD0, 0x12, 0xAE, 0xE0,
+	0xB2, 0x79, 0x8E, 0x62, 0x84, 0x23, 0x42, 0x8E,
+	0xFC, 0xD5, 0xA4, 0x0C, 0xAE, 0xF6, 0xBF, 0x50,
+	0xD8, 0xEA, 0x88, 0x5E, 0xBF, 0x73, 0xA6, 0xB9,
+	0xFD, 0x79, 0xB5, 0xE1, 0x8F, 0x67, 0xD1, 0x34,
+	0x1A, 0xC8, 0x23, 0x7A, 0x75, 0xC3, 0xCF, 0xC9,
+	0x20, 0x04, 0xA1, 0xC5, 0xA4, 0x0E, 0x36, 0x6B,
+	0xC4, 0x4D, 0x00, 0x17, 0x6A, 0xF7, 0x1C, 0x15,
+	0xE4, 0x8C, 0x86, 0xD3, 0x7E, 0x01, 0x37, 0x23,
+	0xCA, 0xAC, 0x72, 0x23, 0xAB, 0x3B, 0xF4, 0xD5,
+	0x4F, 0x18, 0x28, 0x71, 0x3B, 0x2B, 0x4A, 0x6F,
+	0xE4, 0x0F, 0xAB, 0x74, 0x40, 0x5C, 0xB7, 0x38,
+	0xB0, 0x64, 0xC0, 0x6E, 0xCC, 0x76, 0xE9, 0xEF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+/*
+ * RFC 5114, 2.1.
+ * Group 22 - 1024-bit MODP Group with 160-bit Prime Order Subgroup
+ */
+static const u8 dh_group22_generator[] = {
+	0xA4, 0xD1, 0xCB, 0xD5, 0xC3, 0xFD, 0x34, 0x12,
+	0x67, 0x65, 0xA4, 0x42, 0xEF, 0xB9, 0x99, 0x05,
+	0xF8, 0x10, 0x4D, 0xD2, 0x58, 0xAC, 0x50, 0x7F,
+	0xD6, 0x40, 0x6C, 0xFF, 0x14, 0x26, 0x6D, 0x31,
+	0x26, 0x6F, 0xEA, 0x1E, 0x5C, 0x41, 0x56, 0x4B,
+	0x77, 0x7E, 0x69, 0x0F, 0x55, 0x04, 0xF2, 0x13,
+	0x16, 0x02, 0x17, 0xB4, 0xB0, 0x1B, 0x88, 0x6A,
+	0x5E, 0x91, 0x54, 0x7F, 0x9E, 0x27, 0x49, 0xF4,
+	0xD7, 0xFB, 0xD7, 0xD3, 0xB9, 0xA9, 0x2E, 0xE1,
+	0x90, 0x9D, 0x0D, 0x22, 0x63, 0xF8, 0x0A, 0x76,
+	0xA6, 0xA2, 0x4C, 0x08, 0x7A, 0x09, 0x1F, 0x53,
+	0x1D, 0xBF, 0x0A, 0x01, 0x69, 0xB6, 0xA2, 0x8A,
+	0xD6, 0x62, 0xA4, 0xD1, 0x8E, 0x73, 0xAF, 0xA3,
+	0x2D, 0x77, 0x9D, 0x59, 0x18, 0xD0, 0x8B, 0xC8,
+	0x85, 0x8F, 0x4D, 0xCE, 0xF9, 0x7C, 0x2A, 0x24,
+	0x85, 0x5E, 0x6E, 0xEB, 0x22, 0xB3, 0xB2, 0xE5
+};
+static const u8 dh_group22_prime[] = {
+	0xB1, 0x0B, 0x8F, 0x96, 0xA0, 0x80, 0xE0, 0x1D,
+	0xDE, 0x92, 0xDE, 0x5E, 0xAE, 0x5D, 0x54, 0xEC,
+	0x52, 0xC9, 0x9F, 0xBC, 0xFB, 0x06, 0xA3, 0xC6,
+	0x9A, 0x6A, 0x9D, 0xCA, 0x52, 0xD2, 0x3B, 0x61,
+	0x60, 0x73, 0xE2, 0x86, 0x75, 0xA2, 0x3D, 0x18,
+	0x98, 0x38, 0xEF, 0x1E, 0x2E, 0xE6, 0x52, 0xC0,
+	0x13, 0xEC, 0xB4, 0xAE, 0xA9, 0x06, 0x11, 0x23,
+	0x24, 0x97, 0x5C, 0x3C, 0xD4, 0x9B, 0x83, 0xBF,
+	0xAC, 0xCB, 0xDD, 0x7D, 0x90, 0xC4, 0xBD, 0x70,
+	0x98, 0x48, 0x8E, 0x9C, 0x21, 0x9A, 0x73, 0x72,
+	0x4E, 0xFF, 0xD6, 0xFA, 0xE5, 0x64, 0x47, 0x38,
+	0xFA, 0xA3, 0x1A, 0x4F, 0xF5, 0x5B, 0xCC, 0xC0,
+	0xA1, 0x51, 0xAF, 0x5F, 0x0D, 0xC8, 0xB4, 0xBD,
+	0x45, 0xBF, 0x37, 0xDF, 0x36, 0x5C, 0x1A, 0x65,
+	0xE6, 0x8C, 0xFD, 0xA7, 0x6D, 0x4D, 0xA7, 0x08,
+	0xDF, 0x1F, 0xB2, 0xBC, 0x2E, 0x4A, 0x43, 0x71
+};
+static const u8 dh_group22_order[] = {
+	0xF5, 0x18, 0xAA, 0x87, 0x81, 0xA8, 0xDF, 0x27,
+	0x8A, 0xBA, 0x4E, 0x7D, 0x64, 0xB7, 0xCB, 0x9D,
+	0x49, 0x46, 0x23, 0x53
+};
+
+/*
+ * RFC 5114, 2.2.
+ * Group 23 - 2048-bit MODP Group with 224-bit Prime Order Subgroup
+ */
+static const u8 dh_group23_generator[] = {
+	0xAC, 0x40, 0x32, 0xEF, 0x4F, 0x2D, 0x9A, 0xE3,
+	0x9D, 0xF3, 0x0B, 0x5C, 0x8F, 0xFD, 0xAC, 0x50,
+	0x6C, 0xDE, 0xBE, 0x7B, 0x89, 0x99, 0x8C, 0xAF,
+	0x74, 0x86, 0x6A, 0x08, 0xCF, 0xE4, 0xFF, 0xE3,
+	0xA6, 0x82, 0x4A, 0x4E, 0x10, 0xB9, 0xA6, 0xF0,
+	0xDD, 0x92, 0x1F, 0x01, 0xA7, 0x0C, 0x4A, 0xFA,
+	0xAB, 0x73, 0x9D, 0x77, 0x00, 0xC2, 0x9F, 0x52,
+	0xC5, 0x7D, 0xB1, 0x7C, 0x62, 0x0A, 0x86, 0x52,
+	0xBE, 0x5E, 0x90, 0x01, 0xA8, 0xD6, 0x6A, 0xD7,
+	0xC1, 0x76, 0x69, 0x10, 0x19, 0x99, 0x02, 0x4A,
+	0xF4, 0xD0, 0x27, 0x27, 0x5A, 0xC1, 0x34, 0x8B,
+	0xB8, 0xA7, 0x62, 0xD0, 0x52, 0x1B, 0xC9, 0x8A,
+	0xE2, 0x47, 0x15, 0x04, 0x22, 0xEA, 0x1E, 0xD4,
+	0x09, 0x93, 0x9D, 0x54, 0xDA, 0x74, 0x60, 0xCD,
+	0xB5, 0xF6, 0xC6, 0xB2, 0x50, 0x71, 0x7C, 0xBE,
+	0xF1, 0x80, 0xEB, 0x34, 0x11, 0x8E, 0x98, 0xD1,
+	0x19, 0x52, 0x9A, 0x45, 0xD6, 0xF8, 0x34, 0x56,
+	0x6E, 0x30, 0x25, 0xE3, 0x16, 0xA3, 0x30, 0xEF,
+	0xBB, 0x77, 0xA8, 0x6F, 0x0C, 0x1A, 0xB1, 0x5B,
+	0x05, 0x1A, 0xE3, 0xD4, 0x28, 0xC8, 0xF8, 0xAC,
+	0xB7, 0x0A, 0x81, 0x37, 0x15, 0x0B, 0x8E, 0xEB,
+	0x10, 0xE1, 0x83, 0xED, 0xD1, 0x99, 0x63, 0xDD,
+	0xD9, 0xE2, 0x63, 0xE4, 0x77, 0x05, 0x89, 0xEF,
+	0x6A, 0xA2, 0x1E, 0x7F, 0x5F, 0x2F, 0xF3, 0x81,
+	0xB5, 0x39, 0xCC, 0xE3, 0x40, 0x9D, 0x13, 0xCD,
+	0x56, 0x6A, 0xFB, 0xB4, 0x8D, 0x6C, 0x01, 0x91,
+	0x81, 0xE1, 0xBC, 0xFE, 0x94, 0xB3, 0x02, 0x69,
+	0xED, 0xFE, 0x72, 0xFE, 0x9B, 0x6A, 0xA4, 0xBD,
+	0x7B, 0x5A, 0x0F, 0x1C, 0x71, 0xCF, 0xFF, 0x4C,
+	0x19, 0xC4, 0x18, 0xE1, 0xF6, 0xEC, 0x01, 0x79,
+	0x81, 0xBC, 0x08, 0x7F, 0x2A, 0x70, 0x65, 0xB3,
+	0x84, 0xB8, 0x90, 0xD3, 0x19, 0x1F, 0x2B, 0xFA
+};
+static const u8 dh_group23_prime[] = {
+	0xAD, 0x10, 0x7E, 0x1E, 0x91, 0x23, 0xA9, 0xD0,
+	0xD6, 0x60, 0xFA, 0xA7, 0x95, 0x59, 0xC5, 0x1F,
+	0xA2, 0x0D, 0x64, 0xE5, 0x68, 0x3B, 0x9F, 0xD1,
+	0xB5, 0x4B, 0x15, 0x97, 0xB6, 0x1D, 0x0A, 0x75,
+	0xE6, 0xFA, 0x14, 0x1D, 0xF9, 0x5A, 0x56, 0xDB,
+	0xAF, 0x9A, 0x3C, 0x40, 0x7B, 0xA1, 0xDF, 0x15,
+	0xEB, 0x3D, 0x68, 0x8A, 0x30, 0x9C, 0x18, 0x0E,
+	0x1D, 0xE6, 0xB8, 0x5A, 0x12, 0x74, 0xA0, 0xA6,
+	0x6D, 0x3F, 0x81, 0x52, 0xAD, 0x6A, 0xC2, 0x12,
+	0x90, 0x37, 0xC9, 0xED, 0xEF, 0xDA, 0x4D, 0xF8,
+	0xD9, 0x1E, 0x8F, 0xEF, 0x55, 0xB7, 0x39, 0x4B,
+	0x7A, 0xD5, 0xB7, 0xD0, 0xB6, 0xC1, 0x22, 0x07,
+	0xC9, 0xF9, 0x8D, 0x11, 0xED, 0x34, 0xDB, 0xF6,
+	0xC6, 0xBA, 0x0B, 0x2C, 0x8B, 0xBC, 0x27, 0xBE,
+	0x6A, 0x00, 0xE0, 0xA0, 0xB9, 0xC4, 0x97, 0x08,
+	0xB3, 0xBF, 0x8A, 0x31, 0x70, 0x91, 0x88, 0x36,
+	0x81, 0x28, 0x61, 0x30, 0xBC, 0x89, 0x85, 0xDB,
+	0x16, 0x02, 0xE7, 0x14, 0x41, 0x5D, 0x93, 0x30,
+	0x27, 0x82, 0x73, 0xC7, 0xDE, 0x31, 0xEF, 0xDC,
+	0x73, 0x10, 0xF7, 0x12, 0x1F, 0xD5, 0xA0, 0x74,
+	0x15, 0x98, 0x7D, 0x9A, 0xDC, 0x0A, 0x48, 0x6D,
+	0xCD, 0xF9, 0x3A, 0xCC, 0x44, 0x32, 0x83, 0x87,
+	0x31, 0x5D, 0x75, 0xE1, 0x98, 0xC6, 0x41, 0xA4,
+	0x80, 0xCD, 0x86, 0xA1, 0xB9, 0xE5, 0x87, 0xE8,
+	0xBE, 0x60, 0xE6, 0x9C, 0xC9, 0x28, 0xB2, 0xB9,
+	0xC5, 0x21, 0x72, 0xE4, 0x13, 0x04, 0x2E, 0x9B,
+	0x23, 0xF1, 0x0B, 0x0E, 0x16, 0xE7, 0x97, 0x63,
+	0xC9, 0xB5, 0x3D, 0xCF, 0x4B, 0xA8, 0x0A, 0x29,
+	0xE3, 0xFB, 0x73, 0xC1, 0x6B, 0x8E, 0x75, 0xB9,
+	0x7E, 0xF3, 0x63, 0xE2, 0xFF, 0xA3, 0x1F, 0x71,
+	0xCF, 0x9D, 0xE5, 0x38, 0x4E, 0x71, 0xB8, 0x1C,
+	0x0A, 0xC4, 0xDF, 0xFE, 0x0C, 0x10, 0xE6, 0x4F
+};
+static const u8 dh_group23_order[] = {
+	0x80, 0x1C, 0x0D, 0x34, 0xC5, 0x8D, 0x93, 0xFE,
+	0x99, 0x71, 0x77, 0x10, 0x1F, 0x80, 0x53, 0x5A,
+	0x47, 0x38, 0xCE, 0xBC, 0xBF, 0x38, 0x9A, 0x99,
+	0xB3, 0x63, 0x71, 0xEB
+};
+
+/*
+ * RFC 5114, 2.3.
+ * Group 24 - 2048-bit MODP Group with 256-bit Prime Order Subgroup
+ */
+static const u8 dh_group24_generator[] = {
+	0x3F, 0xB3, 0x2C, 0x9B, 0x73, 0x13, 0x4D, 0x0B,
+	0x2E, 0x77, 0x50, 0x66, 0x60, 0xED, 0xBD, 0x48,
+	0x4C, 0xA7, 0xB1, 0x8F, 0x21, 0xEF, 0x20, 0x54,
+	0x07, 0xF4, 0x79, 0x3A, 0x1A, 0x0B, 0xA1, 0x25,
+	0x10, 0xDB, 0xC1, 0x50, 0x77, 0xBE, 0x46, 0x3F,
+	0xFF, 0x4F, 0xED, 0x4A, 0xAC, 0x0B, 0xB5, 0x55,
+	0xBE, 0x3A, 0x6C, 0x1B, 0x0C, 0x6B, 0x47, 0xB1,
+	0xBC, 0x37, 0x73, 0xBF, 0x7E, 0x8C, 0x6F, 0x62,
+	0x90, 0x12, 0x28, 0xF8, 0xC2, 0x8C, 0xBB, 0x18,
+	0xA5, 0x5A, 0xE3, 0x13, 0x41, 0x00, 0x0A, 0x65,
+	0x01, 0x96, 0xF9, 0x31, 0xC7, 0x7A, 0x57, 0xF2,
+	0xDD, 0xF4, 0x63, 0xE5, 0xE9, 0xEC, 0x14, 0x4B,
+	0x77, 0x7D, 0xE6, 0x2A, 0xAA, 0xB8, 0xA8, 0x62,
+	0x8A, 0xC3, 0x76, 0xD2, 0x82, 0xD6, 0xED, 0x38,
+	0x64, 0xE6, 0x79, 0x82, 0x42, 0x8E, 0xBC, 0x83,
+	0x1D, 0x14, 0x34, 0x8F, 0x6F, 0x2F, 0x91, 0x93,
+	0xB5, 0x04, 0x5A, 0xF2, 0x76, 0x71, 0x64, 0xE1,
+	0xDF, 0xC9, 0x67, 0xC1, 0xFB, 0x3F, 0x2E, 0x55,
+	0xA4, 0xBD, 0x1B, 0xFF, 0xE8, 0x3B, 0x9C, 0x80,
+	0xD0, 0x52, 0xB9, 0x85, 0xD1, 0x82, 0xEA, 0x0A,
+	0xDB, 0x2A, 0x3B, 0x73, 0x13, 0xD3, 0xFE, 0x14,
+	0xC8, 0x48, 0x4B, 0x1E, 0x05, 0x25, 0x88, 0xB9,
+	0xB7, 0xD2, 0xBB, 0xD2, 0xDF, 0x01, 0x61, 0x99,
+	0xEC, 0xD0, 0x6E, 0x15, 0x57, 0xCD, 0x09, 0x15,
+	0xB3, 0x35, 0x3B, 0xBB, 0x64, 0xE0, 0xEC, 0x37,
+	0x7F, 0xD0, 0x28, 0x37, 0x0D, 0xF9, 0x2B, 0x52,
+	0xC7, 0x89, 0x14, 0x28, 0xCD, 0xC6, 0x7E, 0xB6,
+	0x18, 0x4B, 0x52, 0x3D, 0x1D, 0xB2, 0x46, 0xC3,
+	0x2F, 0x63, 0x07, 0x84, 0x90, 0xF0, 0x0E, 0xF8,
+	0xD6, 0x47, 0xD1, 0x48, 0xD4, 0x79, 0x54, 0x51,
+	0x5E, 0x23, 0x27, 0xCF, 0xEF, 0x98, 0xC5, 0x82,
+	0x66, 0x4B, 0x4C, 0x0F, 0x6C, 0xC4, 0x16, 0x59
+};
+static const u8 dh_group24_prime[] = {
+	0x87, 0xA8, 0xE6, 0x1D, 0xB4, 0xB6, 0x66, 0x3C,
+	0xFF, 0xBB, 0xD1, 0x9C, 0x65, 0x19, 0x59, 0x99,
+	0x8C, 0xEE, 0xF6, 0x08, 0x66, 0x0D, 0xD0, 0xF2,
+	0x5D, 0x2C, 0xEE, 0xD4, 0x43, 0x5E, 0x3B, 0x00,
+	0xE0, 0x0D, 0xF8, 0xF1, 0xD6, 0x19, 0x57, 0xD4,
+	0xFA, 0xF7, 0xDF, 0x45, 0x61, 0xB2, 0xAA, 0x30,
+	0x16, 0xC3, 0xD9, 0x11, 0x34, 0x09, 0x6F, 0xAA,
+	0x3B, 0xF4, 0x29, 0x6D, 0x83, 0x0E, 0x9A, 0x7C,
+	0x20, 0x9E, 0x0C, 0x64, 0x97, 0x51, 0x7A, 0xBD,
+	0x5A, 0x8A, 0x9D, 0x30, 0x6B, 0xCF, 0x67, 0xED,
+	0x91, 0xF9, 0xE6, 0x72, 0x5B, 0x47, 0x58, 0xC0,
+	0x22, 0xE0, 0xB1, 0xEF, 0x42, 0x75, 0xBF, 0x7B,
+	0x6C, 0x5B, 0xFC, 0x11, 0xD4, 0x5F, 0x90, 0x88,
+	0xB9, 0x41, 0xF5, 0x4E, 0xB1, 0xE5, 0x9B, 0xB8,
+	0xBC, 0x39, 0xA0, 0xBF, 0x12, 0x30, 0x7F, 0x5C,
+	0x4F, 0xDB, 0x70, 0xC5, 0x81, 0xB2, 0x3F, 0x76,
+	0xB6, 0x3A, 0xCA, 0xE1, 0xCA, 0xA6, 0xB7, 0x90,
+	0x2D, 0x52, 0x52, 0x67, 0x35, 0x48, 0x8A, 0x0E,
+	0xF1, 0x3C, 0x6D, 0x9A, 0x51, 0xBF, 0xA4, 0xAB,
+	0x3A, 0xD8, 0x34, 0x77, 0x96, 0x52, 0x4D, 0x8E,
+	0xF6, 0xA1, 0x67, 0xB5, 0xA4, 0x18, 0x25, 0xD9,
+	0x67, 0xE1, 0x44, 0xE5, 0x14, 0x05, 0x64, 0x25,
+	0x1C, 0xCA, 0xCB, 0x83, 0xE6, 0xB4, 0x86, 0xF6,
+	0xB3, 0xCA, 0x3F, 0x79, 0x71, 0x50, 0x60, 0x26,
+	0xC0, 0xB8, 0x57, 0xF6, 0x89, 0x96, 0x28, 0x56,
+	0xDE, 0xD4, 0x01, 0x0A, 0xBD, 0x0B, 0xE6, 0x21,
+	0xC3, 0xA3, 0x96, 0x0A, 0x54, 0xE7, 0x10, 0xC3,
+	0x75, 0xF2, 0x63, 0x75, 0xD7, 0x01, 0x41, 0x03,
+	0xA4, 0xB5, 0x43, 0x30, 0xC1, 0x98, 0xAF, 0x12,
+	0x61, 0x16, 0xD2, 0x27, 0x6E, 0x11, 0x71, 0x5F,
+	0x69, 0x38, 0x77, 0xFA, 0xD7, 0xEF, 0x09, 0xCA,
+	0xDB, 0x09, 0x4A, 0xE9, 0x1E, 0x1A, 0x15, 0x97
+};
+static const u8 dh_group24_order[] = {
+	0x8C, 0xF8, 0x36, 0x42, 0xA7, 0x09, 0xA0, 0x97,
+	0xB4, 0x47, 0x99, 0x76, 0x40, 0x12, 0x9D, 0xA2,
+	0x99, 0xB1, 0xA4, 0x7D, 0x1E, 0xB3, 0x75, 0x0B,
+	0xA3, 0x08, 0xB0, 0xFE, 0x64, 0xF5, 0xFB, 0xD3
+};
 
 #endif /* ALL_DH_GROUPS */
 
 
-#define DH_GROUP(id) \
+#define DH_GROUP(id,safe) \
 { id, dh_group ## id ## _generator, sizeof(dh_group ## id ## _generator), \
-dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime) }
+dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime), \
+dh_group ## id ## _order, sizeof(dh_group ## id ## _order), safe }
 		
 
 static struct dh_group dh_groups[] = {
-	DH_GROUP(5),
+	DH_GROUP(5, 1),
 #ifdef ALL_DH_GROUPS
-	DH_GROUP(1),
-	DH_GROUP(2),
-	DH_GROUP(14),
-	DH_GROUP(15),
-	DH_GROUP(16),
-	DH_GROUP(17),
-	DH_GROUP(18)
+	DH_GROUP(1, 1),
+	DH_GROUP(2, 1),
+	DH_GROUP(14, 1),
+	DH_GROUP(15, 1),
+	DH_GROUP(16, 1),
+	DH_GROUP(17, 1),
+	DH_GROUP(18, 1),
+	DH_GROUP(22, 0),
+	DH_GROUP(23, 0),
+	DH_GROUP(24, 0)
 #endif /* ALL_DH_GROUPS */
 };
 
diff --git a/src/crypto/dh_groups.h b/src/crypto/dh_groups.h
index 225f006..d0e74b9 100644
--- a/src/crypto/dh_groups.h
+++ b/src/crypto/dh_groups.h
@@ -15,6 +15,9 @@
 	size_t generator_len;
 	const u8 *prime;
 	size_t prime_len;
+	const u8 *order;
+	size_t order_len;
+	unsigned int safe_prime:1;
 };
 
 const struct dh_group * dh_groups_get(int id);
diff --git a/src/crypto/sha256-prf.c b/src/crypto/sha256-prf.c
index 0da6d13..9a11208 100644
--- a/src/crypto/sha256-prf.c
+++ b/src/crypto/sha256-prf.c
@@ -1,6 +1,6 @@
 /*
  * SHA256-based PRF (IEEE 802.11r)
- * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -29,12 +29,36 @@
 void sha256_prf(const u8 *key, size_t key_len, const char *label,
 		const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
 {
+	sha256_prf_bits(key, key_len, label, data, data_len, buf, buf_len * 8);
+}
+
+
+/**
+ * sha256_prf_bits - IEEE Std 802.11-2012, 11.6.1.7.2 Key derivation function
+ * @key: Key for KDF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bits of key to generate
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key. If the requested buf_len is not divisible by eight, the least
+ * significant 1-7 bits of the last octet in the output are not part of the
+ * requested output.
+ */
+void sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
+		     const u8 *data, size_t data_len, u8 *buf,
+		     size_t buf_len_bits)
+{
 	u16 counter = 1;
 	size_t pos, plen;
 	u8 hash[SHA256_MAC_LEN];
 	const u8 *addr[4];
 	size_t len[4];
 	u8 counter_le[2], length_le[2];
+	size_t buf_len = (buf_len_bits + 7) / 8;
 
 	addr[0] = counter_le;
 	len[0] = 2;
@@ -45,7 +69,7 @@
 	addr[3] = length_le;
 	len[3] = sizeof(length_le);
 
-	WPA_PUT_LE16(length_le, buf_len * 8);
+	WPA_PUT_LE16(length_le, buf_len_bits);
 	pos = 0;
 	while (pos < buf_len) {
 		plen = buf_len - pos;
@@ -57,8 +81,18 @@
 		} else {
 			hmac_sha256_vector(key, key_len, 4, addr, len, hash);
 			os_memcpy(&buf[pos], hash, plen);
+			pos += plen;
 			break;
 		}
 		counter++;
 	}
+
+	/*
+	 * Mask out unused bits in the last octet if it does not use all the
+	 * bits.
+	 */
+	if (buf_len_bits % 8) {
+		u8 mask = 0xff << (8 - buf_len_bits % 8);
+		buf[pos - 1] &= mask;
+	}
 }
diff --git a/src/crypto/sha256.h b/src/crypto/sha256.h
index fcac800..7596a52 100644
--- a/src/crypto/sha256.h
+++ b/src/crypto/sha256.h
@@ -1,6 +1,6 @@
 /*
  * SHA256 hash implementation and interface functions
- * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2013, 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,9 @@
 		size_t data_len, u8 *mac);
 void sha256_prf(const u8 *key, size_t key_len, const char *label,
 	      const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+void sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
+		     const u8 *data, size_t data_len, u8 *buf,
+		     size_t buf_len_bits);
 void tls_prf_sha256(const u8 *secret, size_t secret_len,
 		    const char *label, const u8 *seed, size_t seed_len,
 		    u8 *out, size_t outlen);
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index f7bdc2c..5e8fd65 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -898,6 +898,7 @@
 	size_t supp_rates_len;
 	u16 listen_interval;
 	const struct ieee80211_ht_capabilities *ht_capabilities;
+	const struct ieee80211_vht_capabilities *vht_capabilities;
 	u32 flags; /* bitmask of WPA_STA_* flags */
 	int set; /* Set STA parameters instead of add */
 	u8 qosinfo;
@@ -907,10 +908,19 @@
 	int mode;
 	int freq;
 	int channel;
+	/* for HT */
 	int ht_enabled;
 	int sec_channel_offset; /* 0 = HT40 disabled, -1 = HT40 enabled,
 				 * secondary channel below primary, 1 = HT40
 				 * enabled, secondary channel above primary */
+
+	/* for VHT */
+	int vht_enabled;
+
+	/* valid for both HT and VHT, center_freq2 is non-zero
+	 * only for bandwidth 80 and an 80+80 channel */
+	int center_freq1, center_freq2;
+	int bandwidth;
 };
 
 enum wpa_driver_if_type {
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index a0a5210..c2f5934 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -874,7 +874,7 @@
 }
 #endif /* CONFIG_HS20 */
 
-#if defined(CONFIG_IEEE80211V) && !defined(CONFIG_IEEE80211R)
+#if defined(CONFIG_WNM) && !defined(CONFIG_IEEE80211R)
 static void atheros_raw_recv_11v(void *ctx, const u8 *src_addr, const u8 *buf,
 				 size_t len)
 {
@@ -921,9 +921,9 @@
 		break;
 	}
 }
-#endif /* CONFIG_IEEE80211V */
+#endif /* CONFIG_WNM */
 
-#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211V)
+#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_WNM)
 static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
 				size_t len)
 {
@@ -933,9 +933,9 @@
 #ifdef CONFIG_IEEE80211R
 	atheros_raw_recv_11r(ctx, src_addr, buf, len);
 #endif /* CONFIG_IEEE80211R */
-#if defined(CONFIG_IEEE80211V) && !defined(CONFIG_IEEE80211R)
+#if defined(CONFIG_WNM) && !defined(CONFIG_IEEE80211R)
 	atheros_raw_recv_11v(ctx, src_addr, buf, len);
-#endif /* CONFIG_IEEE80211V */
+#endif /* CONFIG_WNM */
 #ifdef CONFIG_HS20
 	atheros_raw_recv_hs20(ctx, src_addr, buf, len);
 #endif /* CONFIG_HS20 */
@@ -957,9 +957,9 @@
 			       IEEE80211_FILTER_TYPE_AUTH |
 			       IEEE80211_FILTER_TYPE_ACTION);
 #endif
-#ifdef CONFIG_IEEE80211V
+#ifdef CONFIG_WNM
 	filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION;
-#endif /* CONFIG_IEEE80211V */
+#endif /* CONFIG_WNM */
 #ifdef CONFIG_HS20
 	filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION;
 #endif /* CONFIG_HS20 */
@@ -1688,13 +1688,17 @@
 	linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0);
 	atheros_set_privacy(drv, 0); /* default to no privacy */
 
-	atheros_receive_pkt(drv);
+	if (atheros_receive_pkt(drv))
+		goto bad;
 
 	if (atheros_wireless_event_init(drv))
 		goto bad;
 
 	return drv;
 bad:
+	atheros_reset_appfilter(drv);
+	if (drv->sock_raw)
+		l2_packet_deinit(drv->sock_raw);
 	if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit)
 		l2_packet_deinit(drv->sock_recv);
 	if (drv->sock_xmit != NULL)
@@ -1959,7 +1963,7 @@
 }
 
 
-#ifdef CONFIG_IEEE80211V
+#ifdef CONFIG_WNM
 static int athr_wnm_tfs(struct atheros_driver_data *drv, const u8* peer,
 			u8 *ie, u16 *len, enum wnm_oper oper)
 {
@@ -2112,7 +2116,7 @@
 		return -1;
 	}
 }
-#endif /* CONFIG_IEEE80211V */
+#endif /* CONFIG_WNM */
 
 
 const struct wpa_driver_ops wpa_driver_atheros_ops = {
@@ -2146,7 +2150,7 @@
 	.add_sta_node    	= atheros_add_sta_node,
 #endif /* CONFIG_IEEE80211R */
 	.send_action		= atheros_send_action,
-#ifdef CONFIG_IEEE80211V
+#ifdef CONFIG_WNM
 	.wnm_oper		= atheros_wnm_oper,
-#endif /* CONFIG_IEEE80211V */
+#endif /* CONFIG_WNM */
 };
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index a0644b4..e530911 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -195,6 +195,7 @@
 
 	int freq;
 
+	void *ctx;
 	struct nl_handle *nl_preq, *nl_mgmt;
 	struct nl_cb *nl_cb;
 
@@ -1538,7 +1539,7 @@
 }
 
 
-static void mlme_event_michael_mic_failure(struct wpa_driver_nl80211_data *drv,
+static void mlme_event_michael_mic_failure(struct i802_bss *bss,
 					   struct nlattr *tb[])
 {
 	union wpa_event_data data;
@@ -1570,7 +1571,7 @@
 		wpa_printf(MSG_DEBUG, "nl80211: Key Id %d", key_id);
 	}
 
-	wpa_supplicant_event(drv->ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+	wpa_supplicant_event(bss->ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
 }
 
 
@@ -2177,9 +2178,11 @@
 }
 
 
-static void do_process_drv_event(struct wpa_driver_nl80211_data *drv,
-				 int cmd, struct nlattr **tb)
+static void do_process_drv_event(struct i802_bss *bss, int cmd,
+				 struct nlattr **tb)
 {
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
 	if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED &&
 	    (cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
 	     cmd == NL80211_CMD_SCAN_ABORTED)) {
@@ -2252,7 +2255,7 @@
 				      tb[NL80211_ATTR_DISCONNECTED_BY_AP]);
 		break;
 	case NL80211_CMD_MICHAEL_MIC_FAILURE:
-		mlme_event_michael_mic_failure(drv, tb);
+		mlme_event_michael_mic_failure(bss, tb);
 		break;
 	case NL80211_CMD_JOIN_IBSS:
 		mlme_event_join_ibss(drv, tb);
@@ -2307,21 +2310,25 @@
 	struct wpa_driver_nl80211_data *drv = arg;
 	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct i802_bss *bss;
+	int ifidx = -1;
 
 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
 		  genlmsg_attrlen(gnlh, 0), NULL);
 
-	if (tb[NL80211_ATTR_IFINDEX]) {
-		int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
-		if (ifindex != drv->ifindex && !have_ifidx(drv, ifindex)) {
-			wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)"
-				   " for foreign interface (ifindex %d)",
-				   gnlh->cmd, ifindex);
+	if (tb[NL80211_ATTR_IFINDEX])
+		ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
+
+	for (bss = &drv->first_bss; bss; bss = bss->next) {
+		if (ifidx == -1 || ifidx == bss->ifindex) {
+			do_process_drv_event(bss, gnlh->cmd, tb);
 			return NL_SKIP;
 		}
 	}
 
-	do_process_drv_event(drv, gnlh->cmd, tb);
+	wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d) for foreign "
+		   "interface (ifindex %d)", gnlh->cmd, ifidx);
+
 	return NL_SKIP;
 }
 
@@ -2333,6 +2340,7 @@
 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
 	struct wpa_driver_nl80211_data *drv, *tmp;
 	int ifidx = -1;
+	struct i802_bss *bss;
 
 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
 		  genlmsg_attrlen(gnlh, 0), NULL);
@@ -2342,9 +2350,12 @@
 
 	dl_list_for_each_safe(drv, tmp, &global->interfaces,
 			      struct wpa_driver_nl80211_data, list) {
-		if (ifidx == -1 || ifidx == drv->ifindex ||
-		    have_ifidx(drv, ifidx))
-			do_process_drv_event(drv, gnlh->cmd, tb);
+		for (bss = &drv->first_bss; bss; bss = bss->next) {
+			if (ifidx == -1 || ifidx == bss->ifindex) {
+				do_process_drv_event(bss, gnlh->cmd, tb);
+				return NL_SKIP;
+			}
+		}
 	}
 
 	return NL_SKIP;
@@ -3087,6 +3098,8 @@
 	drv->ctx = ctx;
 	bss = &drv->first_bss;
 	bss->drv = drv;
+	bss->ctx = ctx;
+
 	os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname));
 	drv->monitor_ifidx = -1;
 	drv->monitor_sock = -1;
@@ -3284,6 +3297,9 @@
 	/* WNM - BSS Transition Management Request */
 	if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x07", 2) < 0)
 		return -1;
+	/* WNM-Sleep Mode Response */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x11", 2) < 0)
+		return -1;
 
 	return 0;
 }
@@ -4989,19 +5005,35 @@
 				/* crude heuristic */
 				if (mode->channels[idx].freq < 4000)
 					mode->mode = HOSTAPD_MODE_IEEE80211B;
+				else if (mode->channels[idx].freq > 50000)
+					mode->mode = HOSTAPD_MODE_IEEE80211AD;
 				else
 					mode->mode = HOSTAPD_MODE_IEEE80211A;
 				mode_is_set = 1;
 			}
 
-			/* crude heuristic */
-			if (mode->channels[idx].freq < 4000)
+			switch (mode->mode) {
+			case HOSTAPD_MODE_IEEE80211AD:
+				mode->channels[idx].chan =
+					(mode->channels[idx].freq - 56160) /
+					2160;
+				break;
+			case HOSTAPD_MODE_IEEE80211A:
+				mode->channels[idx].chan =
+					mode->channels[idx].freq / 5 - 1000;
+				break;
+			case HOSTAPD_MODE_IEEE80211B:
+			case HOSTAPD_MODE_IEEE80211G:
 				if (mode->channels[idx].freq == 2484)
 					mode->channels[idx].chan = 14;
 				else
-					mode->channels[idx].chan = (mode->channels[idx].freq - 2407) / 5;
-			else
-				mode->channels[idx].chan = mode->channels[idx].freq/5 - 1000;
+					mode->channels[idx].chan =
+						(mode->channels[idx].freq -
+						 2407) / 5;
+				break;
+			default:
+				break;
+			}
 
 			if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
 				mode->channels[idx].flag |=
@@ -5665,16 +5697,16 @@
 
 
 static int wpa_driver_nl80211_set_freq(struct i802_bss *bss,
-				       int freq, int ht_enabled,
-				       int sec_channel_offset)
+				       struct hostapd_freq_params *freq)
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
 	int ret;
 
-	wpa_printf(MSG_DEBUG, "nl80211: Set freq %d (ht_enabled=%d "
-		   "sec_channel_offset=%d)",
-		   freq, ht_enabled, sec_channel_offset);
+	wpa_printf(MSG_DEBUG, "nl80211: Set freq %d (ht_enabled=%d, vht_enabled=%d,"
+		   " bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)",
+		   freq->freq, freq->ht_enabled, freq->vht_enabled,
+		   freq->bandwidth, freq->center_freq1, freq->center_freq2);
 	msg = nlmsg_alloc();
 	if (!msg)
 		return -1;
@@ -5682,9 +5714,38 @@
 	nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY);
 
 	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
-	if (ht_enabled) {
-		switch (sec_channel_offset) {
+	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq);
+	if (freq->vht_enabled) {
+		switch (freq->bandwidth) {
+		case 20:
+			NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
+				    NL80211_CHAN_WIDTH_20);
+			break;
+		case 40:
+			NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
+				    NL80211_CHAN_WIDTH_40);
+			break;
+		case 80:
+			if (freq->center_freq2)
+				NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
+					    NL80211_CHAN_WIDTH_80P80);
+			else
+				NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
+					    NL80211_CHAN_WIDTH_80);
+			break;
+		case 160:
+			NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
+				    NL80211_CHAN_WIDTH_160);
+			break;
+		default:
+			return -1;
+		}
+		NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, freq->center_freq1);
+		if (freq->center_freq2)
+			NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ2,
+				    freq->center_freq2);
+	} else if (freq->ht_enabled) {
+		switch (freq->sec_channel_offset) {
 		case -1:
 			NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
 				    NL80211_CHAN_HT40MINUS);
@@ -5703,11 +5764,11 @@
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	msg = NULL;
 	if (ret == 0) {
-		bss->freq = freq;
+		bss->freq = freq->freq;
 		return 0;
 	}
 	wpa_printf(MSG_DEBUG, "nl80211: Failed to set channel (freq=%d): "
-		   "%d (%s)", freq, ret, strerror(-ret));
+		   "%d (%s)", freq->freq, ret, strerror(-ret));
 nla_put_failure:
 	nlmsg_free(msg);
 	return -1;
@@ -5768,6 +5829,12 @@
 			params->ht_capabilities);
 	}
 
+	if (params->vht_capabilities) {
+		NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY,
+			sizeof(*params->vht_capabilities),
+			params->vht_capabilities);
+	}
+
 	os_memset(&upd, 0, sizeof(upd));
 	upd.mask = sta_flags_nl80211(params->flags);
 	upd.set = upd.mask;
@@ -6603,7 +6670,10 @@
 static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv,
 				 struct wpa_driver_associate_params *params)
 {
-	enum nl80211_iftype nlmode;
+	enum nl80211_iftype nlmode, old_mode;
+	struct hostapd_freq_params freq = {
+		.freq = params->freq,
+	};
 
 	if (params->p2p) {
 		wpa_printf(MSG_DEBUG, "nl80211: Setup AP operations for P2P "
@@ -6612,8 +6682,15 @@
 	} else
 		nlmode = NL80211_IFTYPE_AP;
 
-	if (wpa_driver_nl80211_set_mode(&drv->first_bss, nlmode) ||
-	    wpa_driver_nl80211_set_freq(&drv->first_bss, params->freq, 0, 0)) {
+	old_mode = drv->nlmode;
+	if (wpa_driver_nl80211_set_mode(&drv->first_bss, nlmode)) {
+		nl80211_remove_monitor_interface(drv);
+		return -1;
+	}
+
+	if (wpa_driver_nl80211_set_freq(&drv->first_bss, &freq)) {
+		if (old_mode != nlmode)
+			wpa_driver_nl80211_set_mode(&drv->first_bss, old_mode);
 		nl80211_remove_monitor_interface(drv);
 		return -1;
 	}
@@ -6899,6 +6976,11 @@
 		NLA_PUT_U32(msg, NL80211_ATTR_AKM_SUITES, mgmt);
 	}
 
+#ifdef CONFIG_IEEE80211W
+	if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED)
+		NLA_PUT_U32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED);
+#endif /* CONFIG_IEEE80211W */
+
 	if (params->disable_ht)
 		NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT);
 
@@ -7300,8 +7382,7 @@
 static int i802_set_freq(void *priv, struct hostapd_freq_params *freq)
 {
 	struct i802_bss *bss = priv;
-	return wpa_driver_nl80211_set_freq(bss, freq->freq, freq->ht_enabled,
-					   freq->sec_channel_offset);
+	return wpa_driver_nl80211_set_freq(bss, freq);
 }
 
 
@@ -8137,6 +8218,7 @@
 		new_bss->drv = drv;
 		new_bss->next = drv->first_bss.next;
 		new_bss->freq = drv->first_bss.freq;
+		new_bss->ctx = bss_ctx;
 		drv->first_bss.next = new_bss;
 		if (drv_priv)
 			*drv_priv = new_bss;
diff --git a/src/drivers/driver_test.c b/src/drivers/driver_test.c
index 89aee7d..bd65dd8 100644
--- a/src/drivers/driver_test.c
+++ b/src/drivers/driver_test.c
@@ -1321,7 +1321,8 @@
 		for (i = 0; i < drv->num_scanres; i++) {
 			struct wpa_scan_res *bss = drv->scanres[i];
 			if (p2p_scan_res_handler(drv->p2p, bss->bssid,
-						 bss->freq, bss->level,
+						 bss->freq, bss->age,
+						 bss->level,
 						 (const u8 *) (bss + 1),
 						 bss->ie_len) > 0)
 				return;
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index 1a9a819..e3e19f8 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -118,8 +118,9 @@
  *	to get a list of all present wiphys.
  * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or
  *	%NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME,
- *	%NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ,
- *	%NL80211_ATTR_WIPHY_CHANNEL_TYPE, %NL80211_ATTR_WIPHY_RETRY_SHORT,
+ *	%NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ (and the
+ *	attributes determining the channel width; this is used for setting
+ *	monitor mode channel),  %NL80211_ATTR_WIPHY_RETRY_SHORT,
  *	%NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
  *	and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD.
  *	However, for setting the channel, see %NL80211_CMD_SET_CHANNEL
@@ -171,7 +172,7 @@
  *	%NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY,
  *	%NL80211_ATTR_AUTH_TYPE and %NL80211_ATTR_INACTIVITY_TIMEOUT.
  *	The channel to use can be set on the interface or be given using the
- *	%NL80211_ATTR_WIPHY_FREQ and %NL80211_ATTR_WIPHY_CHANNEL_TYPE attrs.
+ *	%NL80211_ATTR_WIPHY_FREQ and the attributes determining channel width.
  * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP
  * @NL80211_CMD_STOP_AP: Stop AP operation on the given interface
  * @NL80211_CMD_DEL_BEACON: old alias for %NL80211_CMD_STOP_AP
@@ -401,8 +402,7 @@
  *	a response while being associated to an AP on another channel.
  *	%NL80211_ATTR_IFINDEX is used to specify which interface (and thus
  *	radio) is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the
- *	frequency for the operation and %NL80211_ATTR_WIPHY_CHANNEL_TYPE may be
- *	optionally used to specify additional channel parameters.
+ *	frequency for the operation.
  *	%NL80211_ATTR_DURATION is used to specify the duration in milliseconds
  *	to remain on the channel. This command is also used as an event to
  *	notify when the requested duration starts (it may take a while for the
@@ -440,12 +440,11 @@
  *	as an event indicating reception of a frame that was not processed in
  *	kernel code, but is for us (i.e., which may need to be processed in a
  *	user space application). %NL80211_ATTR_FRAME is used to specify the
- *	frame contents (including header). %NL80211_ATTR_WIPHY_FREQ (and
- *	optionally %NL80211_ATTR_WIPHY_CHANNEL_TYPE) is used to indicate on
- *	which channel the frame is to be transmitted or was received. If this
- *	channel is not the current channel (remain-on-channel or the
- *	operational channel) the device will switch to the given channel and
- *	transmit the frame, optionally waiting for a response for the time
+ *	frame contents (including header). %NL80211_ATTR_WIPHY_FREQ is used
+ *	to indicate on which channel the frame is to be transmitted or was
+ *	received. If this channel is not the current channel (remain-on-channel
+ *	or the operational channel) the device will switch to the given channel
+ *	and transmit the frame, optionally waiting for a response for the time
  *	specified using %NL80211_ATTR_DURATION. When called, this operation
  *	returns a cookie (%NL80211_ATTR_COOKIE) that will be included with the
  *	TX status event pertaining to the TX request.
@@ -473,8 +472,8 @@
  *	command is used as an event to indicate the that a trigger level was
  *	reached.
  * @NL80211_CMD_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ
- *	and %NL80211_ATTR_WIPHY_CHANNEL_TYPE) the given interface (identifed
- *	by %NL80211_ATTR_IFINDEX) shall operate on.
+ *	and the attributes determining channel width) the given interface
+ *	(identifed by %NL80211_ATTR_IFINDEX) shall operate on.
  *	In case multiple channels are supported by the device, the mechanism
  *	with which it switches channels is implementation-defined.
  *	When a monitor interface is given, it can only switch channel while
@@ -568,8 +567,8 @@
  *
  * @NL80211_CMD_CH_SWITCH_NOTIFY: An AP or GO may decide to switch channels
  *	independently of the userspace SME, send this event indicating
- *	%NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ with
- *	%NL80211_ATTR_WIPHY_CHANNEL_TYPE.
+ *	%NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ and the
+ *	attributes determining channel width.
  *
  * @NL80211_CMD_START_P2P_DEVICE: Start the given P2P Device, identified by
  *	its %NL80211_ATTR_WDEV identifier. It must have been created with
@@ -773,14 +772,26 @@
  *	/sys/class/ieee80211/<phyname>/index
  * @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming)
  * @NL80211_ATTR_WIPHY_TXQ_PARAMS: a nested array of TX queue parameters
- * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz
+ * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz,
+ *	defines the channel together with the (deprecated)
+ *	%NL80211_ATTR_WIPHY_CHANNEL_TYPE attribute or the attributes
+ *	%NL80211_ATTR_CHANNEL_WIDTH and if needed %NL80211_ATTR_CENTER_FREQ1
+ *	and %NL80211_ATTR_CENTER_FREQ2
+ * @NL80211_ATTR_CHANNEL_WIDTH: u32 attribute containing one of the values
+ *	of &enum nl80211_chan_width, describing the channel width. See the
+ *	documentation of the enum for more information.
+ * @NL80211_ATTR_CENTER_FREQ1: Center frequency of the first part of the
+ *	channel, used for anything but 20 MHz bandwidth
+ * @NL80211_ATTR_CENTER_FREQ2: Center frequency of the second part of the
+ *	channel, used only for 80+80 MHz bandwidth
  * @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ
- *	if HT20 or HT40 are allowed (i.e., 802.11n disabled if not included):
+ *	if HT20 or HT40 are to be used (i.e., HT disabled if not included):
  *	NL80211_CHAN_NO_HT = HT not allowed (i.e., same as not including
  *		this attribute)
  *	NL80211_CHAN_HT20 = HT20 only
  *	NL80211_CHAN_HT40MINUS = secondary channel is below the primary channel
  *	NL80211_CHAN_HT40PLUS = secondary channel is above the primary channel
+ *	This attribute is now deprecated.
  * @NL80211_ATTR_WIPHY_RETRY_SHORT: TX retry limit for frames whose length is
  *	less than or equal to the RTS threshold; allowed range: 1..255;
  *	dot11ShortRetryLimit; u8
@@ -1292,6 +1303,13 @@
  *
  * @NL80211_ATTR_SCAN_FLAGS: scan request control flags (u32)
  *
+ * @NL80211_ATTR_P2P_CTWINDOW: P2P GO Client Traffic Window (u8), used with
+ *	the START_AP and SET_BSS commands
+ * @NL80211_ATTR_P2P_OPPPS: P2P GO opportunistic PS (u8), used with the
+ *	START_AP and SET_BSS commands. This can have the values 0 or 1;
+ *	if not given in START_AP 0 is assumed, if not given in SET_BSS
+ *	no change is made.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1555,6 +1573,13 @@
 
 	NL80211_ATTR_SCAN_FLAGS,
 
+	NL80211_ATTR_CHANNEL_WIDTH,
+	NL80211_ATTR_CENTER_FREQ1,
+	NL80211_ATTR_CENTER_FREQ2,
+
+	NL80211_ATTR_P2P_CTWINDOW,
+	NL80211_ATTR_P2P_OPPPS,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -1719,10 +1744,15 @@
  * @__NL80211_RATE_INFO_INVALID: attribute number 0 is reserved
  * @NL80211_RATE_INFO_BITRATE: total bitrate (u16, 100kbit/s)
  * @NL80211_RATE_INFO_MCS: mcs index for 802.11n (u8)
- * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 Mhz dualchannel bitrate
+ * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 MHz dualchannel bitrate
  * @NL80211_RATE_INFO_SHORT_GI: 400ns guard interval
  * @NL80211_RATE_INFO_BITRATE32: total bitrate (u32, 100kbit/s)
  * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined
+ * @NL80211_RATE_INFO_VHT_MCS: MCS index for VHT (u8)
+ * @NL80211_RATE_INFO_VHT_NSS: number of streams in VHT (u8)
+ * @NL80211_RATE_INFO_80_MHZ_WIDTH: 80 MHz VHT rate
+ * @NL80211_RATE_INFO_80P80_MHZ_WIDTH: 80+80 MHz VHT rate
+ * @NL80211_RATE_INFO_160_MHZ_WIDTH: 160 MHz VHT rate
  * @__NL80211_RATE_INFO_AFTER_LAST: internal use
  */
 enum nl80211_rate_info {
@@ -1732,6 +1762,11 @@
 	NL80211_RATE_INFO_40_MHZ_WIDTH,
 	NL80211_RATE_INFO_SHORT_GI,
 	NL80211_RATE_INFO_BITRATE32,
+	NL80211_RATE_INFO_VHT_MCS,
+	NL80211_RATE_INFO_VHT_NSS,
+	NL80211_RATE_INFO_80_MHZ_WIDTH,
+	NL80211_RATE_INFO_80P80_MHZ_WIDTH,
+	NL80211_RATE_INFO_160_MHZ_WIDTH,
 
 	/* keep last */
 	__NL80211_RATE_INFO_AFTER_LAST,
@@ -2440,6 +2475,15 @@
 #define NL80211_TXQ_Q_BE	NL80211_AC_BE
 #define NL80211_TXQ_Q_BK	NL80211_AC_BK
 
+/**
+ * enum nl80211_channel_type - channel type
+ * @NL80211_CHAN_NO_HT: 20 MHz, non-HT channel
+ * @NL80211_CHAN_HT20: 20 MHz HT channel
+ * @NL80211_CHAN_HT40MINUS: HT40 channel, secondary channel
+ *	below the control channel
+ * @NL80211_CHAN_HT40PLUS: HT40 channel, secondary channel
+ *	above the control channel
+ */
 enum nl80211_channel_type {
 	NL80211_CHAN_NO_HT,
 	NL80211_CHAN_HT20,
@@ -2448,6 +2492,32 @@
 };
 
 /**
+ * enum nl80211_chan_width - channel width definitions
+ *
+ * These values are used with the %NL80211_ATTR_CHANNEL_WIDTH
+ * attribute.
+ *
+ * @NL80211_CHAN_WIDTH_20_NOHT: 20 MHz, non-HT channel
+ * @NL80211_CHAN_WIDTH_20: 20 MHz HT channel
+ * @NL80211_CHAN_WIDTH_40: 40 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
+ *	attribute must be provided as well
+ * @NL80211_CHAN_WIDTH_80: 80 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
+ *	attribute must be provided as well
+ * @NL80211_CHAN_WIDTH_80P80: 80+80 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
+ *	and %NL80211_ATTR_CENTER_FREQ2 attributes must be provided as well
+ * @NL80211_CHAN_WIDTH_160: 160 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
+ *	attribute must be provided as well
+ */
+enum nl80211_chan_width {
+	NL80211_CHAN_WIDTH_20_NOHT,
+	NL80211_CHAN_WIDTH_20,
+	NL80211_CHAN_WIDTH_40,
+	NL80211_CHAN_WIDTH_80,
+	NL80211_CHAN_WIDTH_80P80,
+	NL80211_CHAN_WIDTH_160,
+};
+
+/**
  * enum nl80211_bss - netlink attributes for a BSS
  *
  * @__NL80211_BSS_INVALID: invalid
@@ -3066,6 +3136,10 @@
  * @NL80211_FEATURE_NEED_OBSS_SCAN: The driver expects userspace to perform
  *	OBSS scans and generate 20/40 BSS coex reports. This flag is used only
  *	for drivers implementing the CONNECT API, for AUTH/ASSOC it is implied.
+ * @NL80211_FEATURE_P2P_GO_CTWIN: P2P GO implementation supports CT Window
+ *	setting
+ * @NL80211_FEATURE_P2P_GO_OPPPS: P2P GO implementation supports opportunistic
+ *	powersave
  */
 enum nl80211_feature_flags {
 	NL80211_FEATURE_SK_TX_STATUS			= 1 << 0,
@@ -3079,6 +3153,8 @@
 	NL80211_FEATURE_AP_SCAN				= 1 << 8,
 	NL80211_FEATURE_VIF_TXPOWER			= 1 << 9,
 	NL80211_FEATURE_NEED_OBSS_SCAN			= 1 << 10,
+	NL80211_FEATURE_P2P_GO_CTWIN			= 1 << 11,
+	NL80211_FEATURE_P2P_GO_OPPPS			= 1 << 12,
 };
 
 /**
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index a4c9b25..85c242a 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -891,6 +891,7 @@
 
 	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED
 		"EAP authentication started");
+	eap_notify_status(sm, "started", "");
 
 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, req,
 			       &msg_len);
diff --git a/src/eap_peer/eap_fast_pac.c b/src/eap_peer/eap_fast_pac.c
index fc987da..8c480b9 100644
--- a/src/eap_peer/eap_fast_pac.c
+++ b/src/eap_peer/eap_fast_pac.c
@@ -422,8 +422,12 @@
 	if (eap_fast_init_pac_data(sm, pac_file, &rc) < 0)
 		return 0;
 
-	if (eap_fast_read_line(&rc, &pos) < 0 ||
-	    os_strcmp(pac_file_hdr, rc.buf) != 0)
+	if (eap_fast_read_line(&rc, &pos) < 0) {
+		/* empty file - assume it is fine to overwrite */
+		eap_fast_deinit_pac_data(&rc);
+		return 0;
+	}
+	if (os_strcmp(pac_file_hdr, rc.buf) != 0)
 		err = "Unrecognized header line";
 
 	while (!err && eap_fast_read_line(&rc, &pos) == 0) {
diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c
index 177b58d..469b9a0 100644
--- a/src/eap_server/eap_server_aka.c
+++ b/src/eap_server/eap_server_aka.c
@@ -731,6 +731,17 @@
 		return;
 	}
 
+	if (((data->eap_method == EAP_TYPE_AKA_PRIME &&
+	      username[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX) ||
+	     (data->eap_method == EAP_TYPE_AKA &&
+	      username[0] == EAP_AKA_REAUTH_ID_PREFIX)) &&
+	    data->identity_round == 1) {
+		/* Remain in IDENTITY state for another round to request full
+		 * auth identity since we did not recognize reauth id */
+		os_free(username);
+		return;
+	}
+
 	if ((data->eap_method == EAP_TYPE_AKA_PRIME &&
 	     username[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) ||
 	    (data->eap_method == EAP_TYPE_AKA &&
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index b122f7c..aaacc9a 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -630,6 +630,7 @@
  *	P2P Device Address or P2P Interface Address)
  * @level: Signal level (signal strength of the received frame from the peer)
  * @freq: Frequency on which the Beacon or Probe Response frame was received
+ * @age_ms: Age of the information in milliseconds
  * @ies: IEs from the Beacon or Probe Response frame
  * @ies_len: Length of ies buffer in octets
  * @scan_res: Whether this was based on scan results
@@ -640,13 +641,15 @@
  * like Provision Discovery Request that contains P2P Capability and P2P Device
  * Info attributes.
  */
-int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, int level,
-		   const u8 *ies, size_t ies_len, int scan_res)
+int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
+		   unsigned int age_ms, int level, const u8 *ies,
+		   size_t ies_len, int scan_res)
 {
 	struct p2p_device *dev;
 	struct p2p_message msg;
 	const u8 *p2p_dev_addr;
 	int i;
+	struct os_time time_now, time_tmp_age, entry_ts;
 
 	os_memset(&msg, 0, sizeof(msg));
 	if (p2p_parse_ies(ies, ies_len, &msg)) {
@@ -673,6 +676,7 @@
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Do not add peer "
 			"filter for " MACSTR " due to peer filter",
 			MAC2STR(p2p_dev_addr));
+		p2p_parse_free(&msg);
 		return 0;
 	}
 
@@ -681,7 +685,24 @@
 		p2p_parse_free(&msg);
 		return -1;
 	}
-	os_get_time(&dev->last_seen);
+
+	os_get_time(&time_now);
+	time_tmp_age.sec = age_ms / 1000;
+	time_tmp_age.usec = (age_ms % 1000) * 1000;
+	os_time_sub(&time_now, &time_tmp_age, &entry_ts);
+
+	/*
+	 * Update the device entry only if the new peer
+	 * entry is newer than the one previously stored.
+	 */
+	if (dev->last_seen.usec > 0 &&
+	    os_time_before(&entry_ts, &dev->last_seen)) {
+		p2p_parse_free(&msg);
+		return -1;
+	}
+
+	os_memcpy(&dev->last_seen, &entry_ts, sizeof(struct os_time));
+
 	dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY);
 
 	if (os_memcmp(addr, p2p_dev_addr, ETH_ALEN) != 0)
@@ -1123,7 +1144,9 @@
 	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Stopping find");
 	eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
 	p2p_clear_timeout(p2p);
-	if (p2p->state == P2P_SEARCH)
+	if (p2p->state == P2P_SEARCH ||
+	    p2p->state == P2P_CONTINUE_SEARCH_WHEN_READY ||
+	    p2p->state == P2P_SEARCH_WHEN_READY)
 		wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, P2P_EVENT_FIND_STOPPED);
 	p2p_set_state(p2p, P2P_IDLE);
 	p2p_free_req_dev_types(p2p);
@@ -1166,89 +1189,115 @@
 }
 
 
-static int p2p_prepare_channel(struct p2p_data *p2p, unsigned int force_freq,
-			       unsigned int pref_freq)
+static int p2p_prepare_channel_pref(struct p2p_data *p2p,
+				    unsigned int force_freq,
+				    unsigned int pref_freq)
 {
-	if (force_freq || pref_freq) {
-		u8 op_reg_class, op_channel;
-		unsigned int freq = force_freq ? force_freq : pref_freq;
-		if (p2p_freq_to_channel(p2p->cfg->country, freq,
-					&op_reg_class, &op_channel) < 0) {
-			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
-				"P2P: Unsupported frequency %u MHz",
-				freq);
-			return -1;
-		}
-		if (!p2p_channels_includes(&p2p->cfg->channels, op_reg_class,
-					   op_channel)) {
-			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
-				"P2P: Frequency %u MHz (oper_class %u "
-				"channel %u) not allowed for P2P",
-				freq, op_reg_class, op_channel);
-			return -1;
-		}
-		p2p->op_reg_class = op_reg_class;
-		p2p->op_channel = op_channel;
-		if (force_freq) {
-			p2p->channels.reg_classes = 1;
-			p2p->channels.reg_class[0].channels = 1;
-			p2p->channels.reg_class[0].reg_class =
-				p2p->op_reg_class;
-			p2p->channels.reg_class[0].channel[0] = p2p->op_channel;
-		} else {
-			os_memcpy(&p2p->channels, &p2p->cfg->channels,
-				  sizeof(struct p2p_channels));
-		}
+	u8 op_class, op_channel;
+	unsigned int freq = force_freq ? force_freq : pref_freq;
+
+	if (p2p_freq_to_channel(p2p->cfg->country, freq,
+				&op_class, &op_channel) < 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unsupported frequency %u MHz", freq);
+		return -1;
+	}
+
+	if (!p2p_channels_includes(&p2p->cfg->channels, op_class, op_channel)) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Frequency %u MHz (oper_class %u channel %u) not "
+			"allowed for P2P", freq, op_class, op_channel);
+		return -1;
+	}
+
+	p2p->op_reg_class = op_class;
+	p2p->op_channel = op_channel;
+
+	if (force_freq) {
+		p2p->channels.reg_classes = 1;
+		p2p->channels.reg_class[0].channels = 1;
+		p2p->channels.reg_class[0].reg_class = p2p->op_reg_class;
+		p2p->channels.reg_class[0].channel[0] = p2p->op_channel;
 	} else {
-		u8 op_reg_class, op_channel;
-
-		if (!p2p->cfg->cfg_op_channel && p2p->best_freq_overall > 0 &&
-		    p2p_supported_freq(p2p, p2p->best_freq_overall) &&
-		    p2p_freq_to_channel(p2p->cfg->country,
-					p2p->best_freq_overall,
-					&op_reg_class, &op_channel) == 0) {
-			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
-				"P2P: Select best overall channel as "
-				"operating channel preference");
-			p2p->op_reg_class = op_reg_class;
-			p2p->op_channel = op_channel;
-		} else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_5 > 0 &&
-			   p2p_supported_freq(p2p, p2p->best_freq_5) &&
-			   p2p_freq_to_channel(p2p->cfg->country,
-					       p2p->best_freq_5,
-					       &op_reg_class, &op_channel) ==
-			   0) {
-			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
-				"P2P: Select best 5 GHz channel as "
-				"operating channel preference");
-			p2p->op_reg_class = op_reg_class;
-			p2p->op_channel = op_channel;
-		} else if (!p2p->cfg->cfg_op_channel &&
-			   p2p->best_freq_24 > 0 &&
-			   p2p_supported_freq(p2p, p2p->best_freq_24) &&
-			   p2p_freq_to_channel(p2p->cfg->country,
-					       p2p->best_freq_24,
-					       &op_reg_class, &op_channel) ==
-			   0) {
-			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
-				"P2P: Select best 2.4 GHz channel as "
-				"operating channel preference");
-			p2p->op_reg_class = op_reg_class;
-			p2p->op_channel = op_channel;
-		} else {
-			p2p->op_reg_class = p2p->cfg->op_reg_class;
-			p2p->op_channel = p2p->cfg->op_channel;
-		}
-
 		os_memcpy(&p2p->channels, &p2p->cfg->channels,
 			  sizeof(struct p2p_channels));
 	}
+
+	return 0;
+}
+
+
+static void p2p_prepare_channel_best(struct p2p_data *p2p)
+{
+	u8 op_class, op_channel;
+
+	if (!p2p->cfg->cfg_op_channel && p2p->best_freq_overall > 0 &&
+	    p2p_supported_freq(p2p, p2p->best_freq_overall) &&
+	    p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_overall,
+				&op_class, &op_channel) == 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Select best "
+			"overall channel as operating channel preference");
+		p2p->op_reg_class = op_class;
+		p2p->op_channel = op_channel;
+	} else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_5 > 0 &&
+		   p2p_supported_freq(p2p, p2p->best_freq_5) &&
+		   p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_5,
+				       &op_class, &op_channel) == 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Select best 5 GHz "
+			"channel as operating channel preference");
+		p2p->op_reg_class = op_class;
+		p2p->op_channel = op_channel;
+	} else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_24 > 0 &&
+		   p2p_supported_freq(p2p, p2p->best_freq_24) &&
+		   p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_24,
+				       &op_class, &op_channel) == 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Select best 2.4 "
+			"GHz channel as operating channel preference");
+		p2p->op_reg_class = op_class;
+		p2p->op_channel = op_channel;
+	} else {
+		p2p->op_reg_class = p2p->cfg->op_reg_class;
+		p2p->op_channel = p2p->cfg->op_channel;
+	}
+
+	os_memcpy(&p2p->channels, &p2p->cfg->channels,
+		  sizeof(struct p2p_channels));
+}
+
+
+/**
+ * p2p_prepare_channel - Select operating channel for GO Negotiation
+ * @p2p: P2P module context from p2p_init()
+ * @dev: Selected peer device
+ * @force_freq: Forced frequency in MHz or 0 if not forced
+ * @pref_freq: Preferred frequency in MHz or 0 if no preference
+ * Returns: 0 on success, -1 on failure (channel not supported for P2P)
+ *
+ * This function is used to do initial operating channel selection for GO
+ * Negotiation prior to having received peer information. The selected channel
+ * may be further optimized in p2p_reselect_channel() once the peer information
+ * is available.
+ */
+static int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev,
+			       unsigned int force_freq, unsigned int pref_freq)
+{
+	if (force_freq || pref_freq) {
+		if (p2p_prepare_channel_pref(p2p, force_freq, pref_freq) < 0)
+			return -1;
+	} else {
+		p2p_prepare_channel_best(p2p);
+	}
 	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 		"P2P: Own preference for operation channel: "
 		"Operating Class %u Channel %u%s",
 		p2p->op_reg_class, p2p->op_channel,
 		force_freq ? " (forced)" : "");
 
+	if (force_freq)
+		dev->flags |= P2P_DEV_FORCE_FREQ;
+	else
+		dev->flags &= ~P2P_DEV_FORCE_FREQ;
+
 	return 0;
 }
 
@@ -1289,9 +1338,6 @@
 		MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
 		wps_method, persistent_group, pd_before_go_neg);
 
-	if (p2p_prepare_channel(p2p, force_freq, pref_freq) < 0)
-		return -1;
-
 	dev = p2p_get_device(p2p, peer_addr);
 	if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
@@ -1300,6 +1346,9 @@
 		return -1;
 	}
 
+	if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq) < 0)
+		return -1;
+
 	if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
 		if (!(dev->info.dev_capab &
 		      P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
@@ -1339,8 +1388,16 @@
 	dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
 	if (pd_before_go_neg)
 		dev->flags |= P2P_DEV_PD_BEFORE_GO_NEG;
-	else
+	else {
 		dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG;
+		/*
+		 * Assign dialog token here to use the same value in each
+		 * retry within the same GO Negotiation exchange.
+		 */
+		dev->dialog_token++;
+		if (dev->dialog_token == 0)
+			dev->dialog_token = 1;
+	}
 	dev->connect_reqs = 0;
 	dev->go_neg_req_sent = 0;
 	dev->go_state = UNKNOWN_GO;
@@ -1367,11 +1424,6 @@
 	dev->wps_method = wps_method;
 	dev->status = P2P_SC_SUCCESS;
 
-	if (force_freq)
-		dev->flags |= P2P_DEV_FORCE_FREQ;
-	else
-		dev->flags &= ~P2P_DEV_FORCE_FREQ;
-
 	if (p2p->p2p_scan_running) {
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 			"P2P: p2p_scan running - delay connect send");
@@ -1401,9 +1453,6 @@
 		MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
 		wps_method, persistent_group);
 
-	if (p2p_prepare_channel(p2p, force_freq, pref_freq) < 0)
-		return -1;
-
 	dev = p2p_get_device(p2p, peer_addr);
 	if (dev == NULL) {
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
@@ -1412,6 +1461,9 @@
 		return -1;
 	}
 
+	if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq) < 0)
+		return -1;
+
 	p2p->ssid_set = 0;
 	if (force_ssid) {
 		wpa_hexdump_ascii(MSG_DEBUG, "P2P: Forced SSID",
@@ -1432,11 +1484,6 @@
 	dev->wps_method = wps_method;
 	dev->status = P2P_SC_SUCCESS;
 
-	if (force_freq)
-		dev->flags |= P2P_DEV_FORCE_FREQ;
-	else
-		dev->flags &= ~P2P_DEV_FORCE_FREQ;
-
 	return 0;
 }
 
@@ -2840,9 +2887,10 @@
 
 
 int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
-			 int level, const u8 *ies, size_t ies_len)
+			 unsigned int age, int level, const u8 *ies,
+			 size_t ies_len)
 {
-	p2p_add_device(p2p, bssid, freq, level, ies, ies_len, 1);
+	p2p_add_device(p2p, bssid, freq, age, level, ies, ies_len, 1);
 
 	return 0;
 }
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index 045e6f7..18e733b 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -1200,6 +1200,7 @@
  * @p2p: P2P module context from p2p_init()
  * @bssid: BSSID of the scan result
  * @freq: Frequency of the channel on which the device was found in MHz
+ * @age: Age of the scan result in milliseconds
  * @level: Signal level (signal strength of the received Beacon/Probe Response
  *	frame)
  * @ies: Pointer to IEs from the scan result
@@ -1221,7 +1222,8 @@
  * start of a pending operation, e.g., to start a pending GO negotiation.
  */
 int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
-			 int level, const u8 *ies, size_t ies_len);
+			 unsigned int age, int level, const u8 *ies,
+			 size_t ies_len);
 
 /**
  * p2p_scan_res_handled - Indicate end of scan results
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index fe4b560..37d43bb 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -145,9 +145,6 @@
 	if (buf == NULL)
 		return NULL;
 
-	peer->dialog_token++;
-	if (peer->dialog_token == 0)
-		peer->dialog_token = 1;
 	p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_REQ, peer->dialog_token);
 
 	len = p2p_buf_add_ie_hdr(buf);
@@ -336,6 +333,17 @@
 }
 
 
+/**
+ * p2p_reselect_channel - Re-select operating channel based on peer information
+ * @p2p: P2P module context from p2p_init()
+ * @intersection: Support channel list intersection from local and peer
+ *
+ * This function is used to re-select the best channel after having received
+ * information from the peer to allow supported channel lists to be intersected.
+ * This can be used to improve initial channel selection done in
+ * p2p_prepare_channel() prior to the start of GO Negotiation. In addition, this
+ * can be used for Invitation case.
+ */
 void p2p_reselect_channel(struct p2p_data *p2p,
 			  struct p2p_channels *intersection)
 {
@@ -390,6 +398,35 @@
 		}
 	}
 
+	/* Try a channel where we might be able to use HT40 */
+	for (i = 0; i < intersection->reg_classes; i++) {
+		struct p2p_reg_class *c = &intersection->reg_class[i];
+		if (c->reg_class == 116 || c->reg_class == 117 ||
+		    c->reg_class == 126 || c->reg_class == 127) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Pick possible HT40 channel (reg_class "
+				"%u channel %u) from intersection",
+				c->reg_class, c->channel[0]);
+			p2p->op_reg_class = c->reg_class;
+			p2p->op_channel = c->channel[0];
+			return;
+		}
+	}
+
+	/*
+	 * Try to see if the original channel is in the intersection. If
+	 * so, no need to change anything, as it already contains some
+	 * randomness.
+	 */
+	if (p2p_channels_includes(intersection, p2p->op_reg_class,
+				  p2p->op_channel)) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Using original operating class and channel "
+			"(op_class %u channel %u) from intersection",
+			p2p->op_reg_class, p2p->op_channel);
+		return;
+	}
+
 	/*
 	 * Fall back to whatever is included in the channel intersection since
 	 * no better options seems to be available.
@@ -403,6 +440,60 @@
 }
 
 
+static int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
+				 u8 *status)
+{
+	struct p2p_channels intersection;
+	size_t i;
+
+	p2p_channels_intersect(&p2p->channels, &dev->channels, &intersection);
+	if (intersection.reg_classes == 0 ||
+	    intersection.reg_class[0].channels == 0) {
+		*status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: No common channels found");
+		return -1;
+	}
+
+	for (i = 0; i < intersection.reg_classes; i++) {
+		struct p2p_reg_class *c;
+		c = &intersection.reg_class[i];
+		wpa_printf(MSG_DEBUG, "P2P: reg_class %u", c->reg_class);
+		wpa_hexdump(MSG_DEBUG, "P2P: channels",
+			    c->channel, c->channels);
+	}
+
+	if (!p2p_channels_includes(&intersection, p2p->op_reg_class,
+				   p2p->op_channel)) {
+		if (dev->flags & P2P_DEV_FORCE_FREQ) {
+			*status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer does "
+				"not support the forced channel");
+			return -1;
+		}
+
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Selected operating "
+			"channel (op_class %u channel %u) not acceptable to "
+			"the peer", p2p->op_reg_class, p2p->op_channel);
+		p2p_reselect_channel(p2p, &intersection);
+	} else if (!(dev->flags & P2P_DEV_FORCE_FREQ) &&
+		   !p2p->cfg->cfg_op_channel) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Try to optimize "
+			"channel selection with peer information received; "
+			"previously selected op_class %u channel %u",
+			p2p->op_reg_class, p2p->op_channel);
+		p2p_reselect_channel(p2p, &intersection);
+	}
+
+	if (!p2p->ssid_set) {
+		p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
+		p2p->ssid_set = 1;
+	}
+
+	return 0;
+}
+
+
 void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
 			    const u8 *data, size_t len, int rx_freq)
 {
@@ -619,36 +710,8 @@
 			goto fail;
 		}
 
-		if (go) {
-			struct p2p_channels intersection;
-			size_t i;
-			p2p_channels_intersect(&p2p->channels, &dev->channels,
-					       &intersection);
-			if (intersection.reg_classes == 0 ||
-			    intersection.reg_class[0].channels == 0) {
-				status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
-				wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
-					"P2P: No common channels found");
-				goto fail;
-			}
-			for (i = 0; i < intersection.reg_classes; i++) {
-				struct p2p_reg_class *c;
-				c = &intersection.reg_class[i];
-				wpa_printf(MSG_DEBUG, "P2P: reg_class %u",
-					   c->reg_class);
-				wpa_hexdump(MSG_DEBUG, "P2P: channels",
-					    c->channel, c->channels);
-			}
-			if (!p2p_channels_includes(&intersection,
-						   p2p->op_reg_class,
-						   p2p->op_channel))
-				p2p_reselect_channel(p2p, &intersection);
-
-			if (!p2p->ssid_set) {
-				p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
-				p2p->ssid_set = 1;
-			}
-		}
+		if (go && p2p_go_select_channel(p2p, dev, &status) < 0)
+			goto fail;
 
 		dev->go_state = go ? LOCAL_GO : REMOTE_GO;
 		dev->oper_freq = p2p_channel_to_freq((const char *)
@@ -1021,35 +1084,8 @@
 		goto fail;
 	}
 
-	if (go) {
-		struct p2p_channels intersection;
-		size_t i;
-		p2p_channels_intersect(&p2p->channels, &dev->channels,
-				       &intersection);
-		if (intersection.reg_classes == 0 ||
-		    intersection.reg_class[0].channels == 0) {
-			status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
-			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
-				"P2P: No common channels found");
-			goto fail;
-		}
-		for (i = 0; i < intersection.reg_classes; i++) {
-			struct p2p_reg_class *c;
-			c = &intersection.reg_class[i];
-			wpa_printf(MSG_DEBUG, "P2P: reg_class %u",
-				   c->reg_class);
-			wpa_hexdump(MSG_DEBUG, "P2P: channels",
-				    c->channel, c->channels);
-		}
-		if (!p2p_channels_includes(&intersection, p2p->op_reg_class,
-					   p2p->op_channel))
-			p2p_reselect_channel(p2p, &intersection);
-
-		if (!p2p->ssid_set) {
-			p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
-			p2p->ssid_set = 1;
-		}
-	}
+	if (go && p2p_go_select_channel(p2p, dev, &status) < 0)
+		goto fail;
 
 	p2p_set_state(p2p, P2P_GO_NEG);
 	p2p_clear_timeout(p2p);
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
index 8687320..633dd5c 100644
--- a/src/p2p/p2p_group.c
+++ b/src/p2p/p2p_group.c
@@ -169,6 +169,39 @@
 }
 
 
+static struct wpabuf * p2p_group_encaps_probe_resp(struct wpabuf *subelems)
+{
+	struct wpabuf *ie;
+	const u8 *pos, *end;
+	size_t len;
+
+	if (subelems == NULL)
+		return NULL;
+
+	len = wpabuf_len(subelems) + 100;
+
+	ie = wpabuf_alloc(len);
+	if (ie == NULL)
+		return NULL;
+
+	pos = wpabuf_head(subelems);
+	end = pos + wpabuf_len(subelems);
+
+	while (end > pos) {
+		size_t frag_len = end - pos;
+		if (frag_len > 251)
+			frag_len = 251;
+		wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+		wpabuf_put_u8(ie, 4 + frag_len);
+		wpabuf_put_be32(ie, P2P_IE_VENDOR_TYPE);
+		wpabuf_put_data(ie, pos, frag_len);
+		pos += frag_len;
+	}
+
+	return ie;
+}
+
+
 static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group)
 {
 	struct wpabuf *ie;
@@ -367,9 +400,8 @@
 static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group)
 {
 	u8 *group_info;
-	struct wpabuf *ie;
+	struct wpabuf *p2p_subelems, *ie;
 	struct p2p_group_member *m;
-	u8 *len;
 	size_t extra = 0;
 
 #ifdef CONFIG_WIFI_DISPLAY
@@ -377,33 +409,32 @@
 		extra += wpabuf_len(group->wfd_ie);
 #endif /* CONFIG_WIFI_DISPLAY */
 
-	ie = wpabuf_alloc(257 + extra);
-	if (ie == NULL)
+	p2p_subelems = wpabuf_alloc(500 + extra);
+	if (p2p_subelems == NULL)
 		return NULL;
 
 #ifdef CONFIG_WIFI_DISPLAY
 	if (group->wfd_ie)
-		wpabuf_put_buf(ie, group->wfd_ie);
+		wpabuf_put_buf(p2p_subelems, group->wfd_ie);
 #endif /* CONFIG_WIFI_DISPLAY */
 
-	len = p2p_buf_add_ie_hdr(ie);
-
-	p2p_group_add_common_ies(group, ie);
-	p2p_group_add_noa(ie, group->noa);
+	p2p_group_add_common_ies(group, p2p_subelems);
+	p2p_group_add_noa(p2p_subelems, group->noa);
 
 	/* P2P Device Info */
-	p2p_buf_add_device_info(ie, group->p2p, NULL);
+	p2p_buf_add_device_info(p2p_subelems, group->p2p, NULL);
 
 	/* P2P Group Info */
-	group_info = wpabuf_put(ie, 0);
-	wpabuf_put_u8(ie, P2P_ATTR_GROUP_INFO);
-	wpabuf_put_le16(ie, 0); /* Length to be filled */
+	group_info = wpabuf_put(p2p_subelems, 0);
+	wpabuf_put_u8(p2p_subelems, P2P_ATTR_GROUP_INFO);
+	wpabuf_put_le16(p2p_subelems, 0); /* Length to be filled */
 	for (m = group->members; m; m = m->next)
-		p2p_client_info(ie, m);
+		p2p_client_info(p2p_subelems, m);
 	WPA_PUT_LE16(group_info + 1,
-		     (u8 *) wpabuf_put(ie, 0) - group_info - 3);
+		     (u8 *) wpabuf_put(p2p_subelems, 0) - group_info - 3);
 
-	p2p_buf_update_ie_hdr(ie, len);
+	ie = p2p_group_encaps_probe_resp(p2p_subelems);
+	wpabuf_free(p2p_subelems);
 
 	return ie;
 }
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index d2868fb..712544b 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -705,8 +705,9 @@
 						struct p2p_message *msg);
 void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr,
 		      struct p2p_device *dev, struct p2p_message *msg);
-int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, int level,
-		   const u8 *ies, size_t ies_len, int scan_res);
+int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
+		   unsigned int age_ms, int level, const u8 *ies,
+		   size_t ies_len, int scan_res);
 struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr);
 struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p,
 					     const u8 *addr);
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
index 7bf6600..ac67932 100644
--- a/src/p2p/p2p_invitation.c
+++ b/src/p2p/p2p_invitation.c
@@ -176,8 +176,8 @@
 			"P2P: Invitation Request from unknown peer "
 			MACSTR, MAC2STR(sa));
 
-		if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1, 0))
-		{
+		if (p2p_add_device(p2p, sa, rx_freq, 0, 0, data + 1, len - 1,
+				   0)) {
 			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 				"P2P: Invitation Request add device failed "
 				MACSTR, MAC2STR(sa));
diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c
index e40f2b7..ca33f17 100644
--- a/src/p2p/p2p_pd.c
+++ b/src/p2p/p2p_pd.c
@@ -151,8 +151,9 @@
 		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 			"P2P: Provision Discovery Request from "
 			"unknown peer " MACSTR, MAC2STR(sa));
-		if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1, 0))
-		{
+
+		if (p2p_add_device(p2p, sa, rx_freq, 0, 0, data + 1, len - 1,
+				   0)) {
 			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
 			        "P2P: Provision Discovery Request add device "
 				"failed " MACSTR, MAC2STR(sa));
@@ -379,9 +380,6 @@
 		/* TODO: use device discoverability request through GO */
 	}
 
-	dev->dialog_token++;
-	if (dev->dialog_token == 0)
-		dev->dialog_token = 1;
 	req = p2p_build_prov_disc_req(p2p, dev->dialog_token,
 				      dev->req_config_methods,
 				      join ? dev : NULL);
@@ -452,6 +450,14 @@
 	if (p2p->user_initiated_pd)
 		p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES;
 
+	/*
+	 * Assign dialog token here to use the same value in each retry within
+	 * the same PD exchange.
+	 */
+	dev->dialog_token++;
+	if (dev->dialog_token == 0)
+		dev->dialog_token = 1;
+
 	return p2p_send_prov_disc_req(p2p, dev, join, force_freq);
 }
 
diff --git a/src/rsn_supp/peerkey.c b/src/rsn_supp/peerkey.c
index f2bac34..789ac25 100644
--- a/src/rsn_supp/peerkey.c
+++ b/src/rsn_supp/peerkey.c
@@ -217,23 +217,17 @@
 		return -1;
 	}
 
-	cipher = ie.pairwise_cipher & sm->allowed_pairwise_cipher;
-	if (cipher & WPA_CIPHER_CCMP) {
-		wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey");
-		cipher = WPA_CIPHER_CCMP;
-	} else if (cipher & WPA_CIPHER_GCMP) {
-		wpa_printf(MSG_DEBUG, "RSN: Using GCMP for PeerKey");
-		cipher = WPA_CIPHER_GCMP;
-	} else if (cipher & WPA_CIPHER_TKIP) {
-		wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey");
-		cipher = WPA_CIPHER_TKIP;
-	} else {
+	cipher = wpa_pick_pairwise_cipher(ie.pairwise_cipher &
+					  sm->allowed_pairwise_cipher, 0);
+	if (cipher < 0) {
 		wpa_printf(MSG_INFO, "RSN: No acceptable cipher in SMK M2");
 		wpa_supplicant_send_smk_error(sm, src_addr, kde.mac_addr,
 					      STK_MUI_SMK, STK_ERR_CPHR_NS,
 					      ver);
 		return -1;
 	}
+	wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey",
+		   wpa_cipher_txt(cipher));
 
 	/* TODO: find existing entry and if found, use that instead of adding
 	 * a new one; how to handle the case where both ends initiate at the
@@ -496,17 +490,9 @@
 	peerkey->rsnie_p_len = kde->rsn_ie_len;
 	os_memcpy(peerkey->pnonce, kde->nonce, WPA_NONCE_LEN);
 
-	cipher = ie.pairwise_cipher & sm->allowed_pairwise_cipher;
-	if (cipher & WPA_CIPHER_CCMP) {
-		wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey");
-		peerkey->cipher = WPA_CIPHER_CCMP;
-	} else if (cipher & WPA_CIPHER_GCMP) {
-		wpa_printf(MSG_DEBUG, "RSN: Using GCMP for PeerKey");
-		peerkey->cipher = WPA_CIPHER_GCMP;
-	} else if (cipher & WPA_CIPHER_TKIP) {
-		wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey");
-		peerkey->cipher = WPA_CIPHER_TKIP;
-	} else {
+	cipher = wpa_pick_pairwise_cipher(ie.pairwise_cipher &
+					  sm->allowed_pairwise_cipher, 0);
+	if (cipher < 0) {
 		wpa_printf(MSG_INFO, "RSN: SMK Peer STA " MACSTR " selected "
 			   "unacceptable cipher", MAC2STR(kde->mac_addr));
 		wpa_supplicant_send_smk_error(sm, src_addr, kde->mac_addr,
@@ -515,6 +501,9 @@
 		/* TODO: abort negotiation */
 		return -1;
 	}
+	wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey",
+		   wpa_cipher_txt(cipher));
+	peerkey->cipher = cipher;
 
 	return 0;
 }
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index 7646ca8..c38fada 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -1309,21 +1309,10 @@
 		wpa_printf(MSG_DEBUG, "TDLS: No supported rates received");
 		return -1;
 	}
-
-	peer->supp_rates_len = kde->supp_rates_len - 2;
-	if (peer->supp_rates_len > IEEE80211_MAX_SUPP_RATES)
-		peer->supp_rates_len = IEEE80211_MAX_SUPP_RATES;
-	os_memcpy(peer->supp_rates, kde->supp_rates + 2, peer->supp_rates_len);
-
-	if (kde->ext_supp_rates) {
-		int clen = kde->ext_supp_rates_len - 2;
-		if (peer->supp_rates_len + clen > IEEE80211_MAX_SUPP_RATES)
-			clen = IEEE80211_MAX_SUPP_RATES - peer->supp_rates_len;
-		os_memcpy(peer->supp_rates + peer->supp_rates_len,
-			  kde->ext_supp_rates + 2, clen);
-		peer->supp_rates_len += clen;
-	}
-
+	peer->supp_rates_len = merge_byte_arrays(
+		peer->supp_rates, sizeof(peer->supp_rates),
+		kde->supp_rates + 2, kde->supp_rates_len - 2,
+		kde->ext_supp_rates + 2, kde->ext_supp_rates_len - 2);
 	return 0;
 }
 
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 3c45f3a..e50404c 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -2627,7 +2627,7 @@
 }
 
 
-#ifdef CONFIG_IEEE80211V
+#ifdef CONFIG_WNM
 int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf)
 {
 	struct wpa_gtk_data gd;
@@ -2637,7 +2637,6 @@
 #endif /* CONFIG_IEEE80211W */
 	u16 keyinfo;
 	u8 keylen;  /* plaintext key len */
-	u8 keydatalen;
 	u8 *key_rsc;
 
 	os_memset(&gd, 0, sizeof(gd));
@@ -2655,8 +2654,7 @@
 
 	if (subelem_id == WNM_SLEEP_SUBELEM_GTK) {
 		key_rsc = buf + 5;
-		keyinfo = WPA_GET_LE16(buf+2);
-		keydatalen = buf[1] - 11 - 8;
+		keyinfo = WPA_GET_LE16(buf + 2);
 		gd.gtk_len = keylen;
 		if (gd.gtk_len != buf[4]) {
 			wpa_printf(MSG_DEBUG, "GTK len mismatch len %d vs %d",
@@ -2667,18 +2665,7 @@
 		gd.tx = wpa_supplicant_gtk_tx_bit_workaround(
 		         sm, !!(keyinfo & WPA_KEY_INFO_TXRX));
 
-		if (keydatalen % 8) {
-			wpa_printf(MSG_DEBUG, "WPA: Unsupported AES-WRAP len "
-				   "%d", keydatalen);
-			return -1;
-		}
-
-		if (aes_unwrap(sm->ptk.kek, keydatalen / 8, buf + 13, gd.gtk))
-		{
-			wpa_printf(MSG_WARNING, "WNM: AES unwrap failed - "
-				   "could not decrypt GTK");
-			return -1;
-		}
+		os_memcpy(gd.gtk, buf + 13, gd.gtk_len);
 
 		wpa_hexdump_key(MSG_DEBUG, "Install GTK (WNM SLEEP)",
 				gd.gtk, gd.gtk_len);
@@ -2689,22 +2676,11 @@
 		}
 #ifdef CONFIG_IEEE80211W
 	} else if (subelem_id == WNM_SLEEP_SUBELEM_IGTK) {
-		if (buf[1] != 2 + 6 + WPA_IGTK_LEN + 8) {
-			wpa_printf(MSG_DEBUG, "WPA: Unsupported AES-WRAP len "
-				   "%d", buf[1] - 2 - 6 - 8);
-			return -1;
-		}
 		os_memcpy(igd.keyid, buf + 2, 2);
 		os_memcpy(igd.pn, buf + 4, 6);
 
 		keyidx = WPA_GET_LE16(igd.keyid);
-
-		if (aes_unwrap(sm->ptk.kek, WPA_IGTK_LEN / 8, buf + 10,
-			       igd.igtk)) {
-			wpa_printf(MSG_WARNING, "WNM: AES unwrap failed - "
-				   "could not decrypr IGTK");
-			return -1;
-		}
+		os_memcpy(igd.igtk, buf + 10, WPA_IGTK_LEN);
 
 		wpa_hexdump_key(MSG_DEBUG, "Install IGTK (WNM SLEEP)",
 				igd.igtk, WPA_IGTK_LEN);
@@ -2723,4 +2699,4 @@
 
 	return 0;
 }
-#endif /* CONFIG_IEEE80211V */
+#endif /* CONFIG_WNM */
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 8afdf39..791974c 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -365,8 +365,6 @@
 void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr);
 int wpa_tdls_is_external_setup(struct wpa_sm *sm);
 
-#ifdef CONFIG_IEEE80211V
 int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf);
-#endif /* CONFIG_IEEE80211V */
 
 #endif /* WPA_H */
diff --git a/src/utils/common.h b/src/utils/common.h
index 5fc916c..a859042 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -224,69 +224,105 @@
 
 /* Macros for handling unaligned memory accesses */
 
-#define WPA_GET_BE16(a) ((u16) (((a)[0] << 8) | (a)[1]))
-#define WPA_PUT_BE16(a, val)			\
-	do {					\
-		(a)[0] = ((u16) (val)) >> 8;	\
-		(a)[1] = ((u16) (val)) & 0xff;	\
-	} while (0)
+static inline u16 WPA_GET_BE16(const u8 *a)
+{
+	return (a[0] << 8) | a[1];
+}
 
-#define WPA_GET_LE16(a) ((u16) (((a)[1] << 8) | (a)[0]))
-#define WPA_PUT_LE16(a, val)			\
-	do {					\
-		(a)[1] = ((u16) (val)) >> 8;	\
-		(a)[0] = ((u16) (val)) & 0xff;	\
-	} while (0)
+static inline void WPA_PUT_BE16(u8 *a, u16 val)
+{
+	a[0] = val >> 8;
+	a[1] = val & 0xff;
+}
 
-#define WPA_GET_BE24(a) ((((u32) (a)[0]) << 16) | (((u32) (a)[1]) << 8) | \
-			 ((u32) (a)[2]))
-#define WPA_PUT_BE24(a, val)					\
-	do {							\
-		(a)[0] = (u8) ((((u32) (val)) >> 16) & 0xff);	\
-		(a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff);	\
-		(a)[2] = (u8) (((u32) (val)) & 0xff);		\
-	} while (0)
+static inline u16 WPA_GET_LE16(const u8 *a)
+{
+	return (a[1] << 8) | a[0];
+}
 
-#define WPA_GET_BE32(a) ((((u32) (a)[0]) << 24) | (((u32) (a)[1]) << 16) | \
-			 (((u32) (a)[2]) << 8) | ((u32) (a)[3]))
-#define WPA_PUT_BE32(a, val)					\
-	do {							\
-		(a)[0] = (u8) ((((u32) (val)) >> 24) & 0xff);	\
-		(a)[1] = (u8) ((((u32) (val)) >> 16) & 0xff);	\
-		(a)[2] = (u8) ((((u32) (val)) >> 8) & 0xff);	\
-		(a)[3] = (u8) (((u32) (val)) & 0xff);		\
-	} while (0)
+static inline void WPA_PUT_LE16(u8 *a, u16 val)
+{
+	a[1] = val >> 8;
+	a[0] = val & 0xff;
+}
 
-#define WPA_GET_LE32(a) ((((u32) (a)[3]) << 24) | (((u32) (a)[2]) << 16) | \
-			 (((u32) (a)[1]) << 8) | ((u32) (a)[0]))
-#define WPA_PUT_LE32(a, val)					\
-	do {							\
-		(a)[3] = (u8) ((((u32) (val)) >> 24) & 0xff);	\
-		(a)[2] = (u8) ((((u32) (val)) >> 16) & 0xff);	\
-		(a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff);	\
-		(a)[0] = (u8) (((u32) (val)) & 0xff);		\
-	} while (0)
+static inline u32 WPA_GET_BE24(const u8 *a)
+{
+	return (a[0] << 16) | (a[1] << 8) | a[2];
+}
 
-#define WPA_GET_BE64(a) ((((u64) (a)[0]) << 56) | (((u64) (a)[1]) << 48) | \
-			 (((u64) (a)[2]) << 40) | (((u64) (a)[3]) << 32) | \
-			 (((u64) (a)[4]) << 24) | (((u64) (a)[5]) << 16) | \
-			 (((u64) (a)[6]) << 8) | ((u64) (a)[7]))
-#define WPA_PUT_BE64(a, val)				\
-	do {						\
-		(a)[0] = (u8) (((u64) (val)) >> 56);	\
-		(a)[1] = (u8) (((u64) (val)) >> 48);	\
-		(a)[2] = (u8) (((u64) (val)) >> 40);	\
-		(a)[3] = (u8) (((u64) (val)) >> 32);	\
-		(a)[4] = (u8) (((u64) (val)) >> 24);	\
-		(a)[5] = (u8) (((u64) (val)) >> 16);	\
-		(a)[6] = (u8) (((u64) (val)) >> 8);	\
-		(a)[7] = (u8) (((u64) (val)) & 0xff);	\
-	} while (0)
+static inline void WPA_PUT_BE24(u8 *a, u32 val)
+{
+	a[0] = (val >> 16) & 0xff;
+	a[1] = (val >> 8) & 0xff;
+	a[2] = val & 0xff;
+}
 
-#define WPA_GET_LE64(a) ((((u64) (a)[7]) << 56) | (((u64) (a)[6]) << 48) | \
-			 (((u64) (a)[5]) << 40) | (((u64) (a)[4]) << 32) | \
-			 (((u64) (a)[3]) << 24) | (((u64) (a)[2]) << 16) | \
-			 (((u64) (a)[1]) << 8) | ((u64) (a)[0]))
+static inline u32 WPA_GET_BE32(const u8 *a)
+{
+	return (a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3];
+}
+
+static inline void WPA_PUT_BE32(u8 *a, u32 val)
+{
+	a[0] = (val >> 24) & 0xff;
+	a[1] = (val >> 16) & 0xff;
+	a[2] = (val >> 8) & 0xff;
+	a[3] = val & 0xff;
+}
+
+static inline u32 WPA_GET_LE32(const u8 *a)
+{
+	return (a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0];
+}
+
+static inline void WPA_PUT_LE32(u8 *a, u32 val)
+{
+	a[3] = (val >> 24) & 0xff;
+	a[2] = (val >> 16) & 0xff;
+	a[1] = (val >> 8) & 0xff;
+	a[0] = val & 0xff;
+}
+
+static inline u64 WPA_GET_BE64(const u8 *a)
+{
+	return (((u64) a[0]) << 56) | (((u64) a[1]) << 48) |
+		(((u64) a[2]) << 40) | (((u64) a[3]) << 32) |
+		(((u64) a[4]) << 24) | (((u64) a[5]) << 16) |
+		(((u64) a[6]) << 8) | ((u64) a[7]);
+}
+
+static inline void WPA_PUT_BE64(u8 *a, u64 val)
+{
+	a[0] = val >> 56;
+	a[1] = val >> 48;
+	a[2] = val >> 40;
+	a[3] = val >> 32;
+	a[4] = val >> 24;
+	a[5] = val >> 16;
+	a[6] = val >> 8;
+	a[7] = val & 0xff;
+}
+
+static inline u64 WPA_GET_LE64(const u8 *a)
+{
+	return (((u64) a[7]) << 56) | (((u64) a[6]) << 48) |
+		(((u64) a[5]) << 40) | (((u64) a[4]) << 32) |
+		(((u64) a[3]) << 24) | (((u64) a[2]) << 16) |
+		(((u64) a[1]) << 8) | ((u64) a[0]);
+}
+
+static inline void WPA_PUT_LE64(u8 *a, u64 val)
+{
+	a[7] = val >> 56;
+	a[6] = val >> 48;
+	a[5] = val >> 40;
+	a[4] = val >> 32;
+	a[3] = val >> 24;
+	a[2] = val >> 16;
+	a[1] = val >> 8;
+	a[0] = val & 0xff;
+}
 
 
 #ifndef ETH_ALEN
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
index b24836e..11e7e84 100644
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -1298,7 +1298,7 @@
 	    wps_build_uuid_e(probe, reg->wps->uuid) ||
 	    wps_build_device_attrs(&reg->wps->dev, probe) ||
 	    wps_build_probe_config_methods(reg, probe) ||
-	    wps_build_rf_bands(&reg->wps->dev, probe) ||
+	    (reg->dualband && wps_build_rf_bands(&reg->wps->dev, probe)) ||
 	    wps_build_wfa_ext(probe, 0, auth_macs, count) ||
 	    wps_build_vendor_ext(&reg->wps->dev, probe)) {
 		wpabuf_free(beacon);