Cumulative patch from commit 06f14421ea8644d12a7b0be6b583114869f9c451

06f1442 TLS: Parse OCSPResponse to extract BasicOCSPResponse
d560288 TLS: Parse CertificateStatus message
eeba168 TLS: Add status_request ClientHello extension if OCSP is requested
4303d53 TLS: Parse ServerHello extensions
6b7bb42 TLS: Add minimal support for PKCS #12
5ce2941 TLS: Extend PKCS #5 to support PKCS #12 style key decryption
f6a62df TLS: Fix and complete ASN.1 tag list
3c108b7 EAP peer: External server certificate chain validation
b6e5e14 EAP-FAST peer: Fix PAC parser error messages
5b904b3 EAP-FAST: Check T-PRF result in MSK/EMSK derivation
b1d8c5c EAP-FAST peer: Fix error path handling for Session-Id
36478a1 OpenSSL: Support new API for HMAC/EVP_MD_CTX in OpenSSL 1.1.x-pre1
9257610 FT: Fix FTIE generation for EAPOL-Key msg 3/4
e44bd28 FT: Fix sm->assoc_resp_ftie storing on the AP side
59e78c2 FT: Fix FTIE generation for 4-way handshake after FT protocol run
b0ecbd3 AP: Use more readable version of management group cipher in error cases
651c6a8 Add TEST_ASSOC_IE for WPA/RSN IE testing on AP side
58059e6 FST: Print debug entry on MB IE update based on EVENT_AUTH
af041f9 dbus: Add support for vendor specific elements
5c8acf7 EAP-IKEv2: Check HMAC SHA1/MD5 result
7b991b4 Use proper build config for parsing proxy_arp
4db29e6 TLS: Add support for PKCS #5 v2.0 PBES2

Change-Id: I10b71e4d3573ef60a52ea6ff56afcd3a06a0b7b0
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index c2c5693..9b2382f 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -2298,14 +2298,19 @@
 	pos += wpa_ie_len;
 #ifdef CONFIG_IEEE80211R
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
-		int res = wpa_insert_pmkid(kde, pos - kde, sm->pmk_r1_name);
+		int res;
+		size_t elen;
+
+		elen = pos - kde;
+		res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name);
 		if (res < 0) {
 			wpa_printf(MSG_ERROR, "FT: Failed to insert "
 				   "PMKR1Name into RSN IE in EAPOL-Key data");
 			os_free(kde);
 			return;
 		}
-		pos += res;
+		pos -= wpa_ie_len;
+		pos += elen;
 	}
 #endif /* CONFIG_IEEE80211R */
 	if (gtk) {
@@ -2323,10 +2328,18 @@
 		struct wpa_auth_config *conf;
 
 		conf = &sm->wpa_auth->conf;
-		res = wpa_write_ftie(conf, conf->r0_key_holder,
-				     conf->r0_key_holder_len,
-				     NULL, NULL, pos, kde + kde_len - pos,
-				     NULL, 0);
+		if (sm->assoc_resp_ftie &&
+		    kde + kde_len - pos >= 2 + sm->assoc_resp_ftie[1]) {
+			os_memcpy(pos, sm->assoc_resp_ftie,
+				  2 + sm->assoc_resp_ftie[1]);
+			res = 2 + sm->assoc_resp_ftie[1];
+		} else {
+			res = wpa_write_ftie(conf, conf->r0_key_holder,
+					     conf->r0_key_holder_len,
+					     NULL, NULL, pos,
+					     kde + kde_len - pos,
+					     NULL, 0);
+		}
 		if (res < 0) {
 			wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE "
 				   "into EAPOL-Key Key Data");
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index eeaffbf..42242a5 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -720,11 +720,6 @@
 	ftie_len = res;
 	pos += res;
 
-	os_free(sm->assoc_resp_ftie);
-	sm->assoc_resp_ftie = os_malloc(ftie_len);
-	if (sm->assoc_resp_ftie)
-		os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len);
-
 	_ftie = (struct rsn_ftie *) (ftie + 2);
 	if (auth_alg == WLAN_AUTH_FT)
 		_ftie->mic_control[1] = 3; /* Information element count */
@@ -750,6 +745,11 @@
 		       _ftie->mic) < 0)
 		wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
 
+	os_free(sm->assoc_resp_ftie);
+	sm->assoc_resp_ftie = os_malloc(ftie_len);
+	if (sm->assoc_resp_ftie)
+		os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len);
+
 	return pos;
 }
 
diff --git a/src/common/defs.h b/src/common/defs.h
index 6aea375..b3ac4e8 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -312,6 +312,7 @@
 	WPA_CTRL_REQ_EAP_PASSPHRASE,
 	WPA_CTRL_REQ_SIM,
 	WPA_CTRL_REQ_PSK_PASSPHRASE,
+	WPA_CTRL_REQ_EXT_CERT_CHECK,
 	NUM_WPA_CTRL_REQS
 };
 
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index 4091bed..b5f57b3 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -689,9 +689,10 @@
 	if (left >= 4) {
 		data->mgmt_group_cipher = rsn_selector_to_bitfield(pos);
 		if (!wpa_cipher_valid_mgmt_group(data->mgmt_group_cipher)) {
-			wpa_printf(MSG_DEBUG, "%s: Unsupported management "
-				   "group cipher 0x%x", __func__,
-				   data->mgmt_group_cipher);
+			wpa_printf(MSG_DEBUG,
+				   "%s: Unsupported management group cipher 0x%x (%08x)",
+				   __func__, data->mgmt_group_cipher,
+				   WPA_GET_BE32(pos));
 			return -10;
 		}
 		pos += RSN_SELECTOR_LEN;
@@ -1279,13 +1280,13 @@
 
 
 #ifdef CONFIG_IEEE80211R
-int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid)
+int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid)
 {
 	u8 *start, *end, *rpos, *rend;
 	int added = 0;
 
 	start = ies;
-	end = ies + ies_len;
+	end = ies + *ies_len;
 
 	while (start < end) {
 		if (*start == WLAN_EID_RSN)
@@ -1338,11 +1339,29 @@
 		added += 2 + PMKID_LEN;
 		start[1] += 2 + PMKID_LEN;
 	} else {
-		/* PMKID-Count was included; use it */
-		if (WPA_GET_LE16(rpos) != 0) {
-			wpa_printf(MSG_ERROR, "FT: Unexpected PMKID "
-				   "in RSN IE in EAPOL-Key data");
+		u16 num_pmkid;
+
+		if (rend - rpos < 2)
 			return -1;
+		num_pmkid = WPA_GET_LE16(rpos);
+		/* PMKID-Count was included; use it */
+		if (num_pmkid != 0) {
+			u8 *after;
+
+			if (num_pmkid * PMKID_LEN > rend - rpos - 2)
+				return -1;
+			/*
+			 * PMKID may have been included in RSN IE in
+			 * (Re)Association Request frame, so remove the old
+			 * PMKID(s) first before adding the new one.
+			 */
+			wpa_printf(MSG_DEBUG,
+				   "FT: Remove %u old PMKID(s) from RSN IE",
+				   num_pmkid);
+			after = rpos + 2 + num_pmkid * PMKID_LEN;
+			os_memmove(rpos + 2, after, rend - after);
+			start[1] -= num_pmkid * PMKID_LEN;
+			added -= num_pmkid * PMKID_LEN;
 		}
 		WPA_PUT_LE16(rpos, 1);
 		rpos += 2;
@@ -1355,7 +1374,9 @@
 	wpa_hexdump(MSG_DEBUG, "FT: RSN IE after modification "
 		    "(PMKID inserted)", start, 2 + start[1]);
 
-	return added;
+	*ies_len += added;
+
+	return 0;
 }
 #endif /* CONFIG_IEEE80211R */
 
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index ee71bfc..af1d0f0 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -409,7 +409,7 @@
 int wpa_compare_rsn_ie(int ft_initial_assoc,
 		       const u8 *ie1, size_t ie1len,
 		       const u8 *ie2, size_t ie2len);
-int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid);
+int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid);
 
 struct wpa_ft_ies {
 	const u8 *mdie;
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index ad2d2d4..b53bc4a 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -65,6 +65,42 @@
 static int openssl_digest_vector(const EVP_MD *type, size_t num_elem,
 				 const u8 *addr[], const size_t *len, u8 *mac)
 {
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+	EVP_MD_CTX *ctx;
+	size_t i;
+	unsigned int mac_len;
+
+	if (TEST_FAIL())
+		return -1;
+
+	ctx = EVP_MD_CTX_new();
+	if (!ctx)
+		return -1;
+	if (!EVP_DigestInit_ex(ctx, type, NULL)) {
+		wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestInit_ex failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		EVP_MD_CTX_free(ctx);
+		return -1;
+	}
+	for (i = 0; i < num_elem; i++) {
+		if (!EVP_DigestUpdate(ctx, addr[i], len[i])) {
+			wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestUpdate "
+				   "failed: %s",
+				   ERR_error_string(ERR_get_error(), NULL));
+			EVP_MD_CTX_free(ctx);
+			return -1;
+		}
+	}
+	if (!EVP_DigestFinal(ctx, mac, &mac_len)) {
+		wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestFinal failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		EVP_MD_CTX_free(ctx);
+		return -1;
+	}
+	EVP_MD_CTX_free(ctx);
+
+	return 0;
+#else
 	EVP_MD_CTX ctx;
 	size_t i;
 	unsigned int mac_len;
@@ -93,6 +129,7 @@
 	}
 
 	return 0;
+#endif
 }
 
 
@@ -681,7 +718,11 @@
 
 
 struct crypto_hash {
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+	HMAC_CTX *ctx;
+#else
 	HMAC_CTX ctx;
+#endif
 };
 
 
@@ -716,6 +757,19 @@
 	ctx = os_zalloc(sizeof(*ctx));
 	if (ctx == NULL)
 		return NULL;
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+	ctx->ctx = HMAC_CTX_new();
+	if (!ctx->ctx) {
+		os_free(ctx);
+		return NULL;
+	}
+
+	if (HMAC_Init_ex(ctx->ctx, key, key_len, md, NULL) != 1) {
+		HMAC_CTX_free(ctx->ctx);
+		bin_clear_free(ctx, sizeof(*ctx));
+		return NULL;
+	}
+#else
 	HMAC_CTX_init(&ctx->ctx);
 
 #if OPENSSL_VERSION_NUMBER < 0x00909000
@@ -726,6 +780,7 @@
 		return NULL;
 	}
 #endif /* openssl < 0.9.9 */
+#endif
 
 	return ctx;
 }
@@ -735,7 +790,11 @@
 {
 	if (ctx == NULL)
 		return;
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+	HMAC_Update(ctx->ctx, data, len);
+#else
 	HMAC_Update(&ctx->ctx, data, len);
+#endif
 }
 
 
@@ -748,11 +807,18 @@
 		return -2;
 
 	if (mac == NULL || len == NULL) {
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+		HMAC_CTX_free(ctx->ctx);
+#endif
 		bin_clear_free(ctx, sizeof(*ctx));
 		return 0;
 	}
 
 	mdlen = *len;
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+	res = HMAC_Final(ctx->ctx, mac, &mdlen);
+	HMAC_CTX_free(ctx->ctx);
+#else
 #if OPENSSL_VERSION_NUMBER < 0x00909000
 	HMAC_Final(&ctx->ctx, mac, &mdlen);
 	res = 1;
@@ -760,6 +826,7 @@
 	res = HMAC_Final(&ctx->ctx, mac, &mdlen);
 #endif /* openssl < 0.9.9 */
 	HMAC_CTX_cleanup(&ctx->ctx);
