Cumulative patch from commit 3e7f1c7980c6e9fc7173f78aa72b2761fcd8924d (DO NOT MERGE)

3e7f1c7 GnuTLS: Add TLS event callbacks for chain success/failure and peer cert
0eb2ed0 GnuTLS: Add support for OCSP stapling as a client
cf08e9b Add MESH to modes capabilities
db5adfe Add SAE to auth_alg capabilities
0e1bb94 GnuTLS: Verify that server certificate EKU is valid for a server
d4d1f5c GnuTLS: Fix tls_disable_time_checks=1 processing
594d1fc GnuTLS: Add support for private_key and client_cert as blobs
79b1dd9 GnuTLS: Fix DER encoding certificate parsing
a165145 Add "GET tls_library" to provide information on TLS library and version
c3bb84b GnuTLS: Add event callbacks
8ddcd6b GnuTLS: Add support for domain_suffix_match
4bc13bf GnuTLS: Check for any unknown verification failure
e0d431a GnuTLS: Add more debug prints for version and session status
65ec7f4 GnuTLS: Move peer certificate validation into callback function
7c82457 GnuTLS: Remove support for versions older than 2.12.x
e1d63f6 GnuTLS: Remove old version number checks for 1.3.2
ae0a23a GnuTLS: Remove GNUTLS_INTERNAL_STRUCTURE_HACK
db4cf40 GnuTLS: Add support for ca_cert as a blob
224104d TLS: Reject openssl_ciphers parameter in non-OpenSSL cases
b09baf3 Work around Windows build issues
6dbbef9 Define host_to_le32() for Windows builds
7d28e46 Fix os_win32 build
0b40247 Remove Network Security Service (NSS) support
d166947 schannel: Reject subject_match, altsubject_match, suffix_match
59051f8 TLS: Reject subject_match, altsubject_match, suffix_match
f8717ac GnuTLS: Reject subject_match, altsubject_match, suffix_match
e24aef1 Fix a typo in domain_suffix_match documentation
394b547 Improve subject_match and domain_suffix_match documentation
8a42a07 trace: Fix out-of-memory testing logic
79cd993 Add address masks to BSSID lists
b83e455 Add network specific BSSID black and white lists
b3d6a0a Add generic parser for MAC address lists
21c74e8 nl80211: Use a helper function to put mesh_id
85e1fad nl80211: Use a helper function for putting beacon interval
6dfc557 Remove mesh_ht_mode network block parameter
54fe48b mesh: Use the shared function with IBSS to determine channel parameters
f7e889f mesh: Convert channel configuration to use common routines
6334330 mesh: Use a separate variable to track whether HT is enabled
1fc4ab2 nl80211: Move debug prints into nl80211_put_freq_params()
cae87ab nl80211: Add a helper function for putting basic rates
6b8b077 ibss/mesh: Enable HT40 if supported
a828f62 Make check_40mhz_2g4 common
fdd989d Make check_20mhz_bss common
0e550fe Make check_40mhz_5g common
6d5d098 Make get_pri_sec_chan() common
5144274 Introduce common allowed_ht40_channel_pair()
5f10b7f Use common hw_get_freq/hw_get_chan helpers in hostapd
269dfe2 Introduce common hw features
1830817 IBSS: Add WPA_DRIVER_FLAGS_HT_IBSS
f3b8ad4 SAE: Implement retransmission timer
a206e2a SAE: Centralize function for sending initial COMMIT
28c91ee bsd: Fix parsing of ieee80211req_scan_result on FreeBSD and DragonFly
96d1d97 Android: Remove hardcoded ICU include paths from hs20-osu-client
a354bcc D-Bus: Use NoMemory error message from CreateInterface
635874b Handle interface disabled/enabled more consistently
8f2cf37 P2P: Indicate reason=UNAVAILABLE for group netdev going down
86a7fbb Verify that eloop_register_read_sock() succeeds for ctrl_iface setup
27d9701 Fix a memory leak on WPA authenticator error path
c1c07dc Fix hostapd interface addition error path
a156ffd Add support for testing memory allocation failures
52b3943 D-Bus: Fix interface unregistration on error path
96dc9a6 D-Bus (old): Fix interface unregistration on error path
ef03557 Fix memory leak on wpa_supplicant_init_wpa() error path
52a8058 TDLS: Fix an interface addition error path
f2d5728 D-Bus: Fix string array dict entry parser in out-of-memory case
c61bc23 D-Bus: Fix byte array dict entry parser in out-of-memory case
dacf605 D-Bus: Fix Introspect() in case of os_strdup() failure
68a8669 D-Bus (old): Fix wpsReg error message
f0614bc D-Bus (old): Fix message handler error paths
a2af1c7 D-Bus (old): Fix memory leak on error path
3d2e2d5 trace: Fix compiler warning on 32-bit builds with bfd support
b9f6560 eloop: Fix WPA_TRACE tracking in case of realloc failure
e10422c Fix memory leak on hostapd BSS addition error path
2801659 Fix hostapd initialization error path on allocation failure
d58ade2 nl80211: Fix compilation with libnl 1.1 and 2.0
51f3427 crypto: Clear temporary stack buffers after use
77a2c39 crypto: Clear temporary heap allocations before freeing
a15a7fc DH: Clear memory explicitly on private key deinit
77c45e2 Add wpabuf_clear_free() to allow clearing of freed memory
a90c7d9 OpenSSL: Fix pbkdf2_sha1() wrapper
f6ebbcf AES-SIV: Make aes_s2v() static
dcf8fbc nl80211: Simplify event processing error paths
38751d8 nl80211: Remove cfg80211 state mismatch workaround for authentication
64ae244 nl80211: Check support for rekey offload on first use

Change-Id: Ice94c3cf8e39a6d2cac993aacd0f6d45b31c7c15
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 2103747..6e4169b 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -1872,33 +1872,37 @@
 }
 
 
-static struct hostapd_iface * hostapd_data_alloc(
-	struct hapd_interfaces *interfaces, struct hostapd_config *conf)
+static int hostapd_data_alloc(struct hostapd_iface *hapd_iface,
+			      struct hostapd_config *conf)
 {
 	size_t i;
-	struct hostapd_iface *hapd_iface =
-		interfaces->iface[interfaces->count - 1];
 	struct hostapd_data *hapd;
 
-	hapd_iface->conf = conf;
-	hapd_iface->num_bss = conf->num_bss;
-
 	hapd_iface->bss = os_calloc(conf->num_bss,
 				    sizeof(struct hostapd_data *));
 	if (hapd_iface->bss == NULL)
-		return NULL;
+		return -1;
 
 	for (i = 0; i < conf->num_bss; i++) {
 		hapd = hapd_iface->bss[i] =
 			hostapd_alloc_bss_data(hapd_iface, conf, conf->bss[i]);
-		if (hapd == NULL)
-			return NULL;
+		if (hapd == NULL) {
+			while (i > 0) {
+				i--;
+				os_free(hapd_iface->bss[i]);
+				hapd_iface->bss[i] = NULL;
+			}
+			os_free(hapd_iface->bss);
+			hapd_iface->bss = NULL;
+			return -1;
+		}
 		hapd->msg_ctx = hapd;
 	}
 
-	hapd_iface->interfaces = interfaces;
+	hapd_iface->conf = conf;
+	hapd_iface->num_bss = conf->num_bss;
 
-	return hapd_iface;
+	return 0;
 }
 
 
@@ -1945,13 +1949,10 @@
 		}
 
 		if (new_iface) {
-			if (interfaces->driver_init(hapd_iface)) {
-				interfaces->count--;
+			if (interfaces->driver_init(hapd_iface))
 				goto fail;
-			}
 
 			if (hostapd_setup_interface(hapd_iface)) {
-				interfaces->count--;
 				hostapd_deinit_driver(
 					hapd_iface->bss[0]->driver,
 					hapd_iface->bss[0]->drv_priv,
@@ -1975,6 +1976,8 @@
 				hapd_iface->num_bss--;
 				wpa_printf(MSG_DEBUG, "%s: free hapd %p %s",
 					   __func__, hapd, hapd->conf->iface);
+				hostapd_config_free_bss(hapd->conf);
+				hapd->conf = NULL;
 				os_free(hapd);
 				return -1;
 			}
@@ -2005,6 +2008,7 @@
 			   "for interface", __func__);
 		goto fail;
 	}
+	new_iface = hapd_iface;
 
 	if (conf_file && interfaces->config_read_cb) {
 		conf = interfaces->config_read_cb(conf_file);
@@ -2019,17 +2023,18 @@
 		goto fail;
 	}
 
-	hapd_iface = hostapd_data_alloc(interfaces, conf);
-	if (hapd_iface == NULL) {
+	if (hostapd_data_alloc(hapd_iface, conf) < 0) {
 		wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
 			   "for hostapd", __func__);
 		goto fail;
 	}
+	conf = NULL;
 
 	if (start_ctrl_iface(hapd_iface) < 0)
 		goto fail;
 
-	wpa_printf(MSG_INFO, "Add interface '%s'", conf->bss[0]->iface);
+	wpa_printf(MSG_INFO, "Add interface '%s'",
+		   hapd_iface->conf->bss[0]->iface);
 
 	return 0;
 
@@ -2056,6 +2061,10 @@
 			os_free(hapd_iface->bss);
 			hapd_iface->bss = NULL;
 		}
+		if (new_iface) {
+			interfaces->count--;
+			interfaces->iface[interfaces->count] = NULL;
+		}
 		hostapd_cleanup_iface(hapd_iface);
 	}
 	return -1;
@@ -2076,6 +2085,7 @@
 		wpa_printf(MSG_DEBUG, "%s: free hapd %p (%s)",
 			   __func__, hapd, hapd->conf->iface);
 		hostapd_config_free_bss(hapd->conf);
+		hapd->conf = NULL;
 		os_free(hapd);
 
 		iface->num_bss--;
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index f959215..6b0a72d 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -15,6 +15,7 @@
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "common/wpa_ctrl.h"
+#include "common/hw_features_common.h"
 #include "hostapd.h"
 #include "ap_config.h"
 #include "ap_drv_ops.h"
@@ -223,66 +224,16 @@
 #ifdef CONFIG_IEEE80211N
 static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface)
 {
-	int sec_chan, ok, j, first;
-	int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
-			  184, 192 };
-	size_t k;
+	int pri_chan, sec_chan;
 
 	if (!iface->conf->secondary_channel)
 		return 1; /* HT40 not used */
 
-	sec_chan = iface->conf->channel + iface->conf->secondary_channel * 4;
-	wpa_printf(MSG_DEBUG, "HT40: control channel: %d  "
-		   "secondary channel: %d",
-		   iface->conf->channel, sec_chan);
+	pri_chan = iface->conf->channel;
+	sec_chan = pri_chan + iface->conf->secondary_channel * 4;
 
-	/* Verify that HT40 secondary channel is an allowed 20 MHz
-	 * channel */
-	ok = 0;
-	for (j = 0; j < iface->current_mode->num_channels; j++) {
-		struct hostapd_channel_data *chan =
-			&iface->current_mode->channels[j];
-		if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
-		    chan->chan == sec_chan) {
-			ok = 1;
-			break;
-		}
-	}
-	if (!ok) {
-		wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed",
-			   sec_chan);
-		return 0;
-	}
-
-	/*
-	 * Verify that HT40 primary,secondary channel pair is allowed per
-	 * IEEE 802.11n Annex J. This is only needed for 5 GHz band since
-	 * 2.4 GHz rules allow all cases where the secondary channel fits into
-	 * the list of allowed channels (already checked above).
-	 */
-	if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
-		return 1;
-
-	if (iface->conf->secondary_channel > 0)
-		first = iface->conf->channel;
-	else
-		first = sec_chan;
-
-	ok = 0;
-	for (k = 0; k < ARRAY_SIZE(allowed); k++) {
-		if (first == allowed[k]) {
-			ok = 1;
-			break;
-		}
-	}
-	if (!ok) {
-		wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed",
-			   iface->conf->channel,
-			   iface->conf->secondary_channel);
-		return 0;
-	}
-
-	return 1;
+	return allowed_ht40_channel_pair(iface->current_mode, pri_chan,
+					 sec_chan);
 }
 
 
@@ -298,214 +249,34 @@
 }
 
 
-static void ieee80211n_get_pri_sec_chan(struct wpa_scan_res *bss,
-					int *pri_chan, int *sec_chan)
-{
-	struct ieee80211_ht_operation *oper;
-	struct ieee802_11_elems elems;
-
-	*pri_chan = *sec_chan = 0;
-
-	ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
-	if (elems.ht_operation &&
-	    elems.ht_operation_len >= sizeof(*oper)) {
-		oper = (struct ieee80211_ht_operation *) elems.ht_operation;
-		*pri_chan = oper->primary_chan;
-		if (oper->ht_param & HT_INFO_HT_PARAM_STA_CHNL_WIDTH) {
-			int sec = oper->ht_param &
-				HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
-			if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
-				*sec_chan = *pri_chan + 4;
-			else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
-				*sec_chan = *pri_chan - 4;
-		}
-	}
-}
-
-
 static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface,
 				     struct wpa_scan_results *scan_res)
 {
-	int pri_chan, sec_chan, pri_freq, sec_freq, pri_bss, sec_bss;
-	int bss_pri_chan, bss_sec_chan;
-	size_t i;
-	int match;
+	int pri_chan, sec_chan;
+	int res;
 
 	pri_chan = iface->conf->channel;
 	sec_chan = pri_chan + iface->conf->secondary_channel * 4;
-	pri_freq = hostapd_hw_get_freq(iface->bss[0], pri_chan);
-	if (iface->conf->secondary_channel > 0)
-		sec_freq = pri_freq + 20;
-	else
-		sec_freq = pri_freq - 20;
 
-	/*
-	 * Switch PRI/SEC channels if Beacons were detected on selected SEC
-	 * channel, but not on selected PRI channel.
-	 */
-	pri_bss = sec_bss = 0;
-	for (i = 0; i < scan_res->num; i++) {
-		struct wpa_scan_res *bss = scan_res->res[i];
-		if (bss->freq == pri_freq)
-			pri_bss++;
-		else if (bss->freq == sec_freq)
-			sec_bss++;
-	}
-	if (sec_bss && !pri_bss) {
-		wpa_printf(MSG_INFO, "Switch own primary and secondary "
-			   "channel to get secondary channel with no Beacons "
-			   "from other BSSes");
+	res = check_40mhz_5g(iface->current_mode, scan_res, pri_chan, sec_chan);
+
+	if (res == 2)
 		ieee80211n_switch_pri_sec(iface);
-		return 1;
-	}
 
-	/*
-	 * Match PRI/SEC channel with any existing HT40 BSS on the same
-	 * channels that we are about to use (if already mixed order in
-	 * existing BSSes, use own preference).
-	 */
-	match = 0;
-	for (i = 0; i < scan_res->num; i++) {
-		struct wpa_scan_res *bss = scan_res->res[i];
-		ieee80211n_get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
-		if (pri_chan == bss_pri_chan &&
-		    sec_chan == bss_sec_chan) {
-			match = 1;
-			break;
-		}
-	}
-	if (!match) {
-		for (i = 0; i < scan_res->num; i++) {
-			struct wpa_scan_res *bss = scan_res->res[i];
-			ieee80211n_get_pri_sec_chan(bss, &bss_pri_chan,
-						    &bss_sec_chan);
-			if (pri_chan == bss_sec_chan &&
-			    sec_chan == bss_pri_chan) {
-				wpa_printf(MSG_INFO, "Switch own primary and "
-					   "secondary channel due to BSS "
-					   "overlap with " MACSTR,
-					   MAC2STR(bss->bssid));
-				ieee80211n_switch_pri_sec(iface);
-				break;
-			}
-		}
-	}
-
-	return 1;
-}
-
-
-static int ieee80211n_check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq,
-				      int start, int end)
-{
-	struct ieee802_11_elems elems;
-	struct ieee80211_ht_operation *oper;
-
-	if (bss->freq < start || bss->freq > end || bss->freq == pri_freq)
-		return 0;
-
-	ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
-	if (!elems.ht_capabilities) {
-		wpa_printf(MSG_DEBUG, "Found overlapping legacy BSS: "
-			   MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
-		return 1;
-	}
-
-	if (elems.ht_operation &&
-	    elems.ht_operation_len >= sizeof(*oper)) {
-		oper = (struct ieee80211_ht_operation *) elems.ht_operation;
-		if (oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)
-			return 0;
-
-		wpa_printf(MSG_DEBUG, "Found overlapping 20 MHz HT BSS: "
-			   MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
-		return 1;
-	}
-	return 0;
+	return !!res;
 }
 
 
 static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface,
 				      struct wpa_scan_results *scan_res)
 {
-	int pri_freq, sec_freq;
-	int affected_start, affected_end;
-	size_t i;
+	int pri_chan, sec_chan;
 
-	pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
-	if (iface->conf->secondary_channel > 0)
-		sec_freq = pri_freq + 20;
-	else
-		sec_freq = pri_freq - 20;
-	affected_start = (pri_freq + sec_freq) / 2 - 25;
-	affected_end = (pri_freq + sec_freq) / 2 + 25;
-	wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
-		   affected_start, affected_end);
-	for (i = 0; i < scan_res->num; i++) {
-		struct wpa_scan_res *bss = scan_res->res[i];
-		int pri = bss->freq;
-		int sec = pri;
-		int sec_chan, pri_chan;
-		struct ieee802_11_elems elems;
+	pri_chan = iface->conf->channel;
+	sec_chan = pri_chan + iface->conf->secondary_channel * 4;
 
-		/* Check for overlapping 20 MHz BSS */
-		if (ieee80211n_check_20mhz_bss(bss, pri_freq, affected_start,
-					       affected_end)) {
-			wpa_printf(MSG_DEBUG,
-				   "Overlapping 20 MHz BSS is found");
-			return 0;
-		}
-
-		ieee80211n_get_pri_sec_chan(bss, &pri_chan, &sec_chan);
-
-		if (sec_chan) {
-			if (sec_chan < pri_chan)
-				sec = pri - 20;
-			else
-				sec = pri + 20;
-		}
-
-		if ((pri < affected_start || pri > affected_end) &&
-		    (sec < affected_start || sec > affected_end))
-			continue; /* not within affected channel range */
-
-		wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR
-			   " freq=%d pri=%d sec=%d",
-			   MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan);
-
-		if (sec_chan) {
-			if (pri_freq != pri || sec_freq != sec) {
-				wpa_printf(MSG_DEBUG, "40 MHz pri/sec "
-					   "mismatch with BSS " MACSTR
-					   " <%d,%d> (chan=%d%c) vs. <%d,%d>",
-					   MAC2STR(bss->bssid),
-					   pri, sec, pri_chan,
-					   sec > pri ? '+' : '-',
-					   pri_freq, sec_freq);
-				return 0;
-			}
-		}
-
-		ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems,
-				       0);
-		if (elems.ht_capabilities &&
-		    elems.ht_capabilities_len >=
-		    sizeof(struct ieee80211_ht_capabilities)) {
-			struct ieee80211_ht_capabilities *ht_cap =
-				(struct ieee80211_ht_capabilities *)
-				elems.ht_capabilities;
-
-			if (le_to_host16(ht_cap->ht_capabilities_info) &
-			    HT_CAP_INFO_40MHZ_INTOLERANT) {
-				wpa_printf(MSG_DEBUG,
-					   "40 MHz Intolerant is set on channel %d in BSS "
-					   MACSTR, pri, MAC2STR(bss->bssid));
-				return 0;
-			}
-		}
-	}
-
-	return 1;
+	return check_40mhz_2g4(iface->current_mode, scan_res, pri_chan,
+			       sec_chan);
 }
 
 
@@ -1143,35 +914,11 @@
 
 int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
 {
-	int i;
-
-	if (!hapd->iface->current_mode)
-		return 0;
-
-	for (i = 0; i < hapd->iface->current_mode->num_channels; i++) {
-		struct hostapd_channel_data *ch =
-			&hapd->iface->current_mode->channels[i];
-		if (ch->chan == chan)
-			return ch->freq;
-	}
-
-	return 0;
+	return hw_get_freq(hapd->iface->current_mode, chan);
 }
 
 
 int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq)
 {
-	int i;
-
-	if (!hapd->iface->current_mode)
-		return 0;
-
-	for (i = 0; i < hapd->iface->current_mode->num_channels; i++) {
-		struct hostapd_channel_data *ch =
-			&hapd->iface->current_mode->channels[i];
-		if (ch->freq == freq)
-			return ch->chan;
-	}
-
-	return 0;
+	return hw_get_chan(hapd->iface->current_mode, freq);
 }
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 97f98f2..3d4488a 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -328,6 +328,10 @@
 
 #ifdef CONFIG_SAE
 
+#define dot11RSNASAERetransPeriod 40	/* msec */
+#define dot11RSNASAESync 5		/* attempts */
+
+
 static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
 					     struct sta_info *sta, int update)
 {
@@ -483,6 +487,66 @@
 }
 
 
+static int sae_check_big_sync(struct sta_info *sta)
+{
+	if (sta->sae->sync > dot11RSNASAESync) {
+		sta->sae->state = SAE_NOTHING;
+		sta->sae->sync = 0;
+		return -1;
+	}
+	return 0;
+}
+
+
+static void auth_sae_retransmit_timer(void *eloop_ctx, void *eloop_data)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = eloop_data;
+	int ret;
+
+	if (sae_check_big_sync(sta))
+		return;
+	sta->sae->sync++;
+
+	switch (sta->sae->state) {
+	case SAE_COMMITTED:
+		ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0);
+		eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000,
+				       auth_sae_retransmit_timer, hapd, sta);
+		break;
+	case SAE_CONFIRMED:
+		ret = auth_sae_send_confirm(hapd, sta, hapd->own_addr);
+		eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000,
+				       auth_sae_retransmit_timer, hapd, sta);
+		break;
+	default:
+		ret = -1;
+		break;
+	}
+
+	if (ret != WLAN_STATUS_SUCCESS)
+		wpa_printf(MSG_INFO, "SAE: Failed to retransmit: ret=%d", ret);
+}
+
+
+void sae_clear_retransmit_timer(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta);
+}
+
+
+static void sae_set_retransmit_timer(struct hostapd_data *hapd,
+				     struct sta_info *sta)
+{
+	if (!(hapd->conf->mesh & MESH_ENABLED))
+		return;
+
+	eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta);
+	eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000,
+			       auth_sae_retransmit_timer, hapd, sta);
+}
+
+
 static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
 		       const u8 *bssid, u8 auth_transaction)
 {
@@ -530,6 +594,8 @@
 				 * when receiving Confirm from STA.
 				 */
 			}
+			sta->sae->sync = 0;
+			sae_set_retransmit_timer(hapd, sta);
 		} else {
 			hostapd_logger(hapd, sta->addr,
 				       HOSTAPD_MODULE_IEEE80211,
@@ -538,6 +604,7 @@
 		}
 		break;
 	case SAE_COMMITTED:
+		sae_clear_retransmit_timer(hapd, sta);
 		if (auth_transaction == 1) {
 			if (sae_process_commit(sta->sae) < 0)
 				return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -546,14 +613,22 @@
 			if (ret)
 				return ret;
 			sta->sae->state = SAE_CONFIRMED;
+			sta->sae->sync = 0;
+			sae_set_retransmit_timer(hapd, sta);
 		} else if (hapd->conf->mesh & MESH_ENABLED) {
 			/*
 			 * In mesh case, follow SAE finite state machine and
-			 * send Commit now.
+			 * send Commit now, if sync count allows.
 			 */
+			if (sae_check_big_sync(sta))
+				return WLAN_STATUS_SUCCESS;
+			sta->sae->sync++;
+
 			ret = auth_sae_send_commit(hapd, sta, bssid, 1);
 			if (ret)
 				return ret;
+
+			sae_set_retransmit_timer(hapd, sta);
 		} else {
 			/*
 			 * For instructure BSS, send the postponed Confirm from
@@ -575,7 +650,12 @@
 		}
 		break;
 	case SAE_CONFIRMED:
+		sae_clear_retransmit_timer(hapd, sta);
 		if (auth_transaction == 1) {
+			if (sae_check_big_sync(sta))
+				return WLAN_STATUS_SUCCESS;
+			sta->sae->sync++;
+
 			ret = auth_sae_send_commit(hapd, sta, bssid, 1);
 			if (ret)
 				return ret;
@@ -586,6 +666,8 @@
 			ret = auth_sae_send_confirm(hapd, sta, bssid);
 			if (ret)
 				return ret;
+
+			sae_set_retransmit_timer(hapd, sta);
 		} else {
 			sta->flags |= WLAN_STA_AUTH;
 			sta->auth_alg = WLAN_AUTH_SAE;
@@ -603,6 +685,10 @@
 				   MAC2STR(sta->addr));
 			ap_free_sta(hapd, sta);
 		} else {
+			if (sae_check_big_sync(sta))
+				return WLAN_STATUS_SUCCESS;
+			sta->sae->sync++;
+
 			ret = auth_sae_send_confirm(hapd, sta, bssid);
 			sae_clear_temp_data(sta->sae);
 			if (ret)
@@ -632,6 +718,7 @@
 		if (sta->sae == NULL)
 			return;
 		sta->sae->state = SAE_NOTHING;
+		sta->sae->sync = 0;
 	}
 
 	if (auth_transaction == 1) {
@@ -685,6 +772,8 @@
 				return;
 			}
 			sta->sae->state = SAE_COMMITTED;
+			sta->sae->sync = 0;
+			sae_set_retransmit_timer(hapd, sta);
 			return;
 		}
 
@@ -755,6 +844,39 @@
 	}
 	wpabuf_free(data);
 }
+
+
+/**
+ * auth_sae_init_committed - Send COMMIT and start SAE in committed state
+ * @hapd: BSS data for the device initiating the authentication
+ * @sta: the peer to which commit authentication frame is sent
+ *
+ * This function implements Init event handling (IEEE Std 802.11-2012,
+ * 11.3.8.6.3) in which initial COMMIT message is sent. Prior to calling, the
+ * sta->sae structure should be initialized appropriately via a call to
+ * sae_prepare_commit().
+ */
+int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	int ret;
+
+	if (!sta->sae || !sta->sae->tmp)
+		return -1;
+
+	if (sta->sae->state != SAE_NOTHING)
+		return -1;
+
+	ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0);
+	if (ret)
+		return -1;
+
+	sta->sae->state = SAE_COMMITTED;
+	sta->sae->sync = 0;
+	sae_set_retransmit_timer(hapd, sta);
+
+	return 0;
+}
+
 #endif /* CONFIG_SAE */
 
 
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index cf0d3f2..beaeac5 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -89,4 +89,15 @@
 void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr);
 u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid);
 
+int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta);
+#ifdef CONFIG_SAE
+void sae_clear_retransmit_timer(struct hostapd_data *hapd,
+				struct sta_info *sta);
+#else /* CONFIG_SAE */
+static inline void sae_clear_retransmit_timer(struct hostapd_data *hapd,
+					      struct sta_info *sta)
+{
+}
+#endif /* CONFIG_SAE */
+
 #endif /* IEEE802_11_H */
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index debdc06..1c2197a 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -250,6 +250,7 @@
 	eloop_cancel_timeout(ap_handle_session_warning_timer, hapd, sta);
 	eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
 	eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
