[wpa_supplicant] Cumulative patch from b8491ae5a

Also revert local solution for encrypted IMSI and use the upstream version.

Bug: 134177972
Test: Device boots up and connects to WPA3/OWE wifi networks, run traffic.
Test: Able to turn on/off softap, associate wifi STA, run traffic.
Test: Regression test passed (Bug: 137653009)
Change-Id: Ibf6b6ef3495287156c397daa89d02923f981889b
diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h
index b130368..a32c883 100644
--- a/src/eap_server/eap.h
+++ b/src/eap_server/eap.h
@@ -121,6 +121,8 @@
 	int eap_fast_prov;
 	int pac_key_lifetime;
 	int pac_key_refresh_time;
+	int eap_teap_auth;
+	int eap_teap_pac_no_inner;
 	int eap_sim_aka_result_ind;
 	int tnc;
 	struct wps_context *wps;
diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h
index 1cade10..8e6ac46 100644
--- a/src/eap_server/eap_i.h
+++ b/src/eap_server/eap_i.h
@@ -190,6 +190,8 @@
 	} eap_fast_prov;
 	int pac_key_lifetime;
 	int pac_key_refresh_time;
+	int eap_teap_auth;
+	int eap_teap_pac_no_inner;
 	int eap_sim_aka_result_ind;
 	int tnc;
 	u16 pwd_group;
diff --git a/src/eap_server/eap_methods.h b/src/eap_server/eap_methods.h
index 3bf1495..fdbea7a 100644
--- a/src/eap_server/eap_methods.h
+++ b/src/eap_server/eap_methods.h
@@ -41,6 +41,7 @@
 int eap_server_gpsk_register(void);
 int eap_server_vendor_test_register(void);
 int eap_server_fast_register(void);
+int eap_server_teap_register(void);
 int eap_server_wsc_register(void);
 int eap_server_ikev2_register(void);
 int eap_server_tnc_register(void);
diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c
index e8b36e1..724ec15 100644
--- a/src/eap_server/eap_server.c
+++ b/src/eap_server/eap_server.c
@@ -1869,6 +1869,8 @@
 	sm->eap_fast_prov = conf->eap_fast_prov;
 	sm->pac_key_lifetime = conf->pac_key_lifetime;
 	sm->pac_key_refresh_time = conf->pac_key_refresh_time;
+	sm->eap_teap_auth = conf->eap_teap_auth;
+	sm->eap_teap_pac_no_inner = conf->eap_teap_pac_no_inner;
 	sm->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
 	sm->tnc = conf->tnc;
 	sm->wps = conf->wps;
diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c
index 1bea706..1f3884e 100644
--- a/src/eap_server/eap_server_aka.c
+++ b/src/eap_server/eap_server_aka.c
@@ -30,6 +30,7 @@
 	u8 ck[EAP_AKA_CK_LEN];
 	u8 ik[EAP_AKA_IK_LEN];
 	u8 res[EAP_AKA_RES_MAX_LEN];
+	u8 reauth_mac[EAP_SIM_MAC_LEN];
 	size_t res_len;
 	enum {
 		IDENTITY, CHALLENGE, REAUTH, NOTIFICATION, SUCCESS, FAILURE
@@ -542,6 +543,7 @@
 					    struct eap_aka_data *data, u8 id)
 {
 	struct eap_sim_msg *msg;
+	struct wpabuf *buf;
 
 	wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Re-authentication");
 
@@ -581,7 +583,16 @@
 
 	wpa_printf(MSG_DEBUG, "   AT_MAC");
 	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
-	return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, NULL, 0);
+	buf = eap_sim_msg_finish(msg, data->eap_method, data->k_aut, NULL, 0);
+
+	/* Remember this MAC before sending it to the peer. This MAC is used for
+	 * Session-Id calculation after receiving response from the peer and
+	 * after all other checks pass. */
+	os_memcpy(data->reauth_mac,
+		  wpabuf_head(buf) + wpabuf_len(buf) - EAP_SIM_MAC_LEN,
+		  EAP_SIM_MAC_LEN);
+
+	return buf;
 }
 
 
@@ -1304,14 +1315,24 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	*len = 1 + EAP_AKA_RAND_LEN + EAP_AKA_AUTN_LEN;
+	if (!data->reauth)
+		*len = 1 + EAP_AKA_RAND_LEN + EAP_AKA_AUTN_LEN;
+	else
+		*len = 1 + EAP_SIM_NONCE_S_LEN + EAP_SIM_MAC_LEN;
 	id = os_malloc(*len);
 	if (id == NULL)
 		return NULL;
 
 	id[0] = data->eap_method;
-	os_memcpy(id + 1, data->rand, EAP_AKA_RAND_LEN);
-	os_memcpy(id + 1 + EAP_AKA_RAND_LEN, data->autn, EAP_AKA_AUTN_LEN);
+	if (!data->reauth) {
+		os_memcpy(id + 1, data->rand, EAP_AKA_RAND_LEN);
+		os_memcpy(id + 1 + EAP_AKA_RAND_LEN, data->autn,
+			  EAP_AKA_AUTN_LEN);
+	} else {
+		os_memcpy(id + 1, data->nonce_s, EAP_SIM_NONCE_S_LEN);
+		os_memcpy(id + 1 + EAP_SIM_NONCE_S_LEN, data->reauth_mac,
+			  EAP_SIM_MAC_LEN);
+	}
 	wpa_hexdump(MSG_DEBUG, "EAP-AKA: Derived Session-Id", id, *len);
 
 	return id;
diff --git a/src/eap_server/eap_server_gpsk.c b/src/eap_server/eap_server_gpsk.c
index fb3d117..bebb17f 100644
--- a/src/eap_server/eap_server_gpsk.c
+++ b/src/eap_server/eap_server_gpsk.c
@@ -181,7 +181,7 @@
 	if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
 				 data->specifier, start, pos - start, pos) < 0)
 	{
-		os_free(req);
+		wpabuf_free(req);
 		eap_gpsk_state(data, FAILURE);
 		return NULL;
 	}
@@ -379,7 +379,7 @@
 	data->specifier = WPA_GET_BE16(csuite->specifier);
 	wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel %d:%d",
 		   data->vendor, data->specifier);
-	pos += sizeof(*csuite);	
+	pos += sizeof(*csuite);
 
 	if (end - pos < 2) {
 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
diff --git a/src/eap_server/eap_server_pax.c b/src/eap_server/eap_server_pax.c
index 3257789..5ed29ef 100644
--- a/src/eap_server/eap_server_pax.c
+++ b/src/eap_server/eap_server_pax.c
@@ -107,9 +107,14 @@
 		    data->rand.r.x, EAP_PAX_RAND_LEN);
 
 	pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
-	eap_pax_mac(data->mac_id, (u8 *) "", 0,
-		    wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
-		    NULL, 0, NULL, 0, pos);
+	if (eap_pax_mac(data->mac_id, (u8 *) "", 0,
+			wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
+			NULL, 0, NULL, 0, pos) < 0) {
+		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate ICV");
+		data->state = FAILURE;
+		wpabuf_free(req);
+		return NULL;
+	}
 	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
 
 	return req;
@@ -144,18 +149,28 @@
 
 	wpabuf_put_be16(req, EAP_PAX_MAC_LEN);
 	pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
-	eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
-		    data->rand.r.y, EAP_PAX_RAND_LEN,
-		    (u8 *) data->cid, data->cid_len, NULL, 0, pos);
+	if (eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
+			data->rand.r.y, EAP_PAX_RAND_LEN,
+			(u8 *) data->cid, data->cid_len, NULL, 0, pos) < 0) {
+		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate MAC");
+		data->state = FAILURE;
+		wpabuf_free(req);
+		return NULL;
+	}
 	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)",
 		    pos, EAP_PAX_MAC_LEN);
 
 	/* Optional ADE could be added here, if needed */
 
 	pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
-	eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
-		    wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
-		    NULL, 0, NULL, 0, pos);
+	if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
+			wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
+			NULL, 0, NULL, 0, pos) < 0) {
+		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate ICV");
+		data->state = FAILURE;
+		wpabuf_free(req);
+		return NULL;
+	}
 	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
 
 	return req;
@@ -190,7 +205,7 @@
 	u8 icvbuf[EAP_PAX_ICV_LEN], *icv;
 
 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
-	if (pos == NULL || len < sizeof(*resp)) {
+	if (pos == NULL || len < sizeof(*resp) + EAP_PAX_ICV_LEN) {
 		wpa_printf(MSG_INFO, "EAP-PAX: Invalid frame");
 		return TRUE;
 	}
@@ -264,10 +279,15 @@
 		}
 		icv = wpabuf_mhead_u8(respData) + mlen - EAP_PAX_ICV_LEN;
 		wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN);
-		eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
-			    wpabuf_mhead(respData),
-			    wpabuf_len(respData) - EAP_PAX_ICV_LEN,
-			    NULL, 0, NULL, 0, icvbuf);
+		if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
+				wpabuf_mhead(respData),
+				wpabuf_len(respData) - EAP_PAX_ICV_LEN,
+				NULL, 0, NULL, 0, icvbuf) < 0) {
+			wpa_printf(MSG_INFO,
+				   "EAP-PAX: Failed to calculate ICV");
+			return TRUE;
+		}
+
 		if (os_memcmp_const(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) {
 			wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV");
 			wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
@@ -395,10 +415,15 @@
 	}
 	data->keys_set = 1;
 
-	eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
-		    data->rand.r.x, EAP_PAX_RAND_LEN,
-		    data->rand.r.y, EAP_PAX_RAND_LEN,
-		    (u8 *) data->cid, data->cid_len, mac);
+	if (eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
+			data->rand.r.x, EAP_PAX_RAND_LEN,
+			data->rand.r.y, EAP_PAX_RAND_LEN,
+			(u8 *) data->cid, data->cid_len, mac) < 0) {
+		wpa_printf(MSG_INFO, "EAP-PAX: Failed to calculate MAC_CK");
+		data->state = FAILURE;
+		return;
+	}
+
 	if (os_memcmp_const(mac, pos, EAP_PAX_MAC_LEN) != 0) {
 		wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(A, B, CID) in "
 			   "PAX_STD-2");
@@ -417,10 +442,14 @@
 		return;
 	}
 	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
-	eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
-		    wpabuf_head(respData),
-		    wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0,
-		    icvbuf);
+	if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
+			wpabuf_head(respData),
+			wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0,
+			NULL, 0, icvbuf) < 0) {
+		wpa_printf(MSG_INFO, "EAP-PAX: Failed to calculate ICV");
+		return;
+	}
+
 	if (os_memcmp_const(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) {
 		wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV in PAX_STD-2");
 		wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
diff --git a/src/eap_server/eap_server_peap.c b/src/eap_server/eap_server_peap.c
index 92c0e5e..5e125ac 100644
--- a/src/eap_server/eap_server_peap.c
+++ b/src/eap_server/eap_server_peap.c
@@ -362,7 +362,7 @@
 	res = peap_prfplus(data->peap_version, tk, 40,
 			   "Inner Methods Compound Keys",
 			   isk, sizeof(isk), imck, sizeof(imck));
-	os_memset(isk, 0, sizeof(isk));
+	forced_memzero(isk, sizeof(isk));
 	if (res < 0) {
 		os_free(tk);
 		return -1;
@@ -376,7 +376,7 @@
 	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40);
 	os_memcpy(data->cmk, imck + 40, 20);
 	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20);
-	os_memset(imck, 0, sizeof(imck));
+	forced_memzero(imck, sizeof(imck));
 
 	return 0;
 }