+#endif
 	bin_clear_free(ctx, sizeof(*ctx));
 
 	if (res == 1) {
@@ -776,6 +843,30 @@
 			       const u8 *addr[], const size_t *len, u8 *mac,
 			       unsigned int mdlen)
 {
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+	HMAC_CTX *ctx;
+	size_t i;
+	int res;
+
+	if (TEST_FAIL())
+		return -1;
+
+	ctx = HMAC_CTX_new();
+	if (!ctx)
+		return -1;
+	res = HMAC_Init_ex(ctx, key, key_len, type, NULL);
+	if (res != 1)
+		goto done;
+
+	for (i = 0; i < num_elem; i++)
+		HMAC_Update(ctx, addr[i], len[i]);
+
+	res = HMAC_Final(ctx, mac, &mdlen);
+done:
+	HMAC_CTX_free(ctx);
+
+	return res == 1 ? 0 : -1;
+#else
 	HMAC_CTX ctx;
 	size_t i;
 	int res;
@@ -803,6 +894,7 @@
 	HMAC_CTX_cleanup(&ctx);
 
 	return res == 1 ? 0 : -1;
+#endif
 }
 
 
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index 2e56233..bca94d6 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -95,6 +95,7 @@
 #define TLS_CONN_DISABLE_TLSv1_2 BIT(6)
 #define TLS_CONN_EAP_FAST BIT(7)
 #define TLS_CONN_DISABLE_TLSv1_0 BIT(8)
+#define TLS_CONN_EXT_CERT_CHECK BIT(9)
 
 /**
  * struct tls_connection_params - Parameters for TLS connection
diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c
index f994379..b1bec4a 100644
--- a/src/crypto/tls_gnutls.c
+++ b/src/crypto/tls_gnutls.c
@@ -347,6 +347,12 @@
 	if (conn == NULL || params == NULL)
 		return -1;
 
+	if (params->flags & TLS_CONN_EXT_CERT_CHECK) {
+		wpa_printf(MSG_INFO,
+			   "GnuTLS: tls_ext_cert_check=1 not supported");
+		return -1;
+	}
+
 	if (params->subject_match) {
 		wpa_printf(MSG_INFO, "GnuTLS: subject_match not supported");
 		return -1;
diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c
index dcbb31d..8b90d56 100644
--- a/src/crypto/tls_internal.c
+++ b/src/crypto/tls_internal.c
@@ -200,6 +200,12 @@
 	if (conn->client == NULL)
 		return -1;
 
+	if (params->flags & TLS_CONN_EXT_CERT_CHECK) {
+		wpa_printf(MSG_INFO,
+			   "TLS: tls_ext_cert_check=1 not supported");
+		return -1;
+	}
+
 	cred = tlsv1_cred_alloc();
 	if (cred == NULL)
 		return -1;
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index 471ae2b..1d75ba7 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -1583,7 +1583,8 @@
 		return;
 
 	os_memset(&ev, 0, sizeof(ev));
-	if (conn->cert_probe || context->cert_in_cb) {
+	if (conn->cert_probe || (conn->flags & TLS_CONN_EXT_CERT_CHECK) ||
+	    context->cert_in_cb) {
 		cert = get_x509_cert(err_cert);
 		ev.peer_cert.cert = cert;
 	}
@@ -1821,7 +1822,7 @@
 	}
 #endif /* OPENSSL_IS_BORINGSSL */
 
-	if (preverify_ok && context->event_cb != NULL)
+	if (depth == 0 && preverify_ok && context->event_cb != NULL)
 		context->event_cb(context->cb_ctx,
 				  TLS_CERT_CHAIN_SUCCESS, NULL);
 
diff --git a/src/eap_common/eap_fast_common.c b/src/eap_common/eap_fast_common.c
index 151cc78..e8587fd 100644
--- a/src/eap_common/eap_fast_common.c
+++ b/src/eap_common/eap_fast_common.c
@@ -111,22 +111,24 @@
 }
 
 
-void eap_fast_derive_eap_msk(const u8 *simck, u8 *msk)
+int eap_fast_derive_eap_msk(const u8 *simck, u8 *msk)
 {
 	/*
 	 * RFC 4851, Section 5.4: EAP Master Session Key Generation
 	 * MSK = T-PRF(S-IMCK[j], "Session Key Generating Function", 64)
 	 */
 
-	sha1_t_prf(simck, EAP_FAST_SIMCK_LEN,
-		   "Session Key Generating Function", (u8 *) "", 0,
-		   msk, EAP_FAST_KEY_LEN);
+	if (sha1_t_prf(simck, EAP_FAST_SIMCK_LEN,
+		       "Session Key Generating Function", (u8 *) "", 0,
+		       msk, EAP_FAST_KEY_LEN) < 0)
+		return -1;
 	wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (MSK)",
 			msk, EAP_FAST_KEY_LEN);
+	return 0;
 }
 
 
-void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk)
+int eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk)
 {
 	/*
 	 * RFC 4851, Section 5.4: EAP Master Session Key Genreration
@@ -134,11 +136,13 @@
 	 *        "Extended Session Key Generating Function", 64)
 	 */
 
-	sha1_t_prf(simck, EAP_FAST_SIMCK_LEN,
-		   "Extended Session Key Generating Function", (u8 *) "", 0,
-		   emsk, EAP_EMSK_LEN);
+	if (sha1_t_prf(simck, EAP_FAST_SIMCK_LEN,
+		       "Extended Session Key Generating Function", (u8 *) "", 0,
+		       emsk, EAP_EMSK_LEN) < 0)
+		return -1;
 	wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (EMSK)",
 			emsk, EAP_EMSK_LEN);
+	return 0;
 }
 
 
diff --git a/src/eap_common/eap_fast_common.h b/src/eap_common/eap_fast_common.h
index d59a845..6756dd2 100644
--- a/src/eap_common/eap_fast_common.h
+++ b/src/eap_common/eap_fast_common.h
@@ -99,8 +99,8 @@
 				   const u8 *client_random, u8 *master_secret);
 u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn,
 			 const char *label, size_t len);
-void eap_fast_derive_eap_msk(const u8 *simck, u8 *msk);
-void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk);
+int eap_fast_derive_eap_msk(const u8 *simck, u8 *msk);
+int eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk);
 int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv,
 		       int tlv_type, u8 *pos, size_t len);
 
diff --git a/src/eap_common/ikev2_common.c b/src/eap_common/ikev2_common.c
index d60358c..90fb89e 100644
--- a/src/eap_common/ikev2_common.c
+++ b/src/eap_common/ikev2_common.c
@@ -62,13 +62,15 @@
 	case AUTH_HMAC_SHA1_96:
 		if (key_len != 20)
 			return -1;
-		hmac_sha1(key, key_len, data, data_len, tmphash);
+		if (hmac_sha1(key, key_len, data, data_len, tmphash) < 0)
+			return -1;
 		os_memcpy(hash, tmphash, 12);
 		break;
 	case AUTH_HMAC_MD5_96:
 		if (key_len != 16)
 			return -1;
-		hmac_md5(key, key_len, data, data_len, tmphash);
+		if (hmac_md5(key, key_len, data, data_len, tmphash) < 0)
+			return -1;
 		os_memcpy(hash, tmphash, 12);
 		break;
 	default:
@@ -98,16 +100,13 @@
 {
 	switch (alg) {
 	case PRF_HMAC_SHA1:
-		hmac_sha1_vector(key, key_len, num_elem, addr, len, hash);
-		break;
+		return hmac_sha1_vector(key, key_len, num_elem, addr, len,
+					hash);
 	case PRF_HMAC_MD5:
-		hmac_md5_vector(key, key_len, num_elem, addr, len, hash);
-		break;
+		return hmac_md5_vector(key, key_len, num_elem, addr, len, hash);
 	default:
 		return -1;
 	}
-
-	return 0;
 }
 
 
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index 28d5116..5c18978 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -48,6 +48,8 @@
 static const char * eap_sm_method_state_txt(EapMethodState state);
 static const char * eap_sm_decision_txt(EapDecision decision);
 #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field,
+			   const char *msg, size_t msglen);
 
 
 
@@ -320,11 +322,14 @@
 	wpa_printf(MSG_DEBUG, "EAP: Initialize selected EAP method: "
 		   "vendor %u method %u (%s)",
 		   sm->reqVendor, method, sm->m->name);
-	if (reinit)
+	if (reinit) {
 		sm->eap_method_priv = sm->m->init_for_reauth(
 			sm, sm->eap_method_priv);
-	else
+	} else {
+		sm->waiting_ext_cert_check = 0;
+		sm->ext_cert_check = 0;
 		sm->eap_method_priv = sm->m->init(sm);
+	}
 
 	if (sm->eap_method_priv == NULL) {
 		struct eap_peer_config *config = eap_get_config(sm);
@@ -1858,6 +1863,11 @@
 	case TLS_CERT_CHAIN_SUCCESS:
 		eap_notify_status(sm, "remote certificate verification",
 				  "success");
+		if (sm->ext_cert_check) {
+			sm->waiting_ext_cert_check = 1;
+			eap_sm_request(sm, WPA_CTRL_REQ_EXT_CERT_CHECK,
+				       NULL, 0);
+		}
 		break;
 	case TLS_CERT_CHAIN_FAILURE:
 		wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TLS_CERT_ERROR
@@ -2180,10 +2190,10 @@
 #endif /* CONFIG_CTRL_IFACE */
 
 
-#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
 static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field,
 			   const char *msg, size_t msglen)
 {
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
 	struct eap_peer_config *config;
 	const char *txt = NULL;
 	char *tmp;
@@ -2232,16 +2242,17 @@
 	case WPA_CTRL_REQ_SIM:
 		txt = msg;
 		break;
+	case WPA_CTRL_REQ_EXT_CERT_CHECK:
+		break;
 	default:
 		return;
 	}
 
 	if (sm->eapol_cb->eap_param_needed)
 		sm->eapol_cb->eap_param_needed(sm->eapol_ctx, field, txt);
-}
-#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
-#define eap_sm_request(sm, type, msg, msglen) do { } while (0)
 #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+}
+
 
 const char * eap_sm_get_method_name(struct eap_sm *sm)
 {
diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h
index 2b1a1d5..39ddcff 100644
--- a/src/eap_peer/eap_config.h
+++ b/src/eap_peer/eap_config.h
@@ -739,6 +739,20 @@
 	 * erp - Whether EAP Re-authentication Protocol (ERP) is enabled
 	 */
 	int erp;
+
+	/**
+	 * pending_ext_cert_check - External server certificate check status
+	 *
+	 * This field should not be set in configuration step. It is only used
+	 * internally when control interface is used to request external
+	 * validation of server certificate chain.
+	 */
+	enum {
+		NO_CHECK = 0,
+		PENDING_CHECK,
+		EXT_CERT_CHECK_GOOD,
+		EXT_CERT_CHECK_BAD,
+	} pending_ext_cert_check;
 };
 
 
diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c
index 833dcb6..a7f6bef 100644
--- a/src/eap_peer/eap_fast.c
+++ b/src/eap_peer/eap_fast.c
@@ -1,6 +1,6 @@
 /*
  * EAP peer method: EAP-FAST (RFC 4851)
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -67,6 +67,7 @@
 	int simck_idx;
 
 	struct wpabuf *pending_phase2_req;
+	struct wpabuf *pending_resp;
 };
 
 
@@ -254,14 +255,16 @@
 	os_memset(data->emsk, 0, EAP_EMSK_LEN);
 	os_free(data->session_id);
 	wpabuf_free(data->pending_phase2_req);
+	wpabuf_free(data->pending_resp);
 	os_free(data);
 }
 
 
 static int eap_fast_derive_msk(struct eap_fast_data *data)
 {
-	eap_fast_derive_eap_msk(data->simck, data->key_data);
-	eap_fast_derive_eap_emsk(data->simck, data->emsk);
+	if (eap_fast_derive_eap_msk(data->simck, data->key_data) < 0 ||
+	    eap_fast_derive_eap_emsk(data->simck, data->emsk) < 0)
+		return -1;
 	data->success = 1;
 	return 0;
 }
@@ -1567,6 +1570,34 @@
 			res = 1;
 		}
 	} else {
+		if (sm->waiting_ext_cert_check && data->pending_resp) {
+			struct eap_peer_config *config = eap_get_config(sm);
+
+			if (config->pending_ext_cert_check ==
+			    EXT_CERT_CHECK_GOOD) {
+				wpa_printf(MSG_DEBUG,
+					   "EAP-FAST: External certificate check succeeded - continue handshake");
+				resp = data->pending_resp;
+				data->pending_resp = NULL;
+				sm->waiting_ext_cert_check = 0;
+				return resp;
+			}
+
+			if (config->pending_ext_cert_check ==
+			    EXT_CERT_CHECK_BAD) {
+				wpa_printf(MSG_DEBUG,
+					   "EAP-FAST: External certificate check failed - force authentication failure");
+				ret->methodState = METHOD_DONE;
+				ret->decision = DECISION_FAIL;
+				sm->waiting_ext_cert_check = 0;
+				return NULL;
+			}
+
+			wpa_printf(MSG_DEBUG,
+				   "EAP-FAST: Continuing to wait external server certificate validation");
+			return NULL;
+		}
+
 		/* Continue processing TLS handshake (phase 1). */
 		res = eap_peer_tls_process_helper(sm, &data->ssl,
 						  EAP_TYPE_FAST,
@@ -1580,6 +1611,14 @@
 			return resp;
 		}
 