+	sae_clear_retransmit_timer(hapd, sta);
 
 	ieee802_1x_free_station(sta);
 	wpa_auth_sta_deinit(sta->wpa_sm);
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index da2073c..059b884 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -415,6 +415,7 @@
 						wpa_auth);
 	if (wpa_auth->pmksa == NULL) {
 		wpa_printf(MSG_ERROR, "PMKSA cache initialization failed.");
+		os_free(wpa_auth->group);
 		os_free(wpa_auth->wpa_ie);
 		os_free(wpa_auth);
 		return NULL;
@@ -424,6 +425,7 @@
 	wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init();
 	if (wpa_auth->ft_pmk_cache == NULL) {
 		wpa_printf(MSG_ERROR, "FT PMK cache initialization failed.");
+		os_free(wpa_auth->group);
 		os_free(wpa_auth->wpa_ie);
 		pmksa_cache_auth_deinit(wpa_auth->pmksa);
 		os_free(wpa_auth);
diff --git a/src/common/defs.h b/src/common/defs.h
index e1bbd50..2efb985 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -308,17 +308,6 @@
 /* Maximum number of EAP methods to store for EAP server user information */
 #define EAP_MAX_METHODS 8
 
-/**
- * enum ht_mode - channel width and offset
- */
-enum ht_mode {
-	CHAN_UNDEFINED = 0,
-	CHAN_NO_HT,
-	CHAN_HT20,
-	CHAN_HT40PLUS,
-	CHAN_HT40MINUS,
-};
-
 enum mesh_plink_state {
 	PLINK_LISTEN = 1,
 	PLINK_OPEN_SENT,
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
new file mode 100644
index 0000000..942380b
--- /dev/null
+++ b/src/common/hw_features_common.c
@@ -0,0 +1,356 @@
+/*
+ * Common hostapd/wpa_supplicant HW features
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2015, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "defs.h"
+#include "ieee802_11_defs.h"
+#include "ieee802_11_common.h"
+#include "hw_features_common.h"
+
+
+struct hostapd_channel_data * hw_get_channel_chan(struct hostapd_hw_modes *mode,
+						  int chan, int *freq)
+{
+	int i;
+
+	if (freq)
+		*freq = 0;
+
+	if (!mode)
+		return NULL;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		struct hostapd_channel_data *ch = &mode->channels[i];
+		if (ch->chan == chan) {
+			if (freq)
+				*freq = ch->freq;
+			return ch;
+		}
+	}
+
+	return NULL;
+}
+
+
+struct hostapd_channel_data * hw_get_channel_freq(struct hostapd_hw_modes *mode,
+						  int freq, int *chan)
+{
+	int i;
+
+	if (chan)
+		*chan = 0;
+
+	if (!mode)
+		return NULL;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		struct hostapd_channel_data *ch = &mode->channels[i];
+		if (ch->freq == freq) {
+			if (chan)
+				*chan = ch->chan;
+			return ch;
+		}
+	}
+
+	return NULL;
+}
+
+
+int hw_get_freq(struct hostapd_hw_modes *mode, int chan)
+{
+	int freq;
+
+	hw_get_channel_chan(mode, chan, &freq);
+
+	return freq;
+}
+
+
+int hw_get_chan(struct hostapd_hw_modes *mode, int freq)
+{
+	int chan;
+
+	hw_get_channel_freq(mode, freq, &chan);
+
+	return chan;
+}
+
+
+int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
+			      int sec_chan)
+{
+	int ok, j, first;
+	int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
+			  184, 192 };
+	size_t k;
+
+	if (pri_chan == sec_chan || !sec_chan)
+		return 1; /* HT40 not used */
+
+	wpa_printf(MSG_DEBUG,
+		   "HT40: control channel: %d  secondary channel: %d",
+		   pri_chan, sec_chan);
+
+	/* Verify that HT40 secondary channel is an allowed 20 MHz
+	 * channel */
+	ok = 0;
+	for (j = 0; j < mode->num_channels; j++) {
+		struct hostapd_channel_data *chan = &mode->channels[j];
+		if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+		    chan->chan == sec_chan) {
+			ok = 1;
+			break;
+		}
+	}
+	if (!ok) {
+		wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed",
+			   sec_chan);
+		return 0;
+	}
+
+	/*
+	 * Verify that HT40 primary,secondary channel pair is allowed per
+	 * IEEE 802.11n Annex J. This is only needed for 5 GHz band since
+	 * 2.4 GHz rules allow all cases where the secondary channel fits into
+	 * the list of allowed channels (already checked above).
+	 */
+	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+		return 1;
+
+	first = pri_chan < sec_chan ? pri_chan : sec_chan;
+
+	ok = 0;
+	for (k = 0; k < ARRAY_SIZE(allowed); k++) {
+		if (first == allowed[k]) {
+			ok = 1;
+			break;
+		}
+	}
+	if (!ok) {
+		wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed",
+			   pri_chan, sec_chan);
+		return 0;
+	}
+
+	return 1;
+}
+
+
+void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan)
+{
+	struct ieee80211_ht_operation *oper;
+	struct ieee802_11_elems elems;
+
+	*pri_chan = *sec_chan = 0;
+
+	ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
+	if (elems.ht_operation &&
+	    elems.ht_operation_len >= sizeof(*oper)) {
+		oper = (struct ieee80211_ht_operation *) elems.ht_operation;
+		*pri_chan = oper->primary_chan;
+		if (oper->ht_param & HT_INFO_HT_PARAM_STA_CHNL_WIDTH) {
+			int sec = oper->ht_param &
+				HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
+			if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
+				*sec_chan = *pri_chan + 4;
+			else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
+				*sec_chan = *pri_chan - 4;
+		}
+	}
+}
+
+
+int check_40mhz_5g(struct hostapd_hw_modes *mode,
+		   struct wpa_scan_results *scan_res, int pri_chan,
+		   int sec_chan)
+{
+	int pri_freq, sec_freq, pri_bss, sec_bss;
+	int bss_pri_chan, bss_sec_chan;
+	size_t i;
+	int match;
+
+	if (!mode || !scan_res || !pri_chan || !sec_chan)
+		return 0;
+
+	if (pri_chan == sec_chan)
+		return 0;
+
+	pri_freq = hw_get_freq(mode, pri_chan);
+	sec_freq = hw_get_freq(mode, sec_chan);
+
+	/*
+	 * Switch PRI/SEC channels if Beacons were detected on selected SEC
+	 * channel, but not on selected PRI channel.
+	 */
+	pri_bss = sec_bss = 0;
+	for (i = 0; i < scan_res->num; i++) {
+		struct wpa_scan_res *bss = scan_res->res[i];
+		if (bss->freq == pri_freq)
+			pri_bss++;
+		else if (bss->freq == sec_freq)
+			sec_bss++;
+	}
+	if (sec_bss && !pri_bss) {
+		wpa_printf(MSG_INFO,
+			   "Switch own primary and secondary channel to get secondary channel with no Beacons from other BSSes");
+		return 2;
+	}
+
+	/*
+	 * Match PRI/SEC channel with any existing HT40 BSS on the same
+	 * channels that we are about to use (if already mixed order in
+	 * existing BSSes, use own preference).
+	 */
+	match = 0;
+	for (i = 0; i < scan_res->num; i++) {
+		struct wpa_scan_res *bss = scan_res->res[i];
+		get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
+		if (pri_chan == bss_pri_chan &&
+		    sec_chan == bss_sec_chan) {
+			match = 1;
+			break;
+		}
+	}
+	if (!match) {
+		for (i = 0; i < scan_res->num; i++) {
+			struct wpa_scan_res *bss = scan_res->res[i];
+			get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
+			if (pri_chan == bss_sec_chan &&
+			    sec_chan == bss_pri_chan) {
+				wpa_printf(MSG_INFO, "Switch own primary and "
+					   "secondary channel due to BSS "
+					   "overlap with " MACSTR,
+					   MAC2STR(bss->bssid));
+				return 2;
+			}
+		}
+	}
+
+	return 1;
+}
+
+
+int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, int end)
+{
+	struct ieee802_11_elems elems;
+	struct ieee80211_ht_operation *oper;
+
+	if (bss->freq < start || bss->freq > end || bss->freq == pri_freq)
+		return 0;
+
+	ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
+	if (!elems.ht_capabilities) {
+		wpa_printf(MSG_DEBUG, "Found overlapping legacy BSS: "
+			   MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
+		return 1;
+	}
+
+	if (elems.ht_operation &&
+	    elems.ht_operation_len >= sizeof(*oper)) {
+		oper = (struct ieee80211_ht_operation *) elems.ht_operation;
+		if (oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)
+			return 0;
+
+		wpa_printf(MSG_DEBUG, "Found overlapping 20 MHz HT BSS: "
+			   MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
+		return 1;
+	}
+	return 0;
+}
+
+
+int check_40mhz_2g4(struct hostapd_hw_modes *mode,
+		    struct wpa_scan_results *scan_res, int pri_chan,
+		    int sec_chan)
+{
+	int pri_freq, sec_freq;
+	int affected_start, affected_end;
+	size_t i;
+
+	if (!mode || !scan_res || !pri_chan || !sec_chan)
+		return 0;
+
+	if (pri_chan == sec_chan)
+		return 0;
+
+	pri_freq = hw_get_freq(mode, pri_chan);
+	sec_freq = hw_get_freq(mode, sec_chan);
+
+	affected_start = (pri_freq + sec_freq) / 2 - 25;
+	affected_end = (pri_freq + sec_freq) / 2 + 25;
+	wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
+		   affected_start, affected_end);
+	for (i = 0; i < scan_res->num; i++) {
+		struct wpa_scan_res *bss = scan_res->res[i];
+		int pri = bss->freq;
+		int sec = pri;
+		struct ieee802_11_elems elems;
+
+		/* Check for overlapping 20 MHz BSS */
+		if (check_20mhz_bss(bss, pri_freq, affected_start,
+				    affected_end)) {
+			wpa_printf(MSG_DEBUG,
+				   "Overlapping 20 MHz BSS is found");
+			return 0;
+		}
+
+		get_pri_sec_chan(bss, &pri_chan, &sec_chan);
+
+		if (sec_chan) {
+			if (sec_chan < pri_chan)
+				sec = pri - 20;
+			else
+				sec = pri + 20;
+		}
+
+		if ((pri < affected_start || pri > affected_end) &&
+		    (sec < affected_start || sec > affected_end))
+			continue; /* not within affected channel range */
+
+		wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR
+			   " freq=%d pri=%d sec=%d",
+			   MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan);
+
+		if (sec_chan) {
+			if (pri_freq != pri || sec_freq != sec) {
+				wpa_printf(MSG_DEBUG,
+					   "40 MHz pri/sec mismatch with BSS "
+					   MACSTR
+					   " <%d,%d> (chan=%d%c) vs. <%d,%d>",
+					   MAC2STR(bss->bssid),
+					   pri, sec, pri_chan,
+					   sec > pri ? '+' : '-',
+					   pri_freq, sec_freq);
+				return 0;
+			}
+		}
+
+		ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems,
+				       0);
+		if (elems.ht_capabilities &&
+		    elems.ht_capabilities_len >=
+		    sizeof(struct ieee80211_ht_capabilities)) {
+			struct ieee80211_ht_capabilities *ht_cap =
+				(struct ieee80211_ht_capabilities *)
+				elems.ht_capabilities;
+
+			if (le_to_host16(ht_cap->ht_capabilities_info) &
+			    HT_CAP_INFO_40MHZ_INTOLERANT) {
+				wpa_printf(MSG_DEBUG,
+					   "40 MHz Intolerant is set on channel %d in BSS "
+					   MACSTR, pri, MAC2STR(bss->bssid));
+				return 0;
+			}
+		}
+	}
+
+	return 1;
+}
diff --git a/src/common/hw_features_common.h b/src/common/hw_features_common.h
new file mode 100644
index 0000000..046fccd
--- /dev/null
+++ b/src/common/hw_features_common.h
@@ -0,0 +1,34 @@
+/*
+ * Common hostapd/wpa_supplicant HW features
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2015, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HW_FEATURES_COMMON_H
+#define HW_FEATURES_COMMON_H
+
+#include "drivers/driver.h"
+
+struct hostapd_channel_data * hw_get_channel_chan(struct hostapd_hw_modes *mode,
+						  int chan, int *freq);
+struct hostapd_channel_data * hw_get_channel_freq(struct hostapd_hw_modes *mode,
+						  int freq, int *chan);
+
+int hw_get_freq(struct hostapd_hw_modes *mode, int chan);
+int hw_get_chan(struct hostapd_hw_modes *mode, int freq);
+
+int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
+			      int sec_chan);
+void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan);
+int check_40mhz_5g(struct hostapd_hw_modes *mode,
+		   struct wpa_scan_results *scan_res, int pri_chan,
+		   int sec_chan);
+int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, int end);
+int check_40mhz_2g4(struct hostapd_hw_modes *mode,
+		    struct wpa_scan_results *scan_res, int pri_chan,
+		    int sec_chan);
+
+#endif /* HW_FEATURES_COMMON_H */
diff --git a/src/common/sae.h b/src/common/sae.h
index 89d74ab..3ebf40c 100644
--- a/src/common/sae.h
+++ b/src/common/sae.h
@@ -44,6 +44,7 @@
 	u8 pmk[SAE_PMK_LEN];
 	struct crypto_bignum *peer_commit_scalar;
 	int group;
+	int sync;
 	struct sae_temporary_data *tmp;
 };
 
diff --git a/src/crypto/aes-eax.c b/src/crypto/aes-eax.c
index 21941c6..15a09f8 100644
--- a/src/crypto/aes-eax.c
+++ b/src/crypto/aes-eax.c
@@ -71,7 +71,7 @@
 
 	ret = 0;
 fail:
-	os_free(buf);
+	bin_clear_free(buf, buf_len);
 
 	return ret;
 }
diff --git a/src/crypto/aes-siv.c b/src/crypto/aes-siv.c
index ff4b823..5ac82c2 100644
--- a/src/crypto/aes-siv.c
+++ b/src/crypto/aes-siv.c
@@ -11,6 +11,7 @@
 #include "common.h"
 #include "aes.h"
 #include "aes_wrap.h"
+#include "aes_siv.h"
 
 
 static const u8 zero[AES_BLOCK_SIZE];
@@ -60,8 +61,8 @@
 }
 
 
-int aes_s2v(const u8 *key, size_t num_elem, const u8 *addr[],
-	    size_t *len, u8 *mac)
+static int aes_s2v(const u8 *key, size_t num_elem, const u8 *addr[],
+		   size_t *len, u8 *mac)
 {
 	u8 tmp[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE];
 	u8 *buf = NULL;
@@ -94,7 +95,7 @@
 		os_memcpy(buf, addr[i], len[i]);
 		xorend(buf, len[i], tmp, AES_BLOCK_SIZE);
 		ret = omac1_aes_128(key, buf, len[i], mac);
-		os_free(buf);
+		bin_clear_free(buf, len[i]);
 		return ret;
 	}
 