@@ -1326,7 +1326,7 @@
 				   "key");
 		}
 
-		os_memset(csk, 0, sizeof(csk));
+		forced_memzero(csk, sizeof(csk));
 
 		return eapKeyData;
 	}
diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c
index e720a28..a8087c1 100644
--- a/src/eap_server/eap_server_pwd.c
+++ b/src/eap_server/eap_server_pwd.c
@@ -632,7 +632,7 @@
 				       data->id_server, data->id_server_len,
 				       data->id_peer, data->id_peer_len,
 				       (u8 *) &data->token);
-	os_memset(pwhashhash, 0, sizeof(pwhashhash));
+	forced_memzero(pwhashhash, sizeof(pwhashhash));
 	if (res) {
 		wpa_printf(MSG_INFO, "EAP-PWD (server): unable to compute "
 			   "PWE");
diff --git a/src/eap_server/eap_server_sake.c b/src/eap_server/eap_server_sake.c
index 66183f5..2fc2c05 100644
--- a/src/eap_server/eap_server_sake.c
+++ b/src/eap_server/eap_server_sake.c
@@ -204,7 +204,7 @@
 	{
 		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
 		data->state = FAILURE;
-		os_free(msg);
+		wpabuf_free(msg);
 		return NULL;
 	}
 
@@ -340,16 +340,25 @@
 		data->state = FAILURE;
 		return;
 	}
-	eap_sake_derive_keys(sm->user->password,
-			     sm->user->password + EAP_SAKE_ROOT_SECRET_LEN,
-			     data->rand_s, data->rand_p,
-			     (u8 *) &data->tek, data->msk, data->emsk);
+	if (eap_sake_derive_keys(sm->user->password,
+				 sm->user->password + EAP_SAKE_ROOT_SECRET_LEN,
+				 data->rand_s, data->rand_p,
+				 (u8 *) &data->tek, data->msk,
+				 data->emsk) < 0) {
+		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to derive keys");
+		data->state = FAILURE;
+		return;
+	}
 
-	eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
-			     sm->server_id, sm->server_id_len,
-			     data->peerid, data->peerid_len, 1,
-			     wpabuf_head(respData), wpabuf_len(respData),
-			     attr.mic_p, mic_p);
+	if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
+				 sm->server_id, sm->server_id_len,
+				 data->peerid, data->peerid_len, 1,
+				 wpabuf_head(respData), wpabuf_len(respData),
+				 attr.mic_p, mic_p) < 0) {
+		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
+		data->state = FAILURE;
+		return;
+	}
 	if (os_memcmp_const(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
 		wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P");
 		eap_sake_state(data, FAILURE);
@@ -382,11 +391,14 @@
 		return;
 	}
 
-	eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
-			     sm->server_id, sm->server_id_len,
-			     data->peerid, data->peerid_len, 1,
-			     wpabuf_head(respData), wpabuf_len(respData),
-			     attr.mic_p, mic_p);
+	if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
+				 sm->server_id, sm->server_id_len,
+				 data->peerid, data->peerid_len, 1,
+				 wpabuf_head(respData), wpabuf_len(respData),
+				 attr.mic_p, mic_p) < 0) {
+		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
+		return;
+	}
 	if (os_memcmp_const(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
 		wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P");
 		eap_sake_state(data, FAILURE);
diff --git a/src/eap_server/eap_server_sim.c b/src/eap_server/eap_server_sim.c
index 1287827..66a0872 100644
--- a/src/eap_server/eap_server_sim.c
+++ b/src/eap_server/eap_server_sim.c
@@ -26,6 +26,7 @@
 	u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN];
 	u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN];
 	u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN];
+	u8 reauth_mac[EAP_SIM_MAC_LEN];
 	int num_chal;
 	enum {
 		START, CHALLENGE, REAUTH, NOTIFICATION, SUCCESS, FAILURE
@@ -249,6 +250,7 @@
 					    struct eap_sim_data *data, u8 id)
 {
 	struct eap_sim_msg *msg;
+	struct wpabuf *buf;
 
 	wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Re-authentication");
 
@@ -278,7 +280,16 @@
 
 	wpa_printf(MSG_DEBUG, "   AT_MAC");
 	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
-	return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, NULL, 0);
+	buf = eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, NULL, 0);
+
+	/* Remember this MAC before sending it to the peer. This MAC is used for
+	 * Session-Id calculation after receiving response from the peer and
+	 * after all other checks pass. */
+	os_memcpy(data->reauth_mac,
+		  wpabuf_head(buf) + wpabuf_len(buf) - EAP_SIM_MAC_LEN,
+		  EAP_SIM_MAC_LEN);
+
+	return buf;
 }
 
 
@@ -829,15 +840,25 @@
 	if (data->state != SUCCESS)
 		return NULL;
 
-	*len = 1 + data->num_chal * GSM_RAND_LEN + EAP_SIM_NONCE_MT_LEN;
+	if (!data->reauth)
+		*len = 1 + data->num_chal * GSM_RAND_LEN + EAP_SIM_NONCE_MT_LEN;
+	else
+		*len = 1 + EAP_SIM_NONCE_S_LEN + EAP_SIM_MAC_LEN;
 	id = os_malloc(*len);
 	if (id == NULL)
 		return NULL;
 
 	id[0] = EAP_TYPE_SIM;
-	os_memcpy(id + 1, data->rand, data->num_chal * GSM_RAND_LEN);
-	os_memcpy(id + 1 + data->num_chal * GSM_RAND_LEN, data->nonce_mt,
-		  EAP_SIM_NONCE_MT_LEN);
+	if (!data->reauth) {
+		os_memcpy(id + 1, data->rand, data->num_chal * GSM_RAND_LEN);
+		os_memcpy(id + 1 + data->num_chal * GSM_RAND_LEN,
+			  data->nonce_mt, EAP_SIM_NONCE_MT_LEN);
+	} else {
+		os_memcpy(id + 1, data->nonce_s, EAP_SIM_NONCE_S_LEN);
+		os_memcpy(id + 1 + EAP_SIM_NONCE_S_LEN, data->reauth_mac,
+			  EAP_SIM_MAC_LEN);
+
+	}
 	wpa_hexdump(MSG_DEBUG, "EAP-SIM: Derived Session-Id", id, *len);
 
 	return id;