+		if (sm->waiting_ext_cert_check) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-FAST: Waiting external server certificate validation");
+			wpabuf_free(data->pending_resp);
+			data->pending_resp = resp;
+			return NULL;
+		}
+
 		if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
 			char cipher[80];
 			wpa_printf(MSG_DEBUG,
@@ -1644,6 +1683,8 @@
 	data->key_block_p = NULL;
 	wpabuf_free(data->pending_phase2_req);
 	data->pending_phase2_req = NULL;
+	wpabuf_free(data->pending_resp);
+	data->pending_resp = NULL;
 }
 
 
@@ -1721,7 +1762,7 @@
 	struct eap_fast_data *data = priv;
 	u8 *id;
 
-	if (!data->success)
+	if (!data->success || !data->session_id)
 		return NULL;
 
 	id = os_malloc(data->id_len);
diff --git a/src/eap_peer/eap_fast_pac.c b/src/eap_peer/eap_fast_pac.c
index c0986b3..0220cae 100644
--- a/src/eap_peer/eap_fast_pac.c
+++ b/src/eap_peer/eap_fast_pac.c
@@ -455,7 +455,8 @@
 	}
 
 	if (pac) {
-		err = "PAC block not terminated with END";
+		if (!err)
+			err = "PAC block not terminated with END";
 		eap_fast_free_pac(pac);
 	}
 
diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h
index 99b44da..6ab2483 100644
--- a/src/eap_peer/eap_i.h
+++ b/src/eap_peer/eap_i.h
@@ -366,6 +366,8 @@
 	int external_sim;
 
 	unsigned int expected_failure:1;
+	unsigned int ext_cert_check:1;
+	unsigned int waiting_ext_cert_check:1;
 
 	struct dl_list erp_keys; /* struct eap_erp_key */
 };
diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c
index 98a48a6..0596098 100644
--- a/src/eap_peer/eap_peap.c
+++ b/src/eap_peer/eap_peap.c
@@ -1,6 +1,6 @@
 /*
  * EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt)
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -59,6 +59,7 @@
 	size_t id_len;
 
 	struct wpabuf *pending_phase2_req;
+	struct wpabuf *pending_resp;
 	enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding;
 	int crypto_binding_used;
 	u8 binding_nonce[32];
@@ -191,6 +192,7 @@
 	eap_peap_free_key(data);
 	os_free(data->session_id);
 	wpabuf_free(data->pending_phase2_req);
+	wpabuf_free(data->pending_resp);
 	os_free(data);
 }
 
@@ -1006,6 +1008,34 @@
 	    !data->resuming) {
 		res = eap_peap_decrypt(sm, data, ret, req, &msg, &resp);
 	} else {
+		if (sm->waiting_ext_cert_check && data->pending_resp) {
+			struct eap_peer_config *config = eap_get_config(sm);
+
+			if (config->pending_ext_cert_check ==
+			    EXT_CERT_CHECK_GOOD) {
+				wpa_printf(MSG_DEBUG,
+					   "EAP-PEAP: External certificate check succeeded - continue handshake");
+				resp = data->pending_resp;
+				data->pending_resp = NULL;
+				sm->waiting_ext_cert_check = 0;
+				return resp;
+			}
+
+			if (config->pending_ext_cert_check ==
+			    EXT_CERT_CHECK_BAD) {
+				wpa_printf(MSG_DEBUG,
+					   "EAP-PEAP: External certificate check failed - force authentication failure");
+				ret->methodState = METHOD_DONE;
+				ret->decision = DECISION_FAIL;
+				sm->waiting_ext_cert_check = 0;
+				return NULL;
+			}
+
+			wpa_printf(MSG_DEBUG,
+				   "EAP-PEAP: Continuing to wait external server certificate validation");
+			return NULL;
+		}
+
 		res = eap_peer_tls_process_helper(sm, &data->ssl,
 						  EAP_TYPE_PEAP,
 						  data->peap_version, id, &msg,
@@ -1018,6 +1048,16 @@
 			ret->decision = DECISION_FAIL;
 			return resp;
 		}
+
+
+		if (sm->waiting_ext_cert_check) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-PEAP: Waiting external server certificate validation");
+			wpabuf_free(data->pending_resp);
+			data->pending_resp = resp;
+			return NULL;
+		}
+
 		if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
 			char *label;
 			wpa_printf(MSG_DEBUG,
@@ -1123,6 +1163,8 @@
 	struct eap_peap_data *data = priv;
 	wpabuf_free(data->pending_phase2_req);
 	data->pending_phase2_req = NULL;
+	wpabuf_free(data->pending_resp);
+	data->pending_resp = NULL;
 	data->crypto_binding_used = 0;
 }
 
diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c
index 66a027a..ba8e74b 100644
--- a/src/eap_peer/eap_tls.c
+++ b/src/eap_peer/eap_tls.c
@@ -1,6 +1,6 @@
 /*
  * EAP peer method: EAP-TLS (RFC 2716)
- * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2008, 2012-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -25,6 +25,7 @@
 	size_t id_len;
 	void *ssl_ctx;
 	u8 eap_type;
+	struct wpabuf *pending_resp;
 };
 
 
@@ -142,6 +143,7 @@
 	eap_peer_tls_ssl_deinit(sm, &data->ssl);
 	eap_tls_free_key(data);
 	os_free(data->session_id);
+	wpabuf_free(data->pending_resp);
 	os_free(data);
 }
 
@@ -216,6 +218,32 @@
 	struct eap_tls_data *data = priv;
 	struct wpabuf msg;
 
+	if (sm->waiting_ext_cert_check && data->pending_resp) {
+		struct eap_peer_config *config = eap_get_config(sm);
+
+		if (config->pending_ext_cert_check == EXT_CERT_CHECK_GOOD) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TLS: External certificate check succeeded - continue handshake");
+			resp = data->pending_resp;
+			data->pending_resp = NULL;
+			sm->waiting_ext_cert_check = 0;
+			return resp;
+		}
+
+		if (config->pending_ext_cert_check == EXT_CERT_CHECK_BAD) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TLS: External certificate check failed - force authentication failure");
+			ret->methodState = METHOD_DONE;
+			ret->decision = DECISION_FAIL;
+			sm->waiting_ext_cert_check = 0;
+			return NULL;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TLS: Continuing to wait external server certificate validation");
+		return NULL;
+	}
+
 	pos = eap_peer_tls_process_init(sm, &data->ssl, data->eap_type, ret,
 					reqData, &left, &flags);
 	if (pos == NULL)
@@ -237,6 +265,14 @@
 		return eap_tls_failure(sm, data, ret, res, resp, id);
 	}
 
+	if (sm->waiting_ext_cert_check) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TLS: Waiting external server certificate validation");
+		wpabuf_free(data->pending_resp);
+		data->pending_resp = resp;
+		return NULL;
+	}
+
 	if (tls_connection_established(data->ssl_ctx, data->ssl.conn))
 		eap_tls_success(sm, data, ret);
 
@@ -258,6 +294,10 @@
 
 static void eap_tls_deinit_for_reauth(struct eap_sm *sm, void *priv)
 {
+	struct eap_tls_data *data = priv;
+
+	wpabuf_free(data->pending_resp);
+	data->pending_resp = NULL;
 }
 
 
diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c
index af2b754..4b994fd 100644
--- a/src/eap_peer/eap_tls_common.c
+++ b/src/eap_peer/eap_tls_common.c
@@ -80,6 +80,10 @@
 		params->flags |= TLS_CONN_DISABLE_TLSv1_2;
 	if (os_strstr(txt, "tls_disable_tlsv1_2=0"))
 		params->flags &= ~TLS_CONN_DISABLE_TLSv1_2;
+	if (os_strstr(txt, "tls_ext_cert_check=1"))
+		params->flags |= TLS_CONN_EXT_CERT_CHECK;
+	if (os_strstr(txt, "tls_ext_cert_check=0"))
+		params->flags &= ~TLS_CONN_EXT_CERT_CHECK;
 }
 
 
@@ -177,6 +181,8 @@
 
 	params->openssl_ciphers = config->openssl_ciphers;
 
+	sm->ext_cert_check = !!(params->flags & TLS_CONN_EXT_CERT_CHECK);
+
 	return 0;
 }
 
diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c
index b186c91..9741ded 100644
--- a/src/eap_peer/eap_ttls.c
+++ b/src/eap_peer/eap_ttls.c
@@ -1,6 +1,6 @@
 /*
  * EAP peer method: EAP-TTLS (RFC 5281)
- * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -58,6 +58,7 @@
 	size_t id_len;
 
 	struct wpabuf *pending_phase2_req;
+	struct wpabuf *pending_resp;
 
 #ifdef EAP_TNC
 	int ready_for_tnc;
@@ -153,6 +154,7 @@
 	eap_ttls_free_key(data);
 	os_free(data->session_id);
 	wpabuf_free(data->pending_phase2_req);
+	wpabuf_free(data->pending_resp);
 	os_free(data);
 }
 
@@ -1408,6 +1410,32 @@
 {
 	int res;
 
+	if (sm->waiting_ext_cert_check && data->pending_resp) {
+		struct eap_peer_config *config = eap_get_config(sm);
+
+		if (config->pending_ext_cert_check == EXT_CERT_CHECK_GOOD) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TTLS: External certificate check succeeded - continue handshake");
+			*out_data = data->pending_resp;
+			data->pending_resp = NULL;
+			sm->waiting_ext_cert_check = 0;
+			return 0;
+		}
+
+		if (config->pending_ext_cert_check == EXT_CERT_CHECK_BAD) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TTLS: External certificate check failed - force authentication failure");
+			ret->methodState = METHOD_DONE;
+			ret->decision = DECISION_FAIL;
+			sm->waiting_ext_cert_check = 0;
+			return 0;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TTLS: Continuing to wait external server certificate validation");
+		return 0;
+	}
+
 	res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS,
 					  data->ttls_version, identifier,
 					  in_data, out_data);
@@ -1418,6 +1446,15 @@
 		return -1;
 	}
 
+	if (sm->waiting_ext_cert_check) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TTLS: Waiting external server certificate validation");
+		wpabuf_free(data->pending_resp);
+		data->pending_resp = *out_data;
+		*out_data = NULL;
+		return 0;
+	}
+
 	if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
 		wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to "
 			   "Phase 2");
@@ -1557,6 +1594,8 @@
 	struct eap_ttls_data *data = priv;
 	wpabuf_free(data->pending_phase2_req);
 	data->pending_phase2_req = NULL;
+	wpabuf_free(data->pending_resp);
+	data->pending_resp = NULL;
 #ifdef EAP_TNC
 	data->ready_for_tnc = 0;
 	data->tnc_started = 0;
diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c
index 47210e1..e348eb3 100644
--- a/src/eap_server/eap_server_fast.c
+++ b/src/eap_server/eap_server_fast.c
@@ -1564,7 +1564,10 @@
 	if (eapKeyData == NULL)
 		return NULL;
 
-	eap_fast_derive_eap_msk(data->simck, eapKeyData);
+	if (eap_fast_derive_eap_msk(data->simck, eapKeyData) < 0) {
+		os_free(eapKeyData);
+		return NULL;
+	}
 	*len = EAP_FAST_KEY_LEN;
 
 	return eapKeyData;
@@ -1583,7 +1586,10 @@
 	if (eapKeyData == NULL)
 		return NULL;
 
-	eap_fast_derive_eap_emsk(data->simck, eapKeyData);
+	if (eap_fast_derive_eap_emsk(data->simck, eapKeyData) < 0) {
+		os_free(eapKeyData);
+		return NULL;
+	}
 	*len = EAP_EMSK_LEN;
 
 	return eapKeyData;
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 0b5fe51..9bde3c8 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -364,13 +364,12 @@
 		if (rsn_ie_buf == NULL)
 			return -1;
 		os_memcpy(rsn_ie_buf, wpa_ie, wpa_ie_len);
-		res = wpa_insert_pmkid(rsn_ie_buf, wpa_ie_len,
+		res = wpa_insert_pmkid(rsn_ie_buf, &wpa_ie_len,
 				       sm->pmk_r1_name);
 		if (res < 0) {
 			os_free(rsn_ie_buf);
 			return -1;
 		}
-		wpa_ie_len += res;
 
 		if (sm->assoc_resp_ies) {
 			os_memcpy(rsn_ie_buf + wpa_ie_len, sm->assoc_resp_ies,
@@ -2291,6 +2290,9 @@
 #ifdef CONFIG_IEEE80211R
 	os_free(sm->assoc_resp_ies);
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_TESTING_OPTIONS
+	wpabuf_free(sm->test_assoc_ie);
+#endif /* CONFIG_TESTING_OPTIONS */
 	os_free(sm);
 }
 