diff --git a/src/crypto/crypto_module_tests.c b/src/crypto/crypto_module_tests.c
new file mode 100644
index 0000000..7137c27
--- /dev/null
+++ b/src/crypto/crypto_module_tests.c
@@ -0,0 +1,1679 @@
+/*
+ * crypto module tests
+ * Copyright (c) 2014-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "crypto/aes_siv.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/aes.h"
+#include "crypto/ms_funcs.h"
+#include "crypto/crypto.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+
+
+static int test_siv(void)
+{
+#ifdef CONFIG_MESH
+	/* RFC 5297, A.1. Deterministic Authenticated Encryption Example */
+	u8 key[] = {
+		0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
+		0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0,
+		0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+		0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+	};
+	u8 ad[] = {
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+		0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+		0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27
+	};
+	u8 plaintext[] = {
+		0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+		0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee
+	};
+	u8 iv_c[] = {
+		0x85, 0x63, 0x2d, 0x07, 0xc6, 0xe8, 0xf3, 0x7f,
+		0x95, 0x0a, 0xcd, 0x32, 0x0a, 0x2e, 0xcc, 0x93,
+		0x40, 0xc0, 0x2b, 0x96, 0x90, 0xc4, 0xdc, 0x04,
+		0xda, 0xef, 0x7f, 0x6a, 0xfe, 0x5c
+	};
+	/* RFC 5297, A.2. Nonce-Based Authenticated Encryption Example */
+	u8 key_2[] = {
+		0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79, 0x78,
+		0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x70,
+		0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+		0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f
+	};
+	u8 ad1_2[] = {
+		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+		0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+		0xde, 0xad, 0xda, 0xda, 0xde, 0xad, 0xda, 0xda,
+		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
+		0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00
+	};
+	u8 ad2_2[] = {
+		0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
+		0x90, 0xa0
+	};
+	u8 nonce_2[] = {
+		0x09, 0xf9, 0x11, 0x02, 0x9d, 0x74, 0xe3, 0x5b,
+		0xd8, 0x41, 0x56, 0xc5, 0x63, 0x56, 0x88, 0xc0
+	};
+	u8 plaintext_2[] = {
+		0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20,
+		0x73, 0x6f, 0x6d, 0x65, 0x20, 0x70, 0x6c, 0x61,
+		0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x20, 0x74,
+		0x6f, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70,
+		0x74, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20,
+		0x53, 0x49, 0x56, 0x2d, 0x41, 0x45, 0x53
+	};
+	u8 iv_c_2[] = {
+		0x7b, 0xdb, 0x6e, 0x3b, 0x43, 0x26, 0x67, 0xeb,
+		0x06, 0xf4, 0xd1, 0x4b, 0xff, 0x2f, 0xbd, 0x0f,
+		0xcb, 0x90, 0x0f, 0x2f, 0xdd, 0xbe, 0x40, 0x43,
+		0x26, 0x60, 0x19, 0x65, 0xc8, 0x89, 0xbf, 0x17,
+		0xdb, 0xa7, 0x7c, 0xeb, 0x09, 0x4f, 0xa6, 0x63,
+		0xb7, 0xa3, 0xf7, 0x48, 0xba, 0x8a, 0xf8, 0x29,
+		0xea, 0x64, 0xad, 0x54, 0x4a, 0x27, 0x2e, 0x9c,
+		0x48, 0x5b, 0x62, 0xa3, 0xfd, 0x5c, 0x0d
+	};
+	u8 out[2 * AES_BLOCK_SIZE + sizeof(plaintext_2)];
+	const u8 *addr[3];
+	size_t len[3];
+
+	/* RFC 5297, A.1. Deterministic Authenticated Encryption Example */
+	addr[0] = ad;
+	len[0] = sizeof(ad);
+
+	if (aes_siv_encrypt(key, plaintext, sizeof(plaintext),
+			    1, addr, len, out)) {
+		wpa_printf(MSG_ERROR, "AES-SIV mode encryption failed");
+		return 1;
+	}
+	if (os_memcmp(out, iv_c, sizeof(iv_c)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "AES-SIV mode encryption returned invalid cipher text");
+		return 1;
+	}
+
+	if (aes_siv_decrypt(key, iv_c, sizeof(iv_c), 1, addr, len, out)) {
+		wpa_printf(MSG_ERROR, "AES-SIV mode decryption failed");
+		return 1;
+	}
+	if (os_memcmp(out, plaintext, sizeof(plaintext)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "AES-SIV mode decryption returned invalid plain text");
+		return 1;
+	}
+
+	/* RFC 5297, A.2. Nonce-Based Authenticated Encryption Example */
+	addr[0] = ad1_2;
+	len[0] = sizeof(ad1_2);
+	addr[1] = ad2_2;
+	len[1] = sizeof(ad2_2);
+	addr[2] = nonce_2;
+	len[2] = sizeof(nonce_2);
+
+	if (aes_siv_encrypt(key_2, plaintext_2, sizeof(plaintext_2),
+			    3, addr, len, out)) {
+		wpa_printf(MSG_ERROR, "AES-SIV mode encryption failed");
+		return 1;
+	}
+	if (os_memcmp(out, iv_c_2, sizeof(iv_c_2)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "AES-SIV mode encryption returned invalid cipher text");
+		return 1;
+	}
+
+	if (aes_siv_decrypt(key_2, iv_c_2, sizeof(iv_c_2), 3, addr, len, out)) {
+		wpa_printf(MSG_ERROR, "AES-SIV mode decryption failed");
+		return 1;
+	}
+	if (os_memcmp(out, plaintext_2, sizeof(plaintext_2)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "AES-SIV mode decryption returned invalid plain text");
+		return 1;
+	}
+
+	wpa_printf(MSG_INFO, "AES-SIV test cases passed");
+#endif /* CONFIG_MESH */
+
+	return 0;
+}
+
+
+/* OMAC1 AES-128 test vectors from
+ * http://csrc.nist.gov/CryptoToolkit/modes/proposedmodes/omac/omac-ad.pdf
+ * which are same as the examples from NIST SP800-38B
+ * http://csrc.nist.gov/CryptoToolkit/modes/800-38_Series_Publications/SP800-38B.pdf
+ */
+
+struct omac1_test_vector {
+	u8 k[16];
+	u8 msg[64];
+	int msg_len;
+	u8 tag[16];
+};
+
+static struct omac1_test_vector omac1_test_vectors[] =
+{
+	{
+		{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+		  0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c },
+		{ },
+		0,
+		{ 0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28,
+		  0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46 }
+	},
+	{
+		{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+		  0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c },
+		{ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+		  0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a},
+		16,
+		{ 0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44,
+		  0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c }
+	},
+	{
+		{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+		  0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c },
+		{ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+		  0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+		  0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+		  0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+		  0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11 },
+		40,
+		{ 0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30,
+		  0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27 }
+	},
+	{
+		{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+		  0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c },
+		{ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+		  0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+		  0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+		  0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+		  0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+		  0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+		  0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+		  0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 },
+		64,
+		{ 0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92,
+		  0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe }
+	},
+};
+
+
+static int test_omac1_vector(struct omac1_test_vector *tv, unsigned int i)
+{
+	u8 key[] = {
+		0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+		0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+	};
+	u8 msg[] = { 0x12, 0x34, 0x56 };
+	u8 result[24], result2[24];
+	const u8 *addr[3];
+	size_t len[3];
+
+	if (omac1_aes_128(tv->k, tv->msg, tv->msg_len, result) ||
+	    os_memcmp(result, tv->tag, 16) != 0) {
+		wpa_printf(MSG_ERROR, "OMAC1-AES-128 test vector %u failed", i);
+		return 1;
+	}
+
+	if (tv->msg_len > 1) {
+
+		addr[0] = tv->msg;
+		len[0] = 1;
+		addr[1] = tv->msg + 1;
+		len[1] = tv->msg_len - 1;
+
+		if (omac1_aes_128_vector(tv->k, 2, addr, len, result) ||
+		    os_memcmp(result, tv->tag, 16) != 0) {
+			wpa_printf(MSG_ERROR,
+				   "OMAC1-AES-128(vector) test vector %u failed",
+				   i);
+			return 1;
+		}
+
+		addr[0] = tv->msg;
+		len[0] = tv->msg_len - 2;
+		addr[1] = tv->msg + tv->msg_len - 2;
+		len[1] = 1;
+		addr[2] = tv->msg + tv->msg_len - 1;
+		len[2] = 1;
+
+		if (omac1_aes_128_vector(tv->k, 3, addr, len, result) ||
+		    os_memcmp(result, tv->tag, 16) != 0) {
+			wpa_printf(MSG_ERROR,
+				   "OMAC1-AES-128(vector2) test vector %u failed",
+				   i);
+			return 1;
+		}
+	}
+
+	addr[0] = &msg[0];
+	len[0] = 1;
+	addr[1] = &msg[1];
+	len[1] = 1;
+	addr[2] = &msg[2];
+	len[2] = 1;
+	if (omac1_aes_128(key, msg, sizeof(msg), result) ||
+	    omac1_aes_128_vector(key, 3, addr, len, result2) ||
+	    os_memcmp(result, result2, 16) != 0) {
+		wpa_printf(MSG_ERROR, "OMAC1-AES-128 short test mismatch");
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static int test_omac1(void)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(omac1_test_vectors); i++) {
+		if (test_omac1_vector(&omac1_test_vectors[i], i))
+			return 1;
+	}
+
+	wpa_printf(MSG_INFO, "OMAC1-AES-128 test cases passed");
+
+	return 0;
+}
+
+
+static int test_eax(void)
+{
+#ifdef EAP_PSK
+	u8 msg[] = { 0xF7, 0xFB };
+	u8 key[] = { 0x91, 0x94, 0x5D, 0x3F, 0x4D, 0xCB, 0xEE, 0x0B,
+		     0xF4, 0x5E, 0xF5, 0x22, 0x55, 0xF0, 0x95, 0xA4 };
+	u8 nonce[] = { 0xBE, 0xCA, 0xF0, 0x43, 0xB0, 0xA2, 0x3D, 0x84,
+		       0x31, 0x94, 0xBA, 0x97, 0x2C, 0x66, 0xDE, 0xBD };
+	u8 hdr[] = { 0xFA, 0x3B, 0xFD, 0x48, 0x06, 0xEB, 0x53, 0xFA };
+	u8 cipher[] = { 0x19, 0xDD, 0x5C, 0x4C, 0x93, 0x31, 0x04, 0x9D,
+			0x0B, 0xDA, 0xB0, 0x27, 0x74, 0x08, 0xF6, 0x79,
+			0x67, 0xE5 };
+	u8 data[sizeof(msg)], tag[AES_BLOCK_SIZE];
+
+	os_memcpy(data, msg, sizeof(msg));
+	if (aes_128_eax_encrypt(key, nonce, sizeof(nonce), hdr, sizeof(hdr),
+				data, sizeof(data), tag)) {
+		wpa_printf(MSG_ERROR, "AES-128 EAX mode encryption failed");
+		return 1;
+	}
+	if (os_memcmp(data, cipher, sizeof(data)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "AES-128 EAX mode encryption returned invalid cipher text");
+		return 1;
+	}
+	if (os_memcmp(tag, cipher + sizeof(data), AES_BLOCK_SIZE) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "AES-128 EAX mode encryption returned invalid tag");
+		return 1;
+	}
+
+	if (aes_128_eax_decrypt(key, nonce, sizeof(nonce), hdr, sizeof(hdr),
+				data, sizeof(data), tag)) {
+		wpa_printf(MSG_ERROR, "AES-128 EAX mode decryption failed");
+		return 1;
+	}
+	if (os_memcmp(data, msg, sizeof(data)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "AES-128 EAX mode decryption returned invalid plain text");
+		return 1;
+	}
+
+	wpa_printf(MSG_INFO, "AES-128 EAX mode test cases passed");
+#endif /* EAP_PSK */
+
+	return 0;
+}
+
+
+static int test_cbc(void)
+{
+	struct cbc_test_vector {
+		u8 key[16];
+		u8 iv[16];
+		u8 plain[32];
+		u8 cipher[32];
+		size_t len;
+	} vectors[] = {
+		{
+			{ 0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b,
+			  0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06 },
+			{ 0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30,
+			  0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41 },
+			"Single block msg",
+			{ 0xe3, 0x53, 0x77, 0x9c, 0x10, 0x79, 0xae, 0xb8,
+			  0x27, 0x08, 0x94, 0x2d, 0xbe, 0x77, 0x18, 0x1a },
+			16
+		},
+		{
+			{ 0xc2, 0x86, 0x69, 0x6d, 0x88, 0x7c, 0x9a, 0xa0,
+			  0x61, 0x1b, 0xbb, 0x3e, 0x20, 0x25, 0xa4, 0x5a },
+			{ 0x56, 0x2e, 0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28,
+			  0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58 },
+			{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+			  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+			  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+			  0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f },
+			{ 0xd2, 0x96, 0xcd, 0x94, 0xc2, 0xcc, 0xcf, 0x8a,
+			  0x3a, 0x86, 0x30, 0x28, 0xb5, 0xe1, 0xdc, 0x0a,
+			  0x75, 0x86, 0x60, 0x2d, 0x25, 0x3c, 0xff, 0xf9,
+			  0x1b, 0x82, 0x66, 0xbe, 0xa6, 0xd6, 0x1a, 0xb1 },
+			32
+		}
+	};
+	int ret = 0;
+	u8 *buf;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(vectors); i++) {
+		struct cbc_test_vector *tv = &vectors[i];
+
+		buf = os_malloc(tv->len);
+		if (buf == NULL) {
+			ret++;
+			break;
+		}
+
+		os_memcpy(buf, tv->plain, tv->len);
+		if (aes_128_cbc_encrypt(tv->key, tv->iv, buf, tv->len) ||
+		    os_memcmp(buf, tv->cipher, tv->len) != 0) {
+			wpa_printf(MSG_ERROR, "AES-CBC encrypt %d failed", i);
+			ret++;
+		}
+
+		os_memcpy(buf, tv->cipher, tv->len);
+		if (aes_128_cbc_decrypt(tv->key, tv->iv, buf, tv->len) ||
+		    os_memcmp(buf, tv->plain, tv->len) != 0) {
+			wpa_printf(MSG_ERROR, "AES-CBC decrypt %d failed", i);
+			ret++;
+		}
+
+		os_free(buf);
+	}
+
+	return ret;
+}
+
+
+static int test_ecb(void)
+{
+#ifdef EAP_PSK
+	struct ecb_test_vector {
+		char *key;
+		char *plaintext;
+		char *ciphertext;
+	} vectors[] = {
+		/* CAVS 11.1 - ECBGFSbox128.rsp */
+		{
+			"00000000000000000000000000000000",
+			"f34481ec3cc627bacd5dc3fb08f273e6",
+			"0336763e966d92595a567cc9ce537f5e"
+		},
+		{
+			"00000000000000000000000000000000",
+			"9798c4640bad75c7c3227db910174e72",
+			"a9a1631bf4996954ebc093957b234589"
+		},
+		{
+			"00000000000000000000000000000000",
+			"96ab5c2ff612d9dfaae8c31f30c42168",
+			"ff4f8391a6a40ca5b25d23bedd44a597"
+		},
+		{
+			"00000000000000000000000000000000",
+			"6a118a874519e64e9963798a503f1d35",
+			"dc43be40be0e53712f7e2bf5ca707209"
+		},
+		{
+			"00000000000000000000000000000000",
+			"cb9fceec81286ca3e989bd979b0cb284",
+			"92beedab1895a94faa69b632e5cc47ce"
+		},
+		{
+			"00000000000000000000000000000000",
+			"b26aeb1874e47ca8358ff22378f09144",
+			"459264f4798f6a78bacb89c15ed3d601"
+		},
+		{
+			"00000000000000000000000000000000",
+			"58c8e00b2631686d54eab84b91f0aca1",
+			"08a4e2efec8a8e3312ca7460b9040bbf"
+		},
+		/* CAVS 11.1 - ECBKeySbox128.rsp */
+		{
+			"10a58869d74be5a374cf867cfb473859",
+			"00000000000000000000000000000000",
+			"6d251e6944b051e04eaa6fb4dbf78465"
+		},
+		{
+			"caea65cdbb75e9169ecd22ebe6e54675",
+			"00000000000000000000000000000000",
+			"6e29201190152df4ee058139def610bb",
+		}
+	};
+	int ret = 0;
+	unsigned int i;
+	u8 key[16], plain[16], cipher[16], out[16];
+
+	for (i = 0; i < ARRAY_SIZE(vectors); i++) {
+		struct ecb_test_vector *tv = &vectors[i];
+
+		if (hexstr2bin(tv->key, key, sizeof(key)) ||
+		    hexstr2bin(tv->plaintext, plain, sizeof(plain)) ||
+		    hexstr2bin(tv->ciphertext, cipher, sizeof(cipher))) {
+			wpa_printf(MSG_ERROR, "Invalid AES-ECB test vector %u",
+				   i);
+			ret++;
+			continue;
+		}
+
+		if (aes_128_encrypt_block(key, plain, out) < 0 ||
+		    os_memcmp(out, cipher, 16) != 0) {
+			wpa_printf(MSG_ERROR, "AES-ECB encrypt %u failed", i);
+			ret++;
+		}
+	}
+
+	if (!ret)
+		wpa_printf(MSG_INFO, "AES ECB mode test cases passed");
+
+	return ret;
+#endif /* EAP_PSK */
+
+	return 0;
+}
+
+
+static int test_key_wrap(void)
+{
+	int ret = 0;
+
+	/* RFC 3394 - Test vector 4.1 */
+	u8 kek41[] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+	};
+	u8 plain41[] = {
+		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+		0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
+	};
+	u8 crypt41[] = {
+		0x1F, 0xA6, 0x8B, 0x0A, 0x81, 0x12, 0xB4, 0x47,
+		0xAE, 0xF3, 0x4B, 0xD8, 0xFB, 0x5A, 0x7B, 0x82,
+		0x9D, 0x3E, 0x86, 0x23, 0x71, 0xD2, 0xCF, 0xE5
+	};
+	/* RFC 3394 - Test vector 4.2 */
+	u8 kek42[] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17
+	};
+	u8 plain42[] = {
+		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+		0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
+	};
+	u8 crypt42[] = {
+		0x96, 0x77, 0x8B, 0x25, 0xAE, 0x6C, 0xA4, 0x35,
+		0xF9, 0x2B, 0x5B, 0x97, 0xC0, 0x50, 0xAE, 0xD2,
+		0x46, 0x8A, 0xB8, 0xA1, 0x7A, 0xD8, 0x4E, 0x5D
+	};
+	/* RFC 3394 - Test vector 4.3 */
+	u8 kek43[] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+		0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
+	};
+	u8 plain43[] = {
+		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+		0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
+	};
+	u8 crypt43[] = {
+		0x64, 0xE8, 0xC3, 0xF9, 0xCE, 0x0F, 0x5B, 0xA2,
+		0x63, 0xE9, 0x77, 0x79, 0x05, 0x81, 0x8A, 0x2A,
+		0x93, 0xC8, 0x19, 0x1E, 0x7D, 0x6E, 0x8A, 0xE7,
+	};
+	/* RFC 3394 - Test vector 4.4 */
+	u8 kek44[] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17
+	};
+	u8 plain44[] = {
+		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+		0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
+	};
+	u8 crypt44[] = {
+		0x03, 0x1D, 0x33, 0x26, 0x4E, 0x15, 0xD3, 0x32,
+		0x68, 0xF2, 0x4E, 0xC2, 0x60, 0x74, 0x3E, 0xDC,
+		0xE1, 0xC6, 0xC7, 0xDD, 0xEE, 0x72, 0x5A, 0x93,
+		0x6B, 0xA8, 0x14, 0x91, 0x5C, 0x67, 0x62, 0xD2
+	};
+	/* RFC 3394 - Test vector 4.5 */
+	u8 kek45[] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+		0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
+	};
+	u8 plain45[] = {
+		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+		0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
+	};
+	u8 crypt45[] = {
+		0xA8, 0xF9, 0xBC, 0x16, 0x12, 0xC6, 0x8B, 0x3F,
+		0xF6, 0xE6, 0xF4, 0xFB, 0xE3, 0x0E, 0x71, 0xE4,
+		0x76, 0x9C, 0x8B, 0x80, 0xA3, 0x2C, 0xB8, 0x95,
+		0x8C, 0xD5, 0xD1, 0x7D, 0x6B, 0x25, 0x4D, 0xA1,
+	};
+	/* RFC 3394 - Test vector 4.6 */
+	u8 kek46[] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+		0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
+	};
+	u8 plain46[] = {
+		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+		0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
+	};
+	u8 crypt46[] = {
+		0x28, 0xC9, 0xF4, 0x04, 0xC4, 0xB8, 0x10, 0xF4,
+		0xCB, 0xCC, 0xB3, 0x5C, 0xFB, 0x87, 0xF8, 0x26,
+		0x3F, 0x57, 0x86, 0xE2, 0xD8, 0x0E, 0xD3, 0x26,
+		0xCB, 0xC7, 0xF0, 0xE7, 0x1A, 0x99, 0xF4, 0x3B,
+		0xFB, 0x98, 0x8B, 0x9B, 0x7A, 0x02, 0xDD, 0x21
+	};
+	u8 result[40];
+
+	wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.1");
+	if (aes_wrap(kek41, sizeof(kek41), sizeof(plain41) / 8, plain41,
+		     result)) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-128 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, crypt41, sizeof(crypt41)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-128 failed");
+		ret++;
+	}
+	if (aes_unwrap(kek41, sizeof(kek41), sizeof(plain41) / 8, crypt41,
+		       result)) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-128 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, plain41, sizeof(plain41)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-128 failed");
+		ret++;
+	}
+
+	wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.2");
+	if (aes_wrap(kek42, sizeof(kek42), sizeof(plain42) / 8, plain42,
+		     result)) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-192 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, crypt42, sizeof(crypt42)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-192 failed");
+		ret++;
+	}
+	if (aes_unwrap(kek42, sizeof(kek42), sizeof(plain42) / 8, crypt42,
+		       result)) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-192 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, plain42, sizeof(plain42)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-192 failed");
+		ret++;
+	}
+
+	wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.3");
+	if (aes_wrap(kek43, sizeof(kek43), sizeof(plain43) / 8, plain43,
+		     result)) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-256 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, crypt43, sizeof(crypt43)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-256 failed");
+		ret++;
+	}
+	if (aes_unwrap(kek43, sizeof(kek43), sizeof(plain43) / 8, crypt43,
+		       result)) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-256 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, plain43, sizeof(plain43)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-256 failed");
+		ret++;
+	}
+
+	wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.4");
+	if (aes_wrap(kek44, sizeof(kek44), sizeof(plain44) / 8, plain44,
+		     result)) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-192 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, crypt44, sizeof(crypt44)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-192 failed");
+		ret++;
+	}
+	if (aes_unwrap(kek44, sizeof(kek44), sizeof(plain44) / 8, crypt44,
+		       result)) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-192 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, plain44, sizeof(plain44)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-192 failed");
+		ret++;
+	}
+
+	wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.5");
+	if (aes_wrap(kek45, sizeof(kek45), sizeof(plain45) / 8, plain45,
+		     result)) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-256 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, crypt45, sizeof(crypt45)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-256 failed");
+		ret++;
+	}
+	if (aes_unwrap(kek45, sizeof(kek45), sizeof(plain45) / 8, crypt45,
+		       result)) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-256 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, plain45, sizeof(plain45)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-256 failed");
+		ret++;
+	}
+
+	wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.6");
+	if (aes_wrap(kek46, sizeof(kek46), sizeof(plain46) / 8, plain46,
+		     result)) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-256 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, crypt46, sizeof(crypt46)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-256 failed");
+		ret++;
+	}
+	if (aes_unwrap(kek46, sizeof(kek46), sizeof(plain46) / 8, crypt46,
+		       result)) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-256 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, plain46, sizeof(plain46)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-256 failed");
+		ret++;
+	}
+
+	if (!ret)
+		wpa_printf(MSG_INFO, "AES key wrap/unwrap test cases passed");
+
+	return ret;
+}
+
+
+static int test_md5(void)
+{
+	struct {
+		char *data;
+		char *hash;
+	} tests[] = {
+		{
+			"",
+			"\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04"
+			"\xe9\x80\x09\x98\xec\xf8\x42\x7e"
+		},
+		{
+			"a",
+			"\x0c\xc1\x75\xb9\xc0\xf1\xb6\xa8"
+			"\x31\xc3\x99\xe2\x69\x77\x26\x61"
+		},
+		{
+			"abc",
+			"\x90\x01\x50\x98\x3c\xd2\x4f\xb0"
+			"\xd6\x96\x3f\x7d\x28\xe1\x7f\x72"
+		},
+		{
+			"message digest",
+			"\xf9\x6b\x69\x7d\x7c\xb7\x93\x8d"
+			"\x52\x5a\x2f\x31\xaa\xf1\x61\xd0"
+		},
+		{
+			"abcdefghijklmnopqrstuvwxyz",
+			"\xc3\xfc\xd3\xd7\x61\x92\xe4\x00"
+			"\x7d\xfb\x49\x6c\xca\x67\xe1\x3b"
+		},
+		{
+			"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+			"0123456789",
+			"\xd1\x74\xab\x98\xd2\x77\xd9\xf5"
+			"\xa5\x61\x1c\x2c\x9f\x41\x9d\x9f"
+		},
+		{
+			"12345678901234567890123456789012345678901234567890"
+			"123456789012345678901234567890",
+			"\x57\xed\xf4\xa2\x2b\xe3\xc9\x55"
+			"\xac\x49\xda\x2e\x21\x07\xb6\x7a"
+		}
+	};
+	unsigned int i;
+	u8 hash[16];
+	const u8 *addr[2];
+	size_t len[2];
+	int errors = 0;
+
+	for (i = 0; i < ARRAY_SIZE(tests); i++) {
+		wpa_printf(MSG_INFO, "MD5 test case %d", i);
+
+		addr[0] = (u8 *) tests[i].data;
+		len[0] = strlen(tests[i].data);
+		if (md5_vector(1, addr, len, hash) < 0 ||
+		    os_memcmp(hash, tests[i].hash, 16) != 0) {
+			wpa_printf(MSG_INFO, " FAIL");
+			errors++;
+		} else
+			wpa_printf(MSG_INFO, " OK");
+
+		if (len[0]) {
+			addr[0] = (u8 *) tests[i].data;
+			len[0] = strlen(tests[i].data);
+			addr[1] = (u8 *) tests[i].data + 1;
+			len[1] = strlen(tests[i].data) - 1;
+			if (md5_vector(1, addr, len, hash) < 0 ||
+			    os_memcmp(hash, tests[i].hash, 16) != 0) {
+				wpa_printf(MSG_INFO, " FAIL");
+				errors++;
+			} else
+				wpa_printf(MSG_INFO, " OK");
+		}
+	}
+
+	if (!errors)
+		wpa_printf(MSG_INFO, "MD5 test cases passed");
+
+	return errors;
+}
+
+
+static int test_eap_fast(void)
+{
+#ifdef EAP_FAST
+	/* RFC 4851, Appendix B.1 */
+	const u8 pac_key[] = {
+		0x0B, 0x97, 0x39, 0x0F, 0x37, 0x51, 0x78, 0x09,
+		0x81, 0x1E, 0xFD, 0x9C, 0x6E, 0x65, 0x94, 0x2B,
+		0x63, 0x2C, 0xE9, 0x53, 0x89, 0x38, 0x08, 0xBA,
+		0x36, 0x0B, 0x03, 0x7C, 0xD1, 0x85, 0xE4, 0x14
+	};
+	const u8 seed[] = {
+		0x3F, 0xFB, 0x11, 0xC4, 0x6C, 0xBF, 0xA5, 0x7A,
+		0x54, 0x40, 0xDA, 0xE8, 0x22, 0xD3, 0x11, 0xD3,
+		0xF7, 0x6D, 0xE4, 0x1D, 0xD9, 0x33, 0xE5, 0x93,
+		0x70, 0x97, 0xEB, 0xA9, 0xB3, 0x66, 0xF4, 0x2A,
+		0x00, 0x00, 0x00, 0x02, 0x6A, 0x66, 0x43, 0x2A,
+		0x8D, 0x14, 0x43, 0x2C, 0xEC, 0x58, 0x2D, 0x2F,
+		0xC7, 0x9C, 0x33, 0x64, 0xBA, 0x04, 0xAD, 0x3A,
+		0x52, 0x54, 0xD6, 0xA5, 0x79, 0xAD, 0x1E, 0x00
+	};
+	const u8 master_secret[] = {
+		0x4A, 0x1A, 0x51, 0x2C, 0x01, 0x60, 0xBC, 0x02,
+		0x3C, 0xCF, 0xBC, 0x83, 0x3F, 0x03, 0xBC, 0x64,
+		0x88, 0xC1, 0x31, 0x2F, 0x0B, 0xA9, 0xA2, 0x77,
+		0x16, 0xA8, 0xD8, 0xE8, 0xBD, 0xC9, 0xD2, 0x29,
+		0x38, 0x4B, 0x7A, 0x85, 0xBE, 0x16, 0x4D, 0x27,
+		0x33, 0xD5, 0x24, 0x79, 0x87, 0xB1, 0xC5, 0xA2
+	};
+	const u8 key_block[] = {
+		0x59, 0x59, 0xBE, 0x8E, 0x41, 0x3A, 0x77, 0x74,
+		0x8B, 0xB2, 0xE5, 0xD3, 0x60, 0xAC, 0x4D, 0x35,
+		0xDF, 0xFB, 0xC8, 0x1E, 0x9C, 0x24, 0x9C, 0x8B,
+		0x0E, 0xC3, 0x1D, 0x72, 0xC8, 0x84, 0x9D, 0x57,
+		0x48, 0x51, 0x2E, 0x45, 0x97, 0x6C, 0x88, 0x70,
+		0xBE, 0x5F, 0x01, 0xD3, 0x64, 0xE7, 0x4C, 0xBB,
+		0x11, 0x24, 0xE3, 0x49, 0xE2, 0x3B, 0xCD, 0xEF,
+		0x7A, 0xB3, 0x05, 0x39, 0x5D, 0x64, 0x8A, 0x44,
+		0x11, 0xB6, 0x69, 0x88, 0x34, 0x2E, 0x8E, 0x29,
+		0xD6, 0x4B, 0x7D, 0x72, 0x17, 0x59, 0x28, 0x05,
+		0xAF, 0xF9, 0xB7, 0xFF, 0x66, 0x6D, 0xA1, 0x96,
+		0x8F, 0x0B, 0x5E, 0x06, 0x46, 0x7A, 0x44, 0x84,
+		0x64, 0xC1, 0xC8, 0x0C, 0x96, 0x44, 0x09, 0x98,
+		0xFF, 0x92, 0xA8, 0xB4, 0xC6, 0x42, 0x28, 0x71
+	};
+	const u8 sks[] = {
+		0xD6, 0x4B, 0x7D, 0x72, 0x17, 0x59, 0x28, 0x05,
+		0xAF, 0xF9, 0xB7, 0xFF, 0x66, 0x6D, 0xA1, 0x96,
+		0x8F, 0x0B, 0x5E, 0x06, 0x46, 0x7A, 0x44, 0x84,
+		0x64, 0xC1, 0xC8, 0x0C, 0x96, 0x44, 0x09, 0x98,
+		0xFF, 0x92, 0xA8, 0xB4, 0xC6, 0x42, 0x28, 0x71
+	};
+	const u8 isk[] = {
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+	};
+	const u8 imck[] = {
+		0x16, 0x15, 0x3C, 0x3F, 0x21, 0x55, 0xEF, 0xD9,
+		0x7F, 0x34, 0xAE, 0xC8, 0x1A, 0x4E, 0x66, 0x80,
+		0x4C, 0xC3, 0x76, 0xF2, 0x8A, 0xA9, 0x6F, 0x96,
+		0xC2, 0x54, 0x5F, 0x8C, 0xAB, 0x65, 0x02, 0xE1,
+		0x18, 0x40, 0x7B, 0x56, 0xBE, 0xEA, 0xA7, 0xC5,
+		0x76, 0x5D, 0x8F, 0x0B, 0xC5, 0x07, 0xC6, 0xB9,
+		0x04, 0xD0, 0x69, 0x56, 0x72, 0x8B, 0x6B, 0xB8,
+		0x15, 0xEC, 0x57, 0x7B
+	};
+	const u8 msk[] = {
+		0x4D, 0x83, 0xA9, 0xBE, 0x6F, 0x8A, 0x74, 0xED,
+		0x6A, 0x02, 0x66, 0x0A, 0x63, 0x4D, 0x2C, 0x33,
+		0xC2, 0xDA, 0x60, 0x15, 0xC6, 0x37, 0x04, 0x51,
+		0x90, 0x38, 0x63, 0xDA, 0x54, 0x3E, 0x14, 0xB9,
+		0x27, 0x99, 0x18, 0x1E, 0x07, 0xBF, 0x0F, 0x5A,
+		0x5E, 0x3C, 0x32, 0x93, 0x80, 0x8C, 0x6C, 0x49,
+		0x67, 0xED, 0x24, 0xFE, 0x45, 0x40, 0xA0, 0x59,
+		0x5E, 0x37, 0xC2, 0xE9, 0xD0, 0x5D, 0x0A, 0xE3
+	};
+	const u8 emsk[] = {
+		0x3A, 0xD4, 0xAB, 0xDB, 0x76, 0xB2, 0x7F, 0x3B,
+		0xEA, 0x32, 0x2C, 0x2B, 0x74, 0xF4, 0x28, 0x55,
+		0xEF, 0x2D, 0xBA, 0x78, 0xC9, 0x57, 0x2F, 0x0D,
+		0x06, 0xCD, 0x51, 0x7C, 0x20, 0x93, 0x98, 0xA9,
+		0x76, 0xEA, 0x70, 0x21, 0xD7, 0x0E, 0x25, 0x54,
+		0x97, 0xED, 0xB2, 0x8A, 0xF6, 0xED, 0xFD, 0x0A,
+		0x2A, 0xE7, 0xA1, 0x58, 0x90, 0x10, 0x50, 0x44,
+		0xB3, 0x82, 0x85, 0xDB, 0x06, 0x14, 0xD2, 0xF9
+	};
+	/* RFC 4851, Appendix B.2 */
+	u8 tlv[] = {
+		0x80, 0x0C, 0x00, 0x38, 0x00, 0x01, 0x01, 0x00,
+		0xD8, 0x6A, 0x8C, 0x68, 0x3C, 0x32, 0x31, 0xA8,
+		0x56, 0x63, 0xB6, 0x40, 0x21, 0xFE, 0x21, 0x14,
+		0x4E, 0xE7, 0x54, 0x20, 0x79, 0x2D, 0x42, 0x62,
+		0xC9, 0xBF, 0x53, 0x7F, 0x54, 0xFD, 0xAC, 0x58,
+		0x43, 0x24, 0x6E, 0x30, 0x92, 0x17, 0x6D, 0xCF,
+		0xE6, 0xE0, 0x69, 0xEB, 0x33, 0x61, 0x6A, 0xCC,
+		0x05, 0xC5, 0x5B, 0xB7
+	};
+	const u8 compound_mac[] = {
+		0x43, 0x24, 0x6E, 0x30, 0x92, 0x17, 0x6D, 0xCF,
+		0xE6, 0xE0, 0x69, 0xEB, 0x33, 0x61, 0x6A, 0xCC,
+		0x05, 0xC5, 0x5B, 0xB7
+	};
+	u8 buf[512];
+	const u8 *simck, *cmk;
+	int errors = 0;
+
+	wpa_printf(MSG_INFO, "EAP-FAST test cases");
+
+	wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / master_secret");
+	if (sha1_t_prf(pac_key, sizeof(pac_key),
+		       "PAC to master secret label hash",
+		       seed, sizeof(seed), buf, sizeof(master_secret)) < 0 ||
+	    os_memcmp(master_secret, buf, sizeof(master_secret)) != 0) {
+		wpa_printf(MSG_INFO, "T-PRF test - FAILED!");
+		errors++;
+	}
+
+	wpa_printf(MSG_INFO, "- PRF (TLS, SHA1/MD5) test case / key_block");
+	if (tls_prf_sha1_md5(master_secret, sizeof(master_secret),
+			     "key expansion", seed, sizeof(seed),
+			     buf, sizeof(key_block)) ||
+	    os_memcmp(key_block, buf, sizeof(key_block)) != 0) {
+		wpa_printf(MSG_INFO, "PRF test - FAILED!");
+		errors++;
+	}
+
+	wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / IMCK");
+	if (sha1_t_prf(sks, sizeof(sks), "Inner Methods Compound Keys",
+		       isk, sizeof(isk), buf, sizeof(imck)) < 0 ||
+	    os_memcmp(imck, buf, sizeof(imck)) != 0) {
+		wpa_printf(MSG_INFO, "T-PRF test - FAILED!");
+		errors++;
+	}
+
+	simck = imck;
+	cmk = imck + 40;
+
+	wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / MSK");
+	if (sha1_t_prf(simck, 40, "Session Key Generating Function",
+		       (u8 *) "", 0, buf, sizeof(msk)) < 0 ||
+	    os_memcmp(msk, buf, sizeof(msk)) != 0) {
+		wpa_printf(MSG_INFO, "T-PRF test - FAILED!");
+		errors++;
+	}
+
+	wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / EMSK");
+	if (sha1_t_prf(simck, 40, "Extended Session Key Generating Function",
+		       (u8 *) "", 0, buf, sizeof(msk)) < 0 ||
+	    os_memcmp(emsk, buf, sizeof(emsk)) != 0) {
+		wpa_printf(MSG_INFO, "T-PRF test - FAILED!");
+		errors++;
+	}
+
+	wpa_printf(MSG_INFO, "- Compound MAC test case");
+	os_memset(tlv + sizeof(tlv) - 20, 0, 20);
+	if (hmac_sha1(cmk, 20, tlv, sizeof(tlv), tlv + sizeof(tlv) - 20) < 0 ||
+	    os_memcmp(tlv + sizeof(tlv) - 20, compound_mac,
+		      sizeof(compound_mac)) != 0) {
+		wpa_printf(MSG_INFO, "Compound MAC test - FAILED!");
+		errors++;
+	}
+
+	return errors;
+#else /* EAP_FAST */
+	return 0;
+#endif /* EAP_FAST */
+}
+
+
+static u8 key0[] =
+{
+	0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+	0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+	0x0b, 0x0b, 0x0b, 0x0b
+};
+static u8 data0[] = "Hi There";
+static u8 prf0[] =
+{
+	0xbc, 0xd4, 0xc6, 0x50, 0xb3, 0x0b, 0x96, 0x84,
+	0x95, 0x18, 0x29, 0xe0, 0xd7, 0x5f, 0x9d, 0x54,
+	0xb8, 0x62, 0x17, 0x5e, 0xd9, 0xf0, 0x06, 0x06,
+	0xe1, 0x7d, 0x8d, 0xa3, 0x54, 0x02, 0xff, 0xee,
+	0x75, 0xdf, 0x78, 0xc3, 0xd3, 0x1e, 0x0f, 0x88,
+	0x9f, 0x01, 0x21, 0x20, 0xc0, 0x86, 0x2b, 0xeb,
+	0x67, 0x75, 0x3e, 0x74, 0x39, 0xae, 0x24, 0x2e,
+	0xdb, 0x83, 0x73, 0x69, 0x83, 0x56, 0xcf, 0x5a
+};
+
+static u8 key1[] = "Jefe";
+static u8 data1[] = "what do ya want for nothing?";
+static u8 prf1[] =
+{
+	0x51, 0xf4, 0xde, 0x5b, 0x33, 0xf2, 0x49, 0xad,
+	0xf8, 0x1a, 0xeb, 0x71, 0x3a, 0x3c, 0x20, 0xf4,
+	0xfe, 0x63, 0x14, 0x46, 0xfa, 0xbd, 0xfa, 0x58,
+	0x24, 0x47, 0x59, 0xae, 0x58, 0xef, 0x90, 0x09,
+	0xa9, 0x9a, 0xbf, 0x4e, 0xac, 0x2c, 0xa5, 0xfa,
+	0x87, 0xe6, 0x92, 0xc4, 0x40, 0xeb, 0x40, 0x02,
+	0x3e, 0x7b, 0xab, 0xb2, 0x06, 0xd6, 0x1d, 0xe7,
+	0xb9, 0x2f, 0x41, 0x52, 0x90, 0x92, 0xb8, 0xfc
+};
+
+
+static u8 key2[] =
+{
+	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+	0xaa, 0xaa, 0xaa, 0xaa
+};
+static u8 data2[] =
+{
+	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+	0xdd, 0xdd
+};
+static u8 prf2[] =
+{
+	0xe1, 0xac, 0x54, 0x6e, 0xc4, 0xcb, 0x63, 0x6f,
+	0x99, 0x76, 0x48, 0x7b, 0xe5, 0xc8, 0x6b, 0xe1,
+	0x7a, 0x02, 0x52, 0xca, 0x5d, 0x8d, 0x8d, 0xf1,
+	0x2c, 0xfb, 0x04, 0x73, 0x52, 0x52, 0x49, 0xce,
+	0x9d, 0xd8, 0xd1, 0x77, 0xea, 0xd7, 0x10, 0xbc,
+	0x9b, 0x59, 0x05, 0x47, 0x23, 0x91, 0x07, 0xae,
+	0xf7, 0xb4, 0xab, 0xd4, 0x3d, 0x87, 0xf0, 0xa6,
+	0x8f, 0x1c, 0xbd, 0x9e, 0x2b, 0x6f, 0x76, 0x07
+};
+
+
+struct passphrase_test {
+	char *passphrase;
+	char *ssid;
+	char psk[32];
+};
+
+static struct passphrase_test passphrase_tests[] =
+{
+	{
+		"password",
+		"IEEE",
+		{
+			0xf4, 0x2c, 0x6f, 0xc5, 0x2d, 0xf0, 0xeb, 0xef,
+			0x9e, 0xbb, 0x4b, 0x90, 0xb3, 0x8a, 0x5f, 0x90,
+			0x2e, 0x83, 0xfe, 0x1b, 0x13, 0x5a, 0x70, 0xe2,
+			0x3a, 0xed, 0x76, 0x2e, 0x97, 0x10, 0xa1, 0x2e
+		}
+	},
+	{
+		"ThisIsAPassword",
+		"ThisIsASSID",
+		{
+			0x0d, 0xc0, 0xd6, 0xeb, 0x90, 0x55, 0x5e, 0xd6,
+			0x41, 0x97, 0x56, 0xb9, 0xa1, 0x5e, 0xc3, 0xe3,
+			0x20, 0x9b, 0x63, 0xdf, 0x70, 0x7d, 0xd5, 0x08,
+			0xd1, 0x45, 0x81, 0xf8, 0x98, 0x27, 0x21, 0xaf
+		}
+	},
+	{
+		"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+		"ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ",
+		{
+			0xbe, 0xcb, 0x93, 0x86, 0x6b, 0xb8, 0xc3, 0x83,
+			0x2c, 0xb7, 0x77, 0xc2, 0xf5, 0x59, 0x80, 0x7c,
+			0x8c, 0x59, 0xaf, 0xcb, 0x6e, 0xae, 0x73, 0x48,
+			0x85, 0x00, 0x13, 0x00, 0xa9, 0x81, 0xcc, 0x62
+		}
+	},
+};
+
+#define NUM_PASSPHRASE_TESTS ARRAY_SIZE(passphrase_tests)
+
+
+struct rfc6070_test {
+	char *p;
+	char *s;
+	int c;
+	char dk[32];
+	size_t dk_len;
+};
+
+static struct rfc6070_test rfc6070_tests[] =
+{
+	{
+		"password",
+		"salt",
+		1,
+		{
+			0x0c, 0x60, 0xc8, 0x0f, 0x96, 0x1f, 0x0e, 0x71,
+			0xf3, 0xa9, 0xb5, 0x24, 0xaf, 0x60, 0x12, 0x06,
+			0x2f, 0xe0, 0x37, 0xa6
+		},
+		20
+	},
+	{
+		"password",
+		"salt",
+		2,
+		{
+			0xea, 0x6c, 0x01, 0x4d, 0xc7, 0x2d, 0x6f, 0x8c,
+			0xcd, 0x1e, 0xd9, 0x2a, 0xce, 0x1d, 0x41, 0xf0,
+			0xd8, 0xde, 0x89, 0x57
+		},
+		20
+	},
+	{
+		"password",
+		"salt",
+		4096,
+		{
+			0x4b, 0x00, 0x79, 0x01, 0xb7, 0x65, 0x48, 0x9a,
+			0xbe, 0xad, 0x49, 0xd9, 0x26, 0xf7, 0x21, 0xd0,
+			0x65, 0xa4, 0x29, 0xc1
+		},
+		20
+	},
+#if 0 /* This takes quite long to derive.. */
+	{
+		"password",
+		"salt",
+		16777216,
+		{
+			0xee, 0xfe, 0x3d, 0x61, 0xcd, 0x4d, 0xa4, 0xe4,
+			0xe9, 0x94, 0x5b, 0x3d, 0x6b, 0xa2, 0x15, 0x8c,
+			0x26, 0x34, 0xe9, 0x84
+		},
+		20
+	},
+#endif
+	{
+		"passwordPASSWORDpassword",
+		"saltSALTsaltSALTsaltSALTsaltSALTsalt",
+		4096,
+		{
+			0x3d, 0x2e, 0xec, 0x4f, 0xe4, 0x1c, 0x84, 0x9b,
+			0x80, 0xc8, 0xd8, 0x36, 0x62, 0xc0, 0xe4, 0x4a,
+			0x8b, 0x29, 0x1a, 0x96, 0x4c, 0xf2, 0xf0, 0x70,
+			0x38
+		},
+		25
+	},
+#if 0 /* \0 not currently supported in passphrase parameters.. */
+	{
+		"pass\0word",
+		"sa\0lt",
+		4096,
+		{
+			0x56, 0xfa, 0x6a, 0xa7, 0x55, 0x48, 0x09, 0x9d,
+			0xcc, 0x37, 0xd7, 0xf0, 0x34, 0x25, 0xe0, 0xc3
+		},
+		16
+	},
+#endif
+};
+
+#define NUM_RFC6070_TESTS ARRAY_SIZE(rfc6070_tests)
+
+
+static int test_sha1(void)
+{
+	u8 res[512];
+	int ret = 0;
+	unsigned int i;
+
+	wpa_printf(MSG_INFO, "PRF-SHA1 test cases:");
+
+	if (sha1_prf(key0, sizeof(key0), "prefix", data0, sizeof(data0) - 1,
+		     res, sizeof(prf0)) == 0 &&
+	    os_memcmp(res, prf0, sizeof(prf0)) == 0)
+		wpa_printf(MSG_INFO, "Test case 0 - OK");
+	else {
+		wpa_printf(MSG_INFO, "Test case 0 - FAILED!");
+		ret++;
+	}
+
+	if (sha1_prf(key1, sizeof(key1) - 1, "prefix", data1, sizeof(data1) - 1,
+		     res, sizeof(prf1)) == 0 &&
+	    os_memcmp(res, prf1, sizeof(prf1)) == 0)
+		wpa_printf(MSG_INFO, "Test case 1 - OK");
+	else {
+		wpa_printf(MSG_INFO, "Test case 1 - FAILED!");
+		ret++;
+	}
+
+	if (sha1_prf(key2, sizeof(key2), "prefix", data2, sizeof(data2),
+		     res, sizeof(prf2)) == 0 &&
+	    os_memcmp(res, prf2, sizeof(prf2)) == 0)
+		wpa_printf(MSG_INFO, "Test case 2 - OK");
+	else {
+		wpa_printf(MSG_INFO, "Test case 2 - FAILED!");
+		ret++;
+	}
+
+	ret += test_eap_fast();
+
+	wpa_printf(MSG_INFO, "PBKDF2-SHA1 Passphrase test cases:");
+	for (i = 0; i < NUM_PASSPHRASE_TESTS; i++) {
+		u8 psk[32];
+		struct passphrase_test *test = &passphrase_tests[i];
+
+		if (pbkdf2_sha1(test->passphrase,
+				(const u8 *) test->ssid, strlen(test->ssid),
+				4096, psk, 32) == 0 &&
+		    os_memcmp(psk, test->psk, 32) == 0)
+			wpa_printf(MSG_INFO, "Test case %d - OK", i);
+		else {
+			wpa_printf(MSG_INFO, "Test case %d - FAILED!", i);
+			ret++;
+		}
+	}
+
+	wpa_printf(MSG_INFO, "PBKDF2-SHA1 test cases (RFC 6070):");
+	for (i = 0; i < NUM_RFC6070_TESTS; i++) {
+		u8 dk[25];
+		struct rfc6070_test *test = &rfc6070_tests[i];
+
+		if (pbkdf2_sha1(test->p, (const u8 *) test->s, strlen(test->s),
+				test->c, dk, test->dk_len) == 0 &&
+		    os_memcmp(dk, test->dk, test->dk_len) == 0)
+			wpa_printf(MSG_INFO, "Test case %d - OK", i);
+		else {
+			wpa_printf(MSG_INFO, "Test case %d - FAILED!", i);
+			ret++;
+		}
+	}
+
+	if (!ret)
+		wpa_printf(MSG_INFO, "SHA1 test cases passed");
+	return ret;
+}
+
+
+struct {
+	char *data;
+	u8 hash[32];
+} tests[] = {
+	{
+		"abc",
+		{
+			0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
+			0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
+			0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
+			0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad
+		}
+	},
+	{
+		"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+		{
+			0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8,
+			0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39,
+			0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67,
+			0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1
+		}
+	}
+};
+
+struct hmac_test {
+	u8 key[80];
+	size_t key_len;
+	u8 data[128];
+	size_t data_len;
+	u8 hash[32];
+} hmac_tests[] = {
+	/* draft-ietf-ipsec-ciph-sha-256-01.txt */
+	{
+		{
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+			0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+			0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+			0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20
+		},
+		32,
+		"abc", 3,
+		{
+			0xa2, 0x1b, 0x1f, 0x5d, 0x4c, 0xf4, 0xf7, 0x3a,
+			0x4d, 0xd9, 0x39, 0x75, 0x0f, 0x7a, 0x06, 0x6a,
+			0x7f, 0x98, 0xcc, 0x13, 0x1c, 0xb1, 0x6a, 0x66,
+			0x92, 0x75, 0x90, 0x21, 0xcf, 0xab, 0x81, 0x81
+		}
+	},
+	{
+		{
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+			0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+			0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+			0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20
+		},
+		32,
+		"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+		56,
+		{
+			0x10, 0x4f, 0xdc, 0x12, 0x57, 0x32, 0x8f, 0x08,
+			0x18, 0x4b, 0xa7, 0x31, 0x31, 0xc5, 0x3c, 0xae,
+			0xe6, 0x98, 0xe3, 0x61, 0x19, 0x42, 0x11, 0x49,
+			0xea, 0x8c, 0x71, 0x24, 0x56, 0x69, 0x7d, 0x30
+		}
+	},
+	{
+		{
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+			0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+			0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+			0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20
+		},
+		32,
+		"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+		"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+		112,
+		{
+			0x47, 0x03, 0x05, 0xfc, 0x7e, 0x40, 0xfe, 0x34,
+			0xd3, 0xee, 0xb3, 0xe7, 0x73, 0xd9, 0x5a, 0xab,
+			0x73, 0xac, 0xf0, 0xfd, 0x06, 0x04, 0x47, 0xa5,
+			0xeb, 0x45, 0x95, 0xbf, 0x33, 0xa9, 0xd1, 0xa3
+		}
+	},
+	{
+		{
+			0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+			0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+			0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+			0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b
+		},
+		32,
+		"Hi There",
+		8,
+		{
+			0x19, 0x8a, 0x60, 0x7e, 0xb4, 0x4b, 0xfb, 0xc6,
+			0x99, 0x03, 0xa0, 0xf1, 0xcf, 0x2b, 0xbd, 0xc5,
+			0xba, 0x0a, 0xa3, 0xf3, 0xd9, 0xae, 0x3c, 0x1c,
+			0x7a, 0x3b, 0x16, 0x96, 0xa0, 0xb6, 0x8c, 0xf7
+		}
+	},
+	{
+		"Jefe",
+		4,
+		"what do ya want for nothing?",
+		28,
+		{
+			0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e,
+			0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xc7,
+			0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83,
+			0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43
+		}
+	},
+	{
+		{
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
+		},
+		32,
+		{
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd
+		},
+		50,
+		{
+			0xcd, 0xcb, 0x12, 0x20, 0xd1, 0xec, 0xcc, 0xea,
+			0x91, 0xe5, 0x3a, 0xba, 0x30, 0x92, 0xf9, 0x62,
+			0xe5, 0x49, 0xfe, 0x6c, 0xe9, 0xed, 0x7f, 0xdc,
+			0x43, 0x19, 0x1f, 0xbd, 0xe4, 0x5c, 0x30, 0xb0
+		}
+	},
+	{
+		{
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+			0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+			0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+			0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+			0x21, 0x22, 0x23, 0x24, 0x25
+		},
+		37,
+		{
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd
+		},
+		50,
+		{
+			0xd4, 0x63, 0x3c, 0x17, 0xf6, 0xfb, 0x8d, 0x74,
+			0x4c, 0x66, 0xde, 0xe0, 0xf8, 0xf0, 0x74, 0x55,
+			0x6e, 0xc4, 0xaf, 0x55, 0xef, 0x07, 0x99, 0x85,
+			0x41, 0x46, 0x8e, 0xb4, 0x9b, 0xd2, 0xe9, 0x17
+		}
+	},
+	{
+		{
+			0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+			0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+			0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+			0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c
+		},
+		32,
+		"Test With Truncation",
+		20,
+		{
+			0x75, 0x46, 0xaf, 0x01, 0x84, 0x1f, 0xc0, 0x9b,
+			0x1a, 0xb9, 0xc3, 0x74, 0x9a, 0x5f, 0x1c, 0x17,
+			0xd4, 0xf5, 0x89, 0x66, 0x8a, 0x58, 0x7b, 0x27,
+			0x00, 0xa9, 0xc9, 0x7c, 0x11, 0x93, 0xcf, 0x42
+		}
+	},
+	{
+		{
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
+		},
+		80,
+		"Test Using Larger Than Block-Size Key - Hash Key First",
+		54,
+		{
+			0x69, 0x53, 0x02, 0x5e, 0xd9, 0x6f, 0x0c, 0x09,
+			0xf8, 0x0a, 0x96, 0xf7, 0x8e, 0x65, 0x38, 0xdb,
+			0xe2, 0xe7, 0xb8, 0x20, 0xe3, 0xdd, 0x97, 0x0e,
+			0x7d, 0xdd, 0x39, 0x09, 0x1b, 0x32, 0x35, 0x2f
+		}
+	},
+	{
+		{
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
+		},
+		80,
+		"Test Using Larger Than Block-Size Key and Larger Than One "
+		"Block-Size Data",
+		73,
+		{
+			0x63, 0x55, 0xac, 0x22, 0xe8, 0x90, 0xd0, 0xa3,
+			0xc8, 0x48, 0x1a, 0x5c, 0xa4, 0x82, 0x5b, 0xc8,
+			0x84, 0xd3, 0xe7, 0xa1, 0xff, 0x98, 0xa2, 0xfc,
+			0x2a, 0xc7, 0xd8, 0xe0, 0x64, 0xc3, 0xb2, 0xe6
+		}
+	}
+};
+
+
+static int test_sha256(void)
+{
+	unsigned int i;
+	u8 hash[32];
+	const u8 *addr[2];
+	size_t len[2];
+	int errors = 0;
+
+	for (i = 0; i < ARRAY_SIZE(tests); i++) {
+		wpa_printf(MSG_INFO, "SHA256 test case %d:", i + 1);
+
+		addr[0] = (u8 *) tests[i].data;
+		len[0] = strlen(tests[i].data);
+		sha256_vector(1, addr, len, hash);
+		if (memcmp(hash, tests[i].hash, 32) != 0) {
+			wpa_printf(MSG_INFO, " FAIL");
+			errors++;
+		} else
+			wpa_printf(MSG_INFO, " OK");
+
+		if (len[0]) {
+			addr[0] = (u8 *) tests[i].data;
+			len[0] = 1;
+			addr[1] = (u8 *) tests[i].data + 1;
+			len[1] = strlen(tests[i].data) - 1;
+			sha256_vector(2, addr, len, hash);
+			if (memcmp(hash, tests[i].hash, 32) != 0) {
+				wpa_printf(MSG_INFO, " FAIL");
+				errors++;
+			} else
+				wpa_printf(MSG_INFO, " OK");
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(hmac_tests); i++) {
+		struct hmac_test *t = &hmac_tests[i];
+
+		wpa_printf(MSG_INFO, "HMAC-SHA256 test case %d:", i + 1);
+
+		if (hmac_sha256(t->key, t->key_len, t->data, t->data_len,
+				hash) < 0 ||
+		    os_memcmp(hash, t->hash, 32) != 0) {
+			wpa_printf(MSG_INFO, " FAIL");
+			errors++;
+		} else
+			wpa_printf(MSG_INFO, " OK");
+
+		addr[0] = t->data;
+		len[0] = t->data_len;
+		if (hmac_sha256_vector(t->key, t->key_len, 1, addr, len,
+				       hash) < 0 ||
+		    os_memcmp(hash, t->hash, 32) != 0) {
+			wpa_printf(MSG_INFO, " FAIL");
+			errors++;
+		} else
+			wpa_printf(MSG_INFO, " OK");
+
+		if (len[0]) {
+			addr[0] = t->data;
+			len[0] = 1;
+			addr[1] = t->data + 1;
+			len[1] = t->data_len - 1;
+			if (hmac_sha256_vector(t->key, t->key_len, 2, addr, len,
+					       hash) < 0 ||
+			    os_memcmp(hash, t->hash, 32) != 0) {
+				wpa_printf(MSG_INFO, " FAIL");
+				errors++;
+			} else
+				wpa_printf(MSG_INFO, " OK");
+		}
+	}
+
+	wpa_printf(MSG_INFO, "Test IEEE 802.11r KDF");
+	sha256_prf((u8 *) "abc", 3, "KDF test", (u8 *) "data", 4,
+		   hash, sizeof(hash));
+	/* TODO: add proper test case for this */
+
+	if (!errors)
+		wpa_printf(MSG_INFO, "SHA256 test cases passed");
+	return errors;
+}
+
+
+static int test_ms_funcs(void)
+{
+	/* Test vector from RFC2759 example */
+	char *username = "User";
+	char *password = "clientPass";
+	u8 auth_challenge[] = {
+		0x5B, 0x5D, 0x7C, 0x7D, 0x7B, 0x3F, 0x2F, 0x3E,
+		0x3C, 0x2C, 0x60, 0x21, 0x32, 0x26, 0x26, 0x28
+	};
+	u8 peer_challenge[] = {
+		0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A,
+		0x28, 0x29, 0x5F, 0x2B, 0x3A, 0x33, 0x7C, 0x7E
+	};
+	u8 password_hash[] = {
+		0x44, 0xEB, 0xBA, 0x8D, 0x53, 0x12, 0xB8, 0xD6,
+		0x11, 0x47, 0x44, 0x11, 0xF5, 0x69, 0x89, 0xAE
+	};
+	u8 nt_response[] = {
+		0x82, 0x30, 0x9E, 0xCD, 0x8D, 0x70, 0x8B, 0x5E,
+		0xA0, 0x8F, 0xAA, 0x39, 0x81, 0xCD, 0x83, 0x54,
+		0x42, 0x33, 0x11, 0x4A, 0x3D, 0x85, 0xD6, 0xDF
+	};
+	u8 password_hash_hash[] = {
+		0x41, 0xC0, 0x0C, 0x58, 0x4B, 0xD2, 0xD9, 0x1C,
+		0x40, 0x17, 0xA2, 0xA1, 0x2F, 0xA5, 0x9F, 0x3F
+	};
+	u8 authenticator_response[] = {
+		0x40, 0x7A, 0x55, 0x89, 0x11, 0x5F, 0xD0, 0xD6,
+		0x20, 0x9F, 0x51, 0x0F, 0xE9, 0xC0, 0x45, 0x66,
+		0x93, 0x2C, 0xDA, 0x56
+	};
+	u8 master_key[] = {
+		0xFD, 0xEC, 0xE3, 0x71, 0x7A, 0x8C, 0x83, 0x8C,
+		0xB3, 0x88, 0xE5, 0x27, 0xAE, 0x3C, 0xDD, 0x31
+	};
+	u8 send_start_key[] = {
+		0x8B, 0x7C, 0xDC, 0x14, 0x9B, 0x99, 0x3A, 0x1B,
+		0xA1, 0x18, 0xCB, 0x15, 0x3F, 0x56, 0xDC, 0xCB
+	};
+	u8 buf[32];
+	int errors = 0;
+
+	if (nt_password_hash((u8 *) password, os_strlen(password), buf) ||
+	    os_memcmp(password_hash, buf, sizeof(password_hash)) != 0) {
+		wpa_printf(MSG_ERROR, "nt_password_hash failed");
+		errors++;
+	}
+
+	if (generate_nt_response(auth_challenge, peer_challenge,
+				 (u8 *) username, os_strlen(username),
+				 (u8 *) password, os_strlen(password), buf) ||
+	    os_memcmp(nt_response, buf, sizeof(nt_response)) != 0) {
+		wpa_printf(MSG_ERROR, "generate_nt_response failed");
+		errors++;
+	}
+
+	if (hash_nt_password_hash(password_hash, buf) ||
+	    os_memcmp(password_hash_hash, buf,
+		      sizeof(password_hash_hash)) != 0) {
+		wpa_printf(MSG_ERROR, "hash_nt_password_hash failed");
+		errors++;
+	}
+
+	if (generate_authenticator_response((u8 *) password,
+					    os_strlen(password),
+					    peer_challenge, auth_challenge,
+					    (u8 *) username,
+					    os_strlen(username),
+					    nt_response, buf) ||
+	    os_memcmp(authenticator_response, buf,
+		      sizeof(authenticator_response)) != 0) {
+		wpa_printf(MSG_ERROR, "generate_authenticator_response failed");
+		errors++;
+	}
+
+	if (get_master_key(password_hash_hash, nt_response, buf) ||
+	    os_memcmp(master_key, buf, sizeof(master_key)) != 0) {
+		wpa_printf(MSG_ERROR, "get_master_key failed");
+		errors++;
+	}
+
+	if (get_asymetric_start_key(master_key, buf, sizeof(send_start_key),
+				    1, 1) ||
+	    os_memcmp(send_start_key, buf, sizeof(send_start_key)) != 0) {
+		wpa_printf(MSG_ERROR, "get_asymetric_start_key failed");
+		errors++;
+	}
+
+	if (errors)
+		wpa_printf(MSG_ERROR, "ms_funcs: %d errors", errors);
+	else
+		wpa_printf(MSG_INFO, "ms_funcs test cases passed");
+
+	return errors;
+}
+
+
+int crypto_module_tests(void)
+{
+	int ret = 0;
+
+	wpa_printf(MSG_INFO, "crypto module tests");
+	if (test_siv() ||
+	    test_omac1() ||
+	    test_eax() ||
+	    test_cbc() ||
+	    test_ecb() ||
+	    test_key_wrap() ||
+	    test_md5() ||
+	    test_sha1() ||
+	    test_sha256() ||
+	    test_ms_funcs())
+		ret = -1;
+
+	return ret;
+}
diff --git a/src/crypto/crypto_nss.c b/src/crypto/crypto_nss.c
deleted file mode 100644
index acd0a55..0000000
--- a/src/crypto/crypto_nss.c
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Crypto wrapper functions for NSS
- * Copyright (c) 2009, 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 <nspr/prtypes.h>
-#include <nspr/plarenas.h>
-#include <nspr/plhash.h>
-#include <nspr/prtime.h>
-#include <nspr/prinrval.h>
-#include <nspr/prclist.h>
-#include <nspr/prlock.h>
-#include <nss/sechash.h>
-#include <nss/pk11pub.h>
-
-#include "common.h"
-#include "crypto.h"
-
-
-static int nss_hash(HASH_HashType type, unsigned int max_res_len,
-		    size_t num_elem, const u8 *addr[], const size_t *len,
-		    u8 *mac)
-{
-	HASHContext *ctx;
-	size_t i;
-	unsigned int reslen;
-
-	ctx = HASH_Create(type);
-	if (ctx == NULL)
-		return -1;
-
-	HASH_Begin(ctx);
-	for (i = 0; i < num_elem; i++)
-		HASH_Update(ctx, addr[i], len[i]);
-	HASH_End(ctx, mac, &reslen, max_res_len);
-	HASH_Destroy(ctx);
-
-	return 0;
-}
-
-
-void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
-{
-	PK11Context *ctx = NULL;
-	PK11SlotInfo *slot;
-	SECItem *param = NULL;
-	PK11SymKey *symkey = NULL;
-	SECItem item;
-	int olen;
-	u8 pkey[8], next, tmp;
-	int i;
-
-	/* Add parity bits to the key */
-	next = 0;
-	for (i = 0; i < 7; i++) {
-		tmp = key[i];
-		pkey[i] = (tmp >> i) | next | 1;
-		next = tmp << (7 - i);
-	}
-	pkey[i] = next | 1;
-
-	slot = PK11_GetBestSlot(CKM_DES_ECB, NULL);
-	if (slot == NULL) {
-		wpa_printf(MSG_ERROR, "NSS: PK11_GetBestSlot failed");
-		goto out;
-	}
-
-	item.type = siBuffer;
-	item.data = pkey;
-	item.len = 8;
-	symkey = PK11_ImportSymKey(slot, CKM_DES_ECB, PK11_OriginDerive,
-				   CKA_ENCRYPT, &item, NULL);
-	if (symkey == NULL) {
-		wpa_printf(MSG_ERROR, "NSS: PK11_ImportSymKey failed");
-		goto out;
-	}
-
-	param = PK11_GenerateNewParam(CKM_DES_ECB, symkey);
-	if (param == NULL) {
-		wpa_printf(MSG_ERROR, "NSS: PK11_GenerateNewParam failed");
-		goto out;
-	}
-
-	ctx = PK11_CreateContextBySymKey(CKM_DES_ECB, CKA_ENCRYPT,
-					 symkey, param);
-	if (ctx == NULL) {
-		wpa_printf(MSG_ERROR, "NSS: PK11_CreateContextBySymKey("
-			   "CKM_DES_ECB) failed");
-		goto out;
-	}
-
-	if (PK11_CipherOp(ctx, cypher, &olen, 8, (void *) clear, 8) !=
-	    SECSuccess) {
-		wpa_printf(MSG_ERROR, "NSS: PK11_CipherOp failed");
-		goto out;
-	}
-
-out:
-	if (ctx)
-		PK11_DestroyContext(ctx, PR_TRUE);
-	if (symkey)
-		PK11_FreeSymKey(symkey);
-	if (param)
-		SECITEM_FreeItem(param, PR_TRUE);
-}
-
-
-int rc4_skip(const u8 *key, size_t keylen, size_t skip,
-	     u8 *data, size_t data_len)
-{
-	return -1;
-}
-
-
-int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-{
-	return nss_hash(HASH_AlgMD5, 16, num_elem, addr, len, mac);
-}
-
-
-int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-{
-	return nss_hash(HASH_AlgSHA1, 20, num_elem, addr, len, mac);
-}
-
-
-int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
-		  u8 *mac)
-{
-	return nss_hash(HASH_AlgSHA256, 32, num_elem, addr, len, mac);
-}
-
-
-void * aes_encrypt_init(const u8 *key, size_t len)
-{
-	return NULL;
-}
-
-
-void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
-{
-}
-
-
-void aes_encrypt_deinit(void *ctx)
-{
-}
-
-
-void * aes_decrypt_init(const u8 *key, size_t len)
-{
-	return NULL;
-}
-
-
-void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
-{
-}
-
-
-void aes_decrypt_deinit(void *ctx)
-{
-}
-
-
-int crypto_mod_exp(const u8 *base, size_t base_len,
-		   const u8 *power, size_t power_len,
-		   const u8 *modulus, size_t modulus_len,
-		   u8 *result, size_t *result_len)
-{
-	return -1;
-}
-
-
-struct crypto_cipher {
-};
-
-
-struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
-					  const u8 *iv, const u8 *key,
-					  size_t key_len)
-{
-	return NULL;
-}
-
-
-int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
-			  u8 *crypt, size_t len)
-{
-	return -1;
-}
-
-
-int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
-			  u8 *plain, size_t len)
-{
-	return -1;
-}
-
-
-void crypto_cipher_deinit(struct crypto_cipher *ctx)
-{
-}
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index b4c59d1..adb42a4 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -258,7 +258,7 @@
 			   "in AES encrypt", len);
 	}
 	EVP_CIPHER_CTX_cleanup(c);