diff --git a/src/eap_server/eap_server_teap.c b/src/eap_server/eap_server_teap.c
new file mode 100644
index 0000000..d8e5414
--- /dev/null
+++ b/src/eap_server/eap_server_teap.c
@@ -0,0 +1,1947 @@
+/*
+ * EAP-TEAP server (RFC 7170)
+ * Copyright (c) 2004-2019, 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/aes_wrap.h"
+#include "crypto/tls.h"
+#include "crypto/random.h"
+#include "eap_common/eap_teap_common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+
+
+static void eap_teap_reset(struct eap_sm *sm, void *priv);
+
+
+/* Private PAC-Opaque TLV types */
+#define PAC_OPAQUE_TYPE_PAD 0
+#define PAC_OPAQUE_TYPE_KEY 1
+#define PAC_OPAQUE_TYPE_LIFETIME 2
+#define PAC_OPAQUE_TYPE_IDENTITY 3
+
+struct eap_teap_data {
+	struct eap_ssl_data ssl;
+	enum {
+		START, PHASE1, PHASE1B, PHASE2_START, PHASE2_ID,
+		PHASE2_BASIC_AUTH, PHASE2_METHOD, CRYPTO_BINDING, REQUEST_PAC,
+		FAILURE_SEND_RESULT, SUCCESS, FAILURE
+	} state;
+
+	u8 teap_version;
+	u8 peer_version;
+	u16 tls_cs;
+
+	const struct eap_method *phase2_method;
+	void *phase2_priv;
+
+	u8 crypto_binding_nonce[32];
+	int final_result;
+
+	u8 simck_msk[EAP_TEAP_SIMCK_LEN];
+	u8 cmk_msk[EAP_TEAP_CMK_LEN];
+	u8 simck_emsk[EAP_TEAP_SIMCK_LEN];
+	u8 cmk_emsk[EAP_TEAP_CMK_LEN];
+	int simck_idx;
+	int cmk_emsk_available;
+
+	u8 pac_opaque_encr[16];
+	u8 *srv_id;
+	size_t srv_id_len;
+	char *srv_id_info;
+
+	int anon_provisioning;
+	int send_new_pac; /* server triggered re-keying of Tunnel PAC */
+	struct wpabuf *pending_phase2_resp;
+	struct wpabuf *server_outer_tlvs;
+	struct wpabuf *peer_outer_tlvs;
+	u8 *identity; /* from PAC-Opaque */
+	size_t identity_len;
+	int eap_seq;
+	int tnc_started;
+
+	int pac_key_lifetime;
+	int pac_key_refresh_time;
+
+	enum teap_error_codes error_code;
+};
+
+
+static int eap_teap_process_phase2_start(struct eap_sm *sm,
+					 struct eap_teap_data *data);
+
+
+static const char * eap_teap_state_txt(int state)
+{
+	switch (state) {
+	case START:
+		return "START";
+	case PHASE1:
+		return "PHASE1";
+	case PHASE1B:
+		return "PHASE1B";
+	case PHASE2_START:
+		return "PHASE2_START";
+	case PHASE2_ID:
+		return "PHASE2_ID";
+	case PHASE2_BASIC_AUTH:
+		return "PHASE2_BASIC_AUTH";
+	case PHASE2_METHOD:
+		return "PHASE2_METHOD";
+	case CRYPTO_BINDING:
+		return "CRYPTO_BINDING";
+	case REQUEST_PAC:
+		return "REQUEST_PAC";
+	case FAILURE_SEND_RESULT:
+		return "FAILURE_SEND_RESULT";
+	case SUCCESS:
+		return "SUCCESS";
+	case FAILURE:
+		return "FAILURE";
+	default:
+		return "Unknown?!";
+	}
+}
+
+
+static void eap_teap_state(struct eap_teap_data *data, int state)
+{
+	wpa_printf(MSG_DEBUG, "EAP-TEAP: %s -> %s",
+		   eap_teap_state_txt(data->state),
+		   eap_teap_state_txt(state));
+	data->state = state;
+}
+
+
+static EapType eap_teap_req_failure(struct eap_teap_data *data,
+				    enum teap_error_codes error)
+{
+	eap_teap_state(data, FAILURE_SEND_RESULT);
+	return EAP_TYPE_NONE;
+}
+
+
+static int eap_teap_session_ticket_cb(void *ctx, const u8 *ticket, size_t len,
+				      const u8 *client_random,
+				      const u8 *server_random,
+				      u8 *master_secret)
+{
+	struct eap_teap_data *data = ctx;
+	const u8 *pac_opaque;
+	size_t pac_opaque_len;
+	u8 *buf, *pos, *end, *pac_key = NULL;
+	os_time_t lifetime = 0;
+	struct os_time now;
+	u8 *identity = NULL;
+	size_t identity_len = 0;
+
+	wpa_printf(MSG_DEBUG, "EAP-TEAP: SessionTicket callback");
+	wpa_hexdump(MSG_DEBUG, "EAP-TEAP: SessionTicket (PAC-Opaque)",
+		    ticket, len);
+
+	if (len < 4 || WPA_GET_BE16(ticket) != PAC_TYPE_PAC_OPAQUE) {
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: Ignore invalid SessionTicket");
+		return 0;
+	}
+
+	pac_opaque_len = WPA_GET_BE16(ticket + 2);
+	pac_opaque = ticket + 4;
+	if (pac_opaque_len < 8 || pac_opaque_len % 8 ||
+	    pac_opaque_len > len - 4) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Ignore invalid PAC-Opaque (len=%lu left=%lu)",
+			   (unsigned long) pac_opaque_len,
+			   (unsigned long) len);
+		return 0;
+	}
+	wpa_hexdump(MSG_DEBUG, "EAP-TEAP: Received PAC-Opaque",
+		    pac_opaque, pac_opaque_len);
+
+	buf = os_malloc(pac_opaque_len - 8);
+	if (!buf) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Failed to allocate memory for decrypting PAC-Opaque");
+		return 0;
+	}
+
+	if (aes_unwrap(data->pac_opaque_encr, sizeof(data->pac_opaque_encr),
+		       (pac_opaque_len - 8) / 8, pac_opaque, buf) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: Failed to decrypt PAC-Opaque");
+		os_free(buf);
+		/*
+		 * This may have been caused by server changing the PAC-Opaque
+		 * encryption key, so just ignore this PAC-Opaque instead of
+		 * failing the authentication completely. Provisioning can now
+		 * be used to provision a new PAC.
+		 */
+		return 0;
+	}
+
+	end = buf + pac_opaque_len - 8;
+	wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Decrypted PAC-Opaque",
+			buf, end - buf);
+
+	pos = buf;
+	while (end - pos > 1) {
+		u8 id, elen;
+
+		id = *pos++;
+		elen = *pos++;
+		if (elen > end - pos)
+			break;
+
+		switch (id) {
+		case PAC_OPAQUE_TYPE_PAD:
+			goto done;
+		case PAC_OPAQUE_TYPE_KEY:
+			if (elen != EAP_TEAP_PAC_KEY_LEN) {
+				wpa_printf(MSG_DEBUG,
+					   "EAP-TEAP: Invalid PAC-Key length %d",
+					   elen);
+				os_free(buf);
+				return -1;
+			}
+			pac_key = pos;
+			wpa_hexdump_key(MSG_DEBUG,
+					"EAP-TEAP: PAC-Key from decrypted PAC-Opaque",
+					pac_key, EAP_TEAP_PAC_KEY_LEN);
+			break;
+		case PAC_OPAQUE_TYPE_LIFETIME:
+			if (elen != 4) {
+				wpa_printf(MSG_DEBUG,
+					   "EAP-TEAP: Invalid PAC-Key lifetime length %d",
+					   elen);
+				os_free(buf);
+				return -1;
+			}
+			lifetime = WPA_GET_BE32(pos);
+			break;
+		case PAC_OPAQUE_TYPE_IDENTITY:
+			identity = pos;
+			identity_len = elen;
+			break;
+		}
+
+		pos += elen;
+	}
+done:
+
+	if (!pac_key) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: No PAC-Key included in PAC-Opaque");
+		os_free(buf);
+		return -1;
+	}
+
+	if (identity) {
+		wpa_hexdump_ascii(MSG_DEBUG,
+				  "EAP-TEAP: Identity from PAC-Opaque",
+				  identity, identity_len);
+		os_free(data->identity);
+		data->identity = os_malloc(identity_len);
+		if (data->identity) {
+			os_memcpy(data->identity, identity, identity_len);
+			data->identity_len = identity_len;
+		}
+	}
+
+	if (os_get_time(&now) < 0 || lifetime <= 0 || now.sec > lifetime) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: PAC-Key not valid anymore (lifetime=%ld now=%ld)",
+			   lifetime, now.sec);
+		data->send_new_pac = 2;
+		/*
+		 * Allow PAC to be used to allow a PAC update with some level
+		 * of server authentication (i.e., do not fall back to full TLS
+		 * handshake since we cannot be sure that the peer would be
+		 * able to validate server certificate now). However, reject
+		 * the authentication since the PAC was not valid anymore. Peer
+		 * can connect again with the newly provisioned PAC after this.
+		 */
+	} else if (lifetime - now.sec < data->pac_key_refresh_time) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: PAC-Key soft timeout; send an update if authentication succeeds");
+		data->send_new_pac = 1;
+	}
+
+	/* EAP-TEAP uses PAC-Key as the TLS master_secret */
+	os_memcpy(master_secret, pac_key, EAP_TEAP_PAC_KEY_LEN);
+
+	os_free(buf);
+
+	return 1;
+}
+
+
+static int eap_teap_derive_key_auth(struct eap_sm *sm,
+				    struct eap_teap_data *data)
+{
+	int res;
+
+	/* RFC 7170, Section 5.1 */
+	res = tls_connection_export_key(sm->ssl_ctx, data->ssl.conn,
+					TEAP_TLS_EXPORTER_LABEL_SKS, NULL, 0,
+					data->simck_msk, EAP_TEAP_SIMCK_LEN);
+	if (res)
+		return res;
+	wpa_hexdump_key(MSG_DEBUG,
+			"EAP-TEAP: session_key_seed (S-IMCK[0])",
+			data->simck_msk, EAP_TEAP_SIMCK_LEN);
+	os_memcpy(data->simck_emsk, data->simck_msk, EAP_TEAP_SIMCK_LEN);
+	data->simck_idx = 0;
+	return 0;
+}
+
+
+static int eap_teap_update_icmk(struct eap_sm *sm, struct eap_teap_data *data)
+{
+	u8 *msk = NULL, *emsk = NULL;
+	size_t msk_len = 0, emsk_len = 0;
+	int res;
+
+	wpa_printf(MSG_DEBUG, "EAP-TEAP: Deriving ICMK[%d] (S-IMCK and CMK)",
+		   data->simck_idx + 1);
+
+	if (sm->eap_teap_auth == 1)
+		return eap_teap_derive_cmk_basic_pw_auth(data->simck_msk,
+							 data->cmk_msk);
+
+	if (!data->phase2_method || !data->phase2_priv) {
+		wpa_printf(MSG_INFO, "EAP-TEAP: Phase 2 method not available");
+		return -1;
+	}
+
+	if (data->phase2_method->getKey) {
+		msk = data->phase2_method->getKey(sm, data->phase2_priv,
+						  &msk_len);
+		if (!msk) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: Could not fetch Phase 2 MSK");
+			return -1;
+		}
+	}
+
+	if (data->phase2_method->get_emsk) {
+		emsk = data->phase2_method->get_emsk(sm, data->phase2_priv,
+						     &emsk_len);
+	}
+
+	res = eap_teap_derive_imck(data->simck_msk, data->simck_emsk,
+				   msk, msk_len, emsk, emsk_len,
+				   data->simck_msk, data->cmk_msk,
+				   data->simck_emsk, data->cmk_emsk);
+	bin_clear_free(msk, msk_len);
+	bin_clear_free(emsk, emsk_len);
+	if (res == 0) {
+		data->simck_idx++;
+		if (emsk)
+			data->cmk_emsk_available = 1;
+	}
+	return 0;
+}
+
+
+static void * eap_teap_init(struct eap_sm *sm)
+{
+	struct eap_teap_data *data;
+
+	data = os_zalloc(sizeof(*data));
+	if (!data)
+		return NULL;
+	data->teap_version = EAP_TEAP_VERSION;
+	data->state = START;
+
+	if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_TEAP)) {
+		wpa_printf(MSG_INFO, "EAP-TEAP: Failed to initialize SSL.");
+		eap_teap_reset(sm, data);
+		return NULL;
+	}
+
+	/* TODO: Add anon-DH TLS cipher suites (and if one is negotiated,
+	 * enforce inner EAP with mutual authentication to be used) */
+
+	if (tls_connection_set_session_ticket_cb(sm->ssl_ctx, data->ssl.conn,
+						 eap_teap_session_ticket_cb,
+						 data) < 0) {
+		wpa_printf(MSG_INFO,
+			   "EAP-TEAP: Failed to set SessionTicket callback");
+		eap_teap_reset(sm, data);
+		return NULL;
+	}
+
+	if (!sm->pac_opaque_encr_key) {
+		wpa_printf(MSG_INFO,
+			   "EAP-TEAP: No PAC-Opaque encryption key configured");
+		eap_teap_reset(sm, data);
+		return NULL;
+	}
+	os_memcpy(data->pac_opaque_encr, sm->pac_opaque_encr_key,
+		  sizeof(data->pac_opaque_encr));
+
+	if (!sm->eap_fast_a_id) {
+		wpa_printf(MSG_INFO, "EAP-TEAP: No A-ID configured");
+		eap_teap_reset(sm, data);
+		return NULL;
+	}
+	data->srv_id = os_malloc(sm->eap_fast_a_id_len);
+	if (!data->srv_id) {
+		eap_teap_reset(sm, data);
+		return NULL;
+	}
+	os_memcpy(data->srv_id, sm->eap_fast_a_id, sm->eap_fast_a_id_len);
+	data->srv_id_len = sm->eap_fast_a_id_len;
+
+	if (!sm->eap_fast_a_id_info) {
+		wpa_printf(MSG_INFO, "EAP-TEAP: No A-ID-Info configured");
+		eap_teap_reset(sm, data);
+		return NULL;
+	}
+	data->srv_id_info = os_strdup(sm->eap_fast_a_id_info);
+	if (!data->srv_id_info) {
+		eap_teap_reset(sm, data);
+		return NULL;
+	}
+
+	/* PAC-Key lifetime in seconds (hard limit) */
+	data->pac_key_lifetime = sm->pac_key_lifetime;
+
+	/*
+	 * PAC-Key refresh time in seconds (soft limit on remaining hard
+	 * limit). The server will generate a new PAC-Key when this number of
+	 * seconds (or fewer) of the lifetime remains.
+	 */
+	data->pac_key_refresh_time = sm->pac_key_refresh_time;
+
+	return data;
+}
+
+
+static void eap_teap_reset(struct eap_sm *sm, void *priv)
+{
+	struct eap_teap_data *data = priv;
+
+	if (!data)
+		return;
+	if (data->phase2_priv && data->phase2_method)
+		data->phase2_method->reset(sm, data->phase2_priv);
+	eap_server_tls_ssl_deinit(sm, &data->ssl);
+	os_free(data->srv_id);
+	os_free(data->srv_id_info);
+	wpabuf_free(data->pending_phase2_resp);
+	wpabuf_free(data->server_outer_tlvs);
+	wpabuf_free(data->peer_outer_tlvs);
+	os_free(data->identity);
+	forced_memzero(data->simck_msk, EAP_TEAP_SIMCK_LEN);
+	forced_memzero(data->simck_emsk, EAP_TEAP_SIMCK_LEN);
+	forced_memzero(data->cmk_msk, EAP_TEAP_CMK_LEN);
+	forced_memzero(data->cmk_emsk, EAP_TEAP_CMK_LEN);
+	forced_memzero(data->pac_opaque_encr, sizeof(data->pac_opaque_encr));
+	bin_clear_free(data, sizeof(*data));
+}
+
+
+static struct wpabuf * eap_teap_build_start(struct eap_sm *sm,
+					    struct eap_teap_data *data, u8 id)
+{
+	struct wpabuf *req;
+	size_t outer_tlv_len = sizeof(struct teap_tlv_hdr) + data->srv_id_len;
+	const u8 *start, *end;
+
+	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TEAP,
+			    1 + 4 + outer_tlv_len, EAP_CODE_REQUEST, id);
+	if (!req) {
+		wpa_printf(MSG_ERROR,
+			   "EAP-TEAP: Failed to allocate memory for request");
+		eap_teap_state(data, FAILURE);
+		return NULL;
+	}
+
+	wpabuf_put_u8(req, EAP_TLS_FLAGS_START | EAP_TEAP_FLAGS_OUTER_TLV_LEN |
+		      data->teap_version);
+	wpabuf_put_be32(req, outer_tlv_len);
+
+	start = wpabuf_put(req, 0);
+
+	/* RFC 7170, Section 4.2.2: Authority-ID TLV */
+	eap_teap_put_tlv(req, TEAP_TLV_AUTHORITY_ID,
+			 data->srv_id, data->srv_id_len);
+
+	end = wpabuf_put(req, 0);
+	wpabuf_free(data->server_outer_tlvs);
+	data->server_outer_tlvs = wpabuf_alloc_copy(start, end - start);
+	if (!data->server_outer_tlvs) {
+		eap_teap_state(data, FAILURE);
+		return NULL;
+	}
+
+	eap_teap_state(data, PHASE1);
+
+	return req;
+}
+
+
+static int eap_teap_phase1_done(struct eap_sm *sm, struct eap_teap_data *data)
+{
+	char cipher[64];
+
+	wpa_printf(MSG_DEBUG, "EAP-TEAP: Phase 1 done, starting Phase 2");
+
+	data->tls_cs = tls_connection_get_cipher_suite(data->ssl.conn);
+	wpa_printf(MSG_DEBUG, "EAP-TEAP: TLS cipher suite 0x%04x",
+		   data->tls_cs);
+
+	if (tls_get_cipher(sm->ssl_ctx, data->ssl.conn, cipher, sizeof(cipher))
+	    < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Failed to get cipher information");
+		eap_teap_state(data, FAILURE);
+		return -1;
+	}
+	data->anon_provisioning = os_strstr(cipher, "ADH") != NULL;
+
+	if (data->anon_provisioning)
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: Anonymous provisioning");
+
+	if (eap_teap_derive_key_auth(sm, data) < 0) {
+		eap_teap_state(data, FAILURE);
+		return -1;
+	}
+
+	eap_teap_state(data, PHASE2_START);
+
+	return 0;
+}
+
+
+static struct wpabuf * eap_teap_build_phase2_req(struct eap_sm *sm,
+						 struct eap_teap_data *data,
+						 u8 id)
+{
+	struct wpabuf *req;
+
+	if (sm->eap_teap_auth == 1) {
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: Initiate Basic-Password-Auth");
+		req = wpabuf_alloc(sizeof(struct teap_tlv_hdr));
+		if (!req)
+			return NULL;
+		eap_teap_put_tlv_hdr(req, TEAP_TLV_BASIC_PASSWORD_AUTH_REQ, 0);
+		return req;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-TEAP: Initiate inner EAP method");
+	if (!data->phase2_priv) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Phase 2 method not initialized");
+		return NULL;
+	}
+
+	req = data->phase2_method->buildReq(sm, data->phase2_priv, id);
+	if (!req)
+		return NULL;
+
+	wpa_hexdump_buf_key(MSG_MSGDUMP, "EAP-TEAP: Phase 2 EAP-Request", req);
+	return eap_teap_tlv_eap_payload(req);
+}
+
+
+static struct wpabuf * eap_teap_build_crypto_binding(
+	struct eap_sm *sm, struct eap_teap_data *data)
+{
+	struct wpabuf *buf;
+	struct teap_tlv_result *result;
+	struct teap_tlv_crypto_binding *cb;
+	u8 subtype, flags;
+
+	buf = wpabuf_alloc(2 * sizeof(*result) + sizeof(*cb));
+	if (!buf)
+		return NULL;
+
+	if (data->send_new_pac || data->anon_provisioning ||
+	    data->phase2_method)
+		data->final_result = 0;
+	else
+		data->final_result = 1;
+
+	if (!data->final_result || data->eap_seq > 0) {
+		/* Intermediate-Result */
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Add Intermediate-Result TLV (status=SUCCESS)");
+		result = wpabuf_put(buf, sizeof(*result));
+		result->tlv_type = host_to_be16(TEAP_TLV_MANDATORY |
+						TEAP_TLV_INTERMEDIATE_RESULT);
+		result->length = host_to_be16(2);
+		result->status = host_to_be16(TEAP_STATUS_SUCCESS);
+	}
+
+	if (data->final_result) {
+		/* Result TLV */
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Add Result TLV (status=SUCCESS)");
+		result = wpabuf_put(buf, sizeof(*result));
+		result->tlv_type = host_to_be16(TEAP_TLV_MANDATORY |
+						TEAP_TLV_RESULT);
+		result->length = host_to_be16(2);
+		result->status = host_to_be16(TEAP_STATUS_SUCCESS);
+	}
+
+	/* Crypto-Binding TLV */
+	cb = wpabuf_put(buf, sizeof(*cb));
+	cb->tlv_type = host_to_be16(TEAP_TLV_MANDATORY |
+				    TEAP_TLV_CRYPTO_BINDING);
+	cb->length = host_to_be16(sizeof(*cb) - sizeof(struct teap_tlv_hdr));
+	cb->version = EAP_TEAP_VERSION;
+	cb->received_version = data->peer_version;
+	/* FIX: RFC 7170 is not clear on which Flags value to use when
+	 * Crypto-Binding TLV is used with Basic-Password-Auth */
+	flags = data->cmk_emsk_available ?
+		TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC :
+		TEAP_CRYPTO_BINDING_MSK_CMAC;
+	subtype = TEAP_CRYPTO_BINDING_SUBTYPE_REQUEST;
+	cb->subtype = (flags << 4) | subtype;
+	if (random_get_bytes(cb->nonce, sizeof(cb->nonce)) < 0) {
+		wpabuf_free(buf);
+		return NULL;
+	}
+
+	/*
+	 * RFC 7170, Section 4.2.13:
+	 * The nonce in a request MUST have its least significant bit set to 0.
+	 */
+	cb->nonce[sizeof(cb->nonce) - 1] &= ~0x01;
+
+	os_memcpy(data->crypto_binding_nonce, cb->nonce, sizeof(cb->nonce));
+
+	if (eap_teap_compound_mac(data->tls_cs, cb, data->server_outer_tlvs,
+				  data->peer_outer_tlvs, data->cmk_msk,
+				  cb->msk_compound_mac) < 0) {
+		wpabuf_free(buf);
+		return NULL;
+	}
+
+	if (data->cmk_emsk_available &&
+	    eap_teap_compound_mac(data->tls_cs, cb, data->server_outer_tlvs,
+				  data->peer_outer_tlvs, data->cmk_emsk,
+				  cb->emsk_compound_mac) < 0) {
+		wpabuf_free(buf);
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "EAP-TEAP: Add Crypto-Binding TLV: Version %u Received Version %u Flags %u Sub-Type %u",
+		   cb->version, cb->received_version, flags, subtype);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Nonce",
+		    cb->nonce, sizeof(cb->nonce));
+	wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: EMSK Compound MAC",
+		    cb->emsk_compound_mac, sizeof(cb->emsk_compound_mac));
+	wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: MSK Compound MAC",
+		    cb->msk_compound_mac, sizeof(cb->msk_compound_mac));
+
+	return buf;
+}
+
+
+static struct wpabuf * eap_teap_build_pac(struct eap_sm *sm,
+					  struct eap_teap_data *data)
+{
+	u8 pac_key[EAP_TEAP_PAC_KEY_LEN];
+	u8 *pac_buf, *pac_opaque;
+	struct wpabuf *buf;
+	u8 *pos;
+	size_t buf_len, srv_id_info_len, pac_len;
+	struct teap_tlv_hdr *pac_tlv;
+	struct pac_attr_hdr *pac_info;
+	struct teap_tlv_result *result;
+	struct os_time now;
+
+	wpa_printf(MSG_DEBUG, "EAP-TEAP: Build a new PAC");
+
+	if (random_get_bytes(pac_key, EAP_TEAP_PAC_KEY_LEN) < 0 ||
+	    os_get_time(&now) < 0)
+		return NULL;
+	wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: Generated PAC-Key",
+			pac_key, EAP_TEAP_PAC_KEY_LEN);
+
+	pac_len = (2 + EAP_TEAP_PAC_KEY_LEN) + (2 + 4) +
+		(2 + sm->identity_len) + 8;
+	pac_buf = os_malloc(pac_len);
+	if (!pac_buf)
+		return NULL;
+
+	srv_id_info_len = os_strlen(data->srv_id_info);
+
+	pos = pac_buf;
+	*pos++ = PAC_OPAQUE_TYPE_KEY;
+	*pos++ = EAP_TEAP_PAC_KEY_LEN;
+	os_memcpy(pos, pac_key, EAP_TEAP_PAC_KEY_LEN);
+	pos += EAP_TEAP_PAC_KEY_LEN;
+
+	wpa_printf(MSG_DEBUG, "EAP-TEAP: PAC-Key lifetime: %u seconds",
+		   data->pac_key_lifetime);
+	*pos++ = PAC_OPAQUE_TYPE_LIFETIME;
+	*pos++ = 4;
+	WPA_PUT_BE32(pos, now.sec + data->pac_key_lifetime);
+	pos += 4;
+
+	if (sm->identity) {
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-TEAP: PAC-Opaque Identity",
+				  sm->identity, sm->identity_len);
+		*pos++ = PAC_OPAQUE_TYPE_IDENTITY;
+		*pos++ = sm->identity_len;
+		os_memcpy(pos, sm->identity, sm->identity_len);
+		pos += sm->identity_len;
+	}
+
+	pac_len = pos - pac_buf;
+	while (pac_len % 8) {
+		*pos++ = PAC_OPAQUE_TYPE_PAD;
+		pac_len++;
+	}
+
+	pac_opaque = os_malloc(pac_len + 8);
+	if (!pac_opaque) {
+		os_free(pac_buf);
+		return NULL;
+	}
+	if (aes_wrap(data->pac_opaque_encr, sizeof(data->pac_opaque_encr),
+		     pac_len / 8, pac_buf, pac_opaque) < 0) {
+		os_free(pac_buf);
+		os_free(pac_opaque);
+		return NULL;
+	}
+	os_free(pac_buf);
+
+	pac_len += 8;
+	wpa_hexdump(MSG_DEBUG, "EAP-TEAP: PAC-Opaque", pac_opaque, pac_len);
+
+	buf_len = sizeof(*pac_tlv) +
+		sizeof(struct pac_attr_hdr) + EAP_TEAP_PAC_KEY_LEN +
+		sizeof(struct pac_attr_hdr) + pac_len +
+		data->srv_id_len + srv_id_info_len + 100 + sizeof(*result);
+	buf = wpabuf_alloc(buf_len);
+	if (!buf) {
+		os_free(pac_opaque);
+		return NULL;
+	}
+
+	/* Result TLV */
+	wpa_printf(MSG_DEBUG, "EAP-TEAP: Add Result TLV (status=SUCCESS)");
+	result = wpabuf_put(buf, sizeof(*result));
+	WPA_PUT_BE16((u8 *) &result->tlv_type,
+		     TEAP_TLV_MANDATORY | TEAP_TLV_RESULT);
+	WPA_PUT_BE16((u8 *) &result->length, 2);
+	WPA_PUT_BE16((u8 *) &result->status, TEAP_STATUS_SUCCESS);
+
+	/* PAC TLV */
+	wpa_printf(MSG_DEBUG, "EAP-TEAP: Add PAC TLV");
+	pac_tlv = wpabuf_put(buf, sizeof(*pac_tlv));
+	pac_tlv->tlv_type = host_to_be16(TEAP_TLV_MANDATORY | TEAP_TLV_PAC);
+
+	/* PAC-Key */
+	eap_teap_put_tlv(buf, PAC_TYPE_PAC_KEY, pac_key, EAP_TEAP_PAC_KEY_LEN);
+
+	/* PAC-Opaque */
+	eap_teap_put_tlv(buf, PAC_TYPE_PAC_OPAQUE, pac_opaque, pac_len);
+	os_free(pac_opaque);
+
+	/* PAC-Info */
+	pac_info = wpabuf_put(buf, sizeof(*pac_info));
+	pac_info->type = host_to_be16(PAC_TYPE_PAC_INFO);
+
+	/* PAC-Lifetime (inside PAC-Info) */
+	eap_teap_put_tlv_hdr(buf, PAC_TYPE_CRED_LIFETIME, 4);
+	wpabuf_put_be32(buf, now.sec + data->pac_key_lifetime);
+
+	/* A-ID (inside PAC-Info) */
+	eap_teap_put_tlv(buf, PAC_TYPE_A_ID, data->srv_id, data->srv_id_len);
+
+	/* Note: headers may be misaligned after A-ID */
+
+	if (sm->identity) {
+		eap_teap_put_tlv(buf, PAC_TYPE_I_ID, sm->identity,
+				 sm->identity_len);
+	}
+
+	/* A-ID-Info (inside PAC-Info) */
+	eap_teap_put_tlv(buf, PAC_TYPE_A_ID_INFO, data->srv_id_info,
+			 srv_id_info_len);
+
+	/* PAC-Type (inside PAC-Info) */
+	eap_teap_put_tlv_hdr(buf, PAC_TYPE_PAC_TYPE, 2);
+	wpabuf_put_be16(buf, PAC_TYPE_TUNNEL_PAC);
+
+	/* Update PAC-Info and PAC TLV Length fields */
+	pos = wpabuf_put(buf, 0);
+	pac_info->len = host_to_be16(pos - (u8 *) (pac_info + 1));
+	pac_tlv->length = host_to_be16(pos - (u8 *) (pac_tlv + 1));
+
+	return buf;
+}
+
+
+static int eap_teap_encrypt_phase2(struct eap_sm *sm,
+				   struct eap_teap_data *data,
+				   struct wpabuf *plain, int piggyback)
+{
+	struct wpabuf *encr;
+
+	wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TEAP: Encrypting Phase 2 TLVs",
+			    plain);
+	encr = eap_server_tls_encrypt(sm, &data->ssl, plain);
+	wpabuf_free(plain);
+
+	if (!encr)
+		return -1;
+
+	if (data->ssl.tls_out && piggyback) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Piggyback Phase 2 data (len=%d) with last Phase 1 Message (len=%d used=%d)",
+			   (int) wpabuf_len(encr),
+			   (int) wpabuf_len(data->ssl.tls_out),
+			   (int) data->ssl.tls_out_pos);
+		if (wpabuf_resize(&data->ssl.tls_out, wpabuf_len(encr)) < 0) {
+			wpa_printf(MSG_WARNING,
+				   "EAP-TEAP: Failed to resize output buffer");
+			wpabuf_free(encr);
+			return -1;
+		}
+		wpabuf_put_buf(data->ssl.tls_out, encr);
+		wpabuf_free(encr);
+	} else {
+		wpabuf_free(data->ssl.tls_out);
+		data->ssl.tls_out_pos = 0;
+		data->ssl.tls_out = encr;
+	}
+
+	return 0;
+}
+
+
+static struct wpabuf * eap_teap_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+	struct eap_teap_data *data = priv;
+	struct wpabuf *req = NULL;
+	int piggyback = 0;
+
+	if (data->ssl.state == FRAG_ACK) {
+		return eap_server_tls_build_ack(id, EAP_TYPE_TEAP,
+						data->teap_version);
+	}
+
+	if (data->ssl.state == WAIT_FRAG_ACK) {
+		return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TEAP,
+						data->teap_version, id);
+	}
+
+	switch (data->state) {
+	case START:
+		return eap_teap_build_start(sm, data, id);
+	case PHASE1B:
+		if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+			if (eap_teap_phase1_done(sm, data) < 0)
+				return NULL;
+			if (data->state == PHASE2_START) {
+				int res;
+
+				/*
+				 * Try to generate Phase 2 data to piggyback
+				 * with the end of Phase 1 to avoid extra
+				 * roundtrip.
+				 */
+				wpa_printf(MSG_DEBUG,
+					   "EAP-TEAP: Try to start Phase 2");
+				res = eap_teap_process_phase2_start(sm, data);
+				if (res == 1) {
+					req = eap_teap_build_crypto_binding(
+						sm, data);
+					piggyback = 1;
+					break;
+				}
+
+				if (res)
+					break;
+				req = eap_teap_build_phase2_req(sm, data, id);
+				piggyback = 1;
+			}
+		}
+		break;
+	case PHASE2_ID:
+	case PHASE2_BASIC_AUTH:
+	case PHASE2_METHOD:
+		req = eap_teap_build_phase2_req(sm, data, id);
+		break;
+	case CRYPTO_BINDING:
+		req = eap_teap_build_crypto_binding(sm, data);
+		if (data->phase2_method) {
+			/*
+			 * Include the start of the next EAP method in the
+			 * sequence in the same message with Crypto-Binding to
+			 * save a round-trip.
+			 */
+			struct wpabuf *eap;
+
+			eap = eap_teap_build_phase2_req(sm, data, id);
+			req = wpabuf_concat(req, eap);
+			eap_teap_state(data, PHASE2_METHOD);
+		}
+		break;
+	case REQUEST_PAC:
+		req = eap_teap_build_pac(sm, data);
+		break;
+	case FAILURE_SEND_RESULT:
+		req = eap_teap_tlv_result(TEAP_STATUS_FAILURE, 0);
+		if (data->error_code)
+			req = wpabuf_concat(
+				req, eap_teap_tlv_error(data->error_code));
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: %s - unexpected state %d",
+			   __func__, data->state);
+		return NULL;
+	}
+
+	if (req && eap_teap_encrypt_phase2(sm, data, req, piggyback) < 0)
+		return NULL;
+
+	return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TEAP,
+					data->teap_version, id);
+}
+
+
+static Boolean eap_teap_check(struct eap_sm *sm, void *priv,
+			      struct wpabuf *respData)
+{
+	const u8 *pos;
+	size_t len;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TEAP, respData, &len);
+	if (!pos || len < 1) {
+		wpa_printf(MSG_INFO, "EAP-TEAP: Invalid frame");
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+static int eap_teap_phase2_init(struct eap_sm *sm, struct eap_teap_data *data,
+				EapType eap_type)
+{
+	if (data->phase2_priv && data->phase2_method) {
+		data->phase2_method->reset(sm, data->phase2_priv);
+		data->phase2_method = NULL;
+		data->phase2_priv = NULL;
+	}
+	data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF,
+							eap_type);
+	if (!data->phase2_method)
+		return -1;
+
+	sm->init_phase2 = 1;
+	data->phase2_priv = data->phase2_method->init(sm);
+	sm->init_phase2 = 0;
+
+	return data->phase2_priv ? 0 : -1;
+}
+
+
+static void eap_teap_process_phase2_response(struct eap_sm *sm,
+					     struct eap_teap_data *data,
+					     u8 *in_data, size_t in_len)
+{
+	u8 next_type = EAP_TYPE_NONE;
+	struct eap_hdr *hdr;
+	u8 *pos;
+	size_t left;
+	struct wpabuf buf;
+	const struct eap_method *m = data->phase2_method;
+	void *priv = data->phase2_priv;
+
+	if (!priv) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: %s - Phase 2 not initialized?!",
+			   __func__);
+		return;
+	}
+
+	hdr = (struct eap_hdr *) in_data;
+	pos = (u8 *) (hdr + 1);
+
+	if (in_len > sizeof(*hdr) && *pos == EAP_TYPE_NAK) {
+		left = in_len - sizeof(*hdr);
+		wpa_hexdump(MSG_DEBUG,
+			    "EAP-TEAP: Phase 2 type Nak'ed; allowed types",
+			    pos + 1, left - 1);
+#ifdef EAP_SERVER_TNC
+		if (m && m->vendor == EAP_VENDOR_IETF &&
+		    m->method == EAP_TYPE_TNC) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Peer Nak'ed required TNC negotiation");
+			next_type = eap_teap_req_failure(data, 0);
+			eap_teap_phase2_init(sm, data, next_type);
+			return;
+		}
+#endif /* EAP_SERVER_TNC */
+		eap_sm_process_nak(sm, pos + 1, left - 1);
+		if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
+		    sm->user->methods[sm->user_eap_method_index].method !=
+		    EAP_TYPE_NONE) {
+			next_type = sm->user->methods[
+				sm->user_eap_method_index++].method;
+			wpa_printf(MSG_DEBUG, "EAP-TEAP: try EAP type %d",
+				   next_type);
+		} else {
+			next_type = eap_teap_req_failure(data, 0);
+		}
+		eap_teap_phase2_init(sm, data, next_type);
+		return;
+	}
+
+	wpabuf_set(&buf, in_data, in_len);
+
+	if (m->check(sm, priv, &buf)) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Phase 2 check() asked to ignore the packet");
+		eap_teap_req_failure(data, TEAP_ERROR_INNER_METHOD);
+		return;
+	}
+
+	m->process(sm, priv, &buf);
+
+	if (!m->isDone(sm, priv))
+		return;
+
+	if (!m->isSuccess(sm, priv)) {
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: Phase 2 method failed");
+		next_type = eap_teap_req_failure(data, TEAP_ERROR_INNER_METHOD);
+		eap_teap_phase2_init(sm, data, next_type);
+		return;
+	}
+
+	switch (data->state) {
+	case PHASE2_ID:
+		if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
+			wpa_hexdump_ascii(MSG_DEBUG,
+					  "EAP-TEAP: Phase 2 Identity not found in the user database",
+					  sm->identity, sm->identity_len);
+			next_type = eap_teap_req_failure(
+				data, TEAP_ERROR_INNER_METHOD);
+			break;
+		}
+
+		eap_teap_state(data, PHASE2_METHOD);
+		if (data->anon_provisioning) {
+			/* TODO: Allow any inner EAP method that provides
+			 * mutual authentication and EMSK derivation (i.e.,
+			 * EAP-pwd or EAP-EKE). */
+			next_type = EAP_TYPE_PWD;
+			sm->user_eap_method_index = 0;
+		} else {
+			next_type = sm->user->methods[0].method;
+			sm->user_eap_method_index = 1;
+		}
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: Try EAP type %d", next_type);
+		break;
+	case PHASE2_METHOD:
+	case CRYPTO_BINDING:
+		eap_teap_update_icmk(sm, data);
+		eap_teap_state(data, CRYPTO_BINDING);
+		data->eap_seq++;
+		next_type = EAP_TYPE_NONE;
+#ifdef EAP_SERVER_TNC
+		if (sm->tnc && !data->tnc_started) {
+			wpa_printf(MSG_DEBUG, "EAP-TEAP: Initialize TNC");
+			next_type = EAP_TYPE_TNC;
+			data->tnc_started = 1;
+		}
+#endif /* EAP_SERVER_TNC */
+		break;
+	case FAILURE:
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: %s - unexpected state %d",
+			   __func__, data->state);
+		break;
+	}
+
+	eap_teap_phase2_init(sm, data, next_type);
+}
+
+
+static void eap_teap_process_phase2_eap(struct eap_sm *sm,
+					struct eap_teap_data *data,
+					u8 *in_data, size_t in_len)
+{
+	struct eap_hdr *hdr;
+	size_t len;
+
+	hdr = (struct eap_hdr *) in_data;
+	if (in_len < (int) sizeof(*hdr)) {
+		wpa_printf(MSG_INFO,
+			   "EAP-TEAP: Too short Phase 2 EAP frame (len=%lu)",
+			   (unsigned long) in_len);
+		eap_teap_req_failure(data, TEAP_ERROR_INNER_METHOD);
+		return;
+	}
+	len = be_to_host16(hdr->length);
+	if (len > in_len) {
+		wpa_printf(MSG_INFO,
+			   "EAP-TEAP: Length mismatch in Phase 2 EAP frame (len=%lu hdr->length=%lu)",
+			   (unsigned long) in_len, (unsigned long) len);
+		eap_teap_req_failure(data, TEAP_ERROR_INNER_METHOD);
+		return;
+	}
+	wpa_printf(MSG_DEBUG,
+		   "EAP-TEAP: Received Phase 2: code=%d identifier=%d length=%lu",
+		   hdr->code, hdr->identifier,
+		   (unsigned long) len);
+	switch (hdr->code) {
+	case EAP_CODE_RESPONSE:
+		eap_teap_process_phase2_response(sm, data, (u8 *) hdr, len);
+		break;
+	default:
+		wpa_printf(MSG_INFO,
+			   "EAP-TEAP: Unexpected code=%d in Phase 2 EAP header",
+			   hdr->code);
+		break;
+	}
+}
+
+
+static void eap_teap_process_basic_auth_resp(struct eap_sm *sm,
+					     struct eap_teap_data *data,
+					     u8 *in_data, size_t in_len)
+{
+	u8 *pos, *end, *username, *password, *new_id;
+	u8 userlen, passlen;
+
+	pos = in_data;
+	end = pos + in_len;
+
+	if (end - pos < 1) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: No room for Basic-Password-Auth-Resp Userlen field");
+		eap_teap_req_failure(data, 0);
+		return;
+	}
+	userlen = *pos++;
+	if (end - pos < userlen) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Truncated Basic-Password-Auth-Resp Username field");
+		eap_teap_req_failure(data, 0);
+		return;
+	}
+	username = pos;
+	pos += userlen;
+	wpa_hexdump_ascii(MSG_DEBUG,
+			  "EAP-TEAP: Basic-Password-Auth-Resp Username",
+			  username, userlen);
+
+	if (end - pos < 1) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: No room for Basic-Password-Auth-Resp Passlen field");
+		eap_teap_req_failure(data, 0);
+		return;
+	}
+	passlen = *pos++;
+	if (end - pos < passlen) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Truncated Basic-Password-Auth-Resp Password field");
+		eap_teap_req_failure(data, 0);
+		return;
+	}
+	password = pos;
+	pos += passlen;
+	wpa_hexdump_ascii_key(MSG_DEBUG,
+			      "EAP-TEAP: Basic-Password-Auth-Resp Password",
+			      password, passlen);
+
+	if (end > pos) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Unexpected %d extra octet(s) at the end of Basic-Password-Auth-Resp TLV",
+			   (int) (end - pos));
+		eap_teap_req_failure(data, 0);
+		return;
+	}
+
+	if (eap_user_get(sm, username, userlen, 1) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Username not found in the user database");
+		eap_teap_req_failure(data, 0);
+		return;
+	}
+
+	if (!sm->user || !sm->user->password || sm->user->password_hash) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: No plaintext user password configured");
+		eap_teap_req_failure(data, 0);
+		return;
+	}
+
+	if (sm->user->password_len != passlen ||
+	    os_memcmp_const(sm->user->password, password, passlen) != 0) {
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: Invalid password");
+		eap_teap_req_failure(data, 0);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-TEAP: Correct password");
+	new_id = os_memdup(username, userlen);
+	if (new_id) {
+		os_free(sm->identity);
+		sm->identity = new_id;
+		sm->identity_len = userlen;
+	}
+	eap_teap_state(data, CRYPTO_BINDING);
+	eap_teap_update_icmk(sm, data);
+}
+
+
+static int eap_teap_parse_tlvs(struct wpabuf *data,
+			       struct eap_teap_tlv_parse *tlv)
+{
+	u16 tlv_type;
+	int mandatory, res;
+	size_t len;
+	u8 *pos, *end;
+
+	os_memset(tlv, 0, sizeof(*tlv));
+
+	pos = wpabuf_mhead(data);
+	end = pos + wpabuf_len(data);
+	while (end - pos > 4) {
+		mandatory = pos[0] & 0x80;
+		tlv_type = WPA_GET_BE16(pos) & 0x3fff;
+		pos += 2;
+		len = WPA_GET_BE16(pos);
+		pos += 2;
+		if (len > (size_t) (end - pos)) {
+			wpa_printf(MSG_INFO, "EAP-TEAP: TLV overflow");
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Received Phase 2: TLV type %u (%s) length %u%s",
+			   tlv_type, eap_teap_tlv_type_str(tlv_type),
+			   (unsigned int) len,
+			   mandatory ? " (mandatory)" : "");
+
+		res = eap_teap_parse_tlv(tlv, tlv_type, pos, len);
+		if (res == -2)
+			break;
+		if (res < 0) {
+			if (mandatory) {
+				wpa_printf(MSG_DEBUG,
+					   "EAP-TEAP: NAK unknown mandatory TLV type %u",
+					   tlv_type);
+				/* TODO: generate NAK TLV */
+				break;
+			}
+
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Ignore unknown optional TLV type %u",
+				   tlv_type);
+		}
+
+		pos += len;
+	}
+
+	return 0;
+}
+
+
+static int eap_teap_validate_crypto_binding(
+	struct eap_teap_data *data, const struct teap_tlv_crypto_binding *cb,
+	size_t bind_len)
+{
+	u8 flags, subtype;
+
+	subtype = cb->subtype & 0x0f;
+	flags = cb->subtype >> 4;
+
+	wpa_printf(MSG_DEBUG,
+		   "EAP-TEAP: Reply Crypto-Binding TLV: Version %u Received Version %u Flags %u Sub-Type %u",
+		   cb->version, cb->received_version, flags, subtype);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Nonce",
+		    cb->nonce, sizeof(cb->nonce));
+	wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: EMSK Compound MAC",
+		    cb->emsk_compound_mac, sizeof(cb->emsk_compound_mac));
+	wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: MSK Compound MAC",
+		    cb->msk_compound_mac, sizeof(cb->msk_compound_mac));
+
+	if (cb->version != EAP_TEAP_VERSION ||
+	    cb->received_version != data->peer_version) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Unexpected version in Crypto-Binding: Version %u Received Version %u",
+			   cb->version, cb->received_version);
+		return -1;
+	}
+
+	if (flags < 1 || flags > 3) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Unexpected Flags in Crypto-Binding: %u",
+			   flags);
+		return -1;
+	}
+
+	if (subtype != TEAP_CRYPTO_BINDING_SUBTYPE_RESPONSE) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Unexpected Sub-Type in Crypto-Binding: %u",
+			   subtype);
+		return -1;
+	}
+
+	if (os_memcmp_const(data->crypto_binding_nonce, cb->nonce,
+			    EAP_TEAP_NONCE_LEN - 1) != 0 ||
+	    (data->crypto_binding_nonce[EAP_TEAP_NONCE_LEN - 1] | 1) !=
+	    cb->nonce[EAP_TEAP_NONCE_LEN - 1]) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Invalid Nonce in Crypto-Binding");
+		return -1;
+	}
+
+	if (flags == TEAP_CRYPTO_BINDING_MSK_CMAC ||
+	    flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC) {
+		u8 msk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN];
+
+		if (eap_teap_compound_mac(data->tls_cs, cb,
+					  data->server_outer_tlvs,
+					  data->peer_outer_tlvs, data->cmk_msk,
+					  msk_compound_mac) < 0)
+			return -1;
+		if (os_memcmp_const(msk_compound_mac, cb->msk_compound_mac,
+				    EAP_TEAP_COMPOUND_MAC_LEN) != 0) {
+			wpa_hexdump(MSG_DEBUG,
+				    "EAP-TEAP: Calculated MSK Compound MAC",
+				    msk_compound_mac,
+				    EAP_TEAP_COMPOUND_MAC_LEN);
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: MSK Compound MAC did not match");
+			return -1;
+		}
+	}
+
+	if ((flags == TEAP_CRYPTO_BINDING_EMSK_CMAC ||
+	     flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC) &&
+	    data->cmk_emsk_available) {
+		u8 emsk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN];
+
+		if (eap_teap_compound_mac(data->tls_cs, cb,
+					  data->server_outer_tlvs,
+					  data->peer_outer_tlvs, data->cmk_emsk,
+					  emsk_compound_mac) < 0)
+			return -1;
+		if (os_memcmp_const(emsk_compound_mac, cb->emsk_compound_mac,
+				    EAP_TEAP_COMPOUND_MAC_LEN) != 0) {
+			wpa_hexdump(MSG_DEBUG,
+				    "EAP-TEAP: Calculated EMSK Compound MAC",
+				    emsk_compound_mac,
+				    EAP_TEAP_COMPOUND_MAC_LEN);
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: EMSK Compound MAC did not match");
+			return -1;
+		}
+	}
+
+	if (flags == TEAP_CRYPTO_BINDING_EMSK_CMAC &&
+	    !data->cmk_emsk_available) {
+		wpa_printf(MSG_INFO,
+			   "EAP-TEAP: Peer included only EMSK Compound MAC, but no locally generated inner EAP EMSK to validate this");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int eap_teap_pac_type(u8 *pac, size_t len, u16 type)
+{
+	struct teap_attr_pac_type *tlv;
+
+	if (!pac || len != sizeof(*tlv))
+		return 0;
+
+	tlv = (struct teap_attr_pac_type *) pac;
+
+	return be_to_host16(tlv->type) == PAC_TYPE_PAC_TYPE &&
+		be_to_host16(tlv->length) == 2 &&
+		be_to_host16(tlv->pac_type) == type;
+}
+
+
+static void eap_teap_process_phase2_tlvs(struct eap_sm *sm,
+					 struct eap_teap_data *data,
+					 struct wpabuf *in_data)
+{
+	struct eap_teap_tlv_parse tlv;
+	int check_crypto_binding = data->state == CRYPTO_BINDING;
+
+	if (eap_teap_parse_tlvs(in_data, &tlv) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Failed to parse received Phase 2 TLVs");
+		return;
+	}
+
+	if (tlv.result == TEAP_STATUS_FAILURE) {
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: Result TLV indicated failure");
+		eap_teap_state(data, FAILURE);
+		return;
+	}
+
+	if (tlv.nak) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Peer NAK'ed Vendor-Id %u NAK-Type %u",
+			   WPA_GET_BE32(tlv.nak), WPA_GET_BE16(tlv.nak + 4));
+		eap_teap_state(data, FAILURE_SEND_RESULT);
+		return;
+	}
+
+	if (data->state == REQUEST_PAC) {
+		u16 type, len, res;
+
+		if (!tlv.pac || tlv.pac_len < 6) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: No PAC Acknowledgement received");
+			eap_teap_state(data, FAILURE);
+			return;
+		}
+
+		type = WPA_GET_BE16(tlv.pac);
+		len = WPA_GET_BE16(tlv.pac + 2);
+		res = WPA_GET_BE16(tlv.pac + 4);
+
+		if (type != PAC_TYPE_PAC_ACKNOWLEDGEMENT || len != 2 ||
+		    res != TEAP_STATUS_SUCCESS) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: PAC TLV did not contain acknowledgement");
+			eap_teap_state(data, FAILURE);
+			return;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: PAC-Acknowledgement received - PAC provisioning succeeded");
+		eap_teap_state(data, SUCCESS);
+		return;
+	}
+
+	if (check_crypto_binding) {
+		if (!tlv.crypto_binding) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: No Crypto-Binding TLV received");
+			eap_teap_state(data, FAILURE);
+			return;
+		}
+
+		if (data->final_result &&
+		    tlv.result != TEAP_STATUS_SUCCESS) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Crypto-Binding TLV without Success Result");
+			eap_teap_state(data, FAILURE);
+			return;
+		}
+
+		if (!data->final_result &&
+		    tlv.iresult != TEAP_STATUS_SUCCESS) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Crypto-Binding TLV without intermediate Success Result");
+			eap_teap_state(data, FAILURE);
+			return;
+		}
+
+		if (eap_teap_validate_crypto_binding(data, tlv.crypto_binding,
+						     tlv.crypto_binding_len)) {
+			eap_teap_state(data, FAILURE);
+			return;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Valid Crypto-Binding TLV received");
+		if (data->final_result) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Authentication completed successfully");
+		}
+
+		if (data->anon_provisioning &&
+		    sm->eap_fast_prov != ANON_PROV &&
+		    sm->eap_fast_prov != BOTH_PROV) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Client is trying to use unauthenticated provisioning which is disabled");
+			eap_teap_state(data, FAILURE);
+			return;
+		}
+
+		if (sm->eap_fast_prov != AUTH_PROV &&
+		    sm->eap_fast_prov != BOTH_PROV &&
+		    tlv.request_action == TEAP_REQUEST_ACTION_PROCESS_TLV &&
+		    eap_teap_pac_type(tlv.pac, tlv.pac_len,
+				      PAC_TYPE_TUNNEL_PAC)) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Client is trying to use authenticated provisioning which is disabled");
+			eap_teap_state(data, FAILURE);
+			return;
+		}
+
+		if (data->anon_provisioning ||
+		    (tlv.request_action == TEAP_REQUEST_ACTION_PROCESS_TLV &&
+		     eap_teap_pac_type(tlv.pac, tlv.pac_len,
+				       PAC_TYPE_TUNNEL_PAC))) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Requested a new Tunnel PAC");
+			eap_teap_state(data, REQUEST_PAC);
+		} else if (data->send_new_pac) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Server triggered re-keying of Tunnel PAC");
+			eap_teap_state(data, REQUEST_PAC);
+		} else if (data->final_result)
+			eap_teap_state(data, SUCCESS);
+	}
+
+	if (tlv.basic_auth_resp) {
+		if (sm->eap_teap_auth != 1) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Unexpected Basic-Password-Auth-Resp when trying to use inner EAP");
+			eap_teap_state(data, FAILURE);
+			return;
+		}
+		eap_teap_process_basic_auth_resp(sm, data, tlv.basic_auth_resp,
+						 tlv.basic_auth_resp_len);
+	}
+
+	if (tlv.eap_payload_tlv) {
+		if (sm->eap_teap_auth == 1) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Unexpected EAP Payload TLV when trying to use Basic-Password-Auth");
+			eap_teap_state(data, FAILURE);
+			return;
+		}
+		eap_teap_process_phase2_eap(sm, data, tlv.eap_payload_tlv,
+					    tlv.eap_payload_tlv_len);
+	}
+}
+
+
+static void eap_teap_process_phase2(struct eap_sm *sm,
+				    struct eap_teap_data *data,
+				    struct wpabuf *in_buf)
+{
+	struct wpabuf *in_decrypted;
+
+	wpa_printf(MSG_DEBUG,
+		   "EAP-TEAP: Received %lu bytes encrypted data for Phase 2",
+		   (unsigned long) wpabuf_len(in_buf));
+
+	if (data->pending_phase2_resp) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Pending Phase 2 response - skip decryption and use old data");
+		eap_teap_process_phase2_tlvs(sm, data,
+					     data->pending_phase2_resp);
+		wpabuf_free(data->pending_phase2_resp);
+		data->pending_phase2_resp = NULL;
+		return;
+	}
+
+	in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn,
+					      in_buf);
+	if (!in_decrypted) {
+		wpa_printf(MSG_INFO,
+			   "EAP-TEAP: Failed to decrypt Phase 2 data");
+		eap_teap_state(data, FAILURE);
+		return;
+	}
+
+	wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TEAP: Decrypted Phase 2 TLVs",
+			    in_decrypted);
+
+	eap_teap_process_phase2_tlvs(sm, data, in_decrypted);
+
+	if (sm->method_pending == METHOD_PENDING_WAIT) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Phase 2 method is in pending wait state - save decrypted response");
+		wpabuf_free(data->pending_phase2_resp);
+		data->pending_phase2_resp = in_decrypted;
+		return;
+	}
+
+	wpabuf_free(in_decrypted);
+}
+
+
+static int eap_teap_process_version(struct eap_sm *sm, void *priv,
+				    int peer_version)
+{
+	struct eap_teap_data *data = priv;
+
+	if (peer_version < 1) {
+		/* Version 1 was the first defined version, so reject 0 */
+		wpa_printf(MSG_INFO,
+			   "EAP-TEAP: Peer used unknown TEAP version %u",
+			   peer_version);
+		return -1;
+	}
+
+	if (peer_version < data->teap_version) {
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: peer ver=%u, own ver=%u; "
+			   "use version %u",
+			   peer_version, data->teap_version, peer_version);
+		data->teap_version = peer_version;
+	}
+
+	data->peer_version = peer_version;
+
+	return 0;
+}
+
+
+static int eap_teap_process_phase1(struct eap_sm *sm,
+				   struct eap_teap_data *data)
+{
+	if (eap_server_tls_phase1(sm, &data->ssl) < 0) {
+		wpa_printf(MSG_INFO, "EAP-TEAP: TLS processing failed");
+		eap_teap_state(data, FAILURE);
+		return -1;
+	}
+
+	if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) ||
+	    wpabuf_len(data->ssl.tls_out) > 0)
+		return 1;
+
+	/*
+	 * Phase 1 was completed with the received message (e.g., when using
+	 * abbreviated handshake), so Phase 2 can be started immediately
+	 * without having to send through an empty message to the peer.
+	 */
+
+	return eap_teap_phase1_done(sm, data);
+}
+
+
+static int eap_teap_process_phase2_start(struct eap_sm *sm,
+					 struct eap_teap_data *data)
+{
+	u8 next_type;
+
+	if (data->identity) {
+		/* Used PAC and identity is from PAC-Opaque */
+		os_free(sm->identity);
+		sm->identity = data->identity;
+		data->identity = NULL;
+		sm->identity_len = data->identity_len;
+		data->identity_len = 0;
+		if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
+			wpa_hexdump_ascii(MSG_DEBUG,
+					  "EAP-TEAP: Phase 2 Identity not found in the user database",
+					  sm->identity, sm->identity_len);
+			next_type = EAP_TYPE_NONE;
+			eap_teap_state(data, PHASE2_METHOD);
+		} else if (sm->eap_teap_pac_no_inner) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Used PAC and identity already known - skip inner auth");
+			/* FIX: Need to derive CMK here. However, how is that
+			 * supposed to be done? RFC 7170 does not tell that for
+			 * the no-inner-auth case. */
+			eap_teap_derive_cmk_basic_pw_auth(data->simck_msk,
+							  data->cmk_msk);
+			eap_teap_state(data, CRYPTO_BINDING);
+			return 1;
+		} else if (sm->eap_teap_auth == 1) {
+			eap_teap_state(data, PHASE2_BASIC_AUTH);
+			return 1;
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TEAP: Identity already known - skip Phase 2 Identity Request");
+			next_type = sm->user->methods[0].method;
+			sm->user_eap_method_index = 1;
+			eap_teap_state(data, PHASE2_METHOD);
+		}
+
+	} else if (sm->eap_teap_auth == 1) {
+		eap_teap_state(data, PHASE2_BASIC_AUTH);
+		return 0;
+	} else {
+		eap_teap_state(data, PHASE2_ID);
+		next_type = EAP_TYPE_IDENTITY;
+	}
+
+	return eap_teap_phase2_init(sm, data, next_type);
+}
+
+
+static void eap_teap_process_msg(struct eap_sm *sm, void *priv,
+				 const struct wpabuf *respData)
+{
+	struct eap_teap_data *data = priv;
+
+	switch (data->state) {
+	case PHASE1:
+	case PHASE1B:
+		if (eap_teap_process_phase1(sm, data))
+			break;
+
+		/* fall through */
+	case PHASE2_START:
+		eap_teap_process_phase2_start(sm, data);
+		break;
+	case PHASE2_ID:
+	case PHASE2_BASIC_AUTH:
+	case PHASE2_METHOD:
+	case CRYPTO_BINDING:
+	case REQUEST_PAC:
+		eap_teap_process_phase2(sm, data, data->ssl.tls_in);
+		break;
+	case FAILURE_SEND_RESULT:
+		/* Protected failure result indication completed. Ignore the
+		 * received message (which is supposed to include Result TLV
+		 * indicating failure) and terminate exchange with cleartext
+		 * EAP-Failure. */
+		eap_teap_state(data, FAILURE);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-TEAP: Unexpected state %d in %s",
+			   data->state, __func__);
+		break;
+	}
+}
+
+
+static void eap_teap_process(struct eap_sm *sm, void *priv,
+			     struct wpabuf *respData)
+{
+	struct eap_teap_data *data = priv;
+	const u8 *pos;
+	size_t len;
+	struct wpabuf *resp = respData;
+	u8 flags;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TEAP, respData, &len);
+	if (!pos || len < 1)
+		return;
+
+	flags = *pos++;
+	len--;
+
+	if (flags & EAP_TEAP_FLAGS_OUTER_TLV_LEN) {
+		/* Extract Outer TLVs from the message before common TLS
+		 * processing */
+		u32 message_len = 0, outer_tlv_len;
+		const u8 *hdr;
+
+		if (data->state != PHASE1) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: Unexpected Outer TLVs in a message that is not the first message from the peer");
+			return;
+		}
+
+		if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
+			if (len < 4) {
+				wpa_printf(MSG_INFO,
+					   "EAP-TEAP: Too short message to include Message Length field");
+				return;
+			}
+
+			message_len = WPA_GET_BE32(pos);
+			pos += 4;
+			len -= 4;
+			if (message_len < 4) {
+				wpa_printf(MSG_INFO,
+					   "EAP-TEAP: Message Length field has too msall value to include Outer TLV Length field");
+				return;
+			}
+		}
+
+		if (len < 4) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: Too short message to include Outer TLVs Length field");
+			return;
+		}
+
+		outer_tlv_len = WPA_GET_BE32(pos);
+		pos += 4;
+		len -= 4;
+
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TEAP: Message Length %u Outer TLV Length %u",
+			  message_len, outer_tlv_len);
+		if (len < outer_tlv_len) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: Too short message to include Outer TLVs field");
+			return;
+		}
+
+		if (message_len &&
+		    (message_len < outer_tlv_len ||
+		     message_len < 4 + outer_tlv_len)) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: Message Length field has too small value to include Outer TLVs");
+			return;
+		}
+
+		if (wpabuf_len(respData) < 4 + outer_tlv_len ||
+		    len < outer_tlv_len)
+			return;
+		resp = wpabuf_alloc(wpabuf_len(respData) - 4 - outer_tlv_len);
+		if (!resp)
+			return;
+		hdr = wpabuf_head(respData);
+		wpabuf_put_u8(resp, *hdr++); /* Code */
+		wpabuf_put_u8(resp, *hdr++); /* Identifier */
+		wpabuf_put_be16(resp, WPA_GET_BE16(hdr) - 4 - outer_tlv_len);
+		hdr += 2;
+		wpabuf_put_u8(resp, *hdr++); /* Type */
+		/* Flags | Ver */
+		wpabuf_put_u8(resp, flags & ~EAP_TEAP_FLAGS_OUTER_TLV_LEN);
+
+		if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED)
+			wpabuf_put_be32(resp, message_len - 4 - outer_tlv_len);
+
+		wpabuf_put_data(resp, pos, len - outer_tlv_len);
+		pos += len - outer_tlv_len;
+		wpabuf_free(data->peer_outer_tlvs);
+		data->peer_outer_tlvs = wpabuf_alloc_copy(pos, outer_tlv_len);
+		if (!data->peer_outer_tlvs)
+			return;
+		wpa_hexdump_buf(MSG_DEBUG, "EAP-TEAP: Outer TLVs",
+				data->peer_outer_tlvs);
+
+		wpa_hexdump_buf(MSG_DEBUG,
+				"EAP-TEAP: TLS Data message after Outer TLV removal",
+				resp);
+		pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TEAP, resp,
+				       &len);
+		if (!pos || len < 1) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TEAP: Invalid frame after Outer TLV removal");
+			return;
+		}
+	}
+
+	if (data->state == PHASE1)
+		eap_teap_state(data, PHASE1B);
+
+	if (eap_server_tls_process(sm, &data->ssl, resp, data,
+				   EAP_TYPE_TEAP, eap_teap_process_version,
+				   eap_teap_process_msg) < 0)
+		eap_teap_state(data, FAILURE);
+
+	if (resp != respData)
+		wpabuf_free(resp);
+}
+
+
+static Boolean eap_teap_isDone(struct eap_sm *sm, void *priv)
+{
+	struct eap_teap_data *data = priv;
+
+	return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_teap_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_teap_data *data = priv;
+	u8 *eapKeyData;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	eapKeyData = os_malloc(EAP_TEAP_KEY_LEN);
+	if (!eapKeyData)
+		return NULL;
+
+	/* FIX: RFC 7170 does not describe whether MSK or EMSK based S-IMCK[j]
+	 * is used in this derivation */
+	if (eap_teap_derive_eap_msk(data->simck_msk, eapKeyData) < 0) {
+		os_free(eapKeyData);
+		return NULL;
+	}
+	*len = EAP_TEAP_KEY_LEN;
+
+	return eapKeyData;
+}
+
+
+static u8 * eap_teap_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_teap_data *data = priv;
+	u8 *eapKeyData;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	eapKeyData = os_malloc(EAP_EMSK_LEN);
+	if (!eapKeyData)
+		return NULL;
+
+	/* FIX: RFC 7170 does not describe whether MSK or EMSK based S-IMCK[j]
+	 * is used in this derivation */
+	if (eap_teap_derive_eap_emsk(data->simck_msk, eapKeyData) < 0) {
+		os_free(eapKeyData);
+		return NULL;
+	}
+	*len = EAP_EMSK_LEN;
+
+	return eapKeyData;
+}
+
+
+static Boolean eap_teap_isSuccess(struct eap_sm *sm, void *priv)
+{
+	struct eap_teap_data *data = priv;
+
+	return data->state == SUCCESS;
+}
+
+
+static u8 * eap_teap_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_teap_data *data = priv;
+	const size_t max_id_len = 100;
+	int res;
+	u8 *id;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	id = os_malloc(max_id_len);
+	if (!id)
+		return NULL;
+
+	id[0] = EAP_TYPE_TEAP;
+	res = tls_get_tls_unique(data->ssl.conn, id + 1, max_id_len - 1);
+	if (res < 0) {
+		os_free(id);
+		wpa_printf(MSG_ERROR, "EAP-TEAP: Failed to derive Session-Id");
+		return NULL;
+	}
+
+	*len = 1 + res;
+	wpa_hexdump(MSG_DEBUG, "EAP-TEAP: Derived Session-Id", id, *len);
+	return id;
+}
+
+
+int eap_server_teap_register(void)
+{
+	struct eap_method *eap;
+
+	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+				      EAP_VENDOR_IETF, EAP_TYPE_TEAP, "TEAP");
+	if (!eap)
+		return -1;
+
+	eap->init = eap_teap_init;
+	eap->reset = eap_teap_reset;
+	eap->buildReq = eap_teap_buildReq;
+	eap->check = eap_teap_check;
+	eap->process = eap_teap_process;
+	eap->isDone = eap_teap_isDone;
+	eap->getKey = eap_teap_getKey;
+	eap->get_emsk = eap_teap_get_emsk;
+	eap->isSuccess = eap_teap_isSuccess;
+	eap->getSessionId = eap_teap_get_session_id;
+
+	return eap_server_method_register(eap);
+}
diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c
index 357e72a..0712d4c 100644
--- a/src/eap_server/eap_server_tls.c
+++ b/src/eap_server/eap_server_tls.c
@@ -261,8 +261,43 @@
 			   "handshake message");
 		return;
 	}