@@ -2692,6 +2694,17 @@
 	if (sm == NULL)
 		return -1;
 
+#ifdef CONFIG_TESTING_OPTIONS
+	if (sm->test_assoc_ie) {
+		wpa_printf(MSG_DEBUG,
+			   "TESTING: Replace association WPA/RSN IE");
+		if (*wpa_ie_len < wpabuf_len(sm->test_assoc_ie))
+			return -1;
+		os_memcpy(wpa_ie, wpabuf_head(sm->test_assoc_ie),
+			  wpabuf_len(sm->test_assoc_ie));
+		res = wpabuf_len(sm->test_assoc_ie);
+	} else
+#endif /* CONFIG_TESTING_OPTIONS */
 	res = wpa_gen_wpa_ie(sm, wpa_ie, *wpa_ie_len);
 	if (res < 0)
 		return -1;
@@ -3031,3 +3044,12 @@
 	}
 	sm->ptk_set = 1;
 }
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf)
+{
+	wpabuf_free(sm->test_assoc_ie);
+	sm->test_assoc_ie = buf;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 1d27453..f9c89b3 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -419,5 +419,6 @@
 int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr);
 
 int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf);
+void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf);
 
 #endif /* WPA_H */
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index dba6b62..f653ba6 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -134,6 +134,10 @@
 #ifdef CONFIG_P2P
 	u8 p2p_ip_addr[3 * 4];
 #endif /* CONFIG_P2P */
+
+#ifdef CONFIG_TESTING_OPTIONS
+	struct wpabuf *test_assoc_ie;
+#endif /* CONFIG_TESTING_OPTIONS */
 };
 
 
diff --git a/src/tls/Makefile b/src/tls/Makefile
index 27cdfca..52a890a 100644
--- a/src/tls/Makefile
+++ b/src/tls/Makefile
@@ -24,6 +24,7 @@
 	tlsv1_client.o \
 	tlsv1_client_read.o \
 	tlsv1_client_write.o \
+	tlsv1_client_ocsp.o \
 	tlsv1_common.o \
 	tlsv1_cred.o \
 	tlsv1_record.o \
diff --git a/src/tls/asn1.h b/src/tls/asn1.h
index 7475007..6bd7df5 100644
--- a/src/tls/asn1.h
+++ b/src/tls/asn1.h
@@ -20,6 +20,7 @@
 #define ASN1_TAG_EXTERNAL	0x08 /* not yet parsed */
 #define ASN1_TAG_REAL		0x09 /* not yet parsed */
 #define ASN1_TAG_ENUMERATED	0x0A /* not yet parsed */
+#define ASN1_TAG_EMBEDDED_PDV	0x0B /* not yet parsed */
 #define ASN1_TAG_UTF8STRING	0x0C /* not yet parsed */
 #define ANS1_TAG_RELATIVE_OID	0x0D
 #define ASN1_TAG_SEQUENCE	0x10 /* shall be constructed */
@@ -35,7 +36,8 @@
 #define ASN1_TAG_VISIBLESTRING	0x1A
 #define ASN1_TAG_GENERALSTRING	0x1B /* not yet parsed */
 #define ASN1_TAG_UNIVERSALSTRING	0x1C /* not yet parsed */
-#define ASN1_TAG_BMPSTRING	0x1D /* not yet parsed */
+#define ASN1_TAG_CHARACTERSTRING	0x1D /* not yet parsed */
+#define ASN1_TAG_BMPSTRING	0x1E /* not yet parsed */
 
 #define ASN1_CLASS_UNIVERSAL		0
 #define ASN1_CLASS_APPLICATION		1
diff --git a/src/tls/pkcs5.c b/src/tls/pkcs5.c
index 8a93483..a2ad83b 100644
--- a/src/tls/pkcs5.c
+++ b/src/tls/pkcs5.c
@@ -1,6 +1,6 @@
 /*
  * PKCS #5 (Password-based Encryption)
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -11,6 +11,7 @@
 #include "common.h"
 #include "crypto/crypto.h"
 #include "crypto/md5.h"
+#include "crypto/sha1.h"
 #include "asn1.h"
 #include "pkcs5.h"
 
@@ -18,30 +19,261 @@
 struct pkcs5_params {
 	enum pkcs5_alg {
 		PKCS5_ALG_UNKNOWN,
-		PKCS5_ALG_MD5_DES_CBC
+		PKCS5_ALG_MD5_DES_CBC,
+		PKCS5_ALG_PBES2,
+		PKCS5_ALG_SHA1_3DES_CBC,
 	} alg;
-	u8 salt[8];
+	u8 salt[64];
 	size_t salt_len;
 	unsigned int iter_count;
+	enum pbes2_enc_alg {
+		PBES2_ENC_ALG_UNKNOWN,
+		PBES2_ENC_ALG_DES_EDE3_CBC,
+	} enc_alg;
+	u8 iv[8];
+	size_t iv_len;
 };
 
 
+static int oid_is_rsadsi(struct asn1_oid *oid)
+{
+	return oid->len >= 4 &&
+		oid->oid[0] == 1 /* iso */ &&
+		oid->oid[1] == 2 /* member-body */ &&
+		oid->oid[2] == 840 /* us */ &&
+		oid->oid[3] == 113549 /* rsadsi */;
+}
+
+
+static int pkcs5_is_oid(struct asn1_oid *oid, unsigned long alg)
+{
+	return oid->len == 7 &&
+		oid_is_rsadsi(oid) &&
+		oid->oid[4] == 1 /* pkcs */ &&
+		oid->oid[5] == 5 /* pkcs-5 */ &&
+		oid->oid[6] == alg;
+}
+
+
+static int enc_alg_is_oid(struct asn1_oid *oid, unsigned long alg)
+{
+	return oid->len == 6 &&
+		oid_is_rsadsi(oid) &&
+		oid->oid[4] == 3 /* encryptionAlgorithm */ &&
+		oid->oid[5] == alg;
+}
+
+
+static int pkcs12_is_pbe_oid(struct asn1_oid *oid, unsigned long alg)
+{
+	return oid->len == 8 &&
+		oid_is_rsadsi(oid) &&
+		oid->oid[4] == 1 /* pkcs */ &&
+		oid->oid[5] == 12 /* pkcs-12 */ &&
+		oid->oid[6] == 1 /* pkcs-12PbeIds */ &&
+		oid->oid[7] == alg;
+}
+
+
 static enum pkcs5_alg pkcs5_get_alg(struct asn1_oid *oid)
 {
-	if (oid->len == 7 &&
-	    oid->oid[0] == 1 /* iso */ &&
-	    oid->oid[1] == 2 /* member-body */ &&
-	    oid->oid[2] == 840 /* us */ &&
-	    oid->oid[3] == 113549 /* rsadsi */ &&
-	    oid->oid[4] == 1 /* pkcs */ &&
-	    oid->oid[5] == 5 /* pkcs-5 */ &&
-	    oid->oid[6] == 3 /* pbeWithMD5AndDES-CBC */)
+	if (pkcs5_is_oid(oid, 3)) /* pbeWithMD5AndDES-CBC (PBES1) */
 		return PKCS5_ALG_MD5_DES_CBC;
-
+	if (pkcs12_is_pbe_oid(oid, 3)) /* pbeWithSHAAnd3-KeyTripleDES-CBC */
+		return PKCS5_ALG_SHA1_3DES_CBC;
+	if (pkcs5_is_oid(oid, 13)) /* id-PBES2 (PBES2) */
+		return PKCS5_ALG_PBES2;
 	return PKCS5_ALG_UNKNOWN;
 }
 
 
+static int pkcs5_get_params_pbes2(struct pkcs5_params *params, const u8 *pos,
+				  const u8 *enc_alg_end)
+{
+	struct asn1_hdr hdr;
+	const u8 *end, *kdf_end;
+	struct asn1_oid oid;
+	char obuf[80];
+
+	/*
+	 * RFC 2898, Ch. A.4
+	 *
+	 * PBES2-params ::= SEQUENCE {
+	 *     keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}},
+	 *     encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} }
+	 *
+	 * PBES2-KDFs ALGORITHM-IDENTIFIER ::=
+	 *     { {PBKDF2-params IDENTIFIED BY id-PBKDF2}, ... }
+	 */
+
+	if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #5: Expected SEQUENCE (PBES2-params) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #5: Expected SEQUENCE (keyDerivationFunc) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	pos = hdr.payload;
+	kdf_end = end = hdr.payload + hdr.length;
+
+	if (asn1_get_oid(pos, end - pos, &oid, &pos)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #5: Failed to parse OID (keyDerivationFunc algorithm)");
+		return -1;
+	}
+
+	asn1_oid_to_str(&oid, obuf, sizeof(obuf));
+	wpa_printf(MSG_DEBUG, "PKCS #5: PBES2 keyDerivationFunc algorithm %s",
+		   obuf);
+	if (!pkcs5_is_oid(&oid, 12)) /* id-PBKDF2 */ {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #5: Unsupported PBES2 keyDerivationFunc algorithm %s",
+			   obuf);
+		return -1;
+	}
+
+	/*
+	 * RFC 2898, C.
+	 *
+	 * PBKDF2-params ::= SEQUENCE {
+	 *     salt CHOICE {
+	 *       specified OCTET STRING,
+	 *       otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}}
+	 *     },
+	 *     iterationCount INTEGER (1..MAX),
+	 *     keyLength INTEGER (1..MAX) OPTIONAL,
+	 *     prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT
+	 *     algid-hmacWithSHA1
+	 * }
+	 */
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #5: Expected SEQUENCE (PBKDF2-params) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	/* For now, only support the salt CHOICE specified (OCTET STRING) */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_OCTETSTRING ||
+	    hdr.length > sizeof(params->salt)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #5: Expected OCTET STRING (salt.specified) - found class %d tag 0x%x size %d",
+			   hdr.class, hdr.tag, hdr.length);
+		return -1;
+	}
+	pos = hdr.payload + hdr.length;
+	os_memcpy(params->salt, hdr.payload, hdr.length);
+	params->salt_len = hdr.length;
+	wpa_hexdump(MSG_DEBUG, "PKCS #5: salt", params->salt, params->salt_len);
+
+	/* iterationCount INTEGER */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #5: Expected INTEGER - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	if (hdr.length == 1) {
+		params->iter_count = *hdr.payload;
+	} else if (hdr.length == 2) {
+		params->iter_count = WPA_GET_BE16(hdr.payload);
+	} else if (hdr.length == 4) {
+		params->iter_count = WPA_GET_BE32(hdr.payload);
+	} else {
+		wpa_hexdump(MSG_DEBUG,
+			    "PKCS #5: Unsupported INTEGER value (iterationCount)",
+			    hdr.payload, hdr.length);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "PKCS #5: iterationCount=0x%x",
+		   params->iter_count);
+	if (params->iter_count == 0 || params->iter_count > 0xffff) {
+		wpa_printf(MSG_INFO, "PKCS #5: Unsupported iterationCount=0x%x",
+			   params->iter_count);
+		return -1;
+	}
+
+	/* For now, ignore optional keyLength and prf */
+
+	pos = kdf_end;
+
+	/* encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} */
+
+	if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #5: Expected SEQUENCE (encryptionScheme) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	if (asn1_get_oid(pos, end - pos, &oid, &pos)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #5: Failed to parse OID (encryptionScheme algorithm)");
+		return -1;
+	}
+
+	asn1_oid_to_str(&oid, obuf, sizeof(obuf));
+	wpa_printf(MSG_DEBUG, "PKCS #5: PBES2 encryptionScheme algorithm %s",
+		   obuf);
+	if (enc_alg_is_oid(&oid, 7)) {
+		params->enc_alg = PBES2_ENC_ALG_DES_EDE3_CBC;
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #5: Unsupported PBES2 encryptionScheme algorithm %s",
+			   obuf);
+		return -1;
+	}
+
+	/*
+	 * RFC 2898, B.2.2:
+	 * The parameters field associated with this OID in an
+	 * AlgorithmIdentifier shall have type OCTET STRING (SIZE(8)),
+	 * specifying the initialization vector for CBC mode.
+	 */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_OCTETSTRING ||
+	    hdr.length != 8) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #5: Expected OCTET STRING (SIZE(8)) (IV) - found class %d tag 0x%x size %d",
+			   hdr.class, hdr.tag, hdr.length);
+		return -1;
+	}
+	os_memcpy(params->iv, hdr.payload, hdr.length);
+	params->iv_len = hdr.length;
+	wpa_hexdump(MSG_DEBUG, "PKCS #5: IV", params->iv, params->iv_len);
+
+	return 0;
+}
+
+
 static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len,
 			    struct pkcs5_params *params)
 {
@@ -71,11 +303,23 @@
 		return -1;
 	}
 