-	os_free(c);
+	bin_clear_free(c, sizeof(*c));
 }
 
 
@@ -309,7 +309,7 @@
 			   "in AES decrypt", len);
 	}
 	EVP_CIPHER_CTX_cleanup(c);
-	os_free(ctx);
+	bin_clear_free(c, sizeof(*c));
 }
 
 
@@ -507,8 +507,8 @@
 	return dh;
 
 err:
-	wpabuf_free(pubkey);
-	wpabuf_free(privkey);
+	wpabuf_clear_free(pubkey);
+	wpabuf_clear_free(privkey);
 	DH_free(dh);
 	return NULL;
 }
@@ -581,7 +581,7 @@
 
 err:
 	BN_clear_free(pub_key);
-	wpabuf_free(res);
+	wpabuf_clear_free(res);
 	return NULL;
 }
 
@@ -638,7 +638,7 @@
 	HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL);
 #else /* openssl < 0.9.9 */
 	if (HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL) != 1) {
-		os_free(ctx);
+		bin_clear_free(ctx, sizeof(*ctx));
 		return NULL;
 	}
 #endif /* openssl < 0.9.9 */
@@ -664,7 +664,7 @@
 		return -2;
 
 	if (mac == NULL || len == NULL) {
-		os_free(ctx);
+		bin_clear_free(ctx, sizeof(*ctx));
 		return 0;
 	}
 