-	if (eap_server_tls_phase1(sm, &data->ssl) < 0)
+	if (eap_server_tls_phase1(sm, &data->ssl) < 0) {
 		eap_tls_state(data, FAILURE);
+		return;
+	}
+
+	if (data->ssl.tls_v13 &&
+	    tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+		struct wpabuf *plain, *encr;
+
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TLS: Send empty application data to indicate end of exchange");
+		/* FIX: This should be an empty application data based on
+		 * draft-ietf-emu-eap-tls13-05, but OpenSSL does not allow zero
+		 * length payload (SSL_write() documentation explicitly
+		 * describes this as not allowed), so work around that for now
+		 * by sending out a payload of one octet. Hopefully the draft
+		 * specification will change to allow this so that no crypto
+		 * library changes are needed. */
+		plain = wpabuf_alloc(1);
+		if (!plain)
+			return;
+		wpabuf_put_u8(plain, 0);
+		encr = eap_server_tls_encrypt(sm, &data->ssl, plain);
+		wpabuf_free(plain);
+		if (!encr)
+			return;
+		if (wpabuf_resize(&data->ssl.tls_out, wpabuf_len(encr)) < 0) {
+			wpa_printf(MSG_INFO,
+				   "EAP-TLS: Failed to resize output buffer");
+			wpabuf_free(encr);
+			return;
+		}
+		wpabuf_put_buf(data->ssl.tls_out, encr);
+		wpa_hexdump_buf(MSG_DEBUG,
+				"EAP-TLS: Data appended to the message", encr);
+		wpabuf_free(encr);
+	}
 }
 
 