+	if (params->alg == PKCS5_ALG_PBES2)
+		return pkcs5_get_params_pbes2(params, pos, enc_alg_end);
+
+	/* PBES1 */
+
 	/*
 	 * PKCS#5, Section 8
 	 * PBEParameter ::= SEQUENCE {
 	 *   salt OCTET STRING SIZE(8),
 	 *   iterationCount INTEGER }
+	 *
+	 * Note: The same implementation can be used to parse the PKCS #12
+	 * version described in RFC 7292, C:
+	 * pkcs-12PbeParams ::= SEQUENCE {
+	 *     salt        OCTET STRING,
+	 *     iterations  INTEGER
+	 * }
 	 */
 
 	if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 ||
@@ -89,11 +333,11 @@
 	pos = hdr.payload;
 	end = hdr.payload + hdr.length;
 
-	/* salt OCTET STRING SIZE(8) */
+	/* salt OCTET STRING SIZE(8) (PKCS #5) or OCTET STRING (PKCS #12) */
 	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
 	    hdr.class != ASN1_CLASS_UNIVERSAL ||
 	    hdr.tag != ASN1_TAG_OCTETSTRING ||
-	    hdr.length != 8) {
+	    hdr.length > sizeof(params->salt)) {
 		wpa_printf(MSG_DEBUG, "PKCS #5: Expected OCTETSTRING SIZE(8) "
 			   "(salt) - found class %d tag 0x%x size %d",
 			   hdr.class, hdr.tag, hdr.length);
@@ -136,6 +380,174 @@
 }
 
 
+static struct crypto_cipher *
+pkcs5_crypto_init_pbes2(struct pkcs5_params *params, const char *passwd)
+{
+	u8 key[24];
+
+	if (params->enc_alg != PBES2_ENC_ALG_DES_EDE3_CBC ||
+	    params->iv_len != 8)
+		return NULL;
+
+	wpa_hexdump_ascii_key(MSG_DEBUG, "PKCS #5: PBES2 password for PBKDF2",
+			      passwd, os_strlen(passwd));
+	wpa_hexdump(MSG_DEBUG, "PKCS #5: PBES2 salt for PBKDF2",
+		    params->salt, params->salt_len);
+	wpa_printf(MSG_DEBUG, "PKCS #5: PBES2 PBKDF2 iterations: %u",
+		   params->iter_count);
+	if (pbkdf2_sha1(passwd, params->salt, params->salt_len,
+			params->iter_count, key, sizeof(key)) < 0)
+		return NULL;
+	wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES EDE3 key", key, sizeof(key));
+	wpa_hexdump(MSG_DEBUG, "PKCS #5: DES IV", params->iv, params->iv_len);
+
+	return crypto_cipher_init(CRYPTO_CIPHER_ALG_3DES, params->iv,
+				  key, sizeof(key));
+}
+
+
+static void add_byte_array_mod(u8 *a, const u8 *b, size_t len)
+{
+	size_t i;
+	unsigned int carry = 0;
+
+	for (i = len - 1; i < len; i--) {
+		carry = carry + a[i] + b[i];
+		a[i] = carry & 0xff;
+		carry >>= 8;
+	}
+}
+
+
+static int pkcs12_key_gen(const u8 *pw, size_t pw_len, const u8 *salt,
+			  size_t salt_len, u8 id, unsigned int iter,
+			  size_t out_len, u8 *out)
+{
+	unsigned int u, v, S_len, P_len, i;
+	u8 *D = NULL, *I = NULL, *B = NULL, *pos;
+	int res = -1;
+
+	/* RFC 7292, B.2 */
+	u = SHA1_MAC_LEN;
+	v = 64;
+
+	/* D = copies of ID */
+	D = os_malloc(v);
+	if (!D)
+		goto done;
+	os_memset(D, id, v);
+
+	/* S = copies of salt; P = copies of password, I = S || P */
+	S_len = v * ((salt_len + v - 1) / v);
+	P_len = v * ((pw_len + v - 1) / v);
+	I = os_malloc(S_len + P_len);
+	if (!I)
+		goto done;
+	pos = I;
+	if (salt_len) {
+		for (i = 0; i < S_len; i++)
+			*pos++ = salt[i % salt_len];
+	}
+	if (pw_len) {
+		for (i = 0; i < P_len; i++)
+			*pos++ = pw[i % pw_len];
+	}
+
+	B = os_malloc(v);
+	if (!B)
+		goto done;
+
+	for (;;) {
+		u8 hash[SHA1_MAC_LEN];
+		const u8 *addr[2];
+		size_t len[2];
+
+		addr[0] = D;
+		len[0] = v;
+		addr[1] = I;
+		len[1] = S_len + P_len;
+		if (sha1_vector(2, addr, len, hash) < 0)
+			goto done;
+
+		addr[0] = hash;
+		len[0] = SHA1_MAC_LEN;
+		for (i = 1; i < iter; i++) {
+			if (sha1_vector(1, addr, len, hash) < 0)
+				goto done;
+		}
+
+		if (out_len <= u) {
+			os_memcpy(out, hash, out_len);
+			res = 0;
+			goto done;
+		}
+
+		os_memcpy(out, hash, u);
+		out += u;
+		out_len -= u;
+
+		/* I_j = (I_j + B + 1) mod 2^(v*8) */
+		/* B = copies of Ai (final hash value) */
+		for (i = 0; i < v; i++)
+			B[i] = hash[i % u];
+		inc_byte_array(B, v);
+		for (i = 0; i < S_len + P_len; i += v)
+			add_byte_array_mod(&I[i], B, v);
+	}
+
+done:
+	os_free(B);
+	os_free(I);
+	os_free(D);
+	return res;
+}
+
+
+#define PKCS12_ID_ENC 1
+#define PKCS12_ID_IV 2
+#define PKCS12_ID_MAC 3
+
+static struct crypto_cipher *
+pkcs12_crypto_init_sha1(struct pkcs5_params *params, const char *passwd)
+{
+	unsigned int i;
+	u8 *pw;
+	size_t pw_len;
+	u8 key[24];
+	u8 iv[8];
+
+	if (params->alg != PKCS5_ALG_SHA1_3DES_CBC)
+		return NULL;
+
+	pw_len = passwd ? os_strlen(passwd) : 0;
+	pw = os_malloc(2 * (pw_len + 1));
+	if (!pw)
+		return NULL;
+	if (pw_len) {
+		for (i = 0; i <= pw_len; i++)
+			WPA_PUT_BE16(&pw[2 * i], passwd[i]);
+		pw_len = 2 * (pw_len + 1);
+	}
+
+	if (pkcs12_key_gen(pw, pw_len, params->salt, params->salt_len,
+			   PKCS12_ID_ENC, params->iter_count,
+			   sizeof(key), key) < 0 ||
+	    pkcs12_key_gen(pw, pw_len, params->salt, params->salt_len,
+			   PKCS12_ID_IV, params->iter_count,
+			   sizeof(iv), iv) < 0) {
+		os_free(pw);
+		return NULL;
+	}
+
+	os_free(pw);
+
+	wpa_hexdump_key(MSG_DEBUG, "PKCS #12: DES key", key, sizeof(key));
+	wpa_hexdump_key(MSG_DEBUG, "PKCS #12: DES IV", iv, sizeof(iv));
+
+	return crypto_cipher_init(CRYPTO_CIPHER_ALG_3DES, iv, key, sizeof(key));
+}
+
+
 static struct crypto_cipher * pkcs5_crypto_init(struct pkcs5_params *params,
 						const char *passwd)
 {
@@ -144,6 +556,12 @@
 	const u8 *addr[2];
 	size_t len[2];
 
+	if (params->alg == PKCS5_ALG_PBES2)
+		return pkcs5_crypto_init_pbes2(params, passwd);
+
+	if (params->alg == PKCS5_ALG_SHA1_3DES_CBC)
+		return pkcs12_crypto_init_sha1(params, passwd);
+
 	if (params->alg != PKCS5_ALG_MD5_DES_CBC)
 		return NULL;
 
diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c
index 846d293..cc404c1 100644
--- a/src/tls/tlsv1_client.c
+++ b/src/tls/tlsv1_client.c
@@ -1,6 +1,6 @@
 /*
  * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246)
- * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -11,6 +11,7 @@
 #include "common.h"
 #include "crypto/sha1.h"
 #include "crypto/tls.h"
+#include "x509v3.h"
 #include "tlsv1_common.h"
 #include "tlsv1_record.h"
 #include "tlsv1_client.h"
@@ -494,6 +495,7 @@
 	tlsv1_client_free_dh(conn);
 	tlsv1_cred_free(conn->cred);
 	wpabuf_free(conn->partial_input);
+	x509_certificate_chain_free(conn->server_cert);
 	os_free(conn);
 }
 
diff --git a/src/tls/tlsv1_client_i.h b/src/tls/tlsv1_client_i.h
index 6c4dbc7..12ec8df 100644
--- a/src/tls/tlsv1_client_i.h
+++ b/src/tls/tlsv1_client_i.h
@@ -36,6 +36,7 @@
 	unsigned int session_ticket_included:1;
 	unsigned int use_session_ticket:1;
 	unsigned int cert_in_cb:1;
+	unsigned int ocsp_resp_received:1;
 
 	struct crypto_public_key *server_rsa_key;
 
@@ -70,6 +71,8 @@
 	void (*event_cb)(void *ctx, enum tls_event ev,
 			 union tls_event_data *data);
 	void *cb_ctx;
+
+	struct x509_certificate *server_cert;
 };
 
 
@@ -87,4 +90,11 @@
 				   const u8 *buf, size_t *len,
 				   u8 **out_data, size_t *out_len);
 
+enum tls_ocsp_result {
+	TLS_OCSP_NO_RESPONSE, TLS_OCSP_INVALID, TLS_OCSP_GOOD, TLS_OCSP_REVOKED
+};
+
+enum tls_ocsp_result tls_process_ocsp_response(struct tlsv1_client *conn,
+					       const u8 *resp, size_t len);
+
 #endif /* TLSV1_CLIENT_I_H */
diff --git a/src/tls/tlsv1_client_ocsp.c b/src/tls/tlsv1_client_ocsp.c
new file mode 100644
index 0000000..bcc7a86
--- /dev/null
+++ b/src/tls/tlsv1_client_ocsp.c
@@ -0,0 +1,169 @@
+/*
+ * TLSv1 client - OCSP
+ * Copyright (c) 2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/tls.h"
+#include "asn1.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+#include "tlsv1_client.h"
+#include "tlsv1_client_i.h"
+
+
+/* RFC 6960, 4.2.1: OCSPResponseStatus ::= ENUMERATED */
+enum ocsp_response_status {
+	OCSP_RESP_STATUS_SUCCESSFUL = 0,
+	OCSP_RESP_STATUS_MALFORMED_REQ = 1,
+	OCSP_RESP_STATUS_INT_ERROR = 2,
+	OCSP_RESP_STATUS_TRY_LATER = 3,
+	/* 4 not used */
+	OCSP_RESP_STATUS_SIG_REQUIRED = 5,
+	OCSP_RESP_STATUS_UNAUTHORIZED = 6,
+};
+
+
+static int is_oid_basic_ocsp_resp(struct asn1_oid *oid)
+{
+	return oid->len == 10 &&
+		oid->oid[0] == 1 /* iso */ &&
+		oid->oid[1] == 3 /* identified-organization */ &&
+		oid->oid[2] == 6 /* dod */ &&
+		oid->oid[3] == 1 /* internet */ &&
+		oid->oid[4] == 5 /* security */ &&
+		oid->oid[5] == 5 /* mechanisms */ &&
+		oid->oid[6] == 7 /* id-pkix */ &&
+		oid->oid[7] == 48 /* id-ad */ &&
+		oid->oid[8] == 1 /* id-pkix-ocsp */ &&
+		oid->oid[9] == 1 /* id-pkix-ocsp-basic */;
+}
+
+
+static enum tls_ocsp_result
+tls_process_basic_ocsp_response(struct tlsv1_client *conn, const u8 *resp,
+				size_t len)
+{
+	wpa_hexdump(MSG_MSGDUMP, "OCSP: BasicOCSPResponse", resp, len);
+
+	/*
+	 * RFC 6960, 4.2.1:
+	 * BasicOCSPResponse       ::= SEQUENCE {
+	 *    tbsResponseData      ResponseData,
+	 *    signatureAlgorithm   AlgorithmIdentifier,
+	 *    signature            BIT STRING,
+	 *    certs            [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+	 */
+
+	/* TODO */
+	return TLS_OCSP_NO_RESPONSE;
+}
+
+
+enum tls_ocsp_result tls_process_ocsp_response(struct tlsv1_client *conn,
+					       const u8 *resp, size_t len)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+	u8 resp_status;
+	struct asn1_oid oid;
+	char obuf[80];
+
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: OCSPResponse", resp, len);
+
+	/*
+	 * RFC 6960, 4.2.1:
+	 * OCSPResponse ::= SEQUENCE {
+	 *    responseStatus  OCSPResponseStatus,
+	 *    responseBytes   [0] EXPLICIT ResponseBytes OPTIONAL }
+	 */
+
+	if (asn1_get_next(resp, len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG,
+			   "OCSP: Expected SEQUENCE (OCSPResponse) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return TLS_OCSP_INVALID;
+	}
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	/* OCSPResponseStatus ::= ENUMERATED */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_ENUMERATED ||
+	    hdr.length != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "OCSP: Expected ENUMERATED (responseStatus) - found class %d tag 0x%x length %u",
+			   hdr.class, hdr.tag, hdr.length);
+		return TLS_OCSP_INVALID;
+	}
+	resp_status = hdr.payload[0];
+	wpa_printf(MSG_DEBUG, "OCSP: responseStatus %u", resp_status);
+	pos = hdr.payload + hdr.length;
+	if (resp_status != OCSP_RESP_STATUS_SUCCESSFUL) {
+		wpa_printf(MSG_DEBUG, "OCSP: No stapling result");
+		return TLS_OCSP_NO_RESPONSE;
+	}
+
+	/* responseBytes   [0] EXPLICIT ResponseBytes OPTIONAL */
+	if (pos == end)
+		return TLS_OCSP_NO_RESPONSE;
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC ||
+	    hdr.tag != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "OCSP: Expected [0] EXPLICIT (responseBytes) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return TLS_OCSP_INVALID;
+	}
+
+	/*
+	 * ResponseBytes ::= SEQUENCE {
+	 *     responseType   OBJECT IDENTIFIER,
+	 *     response       OCTET STRING }
+	 */
+
+	if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG,
+			   "OCSP: Expected SEQUENCE (ResponseBytes) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return TLS_OCSP_INVALID;
+	}
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	/* responseType   OBJECT IDENTIFIER */
+	if (asn1_get_oid(pos, end - pos, &oid, &pos)) {
+		wpa_printf(MSG_DEBUG,
+			   "OCSP: Failed to parse OID (responseType)");
+		return TLS_OCSP_INVALID;
+	}
+	asn1_oid_to_str(&oid, obuf, sizeof(obuf));
+	wpa_printf(MSG_DEBUG, "OCSP: responseType %s", obuf);
+	if (!is_oid_basic_ocsp_resp(&oid)) {
+		wpa_printf(MSG_DEBUG, "OCSP: Ignore unsupported response type");
+		return TLS_OCSP_NO_RESPONSE;
+	}
+
+	/* response       OCTET STRING */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_OCTETSTRING) {
+		wpa_printf(MSG_DEBUG,
+			   "OCSP: Expected OCTET STRING (response) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return TLS_OCSP_INVALID;
+	}
+
+	return tls_process_basic_ocsp_response(conn, hdr.payload, hdr.length);
+}
diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c
index 40c6a46..b1fa15f 100644
--- a/src/tls/tlsv1_client_read.c
+++ b/src/tls/tlsv1_client_read.c
@@ -1,6 +1,6 @@
 /*
  * TLSv1 client - read handshake message
- * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -38,6 +38,43 @@
 }
 
 
+static int tls_process_server_hello_extensions(struct tlsv1_client *conn,
+					       const u8 *pos, size_t len)
+{
+	const u8 *end = pos + len;
+
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerHello extensions",
+		    pos, len);
+	while (pos < end) {
+		u16 ext, elen;
+
+		if (end - pos < 4) {
+			wpa_printf(MSG_INFO, "TLSv1: Truncated ServerHello extension header");
+			return -1;
+		}
+
+		ext = WPA_GET_BE16(pos);
+		pos += 2;
+		elen = WPA_GET_BE16(pos);
+		pos += 2;
+
+		if (elen > end - pos) {
+			wpa_printf(MSG_INFO, "TLSv1: Truncated ServerHello extension");
+			return -1;
+		}
+
+		wpa_printf(MSG_DEBUG, "TLSv1: ServerHello ExtensionType %u",
+			   ext);
+		wpa_hexdump(MSG_DEBUG, "TLSv1: ServerHello extension data",
+			    pos, elen);
+
+		pos += elen;
+	}
+
+	return 0;
+}
+
+
 static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct,
 				    const u8 *in_data, size_t *in_len)
 {
@@ -177,8 +214,24 @@
 	}
 	pos++;
 
+	if (end - pos >= 2) {
+		u16 ext_len;
+
+		ext_len = WPA_GET_BE16(pos);
+		pos += 2;
+		if (end - pos < ext_len) {
+			wpa_printf(MSG_INFO,
+				   "TLSv1: Invalid ServerHello extension length: %u (left: %u)",
+				   ext_len, (unsigned int) (end - pos));
+			goto decode_error;
+		}
+
+		if (tls_process_server_hello_extensions(conn, pos, ext_len))
+			goto decode_error;
+		pos += ext_len;
+	}
+
 	if (end != pos) {
-		/* TODO: ServerHello extensions */
 		wpa_hexdump(MSG_DEBUG, "TLSv1: Unexpected extra data in the "
 			    "end of ServerHello", pos, end - pos);
 		goto decode_error;