@@ -676,7 +676,7 @@
 	res = HMAC_Final(&ctx->ctx, mac, &mdlen);
 #endif /* openssl < 0.9.9 */
 	HMAC_CTX_cleanup(&ctx->ctx);
-	os_free(ctx);
+	bin_clear_free(ctx, sizeof(*ctx));
 
 	if (res == 1) {
 		*len = mdlen;
@@ -693,11 +693,11 @@
 #if OPENSSL_VERSION_NUMBER < 0x00908000
 	if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase),
 				   (unsigned char *) ssid,
-				   ssid_len, 4096, buflen, buf) != 1)
+				   ssid_len, iterations, buflen, buf) != 1)
 		return -1;
 #else /* openssl < 0.9.8 */
 	if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), ssid,
-				   ssid_len, 4096, buflen, buf) != 1)
+				   ssid_len, iterations, buflen, buf) != 1)
 		return -1;
 #endif /* openssl < 0.9.8 */
 	return 0;
diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c
index 58e94c3..d3b2631 100644
--- a/src/crypto/dh_groups.c
+++ b/src/crypto/dh_groups.c
@@ -1198,14 +1198,14 @@
 	if (dh == NULL)
 		return NULL;
 
-	wpabuf_free(*priv);
+	wpabuf_clear_free(*priv);
 	*priv = wpabuf_alloc(dh->prime_len);
 	if (*priv == NULL)
 		return NULL;
 
 	if (random_get_bytes(wpabuf_put(*priv, dh->prime_len), dh->prime_len))
 	{
-		wpabuf_free(*priv);
+		wpabuf_clear_free(*priv);
 		*priv = NULL;
 		return NULL;
 	}
@@ -1224,7 +1224,7 @@
 			   wpabuf_head(*priv), wpabuf_len(*priv),
 			   dh->prime, dh->prime_len, wpabuf_mhead(pv),
 			   &pv_len) < 0) {
-		wpabuf_free(pv);
+		wpabuf_clear_free(pv);
 		wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed");
 		return NULL;
 	}
@@ -1260,7 +1260,7 @@
 			   wpabuf_head(own_private), wpabuf_len(own_private),
 			   dh->prime, dh->prime_len,
 			   wpabuf_mhead(shared), &shared_len) < 0) {
-		wpabuf_free(shared);
+		wpabuf_clear_free(shared);
 		wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed");
 		return NULL;
 	}
diff --git a/src/crypto/md5.c b/src/crypto/md5.c
index db2b8cc..f64dfd3 100644
--- a/src/crypto/md5.c
+++ b/src/crypto/md5.c
@@ -30,6 +30,7 @@
 	u8 tk[16];
 	const u8 *_addr[6];
 	size_t i, _len[6];
+	int res;
 
 	if (num_elem > 5) {
 		/*
@@ -85,7 +86,10 @@
 	_len[0] = 64;
 	_addr[1] = mac;
 	_len[1] = MD5_MAC_LEN;
-	return md5_vector(2, _addr, _len, mac);
+	res = md5_vector(2, _addr, _len, mac);
+	os_memset(k_pad, 0, sizeof(k_pad));
+	os_memset(tk, 0, sizeof(tk));
+	return res;
 }
 
 
diff --git a/src/crypto/sha1-prf.c b/src/crypto/sha1-prf.c
index 90b9e74..4b2d137 100644
--- a/src/crypto/sha1-prf.c
+++ b/src/crypto/sha1-prf.c
@@ -61,6 +61,7 @@
 		}
 		counter++;
 	}
+	os_memset(hash, 0, sizeof(hash));
 
 	return 0;
 }
diff --git a/src/crypto/sha1.c b/src/crypto/sha1.c
index d48c77d..8fce139 100644
--- a/src/crypto/sha1.c
+++ b/src/crypto/sha1.c
@@ -30,6 +30,7 @@
 	unsigned char tk[20];
 	const u8 *_addr[6];
 	size_t _len[6], i;
+	int ret;
 
 	if (num_elem > 5) {
 		/*
@@ -84,7 +85,9 @@
 	_len[0] = 64;
 	_addr[1] = mac;
 	_len[1] = SHA1_MAC_LEN;
-	return sha1_vector(2, _addr, _len, mac);
+	ret = sha1_vector(2, _addr, _len, mac);
+	os_memset(k_pad, 0, sizeof(k_pad));
+	return ret;
 }
 
 
diff --git a/src/crypto/sha256-prf.c b/src/crypto/sha256-prf.c
index 9a11208..79791c0 100644
--- a/src/crypto/sha256-prf.c
+++ b/src/crypto/sha256-prf.c
@@ -95,4 +95,6 @@
 		u8 mask = 0xff << (8 - buf_len_bits % 8);
 		buf[pos - 1] &= mask;
 	}
+
+	os_memset(hash, 0, sizeof(hash));
 }
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index 345ebc7..a4f954c 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -556,4 +556,6 @@
 
 void tls_connection_set_test_flags(struct tls_connection *conn, u32 flags);
 
+int tls_get_library_version(char *buf, size_t buf_len);
+
 #endif /* TLS_H */
diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c
index 20d0a31..f2eacb5 100644
--- a/src/crypto/tls_gnutls.c
+++ b/src/crypto/tls_gnutls.c
@@ -12,61 +12,15 @@
 #ifdef PKCS12_FUNCS
 #include <gnutls/pkcs12.h>
 #endif /* PKCS12_FUNCS */
+#if GNUTLS_VERSION_NUMBER >= 0x030103
+#include <gnutls/ocsp.h>
+#endif /* 3.1.3 */
 
 #include "common.h"
+#include "crypto/crypto.h"
 #include "tls.h"
 
 
-#define WPA_TLS_RANDOM_SIZE 32
-#define WPA_TLS_MASTER_SIZE 48
-
-
-#if LIBGNUTLS_VERSION_NUMBER < 0x010302
-/* GnuTLS 1.3.2 added functions for using master secret. Older versions require
- * use of internal structures to get the master_secret and
- * {server,client}_random.
- */
-#define GNUTLS_INTERNAL_STRUCTURE_HACK
-#endif /* LIBGNUTLS_VERSION_NUMBER < 0x010302 */
-
-
-#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
-/*
- * It looks like gnutls does not provide access to client/server_random and
- * master_key. This is somewhat unfortunate since these are needed for key
- * derivation in EAP-{TLS,TTLS,PEAP,FAST}. Workaround for now is a horrible
- * hack that copies the gnutls_session_int definition from gnutls_int.h so that
- * we can get the needed information.
- */
-
-typedef u8 uint8;
-typedef unsigned char opaque;
-typedef struct {
-    uint8 suite[2];
-} cipher_suite_st;
-
-typedef struct {
-	gnutls_connection_end_t entity;
-	gnutls_kx_algorithm_t kx_algorithm;
-	gnutls_cipher_algorithm_t read_bulk_cipher_algorithm;
-	gnutls_mac_algorithm_t read_mac_algorithm;
-	gnutls_compression_method_t read_compression_algorithm;
-	gnutls_cipher_algorithm_t write_bulk_cipher_algorithm;
-	gnutls_mac_algorithm_t write_mac_algorithm;
-	gnutls_compression_method_t write_compression_algorithm;
-	cipher_suite_st current_cipher_suite;
-	opaque master_secret[WPA_TLS_MASTER_SIZE];
-	opaque client_random[WPA_TLS_RANDOM_SIZE];
-	opaque server_random[WPA_TLS_RANDOM_SIZE];
-	/* followed by stuff we are not interested in */
-} security_parameters_st;
-
-struct gnutls_session_int {
-	security_parameters_st security_parameters;
-	/* followed by things we are not interested in */
-};
-#endif /* LIBGNUTLS_VERSION_NUMBER < 0x010302 */
-
 static int tls_gnutls_ref_count = 0;
 
 struct tls_global {
@@ -78,17 +32,23 @@
 
 	int params_set;
 	gnutls_certificate_credentials_t xcred;
+
+	void (*event_cb)(void *ctx, enum tls_event ev,
+			 union tls_event_data *data);
+	void *cb_ctx;
+	int cert_in_cb;
 };
 
 struct tls_connection {
+	struct tls_global *global;
 	gnutls_session_t session;
-	char *subject_match, *altsubject_match;
 	int read_alerts, write_alerts, failed;
 
 	u8 *pre_shared_secret;
 	size_t pre_shared_secret_len;
 	int established;
 	int verify_peer;
+	unsigned int disable_time_checks:1;
 
 	struct wpabuf *push_buf;
 	struct wpabuf *pull_buf;
@@ -96,9 +56,15 @@
 
 	int params_set;
 	gnutls_certificate_credentials_t xcred;
+
+	char *suffix_match;
+	unsigned int flags;
 };
 
 
+static int tls_connection_verify_peer(gnutls_session_t session);
+
+
 static void tls_log_func(int level, const char *msg)
 {
 	char *s, *pos;
@@ -129,17 +95,11 @@
 {
 	struct tls_global *global;
 
-#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
-	/* Because of the horrible hack to get master_secret and client/server
-	 * random, we need to make sure that the gnutls version is something
-	 * that is expected to have same structure definition for the session
-	 * data.. */
-	const char *ver;
-	const char *ok_ver[] = { "1.2.3", "1.2.4", "1.2.5", "1.2.6", "1.2.9",
-				 "1.3.2",
-				 NULL };
-	int i;
-#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
+	if (tls_gnutls_ref_count == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: Library version %s (runtime) - %s (build)",
+			   gnutls_check_version(NULL), GNUTLS_VERSION);
+	}
 
 	global = os_zalloc(sizeof(*global));
 	if (global == NULL)
@@ -151,28 +111,16 @@
 	}
 	tls_gnutls_ref_count++;
 
-#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
-	ver = gnutls_check_version(NULL);
-	if (ver == NULL) {
-		tls_deinit(global);
-		return NULL;
-	}
-	wpa_printf(MSG_DEBUG, "%s - gnutls version %s", __func__, ver);
-	for (i = 0; ok_ver[i]; i++) {
-		if (strcmp(ok_ver[i], ver) == 0)
-			break;
-	}
-	if (ok_ver[i] == NULL) {
-		wpa_printf(MSG_INFO, "Untested gnutls version %s - this needs "
-			   "to be tested and enabled in tls_gnutls.c", ver);
-		tls_deinit(global);
-		return NULL;
-	}
-#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
-
 	gnutls_global_set_log_function(tls_log_func);
 	if (wpa_debug_show_keys)
 		gnutls_global_set_log_level(11);
+
+	if (conf) {
+		global->event_cb = conf->event_cb;
+		global->cb_ctx = conf->cb_ctx;
+		global->cert_in_cb = conf->cert_in_cb;
+	}
+
 	return global;
 }
 
@@ -246,12 +194,7 @@
 static int tls_gnutls_init_session(struct tls_global *global,
 				   struct tls_connection *conn)
 {
-#if LIBGNUTLS_VERSION_NUMBER >= 0x020200
 	const char *err;
-#else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */
-	const int cert_types[2] = { GNUTLS_CRT_X509, 0 };
-	const int protos[2] = { GNUTLS_TLS1, 0 };
-#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */
 	int ret;
 
 	ret = gnutls_init(&conn->session,
@@ -266,7 +209,6 @@
 	if (ret < 0)
 		goto fail;
 
-#if LIBGNUTLS_VERSION_NUMBER >= 0x020200
 	ret = gnutls_priority_set_direct(conn->session, "NORMAL:-VERS-SSL3.0",
 					 &err);
 	if (ret < 0) {
@@ -274,19 +216,11 @@
 			   "'%s'", err);
 		goto fail;
 	}
-#else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */
-	ret = gnutls_certificate_type_set_priority(conn->session, cert_types);
-	if (ret < 0)
-		goto fail;
-
-	ret = gnutls_protocol_set_priority(conn->session, protos);
-	if (ret < 0)
-		goto fail;
-#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */
 
 	gnutls_transport_set_pull_function(conn->session, tls_pull_func);
 	gnutls_transport_set_push_function(conn->session, tls_push_func);
 	gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr_t) conn);
+	gnutls_session_set_ptr(conn->session, conn);
 
 	return 0;
 
@@ -307,6 +241,7 @@
 	conn = os_zalloc(sizeof(*conn));
 	if (conn == NULL)
 		return NULL;
+	conn->global = global;
 
 	if (tls_gnutls_init_session(global, conn)) {
 		os_free(conn);
@@ -342,10 +277,9 @@
 	gnutls_certificate_free_credentials(conn->xcred);
 	gnutls_deinit(conn->session);
 	os_free(conn->pre_shared_secret);
-	os_free(conn->subject_match);
-	os_free(conn->altsubject_match);
 	wpabuf_free(conn->push_buf);
 	wpabuf_free(conn->pull_buf);
+	os_free(conn->suffix_match);
 	os_free(conn);
 }
 
@@ -403,104 +337,6 @@
 }
 
 
-#if 0
-static int tls_match_altsubject(X509 *cert, const char *match)
-{
-	GENERAL_NAME *gen;
-	char *field, *tmp;
-	void *ext;
-	int i, found = 0;
-	size_t len;
-
-	ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
-
-	for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) {
-		gen = sk_GENERAL_NAME_value(ext, i);
-		switch (gen->type) {
-		case GEN_EMAIL:
-			field = "EMAIL";
-			break;
-		case GEN_DNS:
-			field = "DNS";
-			break;
-		case GEN_URI:
-			field = "URI";
-			break;
-		default:
-			field = NULL;
-			wpa_printf(MSG_DEBUG, "TLS: altSubjectName: "
-				   "unsupported type=%d", gen->type);
-			break;
-		}
-
-		if (!field)
-			continue;
-
-		wpa_printf(MSG_DEBUG, "TLS: altSubjectName: %s:%s",
-			   field, gen->d.ia5->data);
-		len = os_strlen(field) + 1 +
-			strlen((char *) gen->d.ia5->data) + 1;
-		tmp = os_malloc(len);
-		if (tmp == NULL)
-			continue;
-		snprintf(tmp, len, "%s:%s", field, gen->d.ia5->data);
-		if (strstr(tmp, match))
-			found++;
-		os_free(tmp);
-	}
-
-	return found;
-}
-#endif
-
-
-#if 0
-static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
-{
-	char buf[256];
-	X509 *err_cert;
-	int err, depth;
-	SSL *ssl;
-	struct tls_connection *conn;
-	char *match, *altmatch;
-
-	err_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
-	err = X509_STORE_CTX_get_error(x509_ctx);
-	depth = X509_STORE_CTX_get_error_depth(x509_ctx);
-	ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
-					 SSL_get_ex_data_X509_STORE_CTX_idx());
-	X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
-
-	conn = SSL_get_app_data(ssl);
-	match = conn ? conn->subject_match : NULL;
-	altmatch = conn ? conn->altsubject_match : NULL;
-
-	if (!preverify_ok) {
-		wpa_printf(MSG_WARNING, "TLS: Certificate verification failed,"
-			   " error %d (%s) depth %d for '%s'", err,
-			   X509_verify_cert_error_string(err), depth, buf);
-	} else {
-		wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - "
-			   "preverify_ok=%d err=%d (%s) depth=%d buf='%s'",
-			   preverify_ok, err,
-			   X509_verify_cert_error_string(err), depth, buf);
-		if (depth == 0 && match && strstr(buf, match) == NULL) {
-			wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not "
-				   "match with '%s'", buf, match);
-			preverify_ok = 0;
-		} else if (depth == 0 && altmatch &&
-			   !tls_match_altsubject(err_cert, altmatch)) {
-			wpa_printf(MSG_WARNING, "TLS: altSubjectName match "
-				   "'%s' not found", altmatch);
-			preverify_ok = 0;
-		}
-	}
-
-	return preverify_ok;
-}
-#endif
-
-
 int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
 			      const struct tls_connection_params *params)
 {
@@ -509,86 +345,127 @@
 	if (conn == NULL || params == NULL)
 		return -1;
 
-	os_free(conn->subject_match);
-	conn->subject_match = NULL;
 	if (params->subject_match) {
-		conn->subject_match = os_strdup(params->subject_match);
-		if (conn->subject_match == NULL)
+		wpa_printf(MSG_INFO, "GnuTLS: subject_match not supported");
+		return -1;
+	}
+
+	if (params->altsubject_match) {
+		wpa_printf(MSG_INFO, "GnuTLS: altsubject_match not supported");
+		return -1;
+	}
+
+	os_free(conn->suffix_match);
+	conn->suffix_match = NULL;
+	if (params->suffix_match) {
+		conn->suffix_match = os_strdup(params->suffix_match);
+		if (conn->suffix_match == NULL)
 			return -1;
 	}
 
-	os_free(conn->altsubject_match);
-	conn->altsubject_match = NULL;
-	if (params->altsubject_match) {
-		conn->altsubject_match = os_strdup(params->altsubject_match);
-		if (conn->altsubject_match == NULL)
-			return -1;
+	conn->flags = params->flags;
+
+	if (params->openssl_ciphers) {
+		wpa_printf(MSG_INFO, "GnuTLS: openssl_ciphers not supported");
+		return -1;
 	}
 
 	/* TODO: gnutls_certificate_set_verify_flags(xcred, flags); 
 	 * to force peer validation(?) */
 
 	if (params->ca_cert) {
-		conn->verify_peer = 1;
+		wpa_printf(MSG_DEBUG, "GnuTLS: Try to parse %s in DER format",
+			   params->ca_cert);
 		ret = gnutls_certificate_set_x509_trust_file(
-			conn->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM);
+			conn->xcred, params->ca_cert, GNUTLS_X509_FMT_DER);
 		if (ret < 0) {
-			wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' "
-				   "in PEM format: %s", params->ca_cert,
+			wpa_printf(MSG_DEBUG,
+				   "GnuTLS: Failed to read CA cert '%s' in DER format (%s) - try in PEM format",
+				   params->ca_cert,
 				   gnutls_strerror(ret));
 			ret = gnutls_certificate_set_x509_trust_file(
 				conn->xcred, params->ca_cert,
-				GNUTLS_X509_FMT_DER);
+				GNUTLS_X509_FMT_PEM);
 			if (ret < 0) {
-				wpa_printf(MSG_DEBUG, "Failed to read CA cert "
-					   "'%s' in DER format: %s",
+				wpa_printf(MSG_DEBUG,
+					   "Failed to read CA cert '%s' in PEM format: %s",
 					   params->ca_cert,
 					   gnutls_strerror(ret));
 				return -1;
 			}
 		}
+	} else if (params->ca_cert_blob) {
+		gnutls_datum_t ca;
+
+		ca.data = (unsigned char *) params->ca_cert_blob;
+		ca.size = params->ca_cert_blob_len;
+
+		ret = gnutls_certificate_set_x509_trust_mem(
+			conn->xcred, &ca, GNUTLS_X509_FMT_DER);
+		if (ret < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "Failed to parse CA cert in DER format: %s",
+				   gnutls_strerror(ret));
+			ret = gnutls_certificate_set_x509_trust_mem(
+				conn->xcred, &ca, GNUTLS_X509_FMT_PEM);
+			if (ret < 0) {
+				wpa_printf(MSG_DEBUG,
+					   "Failed to parse CA cert in PEM format: %s",
+					   gnutls_strerror(ret));
+				return -1;
+			}
+		}
+	} else if (params->ca_path) {
+		wpa_printf(MSG_INFO, "GnuTLS: ca_path not supported");
+		return -1;
+	}
+
+	conn->disable_time_checks = 0;
+	if (params->ca_cert || params->ca_cert_blob) {
+		conn->verify_peer = 1;
+		gnutls_certificate_set_verify_function(
+			conn->xcred, tls_connection_verify_peer);
 
 		if (params->flags & TLS_CONN_ALLOW_SIGN_RSA_MD5) {
 			gnutls_certificate_set_verify_flags(
 				conn->xcred, GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5);
 		}
 
-#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
 		if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
+			conn->disable_time_checks = 1;
 			gnutls_certificate_set_verify_flags(
 				conn->xcred,
 				GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
 		}
-#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
 	}
 
 	if (params->client_cert && params->private_key) {
 #if GNUTLS_VERSION_NUMBER >= 0x03010b
 		ret = gnutls_certificate_set_x509_key_file2(
 			conn->xcred, params->client_cert, params->private_key,
-			GNUTLS_X509_FMT_PEM, params->private_key_passwd, 0);
+			GNUTLS_X509_FMT_DER, params->private_key_passwd, 0);
 #else
 		/* private_key_passwd not (easily) supported here */
 		ret = gnutls_certificate_set_x509_key_file(
 			conn->xcred, params->client_cert, params->private_key,
-			GNUTLS_X509_FMT_PEM);
+			GNUTLS_X509_FMT_DER);
 #endif
 		if (ret < 0) {
 			wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
-				   "in PEM format: %s", gnutls_strerror(ret));
+				   "in DER format: %s", gnutls_strerror(ret));
 #if GNUTLS_VERSION_NUMBER >= 0x03010b
 			ret = gnutls_certificate_set_x509_key_file2(
 				conn->xcred, params->client_cert,
-				params->private_key, GNUTLS_X509_FMT_DER,
+				params->private_key, GNUTLS_X509_FMT_PEM,
 				params->private_key_passwd, 0);
 #else
 			ret = gnutls_certificate_set_x509_key_file(
 				conn->xcred, params->client_cert,
-				params->private_key, GNUTLS_X509_FMT_DER);
+				params->private_key, GNUTLS_X509_FMT_PEM);
 #endif
 			if (ret < 0) {
 				wpa_printf(MSG_DEBUG, "Failed to read client "
-					   "cert/key in DER format: %s",
+					   "cert/key in PEM format: %s",
 					   gnutls_strerror(ret));
 				return ret;
 			}
@@ -597,7 +474,6 @@
 		int pkcs12_ok = 0;
 #ifdef PKCS12_FUNCS
 		/* Try to load in PKCS#12 format */
-#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
 		ret = gnutls_certificate_set_x509_simple_pkcs12_file(
 			conn->xcred, params->private_key, GNUTLS_X509_FMT_DER,
 			params->private_key_passwd);
@@ -607,7 +483,6 @@
 			return -1;
 		} else
 			pkcs12_ok = 1;
-#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
 #endif /* PKCS12_FUNCS */
 
 		if (!pkcs12_ok) {
@@ -615,8 +490,82 @@
 				   "included");
 			return -1;
 		}
+	} else if (params->client_cert_blob && params->private_key_blob) {
+		gnutls_datum_t cert, key;
+
+		cert.data = (unsigned char *) params->client_cert_blob;
+		cert.size = params->client_cert_blob_len;
+		key.data = (unsigned char *) params->private_key_blob;
+		key.size = params->private_key_blob_len;
+
+#if GNUTLS_VERSION_NUMBER >= 0x03010b
+		ret = gnutls_certificate_set_x509_key_mem2(
+			conn->xcred, &cert, &key, GNUTLS_X509_FMT_DER,
+			params->private_key_passwd, 0);
+#else
+		/* private_key_passwd not (easily) supported here */
+		ret = gnutls_certificate_set_x509_key_mem(
+			conn->xcred, &cert, &key, GNUTLS_X509_FMT_DER);
+#endif
+		if (ret < 0) {
+			wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
+				   "in DER format: %s", gnutls_strerror(ret));
+#if GNUTLS_VERSION_NUMBER >= 0x03010b
+			ret = gnutls_certificate_set_x509_key_mem2(
+				conn->xcred, &cert, &key, GNUTLS_X509_FMT_PEM,
+				params->private_key_passwd, 0);
+#else
+			/* private_key_passwd not (easily) supported here */
+			ret = gnutls_certificate_set_x509_key_mem(
+				conn->xcred, &cert, &key, GNUTLS_X509_FMT_PEM);
+#endif
+			if (ret < 0) {
+				wpa_printf(MSG_DEBUG, "Failed to read client "
+					   "cert/key in PEM format: %s",
+					   gnutls_strerror(ret));
+				return ret;
+			}
+		}
+	} else if (params->private_key_blob) {
+#ifdef PKCS12_FUNCS
+		gnutls_datum_t key;
+
+		key.data = (unsigned char *) params->private_key_blob;
+		key.size = params->private_key_blob_len;
+
+		/* Try to load in PKCS#12 format */
+		ret = gnutls_certificate_set_x509_simple_pkcs12_mem(
+			conn->xcred, &key, GNUTLS_X509_FMT_DER,
+			params->private_key_passwd);
+		if (ret != 0) {
+			wpa_printf(MSG_DEBUG, "Failed to load private_key in "
+				   "PKCS#12 format: %s", gnutls_strerror(ret));
+			return -1;
+		}
+#else /* PKCS12_FUNCS */
+		wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not included");
+		return -1;
+#endif /* PKCS12_FUNCS */
 	}
 
+#if GNUTLS_VERSION_NUMBER >= 0x030103
+	if (params->flags & (TLS_CONN_REQUEST_OCSP | TLS_CONN_REQUIRE_OCSP)) {
+		ret = gnutls_ocsp_status_request_enable_client(conn->session,
+							       NULL, 0, NULL);
+		if (ret != GNUTLS_E_SUCCESS) {
+			wpa_printf(MSG_INFO,
+				   "GnuTLS: Failed to enable OCSP client");
+			return -1;
+		}
+	}
+#else /* 3.1.3 */
+	if (params->flags & TLS_CONN_REQUIRE_OCSP) {
+		wpa_printf(MSG_INFO,
+			   "GnuTLS: OCSP not supported by this version of GnuTLS");
+		return -1;
+	}
+#endif /* 3.1.3 */
+
 	conn->params_set = 1;
 
 	ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE,