@@ -322,16 +357,22 @@
 	struct eap_tls_data *data = priv;
 	u8 *eapKeyData;
 	const char *label;
+	const u8 eap_tls13_context[] = { EAP_TYPE_TLS };
+	const u8 *context = NULL;
+	size_t context_len = 0;
 
 	if (data->state != SUCCESS)
 		return NULL;
 
-	if (data->ssl.tls_v13)
+	if (data->ssl.tls_v13) {
 		label = "EXPORTER_EAP_TLS_Key_Material";
-	else
+		context = eap_tls13_context;
+		context_len = 1;
+	} else {
 		label = "client EAP encryption";
+	}
 	eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label,
-					       NULL, 0,
+					       context, context_len,
 					       EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
 	if (eapKeyData) {
 		*len = EAP_TLS_KEY_LEN;
@@ -351,16 +392,22 @@
 	struct eap_tls_data *data = priv;
 	u8 *eapKeyData, *emsk;
 	const char *label;
+	const u8 eap_tls13_context[] = { EAP_TYPE_TLS };
+	const u8 *context = NULL;
+	size_t context_len = 0;
 
 	if (data->state != SUCCESS)
 		return NULL;
 
-	if (data->ssl.tls_v13)
+	if (data->ssl.tls_v13) {
 		label = "EXPORTER_EAP_TLS_Key_Material";
-	else
+		context = eap_tls13_context;
+		context_len = 1;
+	} else {
 		label = "client EAP encryption";
+	}
 	eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label,
-					       NULL, 0,
+					       context, context_len,
 					       EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
 	if (eapKeyData) {
 		emsk = os_malloc(EAP_EMSK_LEN);
diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c
index 0eca0ff..907101c 100644
--- a/src/eap_server/eap_server_tls_common.c
+++ b/src/eap_server/eap_server_tls_common.c
@@ -145,20 +145,21 @@
 {
 	struct tls_random keys;
 	u8 *out;
+	const u8 context[] = { EAP_TYPE_TLS };
 
 	if (eap_type == EAP_TYPE_TLS && data->tls_v13) {
 		u8 *id, *method_id;
 
 		/* Session-Id = <EAP-Type> || Method-Id
 		 * Method-Id = TLS-Exporter("EXPORTER_EAP_TLS_Method-Id",
-		 *                          "", 64)
+		 *                          Type-Code, 64)
 		 */
 		*len = 1 + 64;
 		id = os_malloc(*len);
 		if (!id)
 			return NULL;
 		method_id = eap_server_tls_derive_key(
-			sm, data, "EXPORTER_EAP_TLS_Method-Id", NULL, 0, 64);
+			sm, data, "EXPORTER_EAP_TLS_Method-Id", context, 1, 64);
 		if (!method_id) {
 			os_free(id);
 			return NULL;
@@ -373,6 +374,8 @@
 	unsigned int tls_msg_len = 0;
 	const u8 *end = *pos + *left;
 
+	wpa_hexdump(MSG_MSGDUMP, "SSL: Received data", *pos, *left);
+
 	if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
 		if (*left < 4) {
 			wpa_printf(MSG_INFO, "SSL: Short frame with TLS "
diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h
index 0b04983..74b1c72 100644
--- a/src/eap_server/eap_tls_common.h
+++ b/src/eap_server/eap_tls_common.h
@@ -62,6 +62,7 @@
 #define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80
 #define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40
 #define EAP_TLS_FLAGS_START 0x20
+#define EAP_TEAP_FLAGS_OUTER_TLV_LEN 0x10
 #define EAP_TLS_VERSION_MASK 0x07
 
  /* could be up to 128 bytes, but only the first 64 bytes are used */