@@ -561,7 +614,12 @@
 		return -1;
 	}
 
-	x509_certificate_chain_free(chain);
+	if (conn->flags & TLS_CONN_REQUEST_OCSP) {
+		x509_certificate_chain_free(conn->server_cert);
+		conn->server_cert = chain;
+	} else {
+		x509_certificate_chain_free(chain);
+	}
 
 	*in_len = end - in_data;
 
@@ -732,6 +790,134 @@
 }
 
 
+static int tls_process_certificate_status(struct tlsv1_client *conn, u8 ct,
+					   const u8 *in_data, size_t *in_len)
+{
+	const u8 *pos, *end;
+	size_t left, len;
+	u8 type, status_type;
+	u32 ocsp_resp_len;
+	enum tls_ocsp_result res;
+
+	if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+		wpa_printf(MSG_DEBUG,
+			   "TLSv1: Expected Handshake; received content type 0x%x",
+			   ct);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	pos = in_data;
+	left = *in_len;
+
+	if (left < 4) {
+		wpa_printf(MSG_DEBUG,
+			   "TLSv1: Too short CertificateStatus (left=%lu)",
+			   (unsigned long) left);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	type = *pos++;
+	len = WPA_GET_BE24(pos);
+	pos += 3;
+	left -= 4;
+
+	if (len > left) {
+		wpa_printf(MSG_DEBUG,
+			   "TLSv1: Mismatch in CertificateStatus length (len=%lu != left=%lu)",
+			   (unsigned long) len, (unsigned long) left);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	end = pos + len;
+
+	if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS) {
+		wpa_printf(MSG_DEBUG,
+			   "TLSv1: Received unexpected handshake message %d (expected CertificateStatus)",
+			   type);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateStatus");
+
+	/*
+	 * struct {
+	 *     CertificateStatusType status_type;
+	 *     select (status_type) {
+	 *         case ocsp: OCSPResponse;
+	 *     } response;
+	 * } CertificateStatus;
+	 */
+	if (end - pos < 1) {
+		wpa_printf(MSG_INFO, "TLSv1: Too short CertificateStatus");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+	status_type = *pos++;
+	wpa_printf(MSG_DEBUG, "TLSv1: CertificateStatus status_type %u",
+		   status_type);
+
+	if (status_type != 1 /* ocsp */) {
+		wpa_printf(MSG_DEBUG,
+			   "TLSv1: Ignore unsupported CertificateStatus");
+		goto skip;
+	}
+
+	/* opaque OCSPResponse<1..2^24-1>; */
+	if (end - pos < 3) {
+		wpa_printf(MSG_INFO, "TLSv1: Too short OCSPResponse");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+	ocsp_resp_len = WPA_GET_BE24(pos);
+	pos += 3;
+	if (end - pos < ocsp_resp_len) {
+		wpa_printf(MSG_INFO, "TLSv1: Truncated OCSPResponse");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+
+	res = tls_process_ocsp_response(conn, pos, ocsp_resp_len);
+	switch (res) {
+	case TLS_OCSP_NO_RESPONSE:
+		if (!(conn->flags & TLS_CONN_REQUIRE_OCSP))
+			goto skip;
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE);
+		return -1;
+	case TLS_OCSP_INVALID:
+		if (!(conn->flags & TLS_CONN_REQUIRE_OCSP))
+			goto skip; /* ignore - process as if no response */
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	case TLS_OCSP_GOOD:
+		wpa_printf(MSG_DEBUG, "TLSv1: OCSP response good");
+		break;
+	case TLS_OCSP_REVOKED:
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_CERTIFICATE_REVOKED);
+		if (conn->server_cert)
+			tls_cert_chain_failure_event(
+				conn, 0, conn->server_cert, TLS_FAIL_REVOKED,
+				"certificate revoked");
+		return -1;
+	}
+	conn->ocsp_resp_received = 1;
+
+skip:
+	*in_len = end - in_data;
+
+	conn->state = SERVER_KEY_EXCHANGE;
+
+	return 0;
+}
+
+
 static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct,
 					   const u8 *in_data, size_t *in_len)
 {
@@ -773,6 +959,10 @@
 
 	end = pos + len;
 
+	if ((conn->flags & TLS_CONN_REQUEST_OCSP) &&
+	    type == TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS)
+		return tls_process_certificate_status(conn, ct, in_data,
+						      in_len);
 	if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST)
 		return tls_process_certificate_request(conn, ct, in_data,
 						       in_len);
@@ -782,7 +972,9 @@
 	if (type != TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE) {
 		wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
 			   "message %d (expected ServerKeyExchange/"
-			   "CertificateRequest/ServerHelloDone)", type);
+			   "CertificateRequest/ServerHelloDone%s)", type,
+			   (conn->flags & TLS_CONN_REQUEST_OCSP) ?
+			   "/CertificateStatus" : "");
 		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
 			  TLS_ALERT_UNEXPECTED_MESSAGE);
 		return -1;