@@ -654,17 +603,17 @@
 
 	if (params->ca_cert) {
 		ret = gnutls_certificate_set_x509_trust_file(
-			global->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM);
+			global->xcred, params->ca_cert, GNUTLS_X509_FMT_DER);
 		if (ret < 0) {
 			wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' "
-				   "in PEM format: %s", params->ca_cert,
+				   "in DER format: %s", params->ca_cert,
 				   gnutls_strerror(ret));
 			ret = gnutls_certificate_set_x509_trust_file(
 				global->xcred, params->ca_cert,
-				GNUTLS_X509_FMT_DER);
+				GNUTLS_X509_FMT_PEM);
 			if (ret < 0) {
 				wpa_printf(MSG_DEBUG, "Failed to read CA cert "
-					   "'%s' in DER format: %s",
+					   "'%s' in PEM format: %s",
 					   params->ca_cert,
 					   gnutls_strerror(ret));
 				goto fail;
@@ -677,29 +626,27 @@
 				GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5);
 		}
 
-#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
 		if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
 			gnutls_certificate_set_verify_flags(
 				global->xcred,
 				GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
 		}
-#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
 	}
 
 	if (params->client_cert && params->private_key) {
 		/* TODO: private_key_passwd? */
 		ret = gnutls_certificate_set_x509_key_file(
 			global->xcred, params->client_cert,
-			params->private_key, GNUTLS_X509_FMT_PEM);
+			params->private_key, GNUTLS_X509_FMT_DER);
 		if (ret < 0) {
 			wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
-				   "in PEM format: %s", gnutls_strerror(ret));
+				   "in DER format: %s", gnutls_strerror(ret));
 			ret = gnutls_certificate_set_x509_key_file(
 				global->xcred, params->client_cert,
-				params->private_key, GNUTLS_X509_FMT_DER);
+				params->private_key, GNUTLS_X509_FMT_PEM);
 			if (ret < 0) {
 				wpa_printf(MSG_DEBUG, "Failed to read client "
-					   "cert/key in DER format: %s",
+					   "cert/key in PEM format: %s",
 					   gnutls_strerror(ret));
 				goto fail;
 			}
@@ -708,7 +655,6 @@
 		int pkcs12_ok = 0;
 #ifdef PKCS12_FUNCS
 		/* Try to load in PKCS#12 format */
-#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
 		ret = gnutls_certificate_set_x509_simple_pkcs12_file(
 			global->xcred, params->private_key,
 			GNUTLS_X509_FMT_DER, params->private_key_passwd);
@@ -718,7 +664,6 @@
 			goto fail;
 		} else
 			pkcs12_ok = 1;
-#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
 #endif /* PKCS12_FUNCS */
 
 		if (!pkcs12_ok) {
@@ -763,37 +708,23 @@
 int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
 			    struct tls_keys *keys)
 {
-#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
-	security_parameters_st *sec;
-#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
+#if GNUTLS_VERSION_NUMBER >= 0x030012
+	gnutls_datum_t client, server;
 
 	if (conn == NULL || conn->session == NULL || keys == NULL)
 		return -1;
 
 	os_memset(keys, 0, sizeof(*keys));
-
-#if LIBGNUTLS_VERSION_NUMBER < 0x020c00
-#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
-	sec = &conn->session->security_parameters;
-	keys->master_key = sec->master_secret;
-	keys->master_key_len = WPA_TLS_MASTER_SIZE;
-	keys->client_random = sec->client_random;
-	keys->server_random = sec->server_random;
-#else /* GNUTLS_INTERNAL_STRUCTURE_HACK */
-	keys->client_random =
-		(u8 *) gnutls_session_get_client_random(conn->session);
-	keys->server_random =
-		(u8 *) gnutls_session_get_server_random(conn->session);
-	/* No access to master_secret */
-#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
-#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */
-
-#if LIBGNUTLS_VERSION_NUMBER < 0x020c00
-	keys->client_random_len = WPA_TLS_RANDOM_SIZE;
-	keys->server_random_len = WPA_TLS_RANDOM_SIZE;
-#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */
+	gnutls_session_get_random(conn->session, &client, &server);
+	keys->client_random = client.data;
+	keys->server_random = server.data;
+	keys->client_random_len = client.size;
+	keys->server_random_len = client.size;
 
 	return 0;
+#else /* 3.0.18 */
+	return -1;
+#endif /* 3.0.18 */
 }
 
 
@@ -801,86 +732,317 @@
 		       const char *label, int server_random_first,
 		       u8 *out, size_t out_len)
 {
-#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
 	if (conn == NULL || conn->session == NULL)
 		return -1;
 
 	return gnutls_prf(conn->session, os_strlen(label), label,
 			  server_random_first, 0, NULL, out_len, (char *) out);
-#else /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
-	return -1;
-#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
 }
 
 
-static int tls_connection_verify_peer(struct tls_connection *conn,
-				      gnutls_alert_description_t *err)
+static void gnutls_tls_fail_event(struct tls_connection *conn,
+				  const gnutls_datum_t *cert, int depth,
+				  const char *subject, const char *err_str,
+				  enum tls_fail_reason reason)
 {
+	union tls_event_data ev;
+	struct tls_global *global = conn->global;
+	struct wpabuf *cert_buf = NULL;
+
+	if (global->event_cb == NULL)
+		return;
+
+	os_memset(&ev, 0, sizeof(ev));
+	ev.cert_fail.depth = depth;
+	ev.cert_fail.subject = subject ? subject : "";
+	ev.cert_fail.reason = reason;
+	ev.cert_fail.reason_txt = err_str;
+	if (cert) {
+		cert_buf = wpabuf_alloc_copy(cert->data, cert->size);
+		ev.cert_fail.cert = cert_buf;
+	}
+	global->event_cb(global->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
+	wpabuf_free(cert_buf);
+}
+
+
+#if GNUTLS_VERSION_NUMBER < 0x030300
+static int server_eku_purpose(gnutls_x509_crt_t cert)
+{
+	unsigned int i;
+
+	for (i = 0; ; i++) {
+		char oid[128];
+		size_t oid_size = sizeof(oid);
+		int res;
+
+		res = gnutls_x509_crt_get_key_purpose_oid(cert, i, oid,
+							  &oid_size, NULL);
+		if (res == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+			if (i == 0) {
+				/* No EKU - assume any use allowed */
+				return 1;
+			}
+			break;
+		}
+
+		if (res < 0) {
+			wpa_printf(MSG_INFO, "GnuTLS: Failed to get EKU");
+			return 0;
+		}
+
+		wpa_printf(MSG_DEBUG, "GnuTLS: Certificate purpose: %s", oid);
+		if (os_strcmp(oid, GNUTLS_KP_TLS_WWW_SERVER) == 0 ||
+		    os_strcmp(oid, GNUTLS_KP_ANY) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+#endif /* < 3.3.0 */
+
+
+static int check_ocsp(struct tls_connection *conn, gnutls_session_t session,
+		      gnutls_alert_description_t *err)
+{
+#if GNUTLS_VERSION_NUMBER >= 0x030103
+	gnutls_datum_t response, buf;
+	gnutls_ocsp_resp_t resp;
+	unsigned int cert_status;
+	int res;
+
+	if (!(conn->flags & (TLS_CONN_REQUEST_OCSP | TLS_CONN_REQUIRE_OCSP)))
+		return 0;
+
+	if (!gnutls_ocsp_status_request_is_checked(session, 0)) {
+		if (conn->flags & TLS_CONN_REQUIRE_OCSP) {
+			wpa_printf(MSG_INFO,
+				   "GnuTLS: No valid OCSP response received");
+			goto ocsp_error;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: Valid OCSP response was not received - continue since OCSP was not required");
+		return 0;
+	}
+
+	/*
+	 * GnuTLS has already verified the OCSP response in
+	 * check_ocsp_response() and rejected handshake if the certificate was
+	 * found to be revoked. However, if the response indicates that the
+	 * status is unknown, handshake continues and reaches here. We need to
+	 * re-import the OCSP response to check for unknown certificate status,
+	 * but we do not need to repeat gnutls_ocsp_resp_check_crt() and
+	 * gnutls_ocsp_resp_verify_direct() calls.
+	 */
+
+	res = gnutls_ocsp_status_request_get(session, &response);
+	if (res != GNUTLS_E_SUCCESS) {
+		wpa_printf(MSG_INFO,
+			   "GnuTLS: OCSP response was received, but it was not valid");
+		goto ocsp_error;
+	}
+
+	if (gnutls_ocsp_resp_init(&resp) != GNUTLS_E_SUCCESS)
+		goto ocsp_error;
+
+	res = gnutls_ocsp_resp_import(resp, &response);
+	if (res != GNUTLS_E_SUCCESS) {
+		wpa_printf(MSG_INFO,
+			   "GnuTLS: Could not parse received OCSP response: %s",
+			   gnutls_strerror(res));
+		gnutls_ocsp_resp_deinit(resp);
+		goto ocsp_error;
+	}
+
+	res = gnutls_ocsp_resp_print(resp, GNUTLS_OCSP_PRINT_FULL, &buf);
+	if (res == GNUTLS_E_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "GnuTLS: %s", buf.data);
+		gnutls_free(buf.data);
+	}
+
+	res = gnutls_ocsp_resp_get_single(resp, 0, NULL, NULL, NULL,
+					  NULL, &cert_status, NULL,
+					  NULL, NULL, NULL);
+	gnutls_ocsp_resp_deinit(resp);
+	if (res != GNUTLS_E_SUCCESS) {
+		wpa_printf(MSG_INFO,
+			   "GnuTLS: Failed to extract OCSP information: %s",
+			   gnutls_strerror(res));
+		goto ocsp_error;
+	}
+
+	if (cert_status == GNUTLS_OCSP_CERT_GOOD) {
+		wpa_printf(MSG_DEBUG, "GnuTLS: OCSP cert status: good");
+	} else if (cert_status == GNUTLS_OCSP_CERT_REVOKED) {
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: OCSP cert status: revoked");
+		goto ocsp_error;
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: OCSP cert status: unknown");
+		if (conn->flags & TLS_CONN_REQUIRE_OCSP)
+			goto ocsp_error;
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: OCSP was not required, so allow connection to continue");
+	}
+
+	return 0;
+
+ocsp_error:
+	gnutls_tls_fail_event(conn, NULL, 0, NULL,
+			      "bad certificate status response",
+			      TLS_FAIL_REVOKED);
+	*err = GNUTLS_A_CERTIFICATE_REVOKED;
+	return -1;
+#else /* GnuTLS 3.1.3 or newer */
+	return 0;
+#endif /* GnuTLS 3.1.3 or newer */
+}
+
+
+static int tls_connection_verify_peer(gnutls_session_t session)
+{
+	struct tls_connection *conn;
 	unsigned int status, num_certs, i;
 	struct os_time now;
 	const gnutls_datum_t *certs;
 	gnutls_x509_crt_t cert;
+	gnutls_alert_description_t err;
+	int res;
 
-	if (gnutls_certificate_verify_peers2(conn->session, &status) < 0) {
+	conn = gnutls_session_get_ptr(session);
+	if (!conn->verify_peer) {
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: No peer certificate verification enabled");
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "GnuTSL: Verifying peer certificate");
+
+#if GNUTLS_VERSION_NUMBER >= 0x030300
+	{
+		gnutls_typed_vdata_st data[1];
+		unsigned int elements = 0;
+
+		os_memset(data, 0, sizeof(data));
+		if (!conn->global->server) {
+			data[elements].type = GNUTLS_DT_KEY_PURPOSE_OID;
+			data[elements].data = (void *) GNUTLS_KP_TLS_WWW_SERVER;
+			elements++;
+		}
+		res = gnutls_certificate_verify_peers(session, data, 1,
+						      &status);
+	}
+#else /* < 3.3.0 */
+	res = gnutls_certificate_verify_peers2(session, &status);
+#endif
+	if (res < 0) {
 		wpa_printf(MSG_INFO, "TLS: Failed to verify peer "
 			   "certificate chain");
-		*err = GNUTLS_A_INTERNAL_ERROR;
-		return -1;
+		err = GNUTLS_A_INTERNAL_ERROR;
+		goto out;
+	}
+
+#if GNUTLS_VERSION_NUMBER >= 0x030104
+	{
+		gnutls_datum_t info;
+		int ret, type;
+
+		type = gnutls_certificate_type_get(session);
+		ret = gnutls_certificate_verification_status_print(status, type,
+								   &info, 0);
+		if (ret < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "GnuTLS: Failed to print verification status");
+			err = GNUTLS_A_INTERNAL_ERROR;
+			goto out;
+		}
+		wpa_printf(MSG_DEBUG, "GnuTLS: %s", info.data);
+		gnutls_free(info.data);
+	}
+#endif /* GnuTLS 3.1.4 or newer */
+
+	certs = gnutls_certificate_get_peers(session, &num_certs);
+	if (certs == NULL || num_certs == 0) {
+		wpa_printf(MSG_INFO, "TLS: No peer certificate chain received");
+		err = GNUTLS_A_UNKNOWN_CA;
+		goto out;
 	}
 
 	if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) {
 		wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted");
-		*err = GNUTLS_A_INTERNAL_ERROR;
 		if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
 			wpa_printf(MSG_INFO, "TLS: Certificate uses insecure "
 				   "algorithm");
-			*err = GNUTLS_A_INSUFFICIENT_SECURITY;
+			gnutls_tls_fail_event(conn, NULL, 0, NULL,
+					      "certificate uses insecure algorithm",
+					      TLS_FAIL_BAD_CERTIFICATE);
+			err = GNUTLS_A_INSUFFICIENT_SECURITY;
+			goto out;
 		}
-#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
 		if (status & GNUTLS_CERT_NOT_ACTIVATED) {
 			wpa_printf(MSG_INFO, "TLS: Certificate not yet "
 				   "activated");
-			*err = GNUTLS_A_CERTIFICATE_EXPIRED;
+			gnutls_tls_fail_event(conn, NULL, 0, NULL,
+					      "certificate not yet valid",
+					      TLS_FAIL_NOT_YET_VALID);
+			err = GNUTLS_A_CERTIFICATE_EXPIRED;
+			goto out;
 		}
 		if (status & GNUTLS_CERT_EXPIRED) {
 			wpa_printf(MSG_INFO, "TLS: Certificate expired");
-			*err = GNUTLS_A_CERTIFICATE_EXPIRED;
+			gnutls_tls_fail_event(conn, NULL, 0, NULL,
+					      "certificate has expired",
+					      TLS_FAIL_EXPIRED);
+			err = GNUTLS_A_CERTIFICATE_EXPIRED;
+			goto out;
 		}
-#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
-		return -1;
+		gnutls_tls_fail_event(conn, NULL, 0, NULL,
+				      "untrusted certificate",
+				      TLS_FAIL_UNTRUSTED);
+		err = GNUTLS_A_INTERNAL_ERROR;
+		goto out;
 	}
 
 	if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
 		wpa_printf(MSG_INFO, "TLS: Peer certificate does not have a "
 			   "known issuer");
-		*err = GNUTLS_A_UNKNOWN_CA;
-		return -1;
+		gnutls_tls_fail_event(conn, NULL, 0, NULL, "signed not found",
+				      TLS_FAIL_UNTRUSTED);
+		err = GNUTLS_A_UNKNOWN_CA;
+		goto out;
 	}
 
 	if (status & GNUTLS_CERT_REVOKED) {
 		wpa_printf(MSG_INFO, "TLS: Peer certificate has been revoked");
-		*err = GNUTLS_A_CERTIFICATE_REVOKED;
-		return -1;
+		gnutls_tls_fail_event(conn, NULL, 0, NULL,
+				      "certificate revoked",
+				      TLS_FAIL_REVOKED);
+		err = GNUTLS_A_CERTIFICATE_REVOKED;
+		goto out;
 	}
 
+	if (status != 0) {
+		wpa_printf(MSG_INFO, "TLS: Unknown verification status: %d",
+			   status);
+		err = GNUTLS_A_INTERNAL_ERROR;
+		goto out;
+	}
+
+	if (check_ocsp(conn, session, &err))
+		goto out;
+
 	os_get_time(&now);
 
-	certs = gnutls_certificate_get_peers(conn->session, &num_certs);
-	if (certs == NULL) {
-		wpa_printf(MSG_INFO, "TLS: No peer certificate chain "
-			   "received");
-		*err = GNUTLS_A_UNKNOWN_CA;
-		return -1;
-	}
-
 	for (i = 0; i < num_certs; i++) {
 		char *buf;
 		size_t len;
 		if (gnutls_x509_crt_init(&cert) < 0) {
 			wpa_printf(MSG_INFO, "TLS: Certificate initialization "
 				   "failed");
-			*err = GNUTLS_A_BAD_CERTIFICATE;
-			return -1;
+			err = GNUTLS_A_BAD_CERTIFICATE;
+			goto out;
 		}
 
 		if (gnutls_x509_crt_import(cert, &certs[i],
@@ -888,8 +1050,8 @@
 			wpa_printf(MSG_INFO, "TLS: Could not parse peer "
 				   "certificate %d/%d", i + 1, num_certs);
 			gnutls_x509_crt_deinit(cert);
-			*err = GNUTLS_A_BAD_CERTIFICATE;
-			return -1;
+			err = GNUTLS_A_BAD_CERTIFICATE;
+			goto out;
 		}
 
 		gnutls_x509_crt_get_dn(cert, NULL, &len);
@@ -902,26 +1064,109 @@
 		wpa_printf(MSG_DEBUG, "TLS: Peer cert chain %d/%d: %s",
 			   i + 1, num_certs, buf);
 
+		if (conn->global->event_cb) {
+			struct wpabuf *cert_buf = NULL;
+			union tls_event_data ev;
+#ifdef CONFIG_SHA256
+			u8 hash[32];
+			const u8 *_addr[1];
+			size_t _len[1];
+#endif /* CONFIG_SHA256 */
+
+			os_memset(&ev, 0, sizeof(ev));
+			if (conn->global->cert_in_cb) {
+				cert_buf = wpabuf_alloc_copy(certs[i].data,
+							     certs[i].size);
+				ev.peer_cert.cert = cert_buf;
+			}
+#ifdef CONFIG_SHA256
+			_addr[0] = certs[i].data;
+			_len[0] = certs[i].size;
+			if (sha256_vector(1, _addr, _len, hash) == 0) {
+				ev.peer_cert.hash = hash;
+				ev.peer_cert.hash_len = sizeof(hash);
+			}
+#endif /* CONFIG_SHA256 */
+			ev.peer_cert.depth = i;
+			ev.peer_cert.subject = buf;
+			conn->global->event_cb(conn->global->cb_ctx,
+					       TLS_PEER_CERTIFICATE, &ev);
+			wpabuf_free(cert_buf);
+		}
+
 		if (i == 0) {
-			/* TODO: validate subject_match and altsubject_match */
+			if (conn->suffix_match &&
+			    !gnutls_x509_crt_check_hostname(
+				    cert, conn->suffix_match)) {
+				wpa_printf(MSG_WARNING,
+					   "TLS: Domain suffix match '%s' not found",
+					   conn->suffix_match);
+				gnutls_tls_fail_event(
+					conn, &certs[i], i, buf,
+					"Domain suffix mismatch",
+					TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
+				err = GNUTLS_A_BAD_CERTIFICATE;
+				gnutls_x509_crt_deinit(cert);
+				os_free(buf);
+				goto out;
+			}
+
+			/* TODO: validate altsubject_match.
+			 * For now, any such configuration is rejected in
+			 * tls_connection_set_params() */
+
+#if GNUTLS_VERSION_NUMBER < 0x030300
+			/*
+			 * gnutls_certificate_verify_peers() not available, so
+			 * need to check EKU separately.
+			 */
+			if (!conn->global->server &&
+			    !server_eku_purpose(cert)) {
+				wpa_printf(MSG_WARNING,
+					   "GnuTLS: No server EKU");
+				gnutls_tls_fail_event(
+					conn, &certs[i], i, buf,
+					"No server EKU",
+					TLS_FAIL_BAD_CERTIFICATE);
+				err = GNUTLS_A_BAD_CERTIFICATE;
+				gnutls_x509_crt_deinit(cert);
+				os_free(buf);
+				goto out;
+			}
+#endif /* < 3.3.0 */
+		}
+
+		if (!conn->disable_time_checks &&
+		    (gnutls_x509_crt_get_expiration_time(cert) < now.sec ||
+		     gnutls_x509_crt_get_activation_time(cert) > now.sec)) {
+			wpa_printf(MSG_INFO, "TLS: Peer certificate %d/%d is "
+				   "not valid at this time",
+				   i + 1, num_certs);
+			gnutls_tls_fail_event(
+				conn, &certs[i], i, buf,
+				"Certificate is not valid at this time",
+				TLS_FAIL_EXPIRED);
+			gnutls_x509_crt_deinit(cert);
+			os_free(buf);
+			err = GNUTLS_A_CERTIFICATE_EXPIRED;
+			goto out;
 		}
 
 		os_free(buf);
 
-		if (gnutls_x509_crt_get_expiration_time(cert) < now.sec ||
-		    gnutls_x509_crt_get_activation_time(cert) > now.sec) {
-			wpa_printf(MSG_INFO, "TLS: Peer certificate %d/%d is "
-				   "not valid at this time",
-				   i + 1, num_certs);
-			gnutls_x509_crt_deinit(cert);
-			*err = GNUTLS_A_CERTIFICATE_EXPIRED;
-			return -1;
-		}
-
 		gnutls_x509_crt_deinit(cert);
 	}
 
+	if (conn->global->event_cb != NULL)
+		conn->global->event_cb(conn->global->cb_ctx,
+				       TLS_CERT_CHAIN_SUCCESS, NULL);
+
 	return 0;
+
+out:
+	conn->failed++;
+	gnutls_alert_send(session, GNUTLS_AL_FATAL, err);
+	return GNUTLS_E_CERTIFICATE_ERROR;
 }
 
 
@@ -979,6 +1224,8 @@
 
 	ret = gnutls_handshake(conn->session);
 	if (ret < 0) {
+		gnutls_alert_description_t alert;
+
 		switch (ret) {
 		case GNUTLS_E_AGAIN:
 			if (global->server && conn->established &&
@@ -989,10 +1236,20 @@
 			}
 			break;
 		case GNUTLS_E_FATAL_ALERT_RECEIVED:
+			alert = gnutls_alert_get(conn->session);
 			wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert",
-				   __func__, gnutls_alert_get_name(
-					   gnutls_alert_get(conn->session)));
+				   __func__, gnutls_alert_get_name(alert));
 			conn->read_alerts++;
+			if (conn->global->event_cb != NULL) {
+				union tls_event_data ev;
+
+				os_memset(&ev, 0, sizeof(ev));
+				ev.alert.is_local = 0;
+				ev.alert.type = gnutls_alert_get_name(alert);
+				ev.alert.description = ev.alert.type;
+				conn->global->event_cb(conn->global->cb_ctx,
+						       TLS_ALERT, &ev);
+			}
 			/* continue */
 		default:
 			wpa_printf(MSG_DEBUG, "%s - gnutls_handshake failed "
@@ -1001,18 +1258,21 @@
 		}
 	} else {
 		size_t size;
-		gnutls_alert_description_t err;
-
-		if (conn->verify_peer &&
-		    tls_connection_verify_peer(conn, &err)) {
-			wpa_printf(MSG_INFO, "TLS: Peer certificate chain "
-				   "failed validation");
-			conn->failed++;
-			gnutls_alert_send(conn->session, GNUTLS_AL_FATAL, err);
-			goto out;
-		}
 
 		wpa_printf(MSG_DEBUG, "TLS: Handshake completed successfully");
+
+#if GNUTLS_VERSION_NUMBER >= 0x03010a
+		{
+			char *desc;
+
+			desc = gnutls_session_get_desc(conn->session);
+			if (desc) {
+				wpa_printf(MSG_DEBUG, "GnuTLS: %s", desc);
+				gnutls_free(desc);
+			}
+		}
+#endif /* GnuTLS 3.1.10 or newer */
+
 		conn->established = 1;
 		if (conn->push_buf == NULL) {
 			/* Need to return something to get final TLS ACK. */
@@ -1036,7 +1296,6 @@
 			*appl_data = gnutls_get_appl_data(conn);
 	}
 
-out:
 	out_data = conn->push_buf;
 	conn->push_buf = NULL;
 	return out_data;
@@ -1201,3 +1460,10 @@
 {
 	return -1;
 }
+
+
+int tls_get_library_version(char *buf, size_t buf_len)
+{
+	return os_snprintf(buf, buf_len, "GnuTLS build=%s run=%s",
+			   GNUTLS_VERSION, gnutls_check_version(NULL));
+}
diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c
index 6563ed2..86375d1 100644
--- a/src/crypto/tls_internal.c
+++ b/src/crypto/tls_internal.c
@@ -190,6 +190,26 @@
 	if (cred == NULL)
 		return -1;
 
+	if (params->subject_match) {
+		wpa_printf(MSG_INFO, "TLS: subject_match not supported");
+		return -1;
+	}
+
+	if (params->altsubject_match) {
+		wpa_printf(MSG_INFO, "TLS: altsubject_match not supported");
+		return -1;
+	}
+
+	if (params->suffix_match) {
+		wpa_printf(MSG_INFO, "TLS: suffix_match not supported");
+		return -1;
+	}
+
+	if (params->openssl_ciphers) {
+		wpa_printf(MSG_INFO, "GnuTLS: openssl_ciphers not supported");
+		return -1;
+	}
+
 	if (tlsv1_set_ca_cert(cred, params->ca_cert,
 			      params->ca_cert_blob, params->ca_cert_blob_len,
 			      params->ca_path)) {
@@ -652,3 +672,9 @@
 #endif /* CONFIG_TLS_INTERNAL_SERVER */
 	return -1;
 }
+
+
+int tls_get_library_version(char *buf, size_t buf_len)
+{
+	return os_snprintf(buf, buf_len, "internal");
+}
diff --git a/src/crypto/tls_none.c b/src/crypto/tls_none.c
index 1a1092a..a6d210a 100644
--- a/src/crypto/tls_none.c
+++ b/src/crypto/tls_none.c
@@ -192,3 +192,9 @@
 {
 	return 0;
 }
+
+
+int tls_get_library_version(char *buf, size_t buf_len)
+{
+	return os_snprintf(buf, buf_len, "none");
+}
diff --git a/src/crypto/tls_nss.c b/src/crypto/tls_nss.c
deleted file mode 100644
index c53c192..0000000
--- a/src/crypto/tls_nss.c
+++ /dev/null
@@ -1,645 +0,0 @@
-/*
- * SSL/TLS interface functions for NSS
- * Copyright (c) 2009, 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 <nspr/prtypes.h>
-#include <nspr/plarenas.h>
-#include <nspr/plhash.h>
-#include <nspr/prio.h>
-#include <nspr/prclist.h>
-#include <nspr/prlock.h>
-#include <nspr/prinit.h>
-#include <nspr/prerror.h>
-#include <nspr/prmem.h>
-#include <nss/nss.h>
-#include <nss/nssilckt.h>
-#include <nss/ssl.h>
-#include <nss/pk11func.h>
-#include <nss/secerr.h>
-
-#include "common.h"
-#include "tls.h"
-
-static int tls_nss_ref_count = 0;
-
-static PRDescIdentity nss_layer_id;
-
-
-struct tls_connection {
-	PRFileDesc *fd;
-
-	int established;
-	int verify_peer;
-	u8 *push_buf, *pull_buf, *pull_buf_offset;
-	size_t push_buf_len, pull_buf_len;
-};
-
-
-static PRStatus nss_io_close(PRFileDesc *fd)
-{
-	wpa_printf(MSG_DEBUG, "NSS: I/O close");
-	return PR_SUCCESS;
-}
-
-
-static PRInt32 nss_io_read(PRFileDesc *fd, void *buf, PRInt32 amount)
-{
-	wpa_printf(MSG_DEBUG, "NSS: I/O read(%d)", amount);
-	return PR_FAILURE;
-}
-
-
-static PRInt32 nss_io_write(PRFileDesc *fd, const void *buf, PRInt32 amount)
-{
-	wpa_printf(MSG_DEBUG, "NSS: I/O write(%d)", amount);
-	return PR_FAILURE;
-}
-
-
-static PRInt32 nss_io_writev(PRFileDesc *fd, const PRIOVec *iov,
-			     PRInt32 iov_size, PRIntervalTime timeout)
-{
-	wpa_printf(MSG_DEBUG, "NSS: I/O writev(%d)", iov_size);
-	return PR_FAILURE;
-}
-
-
-static PRInt32 nss_io_recv(PRFileDesc *fd, void *buf, PRInt32 amount,
-			   PRIntn flags, PRIntervalTime timeout)
-{
-	struct tls_connection *conn = (struct tls_connection *) fd->secret;
-	u8 *end;
-
-	wpa_printf(MSG_DEBUG, "NSS: I/O recv(%d)", amount);
-
-	if (conn->pull_buf == NULL) {
-		wpa_printf(MSG_DEBUG, "NSS: No data available to be read yet");
-		return PR_FAILURE;
-	}
-
-	end = conn->pull_buf + conn->pull_buf_len;
-	if (end - conn->pull_buf_offset < amount)
-		amount = end - conn->pull_buf_offset;
-	os_memcpy(buf, conn->pull_buf_offset, amount);
-	conn->pull_buf_offset += amount;
-	if (conn->pull_buf_offset == end) {
-		wpa_printf(MSG_DEBUG, "%s - pull_buf consumed", __func__);
-		os_free(conn->pull_buf);
-		conn->pull_buf = conn->pull_buf_offset = NULL;
-		conn->pull_buf_len = 0;
-	} else {
-		wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in pull_buf",
-			   __func__,
-			   (unsigned long) (end - conn->pull_buf_offset));
-	}
-	return amount;
-}
-
-
-static PRInt32 nss_io_send(PRFileDesc *fd, const void *buf, PRInt32 amount,
-			   PRIntn flags, PRIntervalTime timeout)
-{
-	struct tls_connection *conn = (struct tls_connection *) fd->secret;
-	u8 *nbuf;
-
-	wpa_printf(MSG_DEBUG, "NSS: I/O %s", __func__);
-	wpa_hexdump(MSG_MSGDUMP, "NSS: I/O send data", buf, amount);
-
-	nbuf = os_realloc(conn->push_buf, conn->push_buf_len + amount);
-	if (nbuf == NULL) {
-		wpa_printf(MSG_ERROR, "NSS: Failed to allocate memory for the "
-			   "data to be sent");
-		return PR_FAILURE;
-	}
-	os_memcpy(nbuf + conn->push_buf_len, buf, amount);
-	conn->push_buf = nbuf;
-	conn->push_buf_len += amount;
-
-	return amount;
-}
-
-
-static PRInt32 nss_io_recvfrom(PRFileDesc *fd, void *buf, PRInt32 amount,
-			       PRIntn flags, PRNetAddr *addr,
-			       PRIntervalTime timeout)
-{
-	wpa_printf(MSG_DEBUG, "NSS: I/O %s", __func__);
-	return PR_FAILURE;
-}
-
-
-static PRInt32 nss_io_sendto(PRFileDesc *fd, const void *buf, PRInt32 amount,
-			     PRIntn flags, const PRNetAddr *addr,
-			     PRIntervalTime timeout)
-{
-	wpa_printf(MSG_DEBUG, "NSS: I/O %s", __func__);
-	return PR_FAILURE;
-}
-
-
-static PRStatus nss_io_getpeername(PRFileDesc *fd, PRNetAddr *addr)
-{
-	wpa_printf(MSG_DEBUG, "NSS: I/O getpeername");
-
-	/*
-	 * It Looks like NSS only supports IPv4 and IPv6 TCP sockets. Provide a
-	 * fake IPv4 address to work around this even though we are not really
-	 * using TCP.
-	 */
-	os_memset(addr, 0, sizeof(*addr));
-	addr->inet.family = PR_AF_INET;
-
-	return PR_SUCCESS;
-}
-
-
-static PRStatus nss_io_getsocketoption(PRFileDesc *fd,
-				       PRSocketOptionData *data)
-{
-	switch (data->option) {
-	case PR_SockOpt_Nonblocking:
-		wpa_printf(MSG_DEBUG, "NSS: I/O getsocketoption(Nonblocking)");
-		data->value.non_blocking = PR_TRUE;
-		return PR_SUCCESS;
-	default:
-		wpa_printf(MSG_DEBUG, "NSS: I/O getsocketoption(%d)",
-			   data->option);
-		return PR_FAILURE;
-	}
-}
-
-
-static const PRIOMethods nss_io = {
-	PR_DESC_LAYERED,
-	nss_io_close,
-	nss_io_read,
-	nss_io_write,
-	NULL /* available */,
-	NULL /* available64 */,
-	NULL /* fsync */,
-	NULL /* fseek */,
-	NULL /* fseek64 */,
-	NULL /* fileinfo */,
-	NULL /* fileinfo64 */,
-	nss_io_writev,
-	NULL /* connect */,
-	NULL /* accept */,
-	NULL /* bind */,
-	NULL /* listen */,
-	NULL /* shutdown */,
-	nss_io_recv,
-	nss_io_send,
-	nss_io_recvfrom,
-	nss_io_sendto,
-	NULL /* poll */,
-	NULL /* acceptread */,
-	NULL /* transmitfile */,
-	NULL /* getsockname */,
-	nss_io_getpeername,
-	NULL /* reserved_fn_6 */,
-	NULL /* reserved_fn_5 */,
-	nss_io_getsocketoption,
-	NULL /* setsocketoption */,
-	NULL /* sendfile */,
-	NULL /* connectcontinue */,
-	NULL /* reserved_fn_3 */,
-	NULL /* reserved_fn_2 */,
-	NULL /* reserved_fn_1 */,
-	NULL /* reserved_fn_0 */
-};
-
-
-static char * nss_password_cb(PK11SlotInfo *slot, PRBool retry, void *arg)
-{
-	wpa_printf(MSG_ERROR, "NSS: TODO - %s", __func__);
-	return NULL;
-}
-
-
-void * tls_init(const struct tls_config *conf)
-{
-	char *dir;
-
-	tls_nss_ref_count++;
-	if (tls_nss_ref_count > 1)
-		return (void *) 1;
-
-	PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
-
-	nss_layer_id = PR_GetUniqueIdentity("wpa_supplicant");
-
-	PK11_SetPasswordFunc(nss_password_cb);
-
-	dir = getenv("SSL_DIR");
-	if (dir) {
-		if (NSS_Init(dir) != SECSuccess) {
-			wpa_printf(MSG_ERROR, "NSS: NSS_Init(cert_dir=%s) "
-				   "failed", dir);
-			return NULL;
-		}
-	} else {
-		if (NSS_NoDB_Init(NULL) != SECSuccess) {
-			wpa_printf(MSG_ERROR, "NSS: NSS_NoDB_Init(NULL) "
-				   "failed");
-			return NULL;
-		}
-	}
-
-	if (SSL_OptionSetDefault(SSL_V2_COMPATIBLE_HELLO, PR_FALSE) !=
-	    SECSuccess ||
-	    SSL_OptionSetDefault(SSL_ENABLE_SSL3, PR_FALSE) != SECSuccess ||
-	    SSL_OptionSetDefault(SSL_ENABLE_SSL2, PR_FALSE) != SECSuccess ||
-	    SSL_OptionSetDefault(SSL_ENABLE_TLS, PR_TRUE) != SECSuccess) {
-		wpa_printf(MSG_ERROR, "NSS: SSL_OptionSetDefault failed");
-		return NULL;
-	}
-
-	if (NSS_SetDomesticPolicy() != SECSuccess) {
-		wpa_printf(MSG_ERROR, "NSS: NSS_SetDomesticPolicy() failed");
-		return NULL;
-	}
-
-	return (void *) 1;
-}
-
-void tls_deinit(void *ssl_ctx)
-{
-	tls_nss_ref_count--;
-	if (tls_nss_ref_count == 0) {
-		if (NSS_Shutdown() != SECSuccess)
-			wpa_printf(MSG_ERROR, "NSS: NSS_Shutdown() failed");
-	}
-}
-
-
-int tls_get_errors(void *tls_ctx)
-{
-	return 0;
-}
-
-
-static SECStatus nss_bad_cert_cb(void *arg, PRFileDesc *fd)
-{
-	struct tls_connection *conn = arg;
-	SECStatus res = SECSuccess;
-	PRErrorCode err;
-	CERTCertificate *cert;
-	char *subject, *issuer;
-
-	err = PR_GetError();
-	if (IS_SEC_ERROR(err))
-		wpa_printf(MSG_DEBUG, "NSS: Bad Server Certificate (sec err "
-			   "%d)", err - SEC_ERROR_BASE);
-	else
-		wpa_printf(MSG_DEBUG, "NSS: Bad Server Certificate (err %d)",
-			   err);
-	cert = SSL_PeerCertificate(fd);
-	subject = CERT_NameToAscii(&cert->subject);
-	issuer = CERT_NameToAscii(&cert->issuer);
-	wpa_printf(MSG_DEBUG, "NSS: Peer certificate subject='%s' issuer='%s'",
-		   subject, issuer);
-	CERT_DestroyCertificate(cert);
-	PR_Free(subject);
-	PR_Free(issuer);
-	if (conn->verify_peer)
-		res = SECFailure;
-
-	return res;
-}
-
-
-static void nss_handshake_cb(PRFileDesc *fd, void *client_data)
-{
-	struct tls_connection *conn = client_data;
-	wpa_printf(MSG_DEBUG, "NSS: Handshake completed");
-	conn->established = 1;
-}
-
-
-struct tls_connection * tls_connection_init(void *tls_ctx)
-{
-	struct tls_connection *conn;
-
-	conn = os_zalloc(sizeof(*conn));
-	if (conn == NULL)
-		return NULL;
-
-	conn->fd = PR_CreateIOLayerStub(nss_layer_id, &nss_io);
-	if (conn->fd == NULL) {
-		os_free(conn);
-		return NULL;
-	}
-	conn->fd->secret = (void *) conn;
-
-	conn->fd = SSL_ImportFD(NULL, conn->fd);
-	if (conn->fd == NULL) {
-		os_free(conn);
-		return NULL;
-	}
-
-	if (SSL_OptionSet(conn->fd, SSL_SECURITY, PR_TRUE) != SECSuccess ||
-	    SSL_OptionSet(conn->fd, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) !=
-	    SECSuccess ||
-	    SSL_OptionSet(conn->fd, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) !=
-	    SECSuccess ||
-	    SSL_OptionSet(conn->fd, SSL_ENABLE_TLS, PR_TRUE) != SECSuccess ||
-	    SSL_BadCertHook(conn->fd, nss_bad_cert_cb, conn) != SECSuccess ||
-	    SSL_HandshakeCallback(conn->fd, nss_handshake_cb, conn) !=
-	    SECSuccess) {
-		wpa_printf(MSG_ERROR, "NSS: Failed to set options");
-		PR_Close(conn->fd);
-		os_free(conn);
-		return NULL;
-	}
-
-	SSL_ResetHandshake(conn->fd, PR_FALSE);
-
-	return conn;
-}
-
-
-void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
-{
-	PR_Close(conn->fd);
-	os_free(conn->push_buf);
-	os_free(conn->pull_buf);
-	os_free(conn);
-}
-
-
-int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
-{
-	return conn->established;
-}
-
-
-int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
-{
-	return -1;
-}
-
-
-int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
-			      const struct tls_connection_params *params)
-{
-	wpa_printf(MSG_ERROR, "NSS: TODO - %s", __func__);
-	return 0;
-}
-
-
-int tls_global_set_params(void *tls_ctx,
-			  const struct tls_connection_params *params)
-{
-	return -1;
-}
-
-
-int tls_global_set_verify(void *tls_ctx, int check_crl)
-{
-	return -1;
-}
-
-
-int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
-			      int verify_peer)
-{
-	conn->verify_peer = verify_peer;
-	return 0;
-}
-
-
-int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
-			    struct tls_keys *keys)
-{
-	/* NSS does not export master secret or client/server random. */
-	return -1;
-}
-
-
-int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
-		       const char *label, int server_random_first,
-		       u8 *out, size_t out_len)
-{
-	if (conn == NULL || server_random_first) {
-		wpa_printf(MSG_INFO, "NSS: Unsupported PRF request "
-			   "(server_random_first=%d)",
-			   server_random_first);
-		return -1;
-	}
-
-	if (SSL_ExportKeyingMaterial(conn->fd, label, NULL, 0, out, out_len) !=
-	    SECSuccess) {
-		wpa_printf(MSG_INFO, "NSS: Failed to use TLS extractor "
-			   "(label='%s' out_len=%d", label, (int) out_len);
-		return -1;
-	}
-
-	return 0;
-}
-
-
-struct wpabuf * tls_connection_handshake(void *tls_ctx,
-					 struct tls_connection *conn,
-					 const struct wpabuf *in_data,
-					 struct wpabuf **appl_data)
-{
-	struct wpabuf *out_data;
-
-	wpa_printf(MSG_DEBUG, "NSS: handshake: in_len=%u",
-		   in_data ? (unsigned int) wpabuf_len(in_data) : 0);
-
-	if (appl_data)
-		*appl_data = NULL;
-
-	if (in_data && wpabuf_len(in_data) > 0) {
-		if (conn->pull_buf) {
-			wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
-				   "pull_buf", __func__,
-				   (unsigned long) conn->pull_buf_len);
-			os_free(conn->pull_buf);
-		}
-		conn->pull_buf = os_malloc(wpabuf_len(in_data));
-		if (conn->pull_buf == NULL)
-			return NULL;
-		os_memcpy(conn->pull_buf, wpabuf_head(in_data),
-			  wpabuf_len(in_data));
-		conn->pull_buf_offset = conn->pull_buf;
-		conn->pull_buf_len = wpabuf_len(in_data);
-	}
-
-	SSL_ForceHandshake(conn->fd);
-
-	if (conn->established && conn->push_buf == NULL) {
-		/* Need to return something to get final TLS ACK. */
-		conn->push_buf = os_malloc(1);
-	}
-
-	if (conn->push_buf == NULL)
-		return NULL;
-	out_data = wpabuf_alloc_ext_data(conn->push_buf, conn->push_buf_len);
-	if (out_data == NULL)
-		os_free(conn->push_buf);
-	conn->push_buf = NULL;
-	conn->push_buf_len = 0;
-	return out_data;
-}
-
-
-struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
-						struct tls_connection *conn,
-						const struct wpabuf *in_data,
-						struct wpabuf **appl_data)
-{
-	return NULL;
-}
-
-
-struct wpabuf * tls_connection_encrypt(void *tls_ctx,
-				       struct tls_connection *conn,
-				       const struct wpabuf *in_data)
-{
-	PRInt32 res;
-	struct wpabuf *buf;
-
-	wpa_printf(MSG_DEBUG, "NSS: encrypt %d bytes",
-		   (int) wpabuf_len(in_data));
-	res = PR_Send(conn->fd, wpabuf_head(in_data), wpabuf_len(in_data), 0,
-		      0);
-	if (res < 0) {
-		wpa_printf(MSG_ERROR, "NSS: Encryption failed");
-		return NULL;
-	}
-	if (conn->push_buf == NULL)
-		return NULL;
-	buf = wpabuf_alloc_ext_data(conn->push_buf, conn->push_buf_len);
-	if (buf == NULL)
-		os_free(conn->push_buf);
-	conn->push_buf = NULL;
-	conn->push_buf_len = 0;
-	return buf;
-}
-
-
-struct wpabuf * tls_connection_decrypt(void *tls_ctx,
-				       struct tls_connection *conn,
-				       const struct wpabuf *in_data)
-{
-	PRInt32 res;
-	struct wpabuf *out;
-
-	wpa_printf(MSG_DEBUG, "NSS: decrypt %d bytes",
-		   (int) wpabuf_len(in_data));
-	if (conn->pull_buf) {
-		wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
-			   "pull_buf", __func__,
-			   (unsigned long) conn->pull_buf_len);
-		os_free(conn->pull_buf);
-	}
-	conn->pull_buf = os_malloc(wpabuf_len(in_data));
-	if (conn->pull_buf == NULL)
-		return NULL;
-	os_memcpy(conn->pull_buf, wpabuf_head(in_data), wpabuf_len(in_data));
-	conn->pull_buf_offset = conn->pull_buf;
-	conn->pull_buf_len = wpabuf_len(in_data);
-
-	/*
-	 * Even though we try to disable TLS compression, it is possible that
-	 * this cannot be done with all TLS libraries. Add extra buffer space
-	 * to handle the possibility of the decrypted data being longer than
-	 * input data.
-	 */
-	out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
-	if (out == NULL)
-		return NULL;
-
-	res = PR_Recv(conn->fd, wpabuf_mhead(out), wpabuf_size(out), 0, 0);
-	wpa_printf(MSG_DEBUG, "NSS: PR_Recv: %d", res);
-	if (res < 0) {
-		wpabuf_free(out);
-		return NULL;
-	}
-	wpabuf_put(out, res);
-
-	return out;
-}
-
-
-int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
-{
-	return 0;
-}
-
-
-int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
-				   u8 *ciphers)
-{
-	return -1;
-}
-
-
-int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
-		   char *buf, size_t buflen)
-{
-	return -1;
-}
-
-
-int tls_connection_enable_workaround(void *tls_ctx,
-				     struct tls_connection *conn)
-{
-	return -1;
-}
-
-
-int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
-				    int ext_type, const u8 *data,
-				    size_t data_len)
-{
-	return -1;
-}
-
-
-int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
-{
-	return 0;
-}
-
-
-int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
-{
-	return 0;
-}
-
-
-int tls_connection_get_write_alerts(void *tls_ctx,
-				    struct tls_connection *conn)
-{
-	return 0;
-}
-
-
-int tls_connection_get_keyblock_size(void *tls_ctx,
-				     struct tls_connection *conn)
-{
-	return -1;
-}
-
-
-unsigned int tls_capabilities(void *tls_ctx)
-{
-	return 0;
-}
-
-
-int tls_connection_set_session_ticket_cb(void *tls_ctx,
-					 struct tls_connection *conn,
-					 tls_session_ticket_cb cb,
-					 void *ctx)
-{
-	return -1;
-}
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index c72134a..5433ebb 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -3554,3 +3554,11 @@
 	return -1;
 #endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
 }
