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/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");
+}