@@ -936,6 +1128,15 @@
 
 	wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHelloDone");
 
+	if ((conn->flags & TLS_CONN_REQUIRE_OCSP) &&
+	    !conn->ocsp_resp_received) {
+		wpa_printf(MSG_INFO,
+			   "TLSv1: No OCSP response received - reject handshake");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE);
+		return -1;
+	}
+
 	*in_len = end - in_data;
 
 	conn->state = CLIENT_KEY_EXCHANGE;
diff --git a/src/tls/tlsv1_client_write.c b/src/tls/tlsv1_client_write.c
index b1906b2..8e8cb5e 100644
--- a/src/tls/tlsv1_client_write.c
+++ b/src/tls/tlsv1_client_write.c
@@ -1,6 +1,6 @@
 /*
  * TLSv1 client - write handshake message
- * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -156,6 +156,44 @@
 		pos += conn->client_hello_ext_len;
 	}
 
+	if (conn->flags & TLS_CONN_REQUEST_OCSP) {
+		wpa_printf(MSG_DEBUG,
+			   "TLSv1: Add status_request extension for OCSP stapling");
+		/* ExtensionsType extension_type = status_request(5) */
+		WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST);
+		pos += 2;
+		/* opaque extension_data<0..2^16-1> length */
+		WPA_PUT_BE16(pos, 5);
+		pos += 2;
+
+		/*
+		 * RFC 6066, 8:
+		 * struct {
+		 *     CertificateStatusType status_type;
+		 *     select (status_type) {
+		 *         case ocsp: OCSPStatusRequest;
+		 *     } request;
+		 * } CertificateStatusRequest;
+		 *
+		 * enum { ocsp(1), (255) } CertificateStatusType;
+		 */
+		*pos++ = 1; /* status_type = ocsp(1) */
+
+		/*
+		 * struct {
+		 *     ResponderID responder_id_list<0..2^16-1>;
+		 *     Extensions  request_extensions;
+		 * } OCSPStatusRequest;
+		 *
+		 * opaque ResponderID<1..2^16-1>;
+		 * opaque Extensions<0..2^16-1>;
+		 */
+		WPA_PUT_BE16(pos, 0); /* responder_id_list(empty) */
+		pos += 2;
+		WPA_PUT_BE16(pos, 0); /* request_extensions(empty) */
+		pos += 2;
+	}
+
 	if (pos == ext_start + 2)
 		pos -= 2; /* no extensions */
 	else