+
+
+int tls_get_library_version(char *buf, size_t buf_len)
+{
+	return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s",
+			   OPENSSL_VERSION_TEXT,
+			   SSLeay_version(SSLEAY_VERSION));
+}
diff --git a/src/crypto/tls_schannel.c b/src/crypto/tls_schannel.c
index 2c2daa8..a43b487 100644
--- a/src/crypto/tls_schannel.c
+++ b/src/crypto/tls_schannel.c
@@ -692,6 +692,26 @@
 	if (conn == NULL)
 		return -1;
 
+	if (params->subject_match) {
+		wpa_printf(MSG_INFO, "TLS: subject_match not supported");
+		return -1;
+	}
+
+	if (params->altsubject_match) {
+		wpa_printf(MSG_INFO, "TLS: altsubject_match not supported");
+		return -1;
+	}
+
+	if (params->suffix_match) {
+		wpa_printf(MSG_INFO, "TLS: suffix_match not supported");
+		return -1;
+	}
+
+	if (params->openssl_ciphers) {
+		wpa_printf(MSG_INFO, "GnuTLS: openssl_ciphers not supported");
+		return -1;
+	}
+
 	if (global->my_cert_store == NULL &&
 	    (global->my_cert_store = CertOpenSystemStore(0, TEXT("MY"))) ==
 	    NULL) {
@@ -730,3 +750,9 @@
 {
 	return 0;
 }
+
+
+int tls_get_library_version(char *buf, size_t buf_len)
+{
+	return os_snprintf(buf, buf_len, "schannel");
+}
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index eeaba66..2c0c685 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -198,7 +198,6 @@
 #define WPA_SCAN_NOISE_INVALID		BIT(1)
 #define WPA_SCAN_LEVEL_INVALID		BIT(2)
 #define WPA_SCAN_LEVEL_DBM		BIT(3)
-#define WPA_SCAN_AUTHENTICATED		BIT(4)
 #define WPA_SCAN_ASSOCIATED		BIT(5)
 
 /**
@@ -1049,10 +1048,9 @@
 	const int *basic_rates;
 	const u8 *ies;
 	int ie_len;
-	int freq;
+	struct hostapd_freq_params freq;
 	int beacon_int;
 	int max_peer_links;
-	enum ht_mode ht_mode;
 	struct wpa_driver_mesh_bss_params conf;
 #define WPA_DRIVER_MESH_FLAG_USER_MPM	0x00000001
 #define WPA_DRIVER_MESH_FLAG_DRIVER_MPM	0x00000002
@@ -1183,6 +1181,8 @@
 #define WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD	0x0000000400000000ULL
 /** Driver supports TDLS channel switching */
 #define WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH	0x0000000800000000ULL
+/** Driver supports IBSS with HT datarates */
+#define WPA_DRIVER_FLAGS_HT_IBSS		0x0000001000000000ULL
 	u64 flags;
 
 #define WPA_DRIVER_SMPS_MODE_STATIC			0x00000001
diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
index c377970..0f1a0f6 100644
--- a/src/drivers/driver_bsd.c
+++ b/src/drivers/driver_bsd.c
@@ -1344,7 +1344,12 @@
 	*pos++ = 1;
 	*pos++ = sr->isr_erp;
 
+#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+	os_memcpy(pos, (u8 *)(sr + 1) + sr->isr_ssid_len + sr->isr_meshid_len,
+		  sr->isr_ie_len);
+#else
 	os_memcpy(pos, (u8 *)(sr + 1) + sr->isr_ssid_len, sr->isr_ie_len);
+#endif
 	pos += sr->isr_ie_len;
 
 	result->ie_len = pos - (u8 *)(result + 1);
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 8527e90..c180f15 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -1583,6 +1583,14 @@
 	drv->ctx = ctx;
 	drv->hostapd = !!hostapd;
 	drv->eapol_sock = -1;
+
+	/*
+	 * There is no driver capability flag for this, so assume it is
+	 * supported and disable this on first attempt to use if the driver
+	 * rejects the command due to missing support.
+	 */
+	drv->set_rekey_offload = 1;
+
 	drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int);
 	drv->if_indices = drv->default_if_indices;
 
@@ -3070,9 +3078,25 @@
 }
 
 
+static int nl80211_put_basic_rates(struct nl_msg *msg, const int *basic_rates)
+{
+	u8 rates[NL80211_MAX_SUPP_RATES];
+	u8 rates_len = 0;
+	int i;
+
+	if (!basic_rates)
+		return 0;
+
+	for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0; i++)
+		rates[rates_len++] = basic_rates[i] / 5;
+
+	return nla_put(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates);
+}
+
+
 static int nl80211_set_bss(struct i802_bss *bss, int cts, int preamble,
 			   int slot, int ht_opmode, int ap_isolate,
-			   int *basic_rates)
+			   const int *basic_rates)
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
@@ -3087,27 +3111,13 @@
 	    (ht_opmode >= 0 &&
 	     nla_put_u16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode)) ||
 	    (ap_isolate >= 0 &&
-	     nla_put_u8(msg, NL80211_ATTR_AP_ISOLATE, ap_isolate)))
-		goto fail;
-
-	if (basic_rates) {
-		u8 rates[NL80211_MAX_SUPP_RATES];
-		u8 rates_len = 0;
-		int i;
-
-		for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0;
-		     i++)
-			rates[rates_len++] = basic_rates[i] / 5;
-
-		if (nla_put(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len,
-			    rates))
-			goto fail;
+	     nla_put_u8(msg, NL80211_ATTR_AP_ISOLATE, ap_isolate)) ||
+	    nl80211_put_basic_rates(msg, basic_rates)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
 	}
 
 	return send_and_recv_msgs(drv, msg, NULL, NULL);
-fail:
-	nlmsg_free(msg);
-	return -ENOBUFS;
 }
 
 
@@ -3158,6 +3168,18 @@
 }
 
 
+static int nl80211_put_beacon_int(struct nl_msg *msg, int beacon_int)
+{
+	if (beacon_int > 0) {
+		wpa_printf(MSG_DEBUG, "  * beacon_int=%d", beacon_int);
+		return nla_put_u32(msg, NL80211_ATTR_BEACON_INTERVAL,
+				   beacon_int);
+	}
+
+	return 0;
+}
+
+
 static int wpa_driver_nl80211_set_ap(void *priv,
 				     struct wpa_driver_ap_params *params)
 {
@@ -3193,8 +3215,7 @@
 		    params->head) ||
 	    nla_put(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len,
 		    params->tail) ||
-	    nla_put_u32(msg, NL80211_ATTR_BEACON_INTERVAL,
-			params->beacon_int) ||
+	    nl80211_put_beacon_int(msg, params->beacon_int) ||
 	    nla_put_u32(msg, NL80211_ATTR_DTIM_PERIOD, params->dtim_period) ||
 	    nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid))
 		goto fail;
@@ -3382,12 +3403,17 @@
 static int nl80211_put_freq_params(struct nl_msg *msg,
 				   const struct hostapd_freq_params *freq)
 {
+	wpa_printf(MSG_DEBUG, "  * freq=%d", freq->freq);
 	if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq))
 		return -ENOBUFS;
 
+	wpa_printf(MSG_DEBUG, "  * vht_enabled=%d", freq->vht_enabled);
+	wpa_printf(MSG_DEBUG, "  * ht_enabled=%d", freq->ht_enabled);
+
 	if (freq->vht_enabled) {
 		enum nl80211_chan_width cw;
 
+		wpa_printf(MSG_DEBUG, "  * bandwidth=%d", freq->bandwidth);
 		switch (freq->bandwidth) {
 		case 20:
 			cw = NL80211_CHAN_WIDTH_20;
@@ -3408,6 +3434,11 @@
 			return -EINVAL;
 		}
 
+		wpa_printf(MSG_DEBUG, "  * channel_width=%d", cw);
+		wpa_printf(MSG_DEBUG, "  * center_freq1=%d",
+			   freq->center_freq1);
+		wpa_printf(MSG_DEBUG, "  * center_freq2=%d",
+			   freq->center_freq2);
 		if (nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, cw) ||
 		    nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1,
 				freq->center_freq1) ||
@@ -3418,6 +3449,8 @@
 	} else if (freq->ht_enabled) {
 		enum nl80211_channel_type ct;
 
+		wpa_printf(MSG_DEBUG, "  * sec_channel_offset=%d",
+			   freq->sec_channel_offset);
 		switch (freq->sec_channel_offset) {
 		case -1:
 			ct = NL80211_CHAN_HT40MINUS;
@@ -3430,6 +3463,7 @@
 			break;
 		}
 
+		wpa_printf(MSG_DEBUG, "  * channel_type=%d", ct);
 		if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, ct))
 			return -ENOBUFS;
 	}
@@ -4207,24 +4241,10 @@
 	os_memcpy(drv->ssid, params->ssid, params->ssid_len);
 	drv->ssid_len = params->ssid_len;
 
-	wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq.freq);
-	wpa_printf(MSG_DEBUG, "  * ht_enabled=%d", params->freq.ht_enabled);
-	wpa_printf(MSG_DEBUG, "  * sec_channel_offset=%d",
-		   params->freq.sec_channel_offset);
-	wpa_printf(MSG_DEBUG, "  * vht_enabled=%d", params->freq.vht_enabled);
-	wpa_printf(MSG_DEBUG, "  * center_freq1=%d", params->freq.center_freq1);
-	wpa_printf(MSG_DEBUG, "  * center_freq2=%d", params->freq.center_freq2);
-	wpa_printf(MSG_DEBUG, "  * bandwidth=%d", params->freq.bandwidth);
-	if (nl80211_put_freq_params(msg, &params->freq) < 0)
+	if (nl80211_put_freq_params(msg, &params->freq) < 0 ||
+	    nl80211_put_beacon_int(msg, params->beacon_int))
 		goto fail;
 
-	if (params->beacon_int > 0) {
-		wpa_printf(MSG_DEBUG, "  * beacon_int=%d", params->beacon_int);
-		if (nla_put_u32(msg, NL80211_ATTR_BEACON_INTERVAL,
-				params->beacon_int))
-			goto fail;
-	}
-
 	ret = nl80211_set_conn_keys(params, msg);
 	if (ret)
 		goto fail;
@@ -6736,7 +6756,12 @@
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nlattr *replay_nested;
 	struct nl_msg *msg;
+	int ret;
 
+	if (!drv->set_rekey_offload)
+		return;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Set rekey offload");
 	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_REKEY_OFFLOAD)) ||
 	    !(replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA)) ||
 	    nla_put(msg, NL80211_REKEY_DATA_KEK, NL80211_KEK_LEN, kek) ||
@@ -6750,7 +6775,12 @@
 
 	nla_nest_end(msg, replay_nested);
 
-	send_and_recv_msgs(drv, msg, NULL, (void *) -1);
+	ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1);
+	if (ret == -EOPNOTSUPP) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Driver does not support rekey offload");
+		drv->set_rekey_offload = 0;
+	}
 }
 
 
@@ -7767,6 +7797,19 @@
 }
 
 
+static int nl80211_put_mesh_id(struct nl_msg *msg, const u8 *mesh_id,
+			       size_t mesh_id_len)
+{
+	if (mesh_id) {
+		wpa_hexdump_ascii(MSG_DEBUG, "  * Mesh ID (SSID)",
+				  mesh_id, mesh_id_len);
+		return nla_put(msg, NL80211_ATTR_MESH_ID, mesh_id_len, mesh_id);
+	}
+
+	return 0;
+}
+
+
 static int
 wpa_driver_nl80211_join_mesh(void *priv,
 			     struct wpa_driver_mesh_join_params *params)
@@ -7779,72 +7822,12 @@
 
 	wpa_printf(MSG_DEBUG, "nl80211: mesh join (ifindex=%d)", drv->ifindex);
 	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_JOIN_MESH);
-	if (!msg)
+	if (!msg ||
+	    nl80211_put_freq_params(msg, &params->freq) ||
+	    nl80211_put_basic_rates(msg, params->basic_rates) ||
+	    nl80211_put_mesh_id(msg, params->meshid, params->meshid_len) ||
+	    nl80211_put_beacon_int(msg, params->beacon_int))
 		goto fail;
-	if (params->freq) {
-		wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq);
-		if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq))
-			goto fail;
-	}
-
-	if (params->ht_mode) {
-		unsigned int ht_value;
-		char *ht_mode = "";
-
-		switch (params->ht_mode) {
-		default:
-		case CHAN_NO_HT:
-			ht_value = NL80211_CHAN_NO_HT;
-			ht_mode = "NOHT";
-			break;
-		case CHAN_HT20:
-			ht_value = NL80211_CHAN_HT20;
-			ht_mode = "HT20";
-			break;
-		case CHAN_HT40PLUS:
-			ht_value = NL80211_CHAN_HT40PLUS;
-			ht_mode = "HT40+";
-			break;
-		case CHAN_HT40MINUS:
-			ht_value = NL80211_CHAN_HT40MINUS;
-			ht_mode = "HT40-";
-			break;
-		}
-		wpa_printf(MSG_DEBUG, "  * ht_mode=%s", ht_mode);
-		if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, ht_value))
-			goto fail;
-	}
-
-	if (params->basic_rates) {
-		u8 rates[NL80211_MAX_SUPP_RATES];
-		u8 rates_len = 0;
-		int i;
-
-		for (i = 0; i < NL80211_MAX_SUPP_RATES; i++) {
-			if (params->basic_rates[i] < 0)
-				break;
-			rates[rates_len++] = params->basic_rates[i] / 5;
-		}
-
-		if (nla_put(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len,
-			    rates))
-			goto fail;
-	}
-
-	if (params->meshid) {
-		wpa_hexdump_ascii(MSG_DEBUG, "  * SSID",
-				  params->meshid, params->meshid_len);
-		if (nla_put(msg, NL80211_ATTR_MESH_ID, params->meshid_len,
-			    params->meshid))
-			goto fail;
-	}
-
-	if (params->beacon_int > 0) {
-		wpa_printf(MSG_DEBUG, "  * beacon_int=%d", params->beacon_int);
-		if (nla_put_u32(msg, NL80211_ATTR_BEACON_INTERVAL,
-				params->beacon_int))
-			goto fail;
-	}
 
 	wpa_printf(MSG_DEBUG, "  * flags=%08X", params->flags);
 
@@ -7893,7 +7876,7 @@
 		goto fail;
 	}
 	ret = 0;
-	bss->freq = params->freq;
+	bss->freq = params->freq.freq;
 	wpa_printf(MSG_DEBUG, "nl80211: mesh join request send successfully");
 
 fail:
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index 6892b31..4567f42 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -142,6 +142,7 @@
 	unsigned int force_connect_cmd:1;
 	unsigned int addr_changed:1;
 	unsigned int get_features_vendor_cmd_avail:1;
+	unsigned int set_rekey_offload:1;
 
 	u64 remain_on_chan_cookie;
 	u64 send_action_cookie;
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 6661a89..5c71603 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -394,6 +394,9 @@
 
 	if (flags & NL80211_FEATURE_TX_POWER_INSERTION)
 		capa->rrm_flags |= WPA_DRIVER_FLAGS_TX_POWER_INSERTION;
+
+	if (flags & NL80211_FEATURE_HT_IBSS)
+		capa->flags |= WPA_DRIVER_FLAGS_HT_IBSS;
 }
 
 
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 85769d8..d555033 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -180,34 +180,23 @@
 }
 
 
-static int nl80211_parse_wmm_params(struct nlattr *wmm_attr,
-				    struct wmm_params *wmm_params)
+static void nl80211_parse_wmm_params(struct nlattr *wmm_attr,
+				     struct wmm_params *wmm_params)
 {
 	struct nlattr *wmm_info[NL80211_STA_WME_MAX + 1];
 	static struct nla_policy wme_policy[NL80211_STA_WME_MAX + 1] = {
 		[NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },
 	};
 
-	if (!wmm_attr) {
-		wpa_printf(MSG_DEBUG, "nl80211: WMM data missing");
-		return -1;
-	}
-
-	if (nla_parse_nested(wmm_info, NL80211_STA_WME_MAX, wmm_attr,
-			     wme_policy)) {
-		wpa_printf(MSG_DEBUG,
-			   "nl80211: Failed to parse nested attributes");
-		return -1;
-	}
-
-	if (!wmm_info[NL80211_STA_WME_UAPSD_QUEUES])
-		return -1;
+	if (!wmm_attr ||
+	    nla_parse_nested(wmm_info, NL80211_STA_WME_MAX, wmm_attr,
+			     wme_policy) ||
+	    !wmm_info[NL80211_STA_WME_UAPSD_QUEUES])
+		return;
 
 	wmm_params->uapsd_queues =
 		nla_get_u8(wmm_info[NL80211_STA_WME_UAPSD_QUEUES]);
 	wmm_params->info_bitmap |= WMM_PARAMS_UAPSD_QUEUES_INFO;
-
-	return 0;
 }
 
 
@@ -1104,10 +1093,8 @@
 	const u8 *addr;
 	union wpa_event_data data;
 
-	if (drv->nlmode != NL80211_IFTYPE_MESH_POINT)
-		return;
-
-	if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_IE])
+	if (drv->nlmode != NL80211_IFTYPE_MESH_POINT ||
+	    !tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_IE])
 		return;
 
 	addr = nla_data(tb[NL80211_ATTR_MAC]);
@@ -1201,14 +1188,11 @@
 	};
 	union wpa_event_data data;
 
-	if (!tb[NL80211_ATTR_MAC])
-		return;
-	if (!tb[NL80211_ATTR_REKEY_DATA])
-		return;
-	if (nla_parse_nested(rekey_info, MAX_NL80211_REKEY_DATA,
-			     tb[NL80211_ATTR_REKEY_DATA], rekey_policy))
-		return;
-	if (!rekey_info[NL80211_REKEY_DATA_REPLAY_CTR])
+	if (!tb[NL80211_ATTR_MAC] ||
+	    !tb[NL80211_ATTR_REKEY_DATA] ||
+	    nla_parse_nested(rekey_info, MAX_NL80211_REKEY_DATA,
+			     tb[NL80211_ATTR_REKEY_DATA], rekey_policy) ||
+	    !rekey_info[NL80211_REKEY_DATA_REPLAY_CTR])
 		return;
 
 	os_memset(&data, 0, sizeof(data));
@@ -1239,12 +1223,10 @@
 
 	wpa_printf(MSG_DEBUG, "nl80211: PMKSA candidate event");
 
-	if (!tb[NL80211_ATTR_PMKSA_CANDIDATE])
-		return;
-	if (nla_parse_nested(cand, MAX_NL80211_PMKSA_CANDIDATE,
-			     tb[NL80211_ATTR_PMKSA_CANDIDATE], cand_policy))
-		return;
-	if (!cand[NL80211_PMKSA_CANDIDATE_INDEX] ||
+	if (!tb[NL80211_ATTR_PMKSA_CANDIDATE] ||
+	    nla_parse_nested(cand, MAX_NL80211_PMKSA_CANDIDATE,
+			     tb[NL80211_ATTR_PMKSA_CANDIDATE], cand_policy) ||
+	    !cand[NL80211_PMKSA_CANDIDATE_INDEX] ||
 	    !cand[NL80211_PMKSA_CANDIDATE_BSSID])
 		return;
 
@@ -1502,10 +1484,8 @@
 		   "nl80211: ACS channel selection vendor event received");
 
 	if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ACS_MAX,
-		      (struct nlattr *) data, len, NULL))
-		return;
-
-	if (!tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL] ||
+		      (struct nlattr *) data, len, NULL) ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL] ||
 	    !tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL])
 		return;
 
@@ -1529,9 +1509,8 @@
 		   "nl80211: Key management roam+auth vendor event received");
 
 	if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX,
-		      (struct nlattr *) data, len, NULL))
-		return;
-	if (!tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID] ||
+		      (struct nlattr *) data, len, NULL) ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID] ||
 	    nla_len(tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID]) != ETH_ALEN ||
 	    !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE] ||
 	    !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE] ||
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index 7538d60..c35b4d7 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -599,9 +599,6 @@
 		enum nl80211_bss_status status;
 		status = nla_get_u32(bss[NL80211_BSS_STATUS]);
 		switch (status) {
-		case NL80211_BSS_STATUS_AUTHENTICATED:
-			r->flags |= WPA_SCAN_AUTHENTICATED;
-			break;
 		case NL80211_BSS_STATUS_ASSOCIATED:
 			r->flags |= WPA_SCAN_ASSOCIATED;
 			break;
@@ -677,23 +674,6 @@
 
 	for (i = 0; i < res->num; i++) {
 		struct wpa_scan_res *r = res->res[i];
-		if (r->flags & WPA_SCAN_AUTHENTICATED) {
-			wpa_printf(MSG_DEBUG, "nl80211: Scan results "
-				   "indicates BSS status with " MACSTR
-				   " as authenticated",
-				   MAC2STR(r->bssid));
-			if (is_sta_interface(drv->nlmode) &&
-			    os_memcmp(r->bssid, drv->bssid, ETH_ALEN) != 0 &&
-			    os_memcmp(r->bssid, drv->auth_bssid, ETH_ALEN) !=
-			    0) {
-				wpa_printf(MSG_DEBUG, "nl80211: Unknown BSSID"
-					   " in local state (auth=" MACSTR
-					   " assoc=" MACSTR ")",
-					   MAC2STR(drv->auth_bssid),
-					   MAC2STR(drv->bssid));
-				clear_state_mismatch(drv, r->bssid);
-			}
-		}
 
 		if (r->flags & WPA_SCAN_ASSOCIATED) {
 			wpa_printf(MSG_DEBUG, "nl80211: Scan results "
@@ -786,9 +766,8 @@
 	wpa_printf(MSG_DEBUG, "nl80211: Scan result dump");
 	for (i = 0; i < res->num; i++) {
 		struct wpa_scan_res *r = res->res[i];
-		wpa_printf(MSG_DEBUG, "nl80211: %d/%d " MACSTR "%s%s",
+		wpa_printf(MSG_DEBUG, "nl80211: %d/%d " MACSTR "%s",
 			   (int) i, (int) res->num, MAC2STR(r->bssid),
-			   r->flags & WPA_SCAN_AUTHENTICATED ? " [auth]" : "",
 			   r->flags & WPA_SCAN_ASSOCIATED ? " [assoc]" : "");
 	}
 
diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h
index 3584bdb..826ddca 100644
--- a/src/eap_peer/eap_config.h
+++ b/src/eap_peer/eap_config.h
@@ -186,6 +186,10 @@
 	 * string is in following format:
 	 *
 	 * /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@n.example.com
+	 *
+	 * Note: Since this is a substring match, this cannot be used securily
+	 * to do a suffix match against a possible domain name in the CN entry.
+	 * For such a use case, domain_suffix_match should be used instead.
 	 */
 	u8 *subject_match;
 
@@ -213,7 +217,7 @@
 	 * If set, this FQDN is used as a suffix match requirement for the
 	 * server certificate in SubjectAltName dNSName element(s). If a
 	 * matching dNSName is found, this constraint is met. If no dNSName
-	 * values are present, this constraint is matched against SubjetName CN
+	 * values are present, this constraint is matched against SubjectName CN
 	 * using same suffix match comparison. Suffix match here means that the
 	 * host/domain name is compared one label at a time starting from the
 	 * top-level domain and all the labels in domain_suffix_match shall be
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index 4baeb3b..10413ed 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -2776,6 +2776,8 @@
 {
 	struct wpa_tdls_peer *peer, *tmp;
 
+	if (!sm)
+		return;
 	peer = sm->tdls;
 
 	wpa_printf(MSG_DEBUG, "TDLS: Tear down peers");
diff --git a/src/utils/common.c b/src/utils/common.c
index 182c6a8..93f1722 100644
--- a/src/utils/common.c
+++ b/src/utils/common.c
@@ -36,6 +36,25 @@
 }
 
 
+static const char * hwaddr_parse(const char *txt, u8 *addr)
+{
+	size_t i;
+
+	for (i = 0; i < ETH_ALEN; i++) {
+		int a;
+
+		a = hex2byte(txt);
+		if (a < 0)
+			return NULL;
+		txt += 2;
+		addr[i] = a;
+		if (i < ETH_ALEN - 1 && *txt++ != ':')
+			return NULL;
+	}
+	return txt;
+}
+
+
 /**
  * hwaddr_aton - Convert ASCII string to MAC address (colon-delimited format)
  * @txt: MAC address as a string (e.g., "00:11:22:33:44:55")
@@ -44,25 +63,46 @@
  */
 int hwaddr_aton(const char *txt, u8 *addr)
 {
-	int i;
+	return hwaddr_parse(txt, addr) ? 0 : -1;
+}
 
-	for (i = 0; i < 6; i++) {
-		int a, b;
 
-		a = hex2num(*txt++);
-		if (a < 0)
+/**
+ * hwaddr_masked_aton - Convert ASCII string with optional mask to MAC address (colon-delimited format)
+ * @txt: MAC address with optional mask as a string (e.g., "00:11:22:33:44:55/ff:ff:ff:ff:00:00")
+ * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes)
+ * @mask: Buffer for the MAC address mask (ETH_ALEN = 6 bytes)
+ * @maskable: Flag to indicate whether a mask is allowed
+ * Returns: 0 on success, -1 on failure (e.g., string not a MAC address)
+ */
+int hwaddr_masked_aton(const char *txt, u8 *addr, u8 *mask, u8 maskable)
+{
+	const char *r;
+
+	/* parse address part */
+	r = hwaddr_parse(txt, addr);
+	if (!r)
+		return -1;
+
+	/* check for optional mask */
+	if (*r == '\0' || isspace(*r)) {
+		/* no mask specified, assume default */
+		os_memset(mask, 0xff, ETH_ALEN);
+	} else if (maskable && *r == '/') {
+		/* mask specified and allowed */
+		r = hwaddr_parse(r + 1, mask);
+		/* parser error? */
+		if (!r)
 			return -1;
-		b = hex2num(*txt++);
-		if (b < 0)
-			return -1;
-		*addr++ = (a << 4) | b;
-		if (i < 5 && *txt++ != ':')
-			return -1;
+	} else {
+		/* mask specified but not allowed or trailing garbage */
+		return -1;
 	}
 
 	return 0;
 }
 
+
 /**
  * hwaddr_compact_aton - Convert ASCII string to MAC address (no colon delimitors format)
  * @txt: MAC address as a string (e.g., "001122334455")
@@ -144,6 +184,30 @@
 }
 
 
+int hwaddr_mask_txt(char *buf, size_t len, const u8 *addr, const u8 *mask)
+{
+	size_t i;
+	int print_mask = 0;
+	int res;
+
+	for (i = 0; i < ETH_ALEN; i++) {
+		if (mask[i] != 0xff) {
+			print_mask = 1;
+			break;
+		}
+	}
+
+	if (print_mask)
+		res = os_snprintf(buf, len, MACSTR "/" MACSTR,
+				  MAC2STR(addr), MAC2STR(mask));
+	else
+		res = os_snprintf(buf, len, MACSTR, MAC2STR(addr));
+	if (os_snprintf_error(len, res))
+		return -1;
+	return res;
+}
+
+
 /**
  * inc_byte_array - Increment arbitrary length byte array by one
  * @counter: Pointer to byte array
diff --git a/src/utils/common.h b/src/utils/common.h
index 7eca409..82a51e5 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -164,6 +164,7 @@
 #define be_to_host16(n) wpa_swap_16(n)
 #define host_to_be16(n) wpa_swap_16(n)
 #define le_to_host32(n) (n)
+#define host_to_le32(n) (n)
 #define be_to_host32(n) wpa_swap_32(n)
 #define host_to_be32(n) wpa_swap_32(n)
 
@@ -471,6 +472,7 @@
 #endif /* __must_check */
 
 int hwaddr_aton(const char *txt, u8 *addr);
+int hwaddr_masked_aton(const char *txt, u8 *addr, u8 *mask, u8 maskable);
 int hwaddr_compact_aton(const char *txt, u8 *addr);
 int hwaddr_aton2(const char *txt, u8 *addr);
 int hex2byte(const char *hex);
@@ -482,6 +484,8 @@
 int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data,
 			       size_t len);
 
+int hwaddr_mask_txt(char *buf, size_t len, const u8 *addr, const u8 *mask);
+
 #ifdef CONFIG_NATIVE_WINDOWS
 void wpa_unicode2ascii_inplace(TCHAR *str);
 TCHAR * wpa_strdup_tchar(const char *str);
diff --git a/src/utils/eloop.c b/src/utils/eloop.c
index 0da6de4..4a565eb 100644
--- a/src/utils/eloop.c
+++ b/src/utils/eloop.c
@@ -242,8 +242,10 @@
 	eloop_trace_sock_remove_ref(table);
 	tmp = os_realloc_array(table->table, table->count + 1,
 			       sizeof(struct eloop_sock));
-	if (tmp == NULL)
+	if (tmp == NULL) {
+		eloop_trace_sock_add_ref(table);
 		return -1;
+	}
 
 	tmp[table->count].sock = sock;
 	tmp[table->count].eloop_data = eloop_data;
diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c
index 523a4d0..86fbd0a 100644
--- a/src/utils/os_unix.c
+++ b/src/utils/os_unix.c
@@ -467,9 +467,105 @@
 
 #ifdef WPA_TRACE
 
+#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
+char wpa_trace_fail_func[256] = { 0 };
+unsigned int wpa_trace_fail_after;
+
+static int testing_fail_alloc(void)
+{
+	const char *func[WPA_TRACE_LEN];
+	size_t i, res, len;
+	char *pos, *next;
+	int match;
+
+	if (!wpa_trace_fail_after)
+		return 0;
+
+	res = wpa_trace_calling_func(func, WPA_TRACE_LEN);
+	i = 0;
+	if (i < res && os_strcmp(func[i], __func__) == 0)
+		i++;
+	if (i < res && os_strcmp(func[i], "os_malloc") == 0)
+		i++;
+	if (i < res && os_strcmp(func[i], "os_zalloc") == 0)
+		i++;
+	if (i < res && os_strcmp(func[i], "os_calloc") == 0)
+		i++;
+	if (i < res && os_strcmp(func[i], "os_realloc") == 0)
+		i++;
+	if (i < res && os_strcmp(func[i], "os_realloc_array") == 0)
+		i++;
+	if (i < res && os_strcmp(func[i], "os_strdup") == 0)
+		i++;
+
+	pos = wpa_trace_fail_func;
+
+	match = 0;
+	while (i < res) {
+		int allow_skip = 1;
+		int maybe = 0;
+
+		if (*pos == '=') {
+			allow_skip = 0;
+			pos++;
+		} else if (*pos == '?') {
+			maybe = 1;
+			pos++;
+		}
+		next = os_strchr(pos, ';');
+		if (next)
+			len = next - pos;
+		else
+			len = os_strlen(pos);
+		if (os_memcmp(pos, func[i], len) != 0) {
+			if (maybe && next) {
+				pos = next + 1;
+				continue;
+			}
+			if (allow_skip) {
+				i++;
+				continue;
+			}
+			return 0;
+		}
+		if (!next) {
+			match = 1;
+			break;
+		}
+		pos = next + 1;
+		i++;
+	}
+	if (!match)
+		return 0;
+
+	wpa_trace_fail_after--;
+	if (wpa_trace_fail_after == 0) {
+		wpa_printf(MSG_INFO, "TESTING: fail allocation at %s",
+			   wpa_trace_fail_func);
+		for (i = 0; i < res; i++)
+			wpa_printf(MSG_INFO, "backtrace[%d] = %s",
+				   (int) i, func[i]);
+		return 1;
+	}
+
+	return 0;
+}
+
+#else
+
+static inline int testing_fail_alloc(void)
+{
+	return 0;
+}
+#endif
+
 void * os_malloc(size_t size)
 {
 	struct os_alloc_trace *a;
+
+	if (testing_fail_alloc())
+		return NULL;
+
 	a = malloc(sizeof(*a) + size);
 	if (a == NULL)
 		return NULL;
diff --git a/src/utils/os_win32.c b/src/utils/os_win32.c
index 57ee132..296ea13 100644
--- a/src/utils/os_win32.c
+++ b/src/utils/os_win32.c
@@ -12,6 +12,7 @@
 #include <wincrypt.h>
 
 #include "os.h"
+#include "common.h"
 
 void os_sleep(os_time_t sec, os_time_t usec)
 {
diff --git a/src/utils/trace.c b/src/utils/trace.c
index 7403c08..8484d27 100644
--- a/src/utils/trace.c
+++ b/src/utils/trace.c
@@ -160,7 +160,7 @@
 	if (abfd == NULL)
 		return;
 
-	data.pc = (bfd_vma) pc;
+	data.pc = (bfd_hostptr_t) pc;
 	data.found = FALSE;
 	bfd_map_over_sections(abfd, find_addr_sect, &data);
 
@@ -201,7 +201,7 @@
 	if (abfd == NULL)
 		return NULL;
 
-	data.pc = (bfd_vma) pc;
+	data.pc = (bfd_hostptr_t) pc;
 	data.found = FALSE;
 	bfd_map_over_sections(abfd, find_addr_sect, &data);
 
@@ -243,6 +243,53 @@
 	wpa_trace_bfd_addr(pc);
 }
 
+
+size_t wpa_trace_calling_func(const char *buf[], size_t len)
+{
+	bfd *abfd;
+	void *btrace_res[WPA_TRACE_LEN];
+	int i, btrace_num;
+	size_t pos = 0;
+
+	if (len == 0)
+		return 0;
+	if (len > WPA_TRACE_LEN)
+		len = WPA_TRACE_LEN;
+
+	wpa_trace_bfd_init();
+	abfd = cached_abfd;
+	if (!abfd)
+		return 0;
+
+	btrace_num = backtrace(btrace_res, len);
+	if (btrace_num < 1)
+		return 0;
+
+	for (i = 0; i < btrace_num; i++) {
+		struct bfd_data data;
+
+		data.pc = (bfd_hostptr_t) btrace_res[i];
+		data.found = FALSE;
+		bfd_map_over_sections(abfd, find_addr_sect, &data);
+
+		while (data.found) {
+			if (data.function &&
+			    (pos > 0 ||
+			     os_strcmp(data.function, __func__) != 0)) {
+				buf[pos++] = data.function;
+				if (pos == len)
+					return pos;
+			}
+
+			data.found = bfd_find_inliner_info(abfd, &data.filename,
+							   &data.function,
+							   &data.line);
+		}
+	}
+
+	return pos;
+}
+
 #else /* WPA_TRACE_BFD */
 
 #define wpa_trace_bfd_init() do { } while (0)
diff --git a/src/utils/trace.h b/src/utils/trace.h
index 38f43fb..43ed86c 100644
--- a/src/utils/trace.h
+++ b/src/utils/trace.h
@@ -40,6 +40,7 @@
 			dl_list_del(&(ptr)->wpa_trace_ref_##name.list); \
 	} while (0)
 void wpa_trace_check_ref(const void *addr);
+size_t wpa_trace_calling_func(const char *buf[], size_t len);
 
 #else /* WPA_TRACE */
 
diff --git a/src/utils/wpabuf.c b/src/utils/wpabuf.c
index b257b36..7aafa0a 100644
--- a/src/utils/wpabuf.c
+++ b/src/utils/wpabuf.c
@@ -205,6 +205,15 @@
 }
 
 
+void wpabuf_clear_free(struct wpabuf *buf)
+{
+	if (buf) {
+		os_memset(wpabuf_mhead(buf), 0, wpabuf_len(buf));
+		wpabuf_free(buf);
+	}
+}
+
+
 void * wpabuf_put(struct wpabuf *buf, size_t len)
 {
 	void *tmp = wpabuf_mhead_u8(buf) + wpabuf_len(buf);
diff --git a/src/utils/wpabuf.h b/src/utils/wpabuf.h
index dbce925..c3ef1ba 100644
--- a/src/utils/wpabuf.h
+++ b/src/utils/wpabuf.h
@@ -32,6 +32,7 @@
 struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len);
 struct wpabuf * wpabuf_dup(const struct wpabuf *src);
 void wpabuf_free(struct wpabuf *buf);
+void wpabuf_clear_free(struct wpabuf *buf);
 void * wpabuf_put(struct wpabuf *buf, size_t len);
 struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b);
 struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len);