diff --git a/src/tls/tlsv1_cred.c b/src/tls/tlsv1_cred.c
index 067562b..92f97c7 100644
--- a/src/tls/tlsv1_cred.c
+++ b/src/tls/tlsv1_cred.c
@@ -1,6 +1,6 @@
 /*
  * TLSv1 credentials
- * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -11,6 +11,9 @@
 #include "common.h"
 #include "base64.h"
 #include "crypto/crypto.h"
+#include "crypto/sha1.h"
+#include "pkcs5.h"
+#include "pkcs8.h"
 #include "x509v3.h"
 #include "tlsv1_cred.h"
 
@@ -325,6 +328,735 @@
 }
 
 
+#ifdef PKCS12_FUNCS
+
+static int oid_is_rsadsi(struct asn1_oid *oid)
+{
+	return oid->len >= 4 &&
+		oid->oid[0] == 1 /* iso */ &&
+		oid->oid[1] == 2 /* member-body */ &&
+		oid->oid[2] == 840 /* us */ &&
+		oid->oid[3] == 113549 /* rsadsi */;
+}
+
+
+static int pkcs12_is_bagtype_oid(struct asn1_oid *oid, unsigned long type)
+{
+	return oid->len == 9 &&
+		oid_is_rsadsi(oid) &&
+		oid->oid[4] == 1 /* pkcs */ &&
+		oid->oid[5] == 12 /* pkcs-12 */ &&
+		oid->oid[6] == 10 &&
+		oid->oid[7] == 1 /* bagtypes */ &&
+		oid->oid[8] == type;
+}
+
+
+static int is_oid_pkcs7(struct asn1_oid *oid)
+{
+	return oid->len == 7 &&
+		oid->oid[0] == 1 /* iso */ &&
+		oid->oid[1] == 2 /* member-body */ &&
+		oid->oid[2] == 840 /* us */ &&
+		oid->oid[3] == 113549 /* rsadsi */ &&
+		oid->oid[4] == 1 /* pkcs */ &&
+		oid->oid[5] == 7 /* pkcs-7 */;
+}
+
+
+static int is_oid_pkcs7_data(struct asn1_oid *oid)
+{
+	return is_oid_pkcs7(oid) && oid->oid[6] == 1 /* data */;
+}
+
+
+static int is_oid_pkcs7_enc_data(struct asn1_oid *oid)
+{
+	return is_oid_pkcs7(oid) && oid->oid[6] == 6 /* encryptedData */;
+}
+
+
+static int is_oid_pkcs9(struct asn1_oid *oid)
+{
+	return oid->len >= 6 &&
+		oid->oid[0] == 1 /* iso */ &&
+		oid->oid[1] == 2 /* member-body */ &&
+		oid->oid[2] == 840 /* us */ &&
+		oid->oid[3] == 113549 /* rsadsi */ &&
+		oid->oid[4] == 1 /* pkcs */ &&
+		oid->oid[5] == 9 /* pkcs-9 */;
+}
+
+
+static int is_oid_pkcs9_friendly_name(struct asn1_oid *oid)
+{
+	return oid->len == 7 && is_oid_pkcs9(oid) &&
+		oid->oid[6] == 20;
+}
+
+
+static int is_oid_pkcs9_local_key_id(struct asn1_oid *oid)
+{
+	return oid->len == 7 && is_oid_pkcs9(oid) &&
+		oid->oid[6] == 21;
+}
+
+
+static int is_oid_pkcs9_x509_cert(struct asn1_oid *oid)
+{
+	return oid->len == 8 && is_oid_pkcs9(oid) &&
+		oid->oid[6] == 22 /* certTypes */ &&
+		oid->oid[7] == 1 /* x509Certificate */;
+}
+
+
+static int pkcs12_keybag(struct tlsv1_credentials *cred,
+			 const u8 *buf, size_t len)
+{
+	/* TODO */
+	return 0;
+}
+
+
+static int pkcs12_pkcs8_keybag(struct tlsv1_credentials *cred,
+			       const u8 *buf, size_t len,
+			       const char *passwd)
+{
+	struct crypto_private_key *key;
+
+	/* PKCS8ShroudedKeyBag ::= EncryptedPrivateKeyInfo */
+	key = pkcs8_enc_key_import(buf, len, passwd);
+	if (!key)
+		return -1;
+
+	wpa_printf(MSG_DEBUG,
+		   "PKCS #12: Successfully decrypted PKCS8ShroudedKeyBag");
+	crypto_private_key_free(cred->key);
+	cred->key = key;
+
+	return 0;
+}
+
+
+static int pkcs12_certbag(struct tlsv1_credentials *cred,
+			  const u8 *buf, size_t len)
+{
+	struct asn1_hdr hdr;
+	struct asn1_oid oid;
+	char obuf[80];
+	const u8 *pos, *end;
+
+	/*
+	 * CertBag ::= SEQUENCE {
+	 *     certId      BAG-TYPE.&id   ({CertTypes}),
+	 *     certValue   [0] EXPLICIT BAG-TYPE.&Type ({CertTypes}{@certId})
+	 * }
+	 */
+
+	if (asn1_get_next(buf, len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected SEQUENCE (CertBag) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	if (asn1_get_oid(pos, end - pos, &oid, &pos)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Failed to parse OID (certId)");
+		return -1;
+	}
+
+	asn1_oid_to_str(&oid, obuf, sizeof(obuf));
+	wpa_printf(MSG_DEBUG, "PKCS #12: certId %s", obuf);
+
+	if (!is_oid_pkcs9_x509_cert(&oid)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Ignored unsupported certificate type (certId %s)",
+			   obuf);
+	}
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC ||
+	    hdr.tag != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected [0] EXPLICIT (certValue) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_OCTETSTRING) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected OCTET STRING (x509Certificate) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "PKCS #12: x509Certificate",
+		    hdr.payload, hdr.length);
+	if (cred->cert) {
+		struct x509_certificate *cert;
+
+		wpa_printf(MSG_DEBUG, "PKCS #12: Ignore extra certificate");
+		cert = x509_certificate_parse(hdr.payload, hdr.length);
+		if (!cert) {
+			wpa_printf(MSG_DEBUG,
+				   "PKCS #12: Failed to parse x509Certificate");
+			return 0;
+		}
+		x509_certificate_chain_free(cert);
+
+		return 0;
+	}
+	return tlsv1_set_cert(cred, NULL, hdr.payload, hdr.length);
+}
+
+
+static int pkcs12_parse_attr_friendly_name(const u8 *pos, const u8 *end)
+{
+	struct asn1_hdr hdr;
+
+	/*
+	 * RFC 2985, 5.5.1:
+	 * friendlyName ATTRIBUTE ::= {
+	 *         WITH SYNTAX BMPString (SIZE(1..pkcs-9-ub-friendlyName))
+	 *         EQUALITY MATCHING RULE caseIgnoreMatch
+	 *         SINGLE VALUE TRUE
+	 *          ID pkcs-9-at-friendlyName
+	 * }
+	 */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_BMPSTRING) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected BMPSTRING (friendlyName) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return 0;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "PKCS #12: friendlyName",
+			  hdr.payload, hdr.length);
+	return 0;
+}
+
+
+static int pkcs12_parse_attr_local_key_id(const u8 *pos, const u8 *end)
+{
+	struct asn1_hdr hdr;
+
+	/*
+	 * RFC 2985, 5.5.2:
+	 * localKeyId ATTRIBUTE ::= {
+	 *         WITH SYNTAX OCTET STRING
+	 *         EQUALITY MATCHING RULE octetStringMatch
+	 *         SINGLE VALUE TRUE
+	 *         ID pkcs-9-at-localKeyId
+	 * }
+	 */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_OCTETSTRING) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected OCTET STRING (localKeyID) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "PKCS #12: localKeyID",
+			hdr.payload, hdr.length);
+	return 0;
+}
+
+
+static int pkcs12_parse_attr(const u8 *pos, size_t len)
+{
+	const u8 *end = pos + len;
+	struct asn1_hdr hdr;
+	struct asn1_oid a_oid;
+	char obuf[80];
+
+	/*
+	 * PKCS12Attribute ::= SEQUENCE {
+	 * attrId      ATTRIBUTE.&id ({PKCS12AttrSet}),
+	 * attrValues  SET OF ATTRIBUTE.&Type ({PKCS12AttrSet}{@attrId})
+	 * }
+	 */
+
+	if (asn1_get_oid(pos, end - pos, &a_oid, &pos)) {
+		wpa_printf(MSG_DEBUG, "PKCS #12: Failed to parse OID (attrId)");
+		return -1;
+	}
+
+	asn1_oid_to_str(&a_oid, obuf, sizeof(obuf));
+	wpa_printf(MSG_DEBUG, "PKCS #12: attrId %s", obuf);
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SET) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected SET (attrValues) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: attrValues",
+			hdr.payload, hdr.length);
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	if (is_oid_pkcs9_friendly_name(&a_oid))
+		return pkcs12_parse_attr_friendly_name(pos, end);
+	if (is_oid_pkcs9_local_key_id(&a_oid))
+		return pkcs12_parse_attr_local_key_id(pos, end);
+
+	wpa_printf(MSG_DEBUG, "PKCS #12: Ignore unknown attribute");
+	return 0;
+}
+
+
+static int pkcs12_safebag(struct tlsv1_credentials *cred,
+			  const u8 *buf, size_t len, const char *passwd)
+{
+	struct asn1_hdr hdr;
+	struct asn1_oid oid;
+	char obuf[80];
+	const u8 *pos = buf, *end = buf + len;
+	const u8 *value;
+	size_t value_len;
+
+	wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: SafeBag", buf, len);
+
+	/* BAG-TYPE ::= TYPE-IDENTIFIER */
+	if (asn1_get_oid(pos, end - pos, &oid, &pos)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Failed to parse OID (BAG-TYPE)");
+		return -1;
+	}
+
+	asn1_oid_to_str(&oid, obuf, sizeof(obuf));
+	wpa_printf(MSG_DEBUG, "PKCS #12: BAG-TYPE %s", obuf);
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC ||
+	    hdr.tag != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected [0] EXPLICIT (bagValue) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return 0;
+	}
+	value = hdr.payload;
+	value_len = hdr.length;
+	wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: bagValue", value, value_len);
+	pos = hdr.payload + hdr.length;
+
+	if (pos < end) {
+		/* bagAttributes  SET OF PKCS12Attribute OPTIONAL */
+		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+		    hdr.class != ASN1_CLASS_UNIVERSAL ||
+		    hdr.tag != ASN1_TAG_SET) {
+			wpa_printf(MSG_DEBUG,
+				   "PKCS #12: Expected SET (bagAttributes) - found class %d tag 0x%x",
+				   hdr.class, hdr.tag);
+			return -1;
+		}
+		wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: bagAttributes",
+				hdr.payload, hdr.length);
+
+		pos = hdr.payload;
+		end = hdr.payload + hdr.length;
+		while (pos < end) {
+			/* PKCS12Attribute ::= SEQUENCE */
+			if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+			    hdr.class != ASN1_CLASS_UNIVERSAL ||
+			    hdr.tag != ASN1_TAG_SEQUENCE) {
+				wpa_printf(MSG_DEBUG,
+					   "PKCS #12: Expected SEQUENCE (PKCS12Attribute) - found class %d tag 0x%x",
+					   hdr.class, hdr.tag);
+				return -1;
+			}
+			if (pkcs12_parse_attr(hdr.payload, hdr.length) < 0)
+				return -1;
+			pos = hdr.payload + hdr.length;
+		}
+	}
+
+	if (pkcs12_is_bagtype_oid(&oid, 1))
+		return pkcs12_keybag(cred, value, value_len);
+	if (pkcs12_is_bagtype_oid(&oid, 2))
+		return pkcs12_pkcs8_keybag(cred, value, value_len, passwd);
+	if (pkcs12_is_bagtype_oid(&oid, 3))
+		return pkcs12_certbag(cred, value, value_len);
+
+	wpa_printf(MSG_DEBUG, "PKCS #12: Ignore unsupported BAG-TYPE");
+	return 0;
+}
+
+
+static int pkcs12_safecontents(struct tlsv1_credentials *cred,
+			       const u8 *buf, size_t len,
+			       const char *passwd)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+
+	/* SafeContents ::= SEQUENCE OF SafeBag */
+	if (asn1_get_next(buf, len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected SEQUENCE (SafeContents) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	/*
+	 * SafeBag ::= SEQUENCE {
+	 *   bagId          BAG-TYPE.&id ({PKCS12BagSet})
+	 *   bagValue       [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}),
+	 *   bagAttributes  SET OF PKCS12Attribute OPTIONAL
+	 * }
+	 */
+
+	while (pos < end) {
+		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+		    hdr.class != ASN1_CLASS_UNIVERSAL ||
+		    hdr.tag != ASN1_TAG_SEQUENCE) {
+			wpa_printf(MSG_DEBUG,
+				   "PKCS #12: Expected SEQUENCE (SafeBag) - found class %d tag 0x%x",
+				   hdr.class, hdr.tag);
+			return -1;
+		}
+		if (pkcs12_safebag(cred, hdr.payload, hdr.length, passwd) < 0)
+			return -1;
+		pos = hdr.payload + hdr.length;
+	}
+
+	return 0;
+}
+
+
+static int pkcs12_parse_content_data(struct tlsv1_credentials *cred,
+				     const u8 *pos, const u8 *end,
+				     const char *passwd)
+{
+	struct asn1_hdr hdr;
+
+	/* Data ::= OCTET STRING */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_OCTETSTRING) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected OCTET STRING (Data) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "PKCS #12: Data", hdr.payload, hdr.length);
+
+	return pkcs12_safecontents(cred, hdr.payload, hdr.length, passwd);
+}
+
+
+static int pkcs12_parse_content_enc_data(struct tlsv1_credentials *cred,
+					 const u8 *pos, const u8 *end,
+					 const char *passwd)
+{
+	struct asn1_hdr hdr;
+	struct asn1_oid oid;
+	char buf[80];
+	const u8 *enc_alg;
+	u8 *data;
+	size_t enc_alg_len, data_len;
+	int res = -1;
+
+	/*
+	 * EncryptedData ::= SEQUENCE {
+	 *   version Version,
+	 *   encryptedContentInfo EncryptedContentInfo }
+	 */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected SEQUENCE (EncryptedData) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return 0;
+	}
+	pos = hdr.payload;
+
+	/* Version ::= INTEGER */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: No INTEGER tag found for version; class=%d tag=0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	if (hdr.length != 1 || hdr.payload[0] != 0) {
+		wpa_printf(MSG_DEBUG, "PKCS #12: Unrecognized PKCS #7 version");
+		return -1;
+	}
+	pos = hdr.payload + hdr.length;
+
+	wpa_hexdump(MSG_MSGDUMP, "PKCS #12: EncryptedContentInfo",
+		    pos, end - pos);
+
+	/*
+	 * EncryptedContentInfo ::= SEQUENCE {
+	 *   contentType ContentType,
+	 *   contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
+	 *   encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL }
+	 */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected SEQUENCE (EncryptedContentInfo) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	/* ContentType ::= OBJECT IDENTIFIER */
+	if (asn1_get_oid(pos, end - pos, &oid, &pos)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Could not find OBJECT IDENTIFIER (contentType)");
+		return -1;
+	}
+	asn1_oid_to_str(&oid, buf, sizeof(buf));
+	wpa_printf(MSG_DEBUG, "PKCS #12: EncryptedContentInfo::contentType %s",
+		   buf);
+
+	if (!is_oid_pkcs7_data(&oid)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Unsupported EncryptedContentInfo::contentType %s",
+			   buf);
+		return 0;
+	}
+
+	/* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG, "PKCS #12: Expected SEQUENCE (ContentEncryptionAlgorithmIdentifier) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	enc_alg = hdr.payload;
+	enc_alg_len = hdr.length;
+	pos = hdr.payload + hdr.length;
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC ||
+	    hdr.tag != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected [0] IMPLICIT (encryptedContent) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	/* EncryptedContent ::= OCTET STRING */
+	data = pkcs5_decrypt(enc_alg, enc_alg_len, hdr.payload, hdr.length,
+			     passwd, &data_len);
+	if (data) {
+		wpa_hexdump_key(MSG_MSGDUMP,
+				"PKCS #12: Decrypted encryptedContent",
+				data, data_len);
+		res = pkcs12_safecontents(cred, data, data_len, passwd);
+		os_free(data);
+	}
+
+	return res;
+}
+
+
+static int pkcs12_parse_content(struct tlsv1_credentials *cred,
+				const u8 *buf, size_t len,
+				const char *passwd)
+{
+	const u8 *pos = buf;
+	const u8 *end = buf + len;
+	struct asn1_oid oid;
+	char txt[80];
+	struct asn1_hdr hdr;
+
+	wpa_hexdump(MSG_MSGDUMP, "PKCS #12: ContentInfo", buf, len);
+
+	if (asn1_get_oid(pos, end - pos, &oid, &pos)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Could not find OBJECT IDENTIFIER (contentType)");
+		return 0;
+	}
+
+	asn1_oid_to_str(&oid, txt, sizeof(txt));
+	wpa_printf(MSG_DEBUG, "PKCS #12: contentType %s", txt);
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC ||
+	    hdr.tag != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected [0] EXPLICIT (content) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return 0;
+	}
+	pos = hdr.payload;
+
+	if (is_oid_pkcs7_data(&oid))
+		return pkcs12_parse_content_data(cred, pos, end, passwd);
+	if (is_oid_pkcs7_enc_data(&oid))
+		return pkcs12_parse_content_enc_data(cred, pos, end, passwd);
+
+	wpa_printf(MSG_DEBUG, "PKCS #12: Ignored unsupported contentType %s",
+		   txt);
+
+	return 0;
+}
+
+
+static int pkcs12_parse(struct tlsv1_credentials *cred,
+			const u8 *key, size_t len, const char *passwd)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+	struct asn1_oid oid;
+	char buf[80];
+
+	/*
+	 * PFX ::= SEQUENCE {
+	 *     version     INTEGER {v3(3)}(v3,...),
+	 *     authSafe    ContentInfo,
+	 *     macData     MacData OPTIONAL
+	 * }
+	 */
+
+	if (asn1_get_next(key, len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected SEQUENCE (PFX) - found class %d tag 0x%x; assume PKCS #12 not used",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: No INTEGER tag found for version; class=%d tag=0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	if (hdr.length != 1 || hdr.payload[0] != 3) {
+		wpa_printf(MSG_DEBUG, "PKCS #12: Unrecognized version");
+		return -1;
+	}
+	pos = hdr.payload + hdr.length;
+
+	/*
+	 * ContentInfo ::= SEQUENCE {
+	 *   contentType ContentType,
+	 *   content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }
+	 */
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected SEQUENCE (authSafe) - found class %d tag 0x%x; assume PKCS #12 not used",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	/* ContentType ::= OBJECT IDENTIFIER */
+	if (asn1_get_oid(pos, end - pos, &oid, &pos)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Could not find OBJECT IDENTIFIER (contentType); assume PKCS #12 not used");
+		return -1;
+	}
+	asn1_oid_to_str(&oid, buf, sizeof(buf));
+	wpa_printf(MSG_DEBUG, "PKCS #12: contentType %s", buf);
+	if (!is_oid_pkcs7_data(&oid)) {
+		wpa_printf(MSG_DEBUG, "PKCS #12: Unsupported contentType %s",
+			   buf);
+		return -1;
+	}
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC ||
+	    hdr.tag != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected [0] EXPLICIT (content) - found class %d tag 0x%x; assume PKCS #12 not used",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	pos = hdr.payload;
+
+	/* Data ::= OCTET STRING */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_OCTETSTRING) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected OCTET STRING (Data) - found class %d tag 0x%x; assume PKCS #12 not used",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	/*
+	 * AuthenticatedSafe ::= SEQUENCE OF ContentInfo
+	 *     -- Data if unencrypted
+	 *     -- EncryptedData if password-encrypted
+	 *     -- EnvelopedData if public key-encrypted
+	 */
+	wpa_hexdump(MSG_MSGDUMP, "PKCS #12: Data content",
+		    hdr.payload, hdr.length);
+
+	if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected SEQUENCE within Data content - found class %d tag 0x%x; assume PKCS #12 not used",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	while (end > pos) {
+		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+		    hdr.class != ASN1_CLASS_UNIVERSAL ||
+		    hdr.tag != ASN1_TAG_SEQUENCE) {
+			wpa_printf(MSG_DEBUG,
+				   "PKCS #12: Expected SEQUENCE (ContentInfo) - found class %d tag 0x%x; assume PKCS #12 not used",
+				   hdr.class, hdr.tag);
+			return -1;
+		}
+		if (pkcs12_parse_content(cred, hdr.payload, hdr.length,
+					 passwd) < 0)
+			return -1;
+
+		pos = hdr.payload + hdr.length;
+	}
+
+	return 0;
+}
+
+#endif /* PKCS12_FUNCS */
+
+
 static int tlsv1_set_key(struct tlsv1_credentials *cred,
 			 const u8 *key, size_t len, const char *passwd)
 {
@@ -333,6 +1065,10 @@
 		cred->key = tlsv1_set_key_pem(key, len);
 	if (cred->key == NULL)
 		cred->key = tlsv1_set_key_enc_pem(key, len, passwd);
+#ifdef PKCS12_FUNCS
+	if (!cred->key)
+		pkcs12_parse(cred, key, len, passwd);
+#endif /* PKCS12_FUNCS */
 	if (cred->key == NULL) {
 		wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key");
 		return -1;