Переглянути джерело

Merge branch 'feature/support_eap_fast_release_v4.2' into 'release/v4.2'

Feature/support eap fast release v4.2

See merge request espressif/esp-idf!16142
Jiang Jiang Jian 4 роки тому
батько
коміт
33a6effd3e
42 змінених файлів з 4646 додано та 137 видалено
  1. 5 0
      components/wpa_supplicant/CMakeLists.txt
  2. 35 0
      components/wpa_supplicant/include/esp_supplicant/esp_wpa2.h
  3. 1 0
      components/wpa_supplicant/include/utils/wpabuf.h
  4. 26 0
      components/wpa_supplicant/port/include/os.h
  5. 0 9
      components/wpa_supplicant/src/common/sae.c
  6. 22 0
      components/wpa_supplicant/src/crypto/crypto.h
  7. 2 1
      components/wpa_supplicant/src/crypto/ms_funcs.c
  8. 1 1
      components/wpa_supplicant/src/crypto/ms_funcs.h
  9. 72 0
      components/wpa_supplicant/src/crypto/sha1-tprf.c
  10. 19 2
      components/wpa_supplicant/src/crypto/tls_mbedtls.c
  11. 230 1
      components/wpa_supplicant/src/eap_peer/eap.c
  12. 10 0
      components/wpa_supplicant/src/eap_peer/eap.h
  13. 112 0
      components/wpa_supplicant/src/eap_peer/eap_config.h
  14. 1807 0
      components/wpa_supplicant/src/eap_peer/eap_fast.c
  15. 270 0
      components/wpa_supplicant/src/eap_peer/eap_fast_common.c
  16. 107 0
      components/wpa_supplicant/src/eap_peer/eap_fast_common.h
  17. 932 0
      components/wpa_supplicant/src/eap_peer/eap_fast_pac.c
  18. 50 0
      components/wpa_supplicant/src/eap_peer/eap_fast_pac.h
  19. 11 1
      components/wpa_supplicant/src/eap_peer/eap_i.h
  20. 1 0
      components/wpa_supplicant/src/eap_peer/eap_methods.h
  21. 280 80
      components/wpa_supplicant/src/eap_peer/eap_mschapv2.c
  22. 17 13
      components/wpa_supplicant/src/eap_peer/eap_tls.c
  23. 5 1
      components/wpa_supplicant/src/eap_peer/eap_tls_common.c
  24. 39 13
      components/wpa_supplicant/src/eap_peer/mschapv2.c
  25. 62 0
      components/wpa_supplicant/src/esp_supplicant/esp_wpa2.c
  26. 1 1
      components/wpa_supplicant/src/rsn_supp/wpa.c
  27. 37 5
      components/wpa_supplicant/src/tls/tls.h
  28. 27 9
      components/wpa_supplicant/src/tls/tls_internal.c
  29. 9 0
      components/wpa_supplicant/src/utils/wpabuf.c
  30. 9 0
      examples/wifi/wifi_eap_fast/CMakeLists.txt
  31. 8 0
      examples/wifi/wifi_eap_fast/Makefile
  32. 67 0
      examples/wifi/wifi_eap_fast/README.md
  33. 4 0
      examples/wifi/wifi_eap_fast/main/CMakeLists.txt
  34. 33 0
      examples/wifi/wifi_eap_fast/main/Kconfig.projbuild
  35. 23 0
      examples/wifi/wifi_eap_fast/main/ca.pem
  36. 9 0
      examples/wifi/wifi_eap_fast/main/component.mk
  37. 0 0
      examples/wifi/wifi_eap_fast/main/pac_file.pac
  38. 70 0
      examples/wifi/wifi_eap_fast/main/server.crt
  39. 27 0
      examples/wifi/wifi_eap_fast/main/server.key
  40. 57 0
      examples/wifi/wifi_eap_fast/main/server.pem
  41. 148 0
      examples/wifi/wifi_eap_fast/main/wifi_eap_fast_main.c
  42. 1 0
      examples/wifi/wifi_eap_fast/sdkconfig.defaults

+ 5 - 0
components/wpa_supplicant/CMakeLists.txt

@@ -40,6 +40,7 @@ set(srcs "port/os_xtensa.c"
     "src/crypto/sha1-internal.c"
     "src/crypto/sha1-pbkdf2.c"
     "src/crypto/sha1.c"
+    "src/crypto/sha1-tprf.c"
     "src/crypto/sha256-internal.c"
     "src/crypto/sha384-internal.c"
     "src/crypto/sha512-internal.c"
@@ -64,6 +65,9 @@ set(srcs "port/os_xtensa.c"
     "src/esp_supplicant/esp_wpas_glue.c"
     "src/esp_supplicant/esp_wps.c"
     "src/esp_supplicant/esp_wpa3.c"
+    "src/eap_peer/eap_fast.c"
+    "src/eap_peer/eap_fast_common.c"
+    "src/eap_peer/eap_fast_pac.c"
     "src/rsn_supp/pmksa_cache.c"
     "src/rsn_supp/wpa.c"
     "src/rsn_supp/wpa_ie.c"
@@ -123,6 +127,7 @@ target_compile_definitions(${COMPONENT_LIB} PRIVATE
     EAP_TTLS
     EAP_TLS
     EAP_PEAP
+    EAP_FAST
     USE_WPA2_TASK
     CONFIG_WPS2
     CONFIG_WPS_PIN

+ 35 - 0
components/wpa_supplicant/include/esp_supplicant/esp_wpa2.h

@@ -19,6 +19,12 @@
 
 #include "esp_err.h"
 
+typedef struct {
+   int fast_provisioning;
+   int fast_max_pac_list_len;
+   bool fast_pac_format_binary;
+} esp_eap_fast_config;
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -191,6 +197,35 @@ esp_err_t esp_wifi_sta_wpa2_ent_set_disable_time_check(bool disable);
   */
 esp_err_t esp_wifi_sta_wpa2_ent_get_disable_time_check(bool *disable);
 
+/**
+  * @brief  Set client pac file
+  *
+  * @attention  1. For files read from the file system, length has to be decremented by 1 byte.
+  * @attention  2. Disabling the WPA_MBEDTLS_CRYPTO config is required to use EAP-FAST.
+  *
+  * @param  pac_file: pointer to the pac file
+  *         pac_file_len: length of the pac file
+  *
+  * @return
+  *    - ESP_OK: succeed
+  *    - ESP_ERR_NO_MEM: fail(internal memory malloc fail)
+  */
+esp_err_t esp_wifi_sta_wpa2_ent_set_pac_file(const unsigned char *pac_file, int pac_file_len);
+
+/**
+  * @brief  Set Phase 1 parameters for EAP-FAST
+  *
+  * @attention  1. Disabling the WPA_MBEDTLS_CRYPTO config is required to use EAP-FAST.
+  *
+  * @param  config: eap fast phase 1 configuration
+  *
+  * @return
+  *    - ESP_OK: succeed
+  *    - ESP_ERR_INVALID_ARG: fail(out of bound arguments)
+  *    - ESP_ERR_NO_MEM: fail(internal memory malloc fail)
+  */
+esp_err_t esp_wifi_sta_wpa2_ent_set_fast_phase1_params(esp_eap_fast_config config);
+
 #ifdef __cplusplus
 }
 #endif

+ 1 - 0
components/wpa_supplicant/include/utils/wpabuf.h

@@ -35,6 +35,7 @@ struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len);
 struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len);
 struct wpabuf * wpabuf_dup(const struct wpabuf *src);
 void wpabuf_free(struct wpabuf *buf);
+void wpabuf_clear_free(struct wpabuf *buf);
 void * wpabuf_put(struct wpabuf *buf, size_t len);
 struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b);
 struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len);

+ 26 - 0
components/wpa_supplicant/port/include/os.h

@@ -281,6 +281,9 @@ char * ets_strdup(const char *s);
 #ifndef os_strlcpy
 #define os_strlcpy(d, s, n) strlcpy((d), (s), (n))
 #endif
+#ifndef os_strcat
+#define os_strcat(d, s) strcat((d), (s))
+#endif
 
 #ifndef os_snprintf
 #ifdef _MSC_VER
@@ -289,9 +292,32 @@ char * ets_strdup(const char *s);
 #define os_snprintf snprintf
 #endif
 #endif
+#ifndef os_sprintf
+#define os_sprintf sprintf
+#endif
 
 static inline int os_snprintf_error(size_t size, int res)
 {
         return res < 0 || (unsigned int) res >= size;
 }
+
+/* Try to prevent most compilers from optimizing out clearing of memory that
+ * becomes unaccessible after this function is called. This is mostly the case
+ * for clearing local stack variables at the end of a function. This is not
+ * exactly perfect, i.e., someone could come up with a compiler that figures out
+ * the pointer is pointing to memset and then end up optimizing the call out, so
+ * try go a bit further by storing the first octet (now zero) to make this even
+ * a bit more difficult to optimize out. Once memset_s() is available, that
+ * could be used here instead. */
+static void * (* const volatile memset_func)(void *, int, size_t) = memset;
+static uint8_t forced_memzero_val;
+
+static inline void forced_memzero(void *ptr, size_t len)
+{
+	memset_func(ptr, 0, len);
+	if (len) {
+		forced_memzero_val = ((uint8_t *) ptr)[0];
+	}
+}
+
 #endif /* OS_H */

+ 0 - 9
components/wpa_supplicant/src/common/sae.c

@@ -18,15 +18,6 @@
 #include "sae.h"
 #include "esp_wifi_crypto_types.h"
 
-/*TBD Move the this api to proper files once they are taken out of lib*/
-void wpabuf_clear_free(struct wpabuf *buf)
-{
-    if (buf) {
-        os_memset(wpabuf_mhead(buf), 0, wpabuf_len(buf));
-        wpabuf_free(buf);
-    }
-}
-
 int sae_set_group(struct sae_data *sae, int group)
 {
 	struct sae_temporary_data *tmp;

+ 22 - 0
components/wpa_supplicant/src/crypto/crypto.h

@@ -76,6 +76,28 @@ int md5_vector_non_fips_allow(size_t num_elem, const u8 *addr[],
 int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len,
 		u8 *mac);
 
+/**
+ * sha384_vector - SHA384 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac);
+
+/**
+ * sha512_vector - SHA512 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac);
+
 /**
  * fips186_2-prf - NIST FIPS Publication 186-2 change notice 1 PRF
  * @seed: Seed/key for the PRF

+ 2 - 1
components/wpa_supplicant/src/crypto/ms_funcs.c

@@ -491,11 +491,12 @@ int new_password_encrypted_with_old_nt_password_hash(
  * @block: 16-octet Block (IN)
  * @cypher: 16-octer Cypher (OUT)
  */
-void nt_password_hash_encrypted_with_block(const u8 *password_hash,
+int nt_password_hash_encrypted_with_block(const u8 *password_hash,
 				      const u8 *block, u8 *cypher)
 {
 	des_encrypt(password_hash, block, cypher);
 	des_encrypt(password_hash + 8, block + 7, cypher + 8);
+	return 0;
 }
 
 

+ 1 - 1
components/wpa_supplicant/src/crypto/ms_funcs.h

@@ -50,7 +50,7 @@ int __must_check new_password_encrypted_with_old_nt_password_hash(
 	const u8 *new_password, size_t new_password_len,
 	const u8 *old_password, size_t old_password_len,
 	u8 *encrypted_pw_block);
-void nt_password_hash_encrypted_with_block(const u8 *password_hash,
+int nt_password_hash_encrypted_with_block(const u8 *password_hash,
 					   const u8 *block, u8 *cypher);
 int old_nt_password_hash_encrypted_with_new_nt_password_hash(
 	const u8 *new_password, size_t new_password_len,

+ 72 - 0
components/wpa_supplicant/src/crypto/sha1-tprf.c

@@ -0,0 +1,72 @@
+/*
+ * SHA1 T-PRF for EAP-FAST
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "sha1.h"
+#include "crypto.h"
+
+/**
+ * sha1_t_prf - EAP-FAST Pseudo-Random Function (T-PRF)
+ * @key: Key for PRF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success, -1 of failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key for EAP-FAST. T-PRF is defined in RFC 4851, Section 5.5.
+ */
+int sha1_t_prf(const u8 *key, size_t key_len, const char *label,
+	       const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len)
+{
+	unsigned char counter = 0;
+	size_t pos, plen;
+	u8 hash[SHA1_MAC_LEN];
+	size_t label_len = os_strlen(label);
+	u8 output_len[2];
+	const unsigned char *addr[5];
+	size_t len[5];
+
+	addr[0] = hash;
+	len[0] = 0;
+	addr[1] = (unsigned char *) label;
+	len[1] = label_len + 1;
+	addr[2] = seed;
+	len[2] = seed_len;
+	addr[3] = output_len;
+	len[3] = 2;
+	addr[4] = &counter;
+	len[4] = 1;
+
+	output_len[0] = (buf_len >> 8) & 0xff;
+	output_len[1] = buf_len & 0xff;
+	pos = 0;
+	while (pos < buf_len) {
+		counter++;
+		plen = buf_len - pos;
+		if (hmac_sha1_vector(key, key_len, 5, addr, len, hash))
+			return -1;
+		if (plen >= SHA1_MAC_LEN) {
+			os_memcpy(&buf[pos], hash, SHA1_MAC_LEN);
+			pos += SHA1_MAC_LEN;
+		} else {
+			os_memcpy(&buf[pos], hash, plen);
+			break;
+		}
+		len[0] = SHA1_MAC_LEN;
+	}
+
+	forced_memzero(hash, SHA1_MAC_LEN);
+
+	return 0;
+}

+ 19 - 2
components/wpa_supplicant/src/crypto/tls_mbedtls.c

@@ -541,7 +541,8 @@ int tls_global_set_verify(void *tls_ctx, int check_crl)
 }
 
 int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
-			      int verify_peer)
+			      int verify_peer, unsigned int flags,
+			      const u8 *session_ctx, size_t session_ctx_len)
 {
 	wpa_printf(MSG_INFO, "TLS: tls_connection_set_verify not supported");
 	return -1;
@@ -818,11 +819,27 @@ static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
 }
 
 int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
-			      const char *label, u8 *out, size_t out_len)
+			      const char *label, const u8 *context,
+			      size_t context_len, u8 *out, size_t out_len)
 {
 	return tls_connection_prf(tls_ctx, conn, label, 0, out, out_len);
 }
 
+int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
+				    u8 *out, size_t out_len)
+{
+	wpa_printf(MSG_INFO, "TLS: tls_connection_get_eap_fast_key not supported, please unset mbedtls crypto and try again");
+	return -1;
+}
+
+int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
+				    int ext_type, const u8 *data,
+				    size_t data_len)
+{
+	wpa_printf(MSG_INFO, "TLS: tls_connection_client_hello_ext not supported, please unset mbedtls crypto and try again");
+	return -1;
+}
+
 int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
 {
 	if (conn->tls_io_data.in_data) {

+ 230 - 1
components/wpa_supplicant/src/eap_peer/eap.c

@@ -210,6 +210,13 @@ int eap_peer_register_methods(void)
 		ret = eap_peer_mschapv2_register();
 #endif
 
+#ifndef USE_MBEDTLS_CRYPTO
+#ifdef EAP_FAST
+	if (ret == 0)
+		ret = eap_peer_fast_register();
+#endif
+#endif
+
 #ifdef EAP_PEAP
 	if (ret == 0)
 		ret = eap_peer_peap_register();
@@ -232,6 +239,88 @@ void eap_deinit_prev_method(struct eap_sm *sm, const char *txt)
 	sm->m = NULL;
 }
 
+static int eap_sm_set_scard_pin(struct eap_sm *sm,
+				struct eap_peer_config *conf)
+{
+	return -1;
+}
+
+static int eap_sm_get_scard_identity(struct eap_sm *sm,
+				     struct eap_peer_config *conf)
+{
+	return -1;
+}
+
+/**
+ * eap_sm_buildIdentity - Build EAP-Identity/Response for the current network
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @id: EAP identifier for the packet
+ * @encrypted: Whether the packet is for encrypted tunnel (EAP phase 2)
+ * Returns: Pointer to the allocated EAP-Identity/Response packet or %NULL on
+ * failure
+ *
+ * This function allocates and builds an EAP-Identity/Response packet for the
+ * current network. The caller is responsible for freeing the returned data.
+ */
+struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted)
+{
+	struct eap_peer_config *config = eap_get_config(sm);
+	struct wpabuf *resp;
+	const u8 *identity;
+	size_t identity_len;
+
+	if (config == NULL) {
+		wpa_printf(MSG_ERROR, "EAP: buildIdentity: configuration "
+			   "was not available");
+		return NULL;
+	}
+
+	if (sm->m && sm->m->get_identity &&
+	    (identity = sm->m->get_identity(sm, sm->eap_method_priv,
+					    &identity_len)) != NULL) {
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP: using method re-auth "
+				  "identity", identity, identity_len);
+	} else if (!encrypted && config->anonymous_identity) {
+		identity = config->anonymous_identity;
+		identity_len = config->anonymous_identity_len;
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP: using anonymous identity",
+				  identity, identity_len);
+	} else {
+		identity = config->identity;
+		identity_len = config->identity_len;
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP: using real identity",
+				  identity, identity_len);
+	}
+
+	if (identity == NULL) {
+		wpa_printf(MSG_ERROR, "EAP: buildIdentity: identity "
+			   "configuration was not available");
+		if (config->pcsc) {
+			if (eap_sm_get_scard_identity(sm, config) < 0)
+				return NULL;
+			identity = config->identity;
+			identity_len = config->identity_len;
+			wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from "
+					  "IMSI", identity, identity_len);
+		} else {
+			eap_sm_request_identity(sm);
+			return NULL;
+		}
+	} else if (config->pcsc) {
+		if (eap_sm_set_scard_pin(sm, config) < 0)
+			return NULL;
+	}
+
+	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, identity_len,
+			     EAP_CODE_RESPONSE, id);
+	if (resp == NULL)
+		return NULL;
+
+	wpabuf_put_data(resp, identity, identity_len);
+
+	return resp;
+}
+
 struct wpabuf * eap_sm_build_identity_resp(struct eap_sm *sm, u8 id, int encrypted)
 {
 	const u8 *identity;
@@ -348,11 +437,12 @@ int eap_peer_config_init(
 	sm->config.client_cert = (u8 *)sm->blob[0].name;
 	sm->config.private_key = (u8 *)sm->blob[1].name;
 	sm->config.ca_cert = (u8 *)sm->blob[2].name;
-
 	sm->config.ca_path = NULL;
 
 	sm->config.fragment_size = 1400; /* fragment size */
 
+	sm->config.pac_file = (char *) "blob://";
+
 	/* anonymous identity */
 	if (g_wpa_anonymous_identity && g_wpa_anonymous_identity_len > 0) {
 	    sm->config.anonymous_identity_len = g_wpa_anonymous_identity_len;
@@ -389,6 +479,10 @@ int eap_peer_config_init(
 			  sm->config.new_password_len);
 	}
 
+	/* To be used only for EAP-FAST */
+	if (g_wpa_phase1_options) {
+		sm->config.phase1 = g_wpa_phase1_options;
+	}
 	return 0;
 	
 }
@@ -445,6 +539,17 @@ int eap_peer_blob_init(struct eap_sm *sm)
 		sm->blob[2].data = g_wpa_ca_cert;
 	}
 
+	if (g_wpa_pac_file && g_wpa_pac_file_len) {
+		sm->blob[3].name = (char *)os_zalloc(sizeof(char) * 8);
+		if (sm->blob[3].name == NULL) {
+			ret = -2;
+			goto _out;
+		}
+		os_strncpy(sm->blob[3].name, "blob://", 8);
+		sm->blob[3].len = g_wpa_pac_file_len;
+		sm->blob[3].data = g_wpa_pac_file;
+	}
+
 	return 0;
 _out:
 	for (i = 0; i < BLOB_NUM; i++) {
@@ -458,6 +563,95 @@ _out:
 	return ret;
 }
 
+#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)
+{
+	struct eap_peer_config *config;
+
+	if (sm == NULL)
+		return;
+	config = eap_get_config(sm);
+	if (config == NULL)
+		return;
+
+	switch (field) {
+	case WPA_CTRL_REQ_EAP_IDENTITY:
+		config->pending_req_identity++;
+		break;
+	case WPA_CTRL_REQ_EAP_PASSWORD:
+		config->pending_req_password++;
+		break;
+	case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
+		config->pending_req_new_password++;
+		break;
+	case WPA_CTRL_REQ_EAP_PIN:
+		config->pending_req_pin++;
+		break;
+	case WPA_CTRL_REQ_EAP_PASSPHRASE:
+		config->pending_req_passphrase++;
+		break;
+	default:
+		return;
+	}
+
+}
+#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)
+{
+	if (sm->m == NULL)
+		return "UNKNOWN";
+	return sm->m->name;
+}
+
+/**
+ * eap_sm_request_identity - Request identity from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * EAP methods can call this function to request identity information for the
+ * current network. This is normally called when the identity is not included
+ * in the network configuration. The request will be sent to monitor programs
+ * through the control interface.
+ */
+void eap_sm_request_identity(struct eap_sm *sm)
+{
+	eap_sm_request(sm, WPA_CTRL_REQ_EAP_IDENTITY, NULL, 0);
+}
+
+
+/**
+ * eap_sm_request_password - Request password from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * EAP methods can call this function to request password information for the
+ * current network. This is normally called when the password is not included
+ * in the network configuration. The request will be sent to monitor programs
+ * through the control interface.
+ */
+void eap_sm_request_password(struct eap_sm *sm)
+{
+	eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSWORD, NULL, 0);
+}
+
+
+/**
+ * eap_sm_request_new_password - Request new password from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * EAP methods can call this function to request new password information for
+ * the current network. This is normally called when the EAP method indicates
+ * that the current password has expired and password change is required. The
+ * request will be sent to monitor programs through the control interface.
+ */
+void eap_sm_request_new_password(struct eap_sm *sm)
+{
+	eap_sm_request(sm, WPA_CTRL_REQ_EAP_NEW_PASSWORD, NULL, 0);
+}
+
+
 void eap_peer_blob_deinit(struct eap_sm *sm)
 {
 	int i;
@@ -472,6 +666,7 @@ void eap_peer_blob_deinit(struct eap_sm *sm)
 	sm->config.client_cert = NULL;
 	sm->config.private_key = NULL;
 	sm->config.ca_cert = NULL;
+	sm->config.pac_file = NULL;
 }
 
 void eap_sm_abort(struct eap_sm *sm)
@@ -533,6 +728,40 @@ const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len)
 	*len = config->new_password_len;
 	return config->new_password;
 }
+
+
+static int eap_copy_buf(u8 **dst, size_t *dst_len,
+			     const u8 *src, size_t src_len)
+{
+	if (src) {
+		*dst = os_memdup(src, src_len);
+		if (*dst == NULL)
+			return -1;
+		*dst_len = src_len;
+	}
+	return 0;
+}
+
+
+/**
+ * eap_set_config_blob - Set or add a named configuration blob
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @blob: New value for the blob
+ *
+ * Adds a new configuration blob or replaces the current value of an existing
+ * blob.
+ */
+void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob)
+{
+    if (!sm)
+        return;
+
+    if (eap_copy_buf((u8 **)&sm->blob[3].data, (size_t *)&sm->blob[3].len, blob->data, blob->len) < 0) {
+		wpa_printf(MSG_ERROR, "EAP: Set config blob: Unable to modify the configuration blob");
+    }
+}
+
+
 /**
  * eap_get_config_blob - Get a named configuration blob
  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()

+ 10 - 0
components/wpa_supplicant/src/eap_peer/eap.h

@@ -39,9 +39,16 @@ int g_wpa_password_len;
 u8 *g_wpa_new_password;
 int g_wpa_new_password_len;
 
+u8 *g_wpa_pac_file;
+int g_wpa_pac_file_len;
+
+char *g_wpa_ttls_phase2_type;
+char *g_wpa_phase1_options;
+
 const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len);
 void eap_deinit_prev_method(struct eap_sm *sm, const char *txt);
 struct wpabuf * eap_sm_build_nak(struct eap_sm *sm, EapType type, u8 id);
+struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted);
 int eap_peer_blob_init(struct eap_sm *sm);
 void eap_peer_blob_deinit(struct eap_sm *sm);
 int eap_peer_config_init(
@@ -50,5 +57,8 @@ int eap_peer_config_init(
 void eap_peer_config_deinit(struct eap_sm *sm);
 void eap_sm_abort(struct eap_sm *sm);
 int eap_peer_register_methods(void);
+void eap_sm_request_identity(struct eap_sm *sm);
+void eap_sm_request_password(struct eap_sm *sm);
+void eap_sm_request_new_password(struct eap_sm *sm);
 
 #endif /* EAP_H */

+ 112 - 0
components/wpa_supplicant/src/eap_peer/eap_config.h

@@ -166,6 +166,17 @@ struct eap_peer_config {
 
 	char *phase2;
 
+	/**
+	 * pcsc - Parameters for PC/SC smartcard interface for USIM and GSM SIM
+	 *
+	 * This field is used to configure PC/SC smartcard interface.
+	 * Currently, the only configuration is whether this field is %NULL (do
+	 * not use PC/SC) or non-NULL (e.g., "") to enable PC/SC.
+	 *
+	 * This field is used for EAP-SIM and EAP-AKA.
+	 */
+	char *pcsc;
+
 	/**
 	 * pin - PIN for USIM, GSM SIM, and smartcards
 	 *
@@ -177,6 +188,87 @@ struct eap_peer_config {
 	 */
 	char *pin;
 
+	/**
+	 * pending_req_identity - Whether there is a pending identity request
+	 *
+	 * This field should not be set in configuration step. It is only used
+	 * internally when control interface is used to request needed
+	 * information.
+	 */
+	int pending_req_identity;
+
+	/**
+	 * pending_req_password - Whether there is a pending password request
+	 *
+	 * This field should not be set in configuration step. It is only used
+	 * internally when control interface is used to request needed
+	 * information.
+	 */
+	int pending_req_password;
+
+	/**
+	 * pending_req_pin - Whether there is a pending PIN request
+	 *
+	 * This field should not be set in configuration step. It is only used
+	 * internally when control interface is used to request needed
+	 * information.
+	 */
+	int pending_req_pin;
+
+	/**
+	 * pending_req_new_password - Pending password update request
+	 *
+	 * This field should not be set in configuration step. It is only used
+	 * internally when control interface is used to request needed
+	 * information.
+	 */
+	int pending_req_new_password;
+
+	/**
+	 * pending_req_passphrase - Pending passphrase request
+	 *
+	 * This field should not be set in configuration step. It is only used
+	 * internally when control interface is used to request needed
+	 * information.
+	 */
+	int pending_req_passphrase;
+
+	/**
+	 * pending_req_sim - Pending SIM request
+	 *
+	 * This field should not be set in configuration step. It is only used
+	 * internally when control interface is used to request needed
+	 * information.
+	 */
+	int pending_req_sim;
+
+	/**
+	 * pending_req_otp - Whether there is a pending OTP request
+	 *
+	 * This field should not be set in configuration step. It is only used
+	 * internally when control interface is used to request needed
+	 * information.
+	 */
+	char *pending_req_otp;
+
+	/**
+	 * pac_file - File path or blob name for the PAC entries (EAP-FAST)
+	 *
+	 * wpa_supplicant will need to be able to create this file and write
+	 * updates to it when PAC is being provisioned or refreshed. Full path
+	 * to the file should be used since working directory may change when
+	 * wpa_supplicant is run in the background.
+	 * Alternatively, a named configuration blob can be used by setting
+	 * this to blob://blob_name.
+	 */
+	char *pac_file;
+
+	/**
+	 * mschapv2_retry - MSCHAPv2 retry in progress
+	 *
+	 * This field is used internally by EAP-MSCHAPv2 and should not be set
+	 * as part of configuration.
+	 */
 	int mschapv2_retry;
 	u8 *new_password;
 	size_t new_password_len;
@@ -214,6 +306,26 @@ struct eap_peer_config {
 	 * 2 = require valid OCSP stapling response
 	 */
 	int ocsp;
+
+	/**
+	 * 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;
+
 };
 
 

+ 1807 - 0
components/wpa_supplicant/src/eap_peer/eap_fast.c

@@ -0,0 +1,1807 @@
+/*
+ * EAP peer method: EAP-FAST (RFC 4851)
+ * 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.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "tls/tls.h"
+#include "crypto/sha1.h"
+#include "eap_peer/eap_tlv_common.h"
+#include "eap_peer/eap_methods.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_config.h"
+#include "eap_fast_pac.h"
+
+#include "eap_fast_pac.c"
+
+/* TODO:
+ * - test session resumption and enable it if it interoperates
+ * - password change (pending mschapv2 packet; replay decrypted packet)
+ */
+
+
+static void eap_fast_deinit(struct eap_sm *sm, void *priv);
+
+
+struct eap_fast_data {
+	struct eap_ssl_data ssl;
+
+	int fast_version;
+
+	const struct eap_method *phase2_method;
+	void *phase2_priv;
+	int phase2_success;
+
+	struct eap_method_type phase2_type;
+	struct eap_method_type *phase2_types;
+	size_t num_phase2_types;
+	int resuming; /* starting a resumed session */
+	struct eap_fast_key_block_provisioning *key_block_p;
+#define EAP_FAST_PROV_UNAUTH 1
+#define EAP_FAST_PROV_AUTH 2
+	int provisioning_allowed; /* Allowed PAC provisioning modes */
+	int provisioning; /* doing PAC provisioning (not the normal auth) */
+	int anon_provisioning; /* doing anonymous (unauthenticated)
+				* provisioning */
+	int session_ticket_used;
+
+	u8 key_data[EAP_FAST_KEY_LEN];
+	u8 *session_id;
+	size_t id_len;
+	u8 emsk[EAP_EMSK_LEN];
+	int success;
+
+	struct eap_fast_pac *pac;
+	struct eap_fast_pac *current_pac;
+	size_t max_pac_list_len;
+	int use_pac_binary_format;
+
+	u8 simck[EAP_FAST_SIMCK_LEN];
+	int simck_idx;
+
+	struct wpabuf *pending_phase2_req;
+	struct wpabuf *pending_resp;
+};
+
+
+static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len,
+				      const u8 *client_random,
+				      const u8 *server_random,
+				      u8 *master_secret)
+{
+	struct eap_fast_data *data = ctx;
+
+	wpa_printf(MSG_DEBUG, "EAP-FAST: SessionTicket callback");
+
+	if (client_random == NULL || server_random == NULL ||
+	    master_secret == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: SessionTicket failed - fall "
+			   "back to full TLS handshake");
+		data->session_ticket_used = 0;
+		if (data->provisioning_allowed) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Try to provision a "
+				   "new PAC-Key");
+			data->provisioning = 1;
+			data->current_pac = NULL;
+		}
+		return 0;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "EAP-FAST: SessionTicket", ticket, len);
+
+	if (data->current_pac == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC-Key available for "
+			   "using SessionTicket");
+		data->session_ticket_used = 0;
+		return 0;
+	}
+
+	eap_fast_derive_master_secret(data->current_pac->pac_key,
+				      server_random, client_random,
+				      master_secret);
+
+	data->session_ticket_used = 1;
+
+	return 1;
+}
+
+
+static void eap_fast_parse_phase1(struct eap_fast_data *data,
+				  const char *phase1)
+{
+	const char *pos;
+
+	pos = os_strstr(phase1, "fast_provisioning=");
+	if (pos) {
+		data->provisioning_allowed = atoi(pos + 18);
+		wpa_printf(MSG_INFO, "EAP-FAST: Automatic PAC provisioning "
+			   "mode: %d", data->provisioning_allowed);
+	}
+
+	pos = os_strstr(phase1, "fast_max_pac_list_len=");
+	if (pos) {
+		data->max_pac_list_len = atoi(pos + 22);
+		if (data->max_pac_list_len == 0)
+			data->max_pac_list_len = 1;
+		wpa_printf(MSG_INFO, "EAP-FAST: Maximum PAC list length: %lu",
+			   (unsigned long) data->max_pac_list_len);
+	}
+
+	pos = os_strstr(phase1, "fast_pac_format=binary");
+	if (pos) {
+		data->use_pac_binary_format = 1;
+		wpa_printf(MSG_INFO, "EAP-FAST: Using binary format for PAC "
+			   "list");
+	}
+}
+
+
+static void * eap_fast_init(struct eap_sm *sm)
+{
+	struct eap_fast_data *data;
+	struct eap_peer_config *config = eap_get_config(sm);
+
+	if (config == NULL)
+		return NULL;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->fast_version = EAP_FAST_VERSION;
+	data->max_pac_list_len = 10;
+
+	if (config->phase1)
+		eap_fast_parse_phase1(data, config->phase1);
+
+	if (eap_peer_select_phase2_methods(config, "auth=",
+					   &data->phase2_types,
+					   &data->num_phase2_types) < 0) {
+		eap_fast_deinit(sm, data);
+		return NULL;
+	}
+
+	data->phase2_type.vendor = EAP_VENDOR_IETF;
+	data->phase2_type.method = EAP_TYPE_NONE;
+
+	if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_FAST)) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL.");
+		eap_fast_deinit(sm, data);
+		return NULL;
+	}
+
+	if (tls_connection_set_session_ticket_cb(sm->ssl_ctx, data->ssl.conn,
+						 eap_fast_session_ticket_cb,
+						 data) < 0) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Failed to set SessionTicket "
+			   "callback");
+		eap_fast_deinit(sm, data);
+		return NULL;
+	}
+
+	/*
+	 * The local RADIUS server in a Cisco AP does not seem to like empty
+	 * fragments before data, so disable that workaround for CBC.
+	 * TODO: consider making this configurable
+	 */
+	if (tls_connection_enable_workaround(sm->ssl_ctx, data->ssl.conn)) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to enable TLS "
+			   "workarounds");
+	}
+
+	if (!config->pac_file) {
+		wpa_printf(MSG_INFO, "EAP-FAST: No PAC file configured");
+		eap_fast_deinit(sm, data);
+		return NULL;
+	}
+
+	if (data->use_pac_binary_format &&
+	    eap_fast_load_pac_bin(sm, &data->pac, config->pac_file) < 0) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Failed to load PAC file");
+		eap_fast_deinit(sm, data);
+		return NULL;
+	}
+
+	if (!data->use_pac_binary_format &&
+	    eap_fast_load_pac(sm, &data->pac, config->pac_file) < 0) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Failed to load PAC file");
+		eap_fast_deinit(sm, data);
+		return NULL;
+	}
+	eap_fast_pac_list_truncate(data->pac, data->max_pac_list_len);
+
+	if (data->pac == NULL && !data->provisioning_allowed) {
+		wpa_printf(MSG_INFO, "EAP-FAST: No PAC configured and "
+			   "provisioning disabled");
+		eap_fast_deinit(sm, data);
+		return NULL;
+	}
+
+	return data;
+}
+
+
+static void eap_fast_deinit(struct eap_sm *sm, void *priv)
+{
+	struct eap_fast_data *data = priv;
+	struct eap_fast_pac *pac, *prev;
+
+	if (data == NULL)
+		return;
+	if (data->phase2_priv && data->phase2_method)
+		data->phase2_method->deinit(sm, data->phase2_priv);
+	os_free(data->phase2_types);
+	os_free(data->key_block_p);
+	eap_peer_tls_ssl_deinit(sm, &data->ssl);
+
+	pac = data->pac;
+	prev = NULL;
+	while (pac) {
+		prev = pac;
+		pac = pac->next;
+		eap_fast_free_pac(prev);
+	}
+	os_memset(data->key_data, 0, EAP_FAST_KEY_LEN);
+	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)
+{
+	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;
+}
+
+
+static int eap_fast_derive_key_auth(struct eap_sm *sm,
+				    struct eap_fast_data *data)
+{
+	u8 *sks;
+
+	/* RFC 4851, Section 5.1:
+	 * Extra key material after TLS key_block: session_key_seed[40]
+	 */
+
+	sks = eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn,
+				  EAP_FAST_SKS_LEN);
+	if (sks == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive "
+			   "session_key_seed");
+		return -1;
+	}
+
+	/*
+	 * RFC 4851, Section 5.2:
+	 * S-IMCK[0] = session_key_seed
+	 */
+	wpa_hexdump_key(MSG_DEBUG,
+			"EAP-FAST: session_key_seed (SKS = S-IMCK[0])",
+			sks, EAP_FAST_SKS_LEN);
+	data->simck_idx = 0;
+	os_memcpy(data->simck, sks, EAP_FAST_SIMCK_LEN);
+	os_free(sks);
+	return 0;
+}
+
+
+static int eap_fast_derive_key_provisioning(struct eap_sm *sm,
+					    struct eap_fast_data *data)
+{
+	os_free(data->key_block_p);
+	data->key_block_p = (struct eap_fast_key_block_provisioning *)
+		eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn,
+				    sizeof(*data->key_block_p));
+	if (data->key_block_p == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block");
+		return -1;
+	}
+	/*
+	 * RFC 4851, Section 5.2:
+	 * S-IMCK[0] = session_key_seed
+	 */
+	wpa_hexdump_key(MSG_DEBUG,
+			"EAP-FAST: session_key_seed (SKS = S-IMCK[0])",
+			data->key_block_p->session_key_seed,
+			sizeof(data->key_block_p->session_key_seed));
+	data->simck_idx = 0;
+	os_memcpy(data->simck, data->key_block_p->session_key_seed,
+		  EAP_FAST_SIMCK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: server_challenge",
+			data->key_block_p->server_challenge,
+			sizeof(data->key_block_p->server_challenge));
+	wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: client_challenge",
+			data->key_block_p->client_challenge,
+			sizeof(data->key_block_p->client_challenge));
+	return 0;
+}
+
+
+static int eap_fast_derive_keys(struct eap_sm *sm, struct eap_fast_data *data)
+{
+	int res;
+
+	if (data->anon_provisioning)
+		res = eap_fast_derive_key_provisioning(sm, data);
+	else
+		res = eap_fast_derive_key_auth(sm, data);
+	return res;
+}
+
+
+static int eap_fast_init_phase2_method(struct eap_sm *sm,
+				       struct eap_fast_data *data)
+{
+	data->phase2_method =
+		eap_peer_get_eap_method(data->phase2_type.vendor,
+					data->phase2_type.method);
+	if (data->phase2_method == NULL)
+		return -1;
+
+	if (data->key_block_p) {
+		sm->auth_challenge = data->key_block_p->server_challenge;
+		sm->peer_challenge = data->key_block_p->client_challenge;
+	}
+	sm->init_phase2 = 1;
+	data->phase2_priv = data->phase2_method->init(sm);
+	sm->init_phase2 = 0;
+	sm->auth_challenge = NULL;
+	sm->peer_challenge = NULL;
+
+	return data->phase2_priv == NULL ? -1 : 0;
+}
+
+
+static int eap_fast_select_phase2_method(struct eap_fast_data *data, u8 type)
+{
+	size_t i;
+
+	/* TODO: TNC with anonymous provisioning; need to require both
+	 * completed MSCHAPv2 and TNC */
+
+	if (data->anon_provisioning && type != EAP_TYPE_MSCHAPV2) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Only EAP-MSCHAPv2 is allowed "
+			   "during unauthenticated provisioning; reject phase2"
+			   " type %d", type);
+		return -1;
+	}
+
+#ifdef EAP_TNC
+	if (type == EAP_TYPE_TNC) {
+		data->phase2_type.vendor = EAP_VENDOR_IETF;
+		data->phase2_type.method = EAP_TYPE_TNC;
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Selected Phase 2 EAP "
+			   "vendor %d method %d for TNC",
+			   data->phase2_type.vendor,
+			   data->phase2_type.method);
+		return 0;
+	}
+#endif /* EAP_TNC */
+
+	for (i = 0; i < data->num_phase2_types; i++) {
+		if (data->phase2_types[i].vendor != EAP_VENDOR_IETF ||
+		    data->phase2_types[i].method != type)
+			continue;
+
+		data->phase2_type.vendor = data->phase2_types[i].vendor;
+		data->phase2_type.method = data->phase2_types[i].method;
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Selected Phase 2 EAP "
+			   "vendor %d method %d",
+			   data->phase2_type.vendor,
+			   data->phase2_type.method);
+		break;
+	}
+
+	if (type != data->phase2_type.method || type == EAP_TYPE_NONE)
+		return -1;
+
+	return 0;
+}
+
+
+static int eap_fast_phase2_request(struct eap_sm *sm,
+				   struct eap_fast_data *data,
+				   struct eap_method_ret *ret,
+				   struct eap_hdr *hdr,
+				   struct wpabuf **resp)
+{
+	size_t len = be_to_host16(hdr->length);
+	u8 *pos;
+	struct eap_method_ret iret;
+	struct eap_peer_config *config = eap_get_config(sm);
+	struct wpabuf msg;
+
+	if (len <= sizeof(struct eap_hdr)) {
+		wpa_printf(MSG_INFO, "EAP-FAST: too short "
+			   "Phase 2 request (len=%lu)", (unsigned long) len);
+		return -1;
+	}
+	pos = (u8 *) (hdr + 1);
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 Request: type=%d", *pos);
+	if (*pos == EAP_TYPE_IDENTITY) {
+		*resp = eap_sm_buildIdentity(sm, hdr->identifier, 1);
+		return 0;
+	}
+
+	if (data->phase2_priv && data->phase2_method &&
+	    *pos != data->phase2_type.method) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 EAP sequence - "
+			   "deinitialize previous method");
+		data->phase2_method->deinit(sm, data->phase2_priv);
+		data->phase2_method = NULL;
+		data->phase2_priv = NULL;
+		data->phase2_type.vendor = EAP_VENDOR_IETF;
+		data->phase2_type.method = EAP_TYPE_NONE;
+	}
+
+	if (data->phase2_type.vendor == EAP_VENDOR_IETF &&
+	    data->phase2_type.method == EAP_TYPE_NONE &&
+	    eap_fast_select_phase2_method(data, *pos) < 0) {
+		if (eap_peer_tls_phase2_nak(data->phase2_types,
+					    data->num_phase2_types,
+					    hdr, resp))
+			return -1;
+		return 0;
+	}
+
+	if ((data->phase2_priv == NULL &&
+	     eap_fast_init_phase2_method(sm, data) < 0) ||
+	    data->phase2_method == NULL) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize "
+			   "Phase 2 EAP method %d", *pos);
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_FAIL;
+		return -1;
+	}
+
+	os_memset(&iret, 0, sizeof(iret));
+	wpabuf_set(&msg, hdr, len);
+	*resp = data->phase2_method->process(sm, data->phase2_priv, &iret,
+					     &msg);
+	if (*resp == NULL ||
+	    (iret.methodState == METHOD_DONE &&
+	     iret.decision == DECISION_FAIL)) {
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_FAIL;
+	} else if ((iret.methodState == METHOD_DONE ||
+		    iret.methodState == METHOD_MAY_CONT) &&
+		   (iret.decision == DECISION_UNCOND_SUCC ||
+		    iret.decision == DECISION_COND_SUCC)) {
+		data->phase2_success = 1;
+	}
+
+	if (*resp == NULL && config &&
+	    (config->pending_req_identity || config->pending_req_password ||
+	     config->pending_req_otp || config->pending_req_new_password ||
+	     config->pending_req_sim)) {
+		wpabuf_clear_free(data->pending_phase2_req);
+		data->pending_phase2_req = wpabuf_alloc_copy(hdr, len);
+	} else if (*resp == NULL)
+		return -1;
+
+	return 0;
+}
+
+
+static struct wpabuf * eap_fast_tlv_nak(int vendor_id, int tlv_type)
+{
+	struct wpabuf *buf;
+	struct eap_tlv_nak_tlv *nak;
+	buf = wpabuf_alloc(sizeof(*nak));
+	if (buf == NULL)
+		return NULL;
+	nak = wpabuf_put(buf, sizeof(*nak));
+	nak->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | EAP_TLV_NAK_TLV);
+	nak->length = host_to_be16(6);
+	nak->vendor_id = host_to_be32(vendor_id);
+	nak->nak_type = host_to_be16(tlv_type);
+	return buf;
+}
+
+
+static struct wpabuf * eap_fast_tlv_result(int status, int intermediate)
+{
+	struct wpabuf *buf;
+	struct eap_tlv_intermediate_result_tlv *result;
+	buf = wpabuf_alloc(sizeof(*result));
+	if (buf == NULL)
+		return NULL;
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Add %sResult TLV(status=%d)",
+		   intermediate ? "Intermediate " : "", status);
+	result = wpabuf_put(buf, sizeof(*result));
+	result->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+					(intermediate ?
+					 EAP_TLV_INTERMEDIATE_RESULT_TLV :
+					 EAP_TLV_RESULT_TLV));
+	result->length = host_to_be16(2);
+	result->status = host_to_be16(status);
+	return buf;
+}
+
+
+static struct wpabuf * eap_fast_tlv_pac_ack(void)
+{
+	struct wpabuf *buf;
+	struct eap_tlv_result_tlv *res;
+	struct eap_tlv_pac_ack_tlv *ack;
+
+	buf = wpabuf_alloc(sizeof(*res) + sizeof(*ack));
+	if (buf == NULL)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Add PAC TLV (ack)");
+	ack = wpabuf_put(buf, sizeof(*ack));
+	ack->tlv_type = host_to_be16(EAP_TLV_PAC_TLV |
+				     EAP_TLV_TYPE_MANDATORY);
+	ack->length = host_to_be16(sizeof(*ack) - sizeof(struct eap_tlv_hdr));
+	ack->pac_type = host_to_be16(PAC_TYPE_PAC_ACKNOWLEDGEMENT);
+	ack->pac_len = host_to_be16(2);
+	ack->result = host_to_be16(EAP_TLV_RESULT_SUCCESS);
+
+	return buf;
+}
+
+
+static struct wpabuf * eap_fast_process_eap_payload_tlv(
+	struct eap_sm *sm, struct eap_fast_data *data,
+	struct eap_method_ret *ret,
+	u8 *eap_payload_tlv, size_t eap_payload_tlv_len)
+{
+	struct eap_hdr *hdr;
+	struct wpabuf *resp = NULL;
+
+	if (eap_payload_tlv_len < sizeof(*hdr)) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: too short EAP "
+			   "Payload TLV (len=%lu)",
+			   (unsigned long) eap_payload_tlv_len);
+		return NULL;
+	}
+
+	hdr = (struct eap_hdr *) eap_payload_tlv;
+	if (be_to_host16(hdr->length) > eap_payload_tlv_len) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: EAP packet overflow in "
+			   "EAP Payload TLV");
+		return NULL;
+	}
+
+	if (hdr->code != EAP_CODE_REQUEST) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Unexpected code=%d in "
+			   "Phase 2 EAP header", hdr->code);
+		return NULL;
+	}
+
+	if (eap_fast_phase2_request(sm, data, ret, hdr, &resp)) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Phase2 Request processing "
+			   "failed");
+		return NULL;
+	}
+
+	return eap_fast_tlv_eap_payload(resp);
+}
+
+
+static int eap_fast_validate_crypto_binding(
+	struct eap_tlv_crypto_binding_tlv *_bind)
+{
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Crypto-Binding TLV: Version %d "
+		   "Received Version %d SubType %d",
+		   _bind->version, _bind->received_version, _bind->subtype);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE",
+		    _bind->nonce, sizeof(_bind->nonce));
+	wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC",
+		    _bind->compound_mac, sizeof(_bind->compound_mac));
+
+	if (_bind->version != EAP_FAST_VERSION ||
+	    _bind->received_version != EAP_FAST_VERSION ||
+	    _bind->subtype != EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Invalid version/subtype in "
+			   "Crypto-Binding TLV: Version %d "
+			   "Received Version %d SubType %d",
+			   _bind->version, _bind->received_version,
+			   _bind->subtype);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static void eap_fast_write_crypto_binding(
+	struct eap_tlv_crypto_binding_tlv *rbind,
+	struct eap_tlv_crypto_binding_tlv *_bind, const u8 *cmk)
+{
+	rbind->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+				       EAP_TLV_CRYPTO_BINDING_TLV);
+	rbind->length = host_to_be16(sizeof(*rbind) -
+				     sizeof(struct eap_tlv_hdr));
+	rbind->version = EAP_FAST_VERSION;
+	rbind->received_version = _bind->version;
+	rbind->subtype = EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE;
+	os_memcpy(rbind->nonce, _bind->nonce, sizeof(_bind->nonce));
+	inc_byte_array(rbind->nonce, sizeof(rbind->nonce));
+	hmac_sha1(cmk, EAP_FAST_CMK_LEN, (u8 *) rbind, sizeof(*rbind),
+		  rbind->compound_mac);
+
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Reply Crypto-Binding TLV: Version %d "
+		   "Received Version %d SubType %d",
+		   rbind->version, rbind->received_version, rbind->subtype);
+	wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE",
+		    rbind->nonce, sizeof(rbind->nonce));
+	wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC",
+		    rbind->compound_mac, sizeof(rbind->compound_mac));
+}
+
+
+static int eap_fast_get_phase2_key(struct eap_sm *sm,
+				   struct eap_fast_data *data,
+				   u8 *isk, size_t isk_len)
+{
+	u8 *key;
+	size_t key_len;
+
+	os_memset(isk, 0, isk_len);
+
+	if (data->phase2_method == NULL || data->phase2_priv == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 method not "
+			   "available");
+		return -1;
+	}
+
+	if (data->phase2_method->isKeyAvailable == NULL ||
+	    data->phase2_method->getKey == NULL)
+		return 0;
+
+	if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) ||
+	    (key = data->phase2_method->getKey(sm, data->phase2_priv,
+					       &key_len)) == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Could not get key material "
+			   "from Phase 2");
+		return -1;
+	}
+
+	if (key_len > isk_len)
+		key_len = isk_len;
+	if (key_len == 32 &&
+	    data->phase2_method->vendor == EAP_VENDOR_IETF &&
+	    data->phase2_method->method == EAP_TYPE_MSCHAPV2) {
+		/*
+		 * EAP-FAST uses reverse order for MS-MPPE keys when deriving
+		 * MSK from EAP-MSCHAPv2. Swap the keys here to get the correct
+		 * ISK for EAP-FAST cryptobinding.
+		 */
+		os_memcpy(isk, key + 16, 16);
+		os_memcpy(isk + 16, key, 16);
+	} else
+		os_memcpy(isk, key, key_len);
+	os_free(key);
+
+	return 0;
+}
+
+
+static int eap_fast_get_cmk(struct eap_sm *sm, struct eap_fast_data *data,
+			    u8 *cmk)
+{
+	u8 isk[32], imck[60];
+
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Determining CMK[%d] for Compound MIC "
+		   "calculation", data->simck_idx + 1);
+
+	/*
+	 * RFC 4851, Section 5.2:
+	 * IMCK[j] = T-PRF(S-IMCK[j-1], "Inner Methods Compound Keys",
+	 *                 MSK[j], 60)
+	 * S-IMCK[j] = first 40 octets of IMCK[j]
+	 * CMK[j] = last 20 octets of IMCK[j]
+	 */
+
+	if (eap_fast_get_phase2_key(sm, data, isk, sizeof(isk)) < 0)
+		return -1;
+	wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: ISK[j]", isk, sizeof(isk));
+	if (sha1_t_prf(data->simck, EAP_FAST_SIMCK_LEN,
+		       "Inner Methods Compound Keys",
+		       isk, sizeof(isk), imck, sizeof(imck)) < 0)
+		return -1;
+	data->simck_idx++;
+	os_memcpy(data->simck, imck, EAP_FAST_SIMCK_LEN);
+	wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: S-IMCK[j]",
+			data->simck, EAP_FAST_SIMCK_LEN);
+	os_memcpy(cmk, imck + EAP_FAST_SIMCK_LEN, EAP_FAST_CMK_LEN);
+	wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: CMK[j]",
+			cmk, EAP_FAST_CMK_LEN);
+
+	return 0;
+}
+
+
+static u8 * eap_fast_write_pac_request(u8 *pos, u16 pac_type)
+{
+	struct eap_tlv_hdr *pac;
+	struct eap_tlv_request_action_tlv *act;
+	struct eap_tlv_pac_type_tlv *type;
+
+	act = (struct eap_tlv_request_action_tlv *) pos;
+	act->tlv_type = host_to_be16(EAP_TLV_REQUEST_ACTION_TLV);
+	act->length = host_to_be16(2);
+	act->action = host_to_be16(EAP_TLV_ACTION_PROCESS_TLV);
+
+	pac = (struct eap_tlv_hdr *) (act + 1);
+	pac->tlv_type = host_to_be16(EAP_TLV_PAC_TLV);
+	pac->length = host_to_be16(sizeof(*type));
+
+	type = (struct eap_tlv_pac_type_tlv *) (pac + 1);
+	type->tlv_type = host_to_be16(PAC_TYPE_PAC_TYPE);
+	type->length = host_to_be16(2);
+	type->pac_type = host_to_be16(pac_type);
+
+	return (u8 *) (type + 1);
+}
+
+
+static struct wpabuf * eap_fast_process_crypto_binding(
+	struct eap_sm *sm, struct eap_fast_data *data,
+	struct eap_method_ret *ret,
+	struct eap_tlv_crypto_binding_tlv *_bind, size_t bind_len)
+{
+	struct wpabuf *resp;
+	u8 *pos;
+	u8 cmk[EAP_FAST_CMK_LEN], cmac[SHA1_MAC_LEN];
+	int res;
+	size_t len;
+
+	if (eap_fast_validate_crypto_binding(_bind) < 0)
+		return NULL;
+
+	if (eap_fast_get_cmk(sm, data, cmk) < 0)
+		return NULL;
+
+	/* Validate received Compound MAC */
+	os_memcpy(cmac, _bind->compound_mac, sizeof(cmac));
+	os_memset(_bind->compound_mac, 0, sizeof(cmac));
+	wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV for Compound "
+		    "MAC calculation", (u8 *) _bind, bind_len);
+	hmac_sha1(cmk, EAP_FAST_CMK_LEN, (u8 *) _bind, bind_len,
+		  _bind->compound_mac);
+	res = os_memcmp_const(cmac, _bind->compound_mac, sizeof(cmac));
+	wpa_hexdump(MSG_DEBUG, "EAP-FAST: Received Compound MAC",
+		    cmac, sizeof(cmac));
+	wpa_hexdump(MSG_DEBUG, "EAP-FAST: Calculated Compound MAC",
+		    _bind->compound_mac, sizeof(cmac));
+	if (res != 0) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Compound MAC did not match");
+		os_memcpy(_bind->compound_mac, cmac, sizeof(cmac));
+		return NULL;
+	}
+
+	/*
+	 * Compound MAC was valid, so authentication succeeded. Reply with
+	 * crypto binding to allow server to complete authentication.
+	 */
+
+	len = sizeof(struct eap_tlv_crypto_binding_tlv);
+	resp = wpabuf_alloc(len);
+	if (resp == NULL)
+		return NULL;
+
+	if (!data->anon_provisioning && data->phase2_success &&
+	    eap_fast_derive_msk(data) < 0) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Failed to generate MSK");
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_FAIL;
+		data->phase2_success = 0;
+		wpabuf_free(resp);
+		return NULL;
+	}
+
+	if (!data->anon_provisioning && data->phase2_success) {
+		os_free(data->session_id);
+		data->session_id = eap_peer_tls_derive_session_id(
+			sm, &data->ssl, EAP_TYPE_FAST, &data->id_len);
+		if (data->session_id) {
+			wpa_hexdump(MSG_DEBUG, "EAP-FAST: Derived Session-Id",
+				    data->session_id, data->id_len);
+		} else {
+			wpa_printf(MSG_ERROR, "EAP-FAST: Failed to derive "
+				   "Session-Id");
+			wpabuf_free(resp);
+			return NULL;
+		}
+	}
+
+	pos = wpabuf_put(resp, sizeof(struct eap_tlv_crypto_binding_tlv));
+	eap_fast_write_crypto_binding((struct eap_tlv_crypto_binding_tlv *)
+				      pos, _bind, cmk);
+
+	return resp;
+}
+
+
+static void eap_fast_parse_pac_tlv(struct eap_fast_pac *entry, int type,
+				   u8 *pos, size_t len, int *pac_key_found)
+{
+	switch (type & 0x7fff) {
+	case PAC_TYPE_PAC_KEY:
+		wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: PAC-Key", pos, len);
+		if (len != EAP_FAST_PAC_KEY_LEN) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid PAC-Key "
+				   "length %lu", (unsigned long) len);
+			break;
+		}
+		*pac_key_found = 1;
+		os_memcpy(entry->pac_key, pos, len);
+		break;
+	case PAC_TYPE_PAC_OPAQUE:
+		wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Opaque", pos, len);
+		entry->pac_opaque = pos;
+		entry->pac_opaque_len = len;
+		break;
+	case PAC_TYPE_PAC_INFO:
+		wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Info", pos, len);
+		entry->pac_info = pos;
+		entry->pac_info_len = len;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored unknown PAC type %d",
+			   type);
+		break;
+	}
+}
+
+
+static int eap_fast_process_pac_tlv(struct eap_fast_pac *entry,
+				    u8 *pac, size_t pac_len)
+{
+	struct pac_tlv_hdr *hdr;
+	u8 *pos;
+	size_t left, len;
+	int type, pac_key_found = 0;
+
+	pos = pac;
+	left = pac_len;
+
+	while (left > sizeof(*hdr)) {
+		hdr = (struct pac_tlv_hdr *) pos;
+		type = be_to_host16(hdr->type);
+		len = be_to_host16(hdr->len);
+		pos += sizeof(*hdr);
+		left -= sizeof(*hdr);
+		if (len > left) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV overrun "
+				   "(type=%d len=%lu left=%lu)",
+				   type, (unsigned long) len,
+				   (unsigned long) left);
+			return -1;
+		}
+
+		eap_fast_parse_pac_tlv(entry, type, pos, len, &pac_key_found);
+
+		pos += len;
+		left -= len;
+	}
+
+	if (!pac_key_found || !entry->pac_opaque || !entry->pac_info) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV does not include "
+			   "all the required fields");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int eap_fast_parse_pac_info(struct eap_fast_pac *entry, int type,
+				   u8 *pos, size_t len)
+{
+	u16 pac_type;
+
+	switch (type & 0x7fff) {
+	case PAC_TYPE_CRED_LIFETIME:
+		if (len != 4) {
+			wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Info - "
+				    "Invalid CRED_LIFETIME length - ignored",
+				    pos, len);
+			return 0;
+		}
+		break;
+	case PAC_TYPE_A_ID:
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - A-ID",
+				  pos, len);
+		entry->a_id = pos;
+		entry->a_id_len = len;
+		break;
+	case PAC_TYPE_I_ID:
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - I-ID",
+				  pos, len);
+		entry->i_id = pos;
+		entry->i_id_len = len;
+		break;
+	case PAC_TYPE_A_ID_INFO:
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - A-ID-Info",
+				  pos, len);
+		entry->a_id_info = pos;
+		entry->a_id_info_len = len;
+		break;
+	case PAC_TYPE_PAC_TYPE:
+		/* RFC 5422, Section 4.2.6 - PAC-Type TLV */
+		if (len != 2) {
+			wpa_printf(MSG_INFO, "EAP-FAST: Invalid PAC-Type "
+				   "length %lu (expected 2)",
+				   (unsigned long) len);
+			wpa_hexdump_ascii(MSG_DEBUG,
+					  "EAP-FAST: PAC-Info - PAC-Type",
+					  pos, len);
+			return -1;
+		}
+		pac_type = WPA_GET_BE16(pos);
+		if (pac_type != PAC_TYPE_TUNNEL_PAC &&
+		    pac_type != PAC_TYPE_USER_AUTHORIZATION &&
+		    pac_type != PAC_TYPE_MACHINE_AUTHENTICATION) {
+			wpa_printf(MSG_INFO, "EAP-FAST: Unsupported PAC Type "
+				   "%d", pac_type);
+			return -1;
+		}
+
+		wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info - PAC-Type %d",
+			   pac_type);
+		entry->pac_type = pac_type;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored unknown PAC-Info "
+			   "type %d", type);
+		break;
+	}
+
+	return 0;
+}
+
+
+static int eap_fast_process_pac_info(struct eap_fast_pac *entry)
+{
+	struct pac_tlv_hdr *hdr;
+	u8 *pos;
+	size_t left, len;
+	int type;
+
+	/* RFC 5422, Section 4.2.4 */
+
+	/* PAC-Type defaults to Tunnel PAC (Type 1) */
+	entry->pac_type = PAC_TYPE_TUNNEL_PAC;
+
+	pos = entry->pac_info;
+	left = entry->pac_info_len;
+	while (left > sizeof(*hdr)) {
+		hdr = (struct pac_tlv_hdr *) pos;
+		type = be_to_host16(hdr->type);
+		len = be_to_host16(hdr->len);
+		pos += sizeof(*hdr);
+		left -= sizeof(*hdr);
+		if (len > left) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info overrun "
+				   "(type=%d len=%lu left=%lu)",
+				   type, (unsigned long) len,
+				   (unsigned long) left);
+			return -1;
+		}
+
+		if (eap_fast_parse_pac_info(entry, type, pos, len) < 0)
+			return -1;
+
+		pos += len;
+		left -= len;
+	}
+
+	if (entry->a_id == NULL || entry->a_id_info == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info does not include "
+			   "all the required fields");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static struct wpabuf * eap_fast_process_pac(struct eap_sm *sm,
+					    struct eap_fast_data *data,
+					    struct eap_method_ret *ret,
+					    u8 *pac, size_t pac_len)
+{
+	struct eap_peer_config *config = eap_get_config(sm);
+	struct eap_fast_pac entry;
+
+	os_memset(&entry, 0, sizeof(entry));
+	if (eap_fast_process_pac_tlv(&entry, pac, pac_len) ||
+	    eap_fast_process_pac_info(&entry))
+		return NULL;
+
+	eap_fast_add_pac(&data->pac, &data->current_pac, &entry);
+	eap_fast_pac_list_truncate(data->pac, data->max_pac_list_len);
+	if (data->use_pac_binary_format)
+		eap_fast_save_pac_bin(sm, data->pac, config->pac_file);
+	else
+		eap_fast_save_pac(sm, data->pac, config->pac_file);
+
+	if (data->provisioning) {
+		if (data->anon_provisioning) {
+			/*
+			 * Unauthenticated provisioning does not provide keying
+			 * material and must end with an EAP-Failure.
+			 * Authentication will be done separately after this.
+			 */
+			data->success = 0;
+			ret->decision = DECISION_FAIL;
+		} else {
+			/*
+			 * Server may or may not allow authenticated
+			 * provisioning also for key generation.
+			 */
+			ret->decision = DECISION_COND_SUCC;
+		}
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV "
+			   "- Provisioning completed successfully");
+		sm->expected_failure = 1;
+	} else {
+		/*
+		 * This is PAC refreshing, i.e., normal authentication that is
+		 * expected to be completed with an EAP-Success. However,
+		 * RFC 5422, Section 3.5 allows EAP-Failure to be sent even
+		 * after protected success exchange in case of EAP-Fast
+		 * provisioning, so we better use DECISION_COND_SUCC here
+		 * instead of DECISION_UNCOND_SUCC.
+		 */
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV "
+			   "- PAC refreshing completed successfully");
+		ret->decision = DECISION_COND_SUCC;
+	}
+	ret->methodState = METHOD_DONE;
+	return eap_fast_tlv_pac_ack();
+}
+
+
+static int eap_fast_parse_decrypted(struct wpabuf *decrypted,
+				    struct eap_fast_tlv_parse *tlv,
+				    struct wpabuf **resp)
+{
+	int mandatory, tlv_type, res;
+	size_t len;
+	u8 *pos, *end;
+
+	os_memset(tlv, 0, sizeof(*tlv));
+
+	/* Parse TLVs from the decrypted Phase 2 data */
+	pos = wpabuf_mhead(decrypted);
+	end = pos + wpabuf_len(decrypted);
+	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-FAST: TLV overflow");
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Received Phase 2: "
+			   "TLV type %d length %u%s",
+			   tlv_type, (unsigned int) len,
+			   mandatory ? " (mandatory)" : "");
+
+		res = eap_fast_parse_tlv(tlv, tlv_type, pos, len);
+		if (res == -2)
+			break;
+		if (res < 0) {
+			if (mandatory) {
+				wpa_printf(MSG_DEBUG, "EAP-FAST: Nak unknown "
+					   "mandatory TLV type %d", tlv_type);
+				*resp = eap_fast_tlv_nak(0, tlv_type);
+				break;
+			} else {
+				wpa_printf(MSG_DEBUG, "EAP-FAST: ignored "
+					   "unknown optional TLV type %d",
+					   tlv_type);
+			}
+		}
+
+		pos += len;
+	}
+
+	return 0;
+}
+
+
+static int eap_fast_encrypt_response(struct eap_sm *sm,
+				     struct eap_fast_data *data,
+				     struct wpabuf *resp,
+				     u8 identifier, struct wpabuf **out_data)
+{
+	if (resp == NULL)
+		return 0;
+
+	wpa_hexdump_buf(MSG_DEBUG, "EAP-FAST: Encrypting Phase 2 data",
+			resp);
+	if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_FAST,
+				 data->fast_version, identifier,
+				 resp, out_data)) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Failed to encrypt a Phase 2 "
+			   "frame");
+	}
+	wpabuf_free(resp);
+
+	return 0;
+}
+
+
+static struct wpabuf * eap_fast_pac_request(void)
+{
+	struct wpabuf *tmp;
+	u8 *pos, *pos2;
+
+	tmp = wpabuf_alloc(sizeof(struct eap_tlv_hdr) +
+			   sizeof(struct eap_tlv_request_action_tlv) +
+			   sizeof(struct eap_tlv_pac_type_tlv));
+	if (tmp == NULL)
+		return NULL;
+
+	pos = wpabuf_put(tmp, 0);
+	pos2 = eap_fast_write_pac_request(pos, PAC_TYPE_TUNNEL_PAC);
+	wpabuf_put(tmp, pos2 - pos);
+	return tmp;
+}
+
+
+static int eap_fast_process_decrypted(struct eap_sm *sm,
+				      struct eap_fast_data *data,
+				      struct eap_method_ret *ret,
+				      u8 identifier,
+				      struct wpabuf *decrypted,
+				      struct wpabuf **out_data)
+{
+	struct wpabuf *resp = NULL, *tmp;
+	struct eap_fast_tlv_parse tlv;
+	int failed = 0;
+
+	if (eap_fast_parse_decrypted(decrypted, &tlv, &resp) < 0)
+		return 0;
+	if (resp)
+		return eap_fast_encrypt_response(sm, data, resp,
+						 identifier, out_data);
+
+	if (tlv.result == EAP_TLV_RESULT_FAILURE) {
+		resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0);
+		return eap_fast_encrypt_response(sm, data, resp,
+						 identifier, out_data);
+	}
+
+	if (tlv.iresult == EAP_TLV_RESULT_FAILURE) {
+		resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1);
+		return eap_fast_encrypt_response(sm, data, resp,
+						 identifier, out_data);
+	}
+
+	if (tlv.crypto_binding) {
+		tmp = eap_fast_process_crypto_binding(sm, data, ret,
+						      tlv.crypto_binding,
+						      tlv.crypto_binding_len);
+		if (tmp == NULL)
+			failed = 1;
+		else
+			resp = wpabuf_concat(resp, tmp);
+	}
+
+	if (tlv.iresult == EAP_TLV_RESULT_SUCCESS) {
+		tmp = eap_fast_tlv_result(failed ? EAP_TLV_RESULT_FAILURE :
+					  EAP_TLV_RESULT_SUCCESS, 1);
+		resp = wpabuf_concat(resp, tmp);
+	}
+
+	if (tlv.eap_payload_tlv) {
+		tmp = eap_fast_process_eap_payload_tlv(
+			sm, data, ret, tlv.eap_payload_tlv,
+			tlv.eap_payload_tlv_len);
+		resp = wpabuf_concat(resp, tmp);
+	}
+
+	if (tlv.pac && tlv.result != EAP_TLV_RESULT_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV without Result TLV "
+			   "acknowledging success");
+		failed = 1;
+	} else if (tlv.pac && tlv.result == EAP_TLV_RESULT_SUCCESS) {
+		tmp = eap_fast_process_pac(sm, data, ret, tlv.pac,
+					   tlv.pac_len);
+		resp = wpabuf_concat(resp, tmp);
+	}
+
+	if (data->current_pac == NULL && data->provisioning &&
+	    !data->anon_provisioning && !tlv.pac &&
+	    (tlv.iresult == EAP_TLV_RESULT_SUCCESS ||
+	     tlv.result == EAP_TLV_RESULT_SUCCESS)) {
+		/*
+		 * Need to request Tunnel PAC when using authenticated
+		 * provisioning.
+		 */
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Request Tunnel PAC");
+		tmp = eap_fast_pac_request();
+		resp = wpabuf_concat(resp, tmp);
+	}
+
+	if (tlv.result == EAP_TLV_RESULT_SUCCESS && !failed) {
+		tmp = eap_fast_tlv_result(EAP_TLV_RESULT_SUCCESS, 0);
+		resp = wpabuf_concat(tmp, resp);
+	} else if (failed) {
+		tmp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0);
+		resp = wpabuf_concat(tmp, resp);
+	}
+
+	if (resp && tlv.result == EAP_TLV_RESULT_SUCCESS && !failed &&
+	    tlv.crypto_binding && data->phase2_success) {
+		if (data->anon_provisioning) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Unauthenticated "
+				   "provisioning completed successfully.");
+			ret->methodState = METHOD_DONE;
+			ret->decision = DECISION_FAIL;
+			sm->expected_failure = 1;
+		} else {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication "
+				   "completed successfully.");
+			if (data->provisioning)
+				ret->methodState = METHOD_MAY_CONT;
+			else
+				ret->methodState = METHOD_DONE;
+			ret->decision = DECISION_UNCOND_SUCC;
+		}
+	}
+
+	if (resp == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: No recognized TLVs - send "
+			   "empty response packet");
+		resp = wpabuf_alloc(1);
+	}
+
+	return eap_fast_encrypt_response(sm, data, resp, identifier,
+					 out_data);
+}
+
+
+static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data,
+			    struct eap_method_ret *ret, u8 identifier,
+			    const struct wpabuf *in_data,
+			    struct wpabuf **out_data)
+{
+	struct wpabuf *in_decrypted;
+	int res;
+
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Received %lu bytes encrypted data for"
+		   " Phase 2", (unsigned long) wpabuf_len(in_data));
+
+	if (data->pending_phase2_req) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Pending Phase 2 request - "
+			   "skip decryption and use old data");
+		/* Clear TLS reassembly state. */
+		eap_peer_tls_reset_input(&data->ssl);
+
+		in_decrypted = data->pending_phase2_req;
+		data->pending_phase2_req = NULL;
+		goto continue_req;
+	}
+
+	if (wpabuf_len(in_data) == 0) {
+		/* Received TLS ACK - requesting more fragments */
+		return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_FAST,
+					    data->fast_version,
+					    identifier, NULL, out_data);
+	}
+
+	res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted);
+	if (res)
+		return res;
+
+continue_req:
+	wpa_hexdump_buf(MSG_MSGDUMP, "EAP-FAST: Decrypted Phase 2 TLV(s)",
+			in_decrypted);
+
+	if (wpabuf_len(in_decrypted) < 4) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Too short Phase 2 "
+			   "TLV frame (len=%lu)",
+			   (unsigned long) wpabuf_len(in_decrypted));
+		wpabuf_free(in_decrypted);
+		return -1;
+	}
+
+	res = eap_fast_process_decrypted(sm, data, ret, identifier,
+					 in_decrypted, out_data);
+
+	wpabuf_free(in_decrypted);
+
+	return res;
+}
+
+
+static const u8 * eap_fast_get_a_id(const u8 *buf, size_t len, size_t *id_len)
+{
+	const u8 *a_id;
+	const struct pac_tlv_hdr *hdr;
+
+	/*
+	 * Parse authority identity (A-ID) from the EAP-FAST/Start. This
+	 * supports both raw A-ID and one inside an A-ID TLV.
+	 */
+	a_id = buf;
+	*id_len = len;
+	if (len > sizeof(*hdr)) {
+		int tlen;
+		hdr = (const struct pac_tlv_hdr *) buf;
+		tlen = be_to_host16(hdr->len);
+		if (be_to_host16(hdr->type) == PAC_TYPE_A_ID &&
+		    sizeof(*hdr) + tlen <= len) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: A-ID was in TLV "
+				   "(Start)");
+			a_id = (const u8 *) (hdr + 1);
+			*id_len = tlen;
+		}
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: A-ID", a_id, *id_len);
+
+	return a_id;
+}
+
+
+static void eap_fast_select_pac(struct eap_fast_data *data,
+				const u8 *a_id, size_t a_id_len)
+{
+	data->current_pac = eap_fast_get_pac(data->pac, a_id, a_id_len,
+					     PAC_TYPE_TUNNEL_PAC);
+	if (data->current_pac == NULL) {
+		/*
+		 * Tunnel PAC was not available for this A-ID. Try to use
+		 * Machine Authentication PAC, if one is available.
+		 */
+		data->current_pac = eap_fast_get_pac(
+			data->pac, a_id, a_id_len,
+			PAC_TYPE_MACHINE_AUTHENTICATION);
+	}
+
+	if (data->current_pac) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: PAC found for this A-ID "
+			   "(PAC-Type %d)", data->current_pac->pac_type);
+		wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-FAST: A-ID-Info",
+				  data->current_pac->a_id_info,
+				  data->current_pac->a_id_info_len);
+	}
+}
+
+
+static int eap_fast_use_pac_opaque(struct eap_sm *sm,
+				   struct eap_fast_data *data,
+				   struct eap_fast_pac *pac)
+{
+	u8 *tlv;
+	size_t tlv_len, olen;
+	struct eap_tlv_hdr *ehdr;
+
+	olen = pac->pac_opaque_len;
+	tlv_len = sizeof(*ehdr) + olen;
+	tlv = os_malloc(tlv_len);
+	if (tlv) {
+		ehdr = (struct eap_tlv_hdr *) tlv;
+		ehdr->tlv_type = host_to_be16(PAC_TYPE_PAC_OPAQUE);
+		ehdr->length = host_to_be16(olen);
+		os_memcpy(ehdr + 1, pac->pac_opaque, olen);
+	}
+	if (tlv == NULL ||
+	    tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn,
+					    TLS_EXT_PAC_OPAQUE,
+					    tlv, tlv_len) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to add PAC-Opaque TLS "
+			   "extension");
+		os_free(tlv);
+		return -1;
+	}
+	os_free(tlv);
+
+	return 0;
+}
+
+
+static int eap_fast_clear_pac_opaque_ext(struct eap_sm *sm,
+					 struct eap_fast_data *data)
+{
+	if (tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn,
+					    TLS_EXT_PAC_OPAQUE, NULL, 0) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to remove PAC-Opaque "
+			   "TLS extension");
+		return -1;
+	}
+	return 0;
+}
+
+
+static int eap_fast_set_provisioning_ciphers(struct eap_sm *sm,
+					     struct eap_fast_data *data)
+{
+	u8 ciphers[7];
+	int count = 0;
+
+	if (data->provisioning_allowed & EAP_FAST_PROV_UNAUTH) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Enabling unauthenticated "
+			   "provisioning TLS cipher suites");
+		ciphers[count++] = TLS_CIPHER_ANON_DH_AES128_SHA;
+	}
+
+	if (data->provisioning_allowed & EAP_FAST_PROV_AUTH) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Enabling authenticated "
+			   "provisioning TLS cipher suites");
+		ciphers[count++] = TLS_CIPHER_RSA_DHE_AES256_SHA;
+		ciphers[count++] = TLS_CIPHER_RSA_DHE_AES128_SHA;
+		ciphers[count++] = TLS_CIPHER_AES256_SHA;
+		ciphers[count++] = TLS_CIPHER_AES128_SHA;
+		ciphers[count++] = TLS_CIPHER_RC4_SHA;
+	}
+
+	ciphers[count++] = TLS_CIPHER_NONE;
+
+	if (tls_connection_set_cipher_list(sm->ssl_ctx, data->ssl.conn,
+					   ciphers)) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Could not configure TLS "
+			   "cipher suites for provisioning");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int eap_fast_process_start(struct eap_sm *sm,
+				  struct eap_fast_data *data, u8 flags,
+				  const u8 *pos, size_t left)
+{
+	const u8 *a_id;
+	size_t a_id_len;
+
+	/* EAP-FAST Version negotiation (section 3.1) */
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Start (server ver=%d, own ver=%d)",
+		   flags & EAP_TLS_VERSION_MASK, data->fast_version);
+	if ((flags & EAP_TLS_VERSION_MASK) < data->fast_version)
+		data->fast_version = flags & EAP_TLS_VERSION_MASK;
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Using FAST version %d",
+		   data->fast_version);
+
+	a_id = eap_fast_get_a_id(pos, left, &a_id_len);
+	eap_fast_select_pac(data, a_id, a_id_len);
+
+	if (data->resuming && data->current_pac) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Trying to resume session - "
+			   "do not add PAC-Opaque to TLS ClientHello");
+		if (eap_fast_clear_pac_opaque_ext(sm, data) < 0)
+			return -1;
+	} else if (data->current_pac) {
+		/*
+		 * PAC found for the A-ID and we are not resuming an old
+		 * session, so add PAC-Opaque extension to ClientHello.
+		 */
+		if (eap_fast_use_pac_opaque(sm, data, data->current_pac) < 0)
+			return -1;
+	} else {
+		/* No PAC found, so we must provision one. */
+		if (!data->provisioning_allowed) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC found and "
+				   "provisioning disabled");
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC found - "
+			   "starting provisioning");
+		if (eap_fast_set_provisioning_ciphers(sm, data) < 0 ||
+		    eap_fast_clear_pac_opaque_ext(sm, data) < 0)
+			return -1;
+		data->provisioning = 1;
+	}
+
+	return 0;
+}
+
+
+static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv,
+					struct eap_method_ret *ret,
+					const struct wpabuf *reqData)
+{
+	const struct eap_hdr *req;
+	size_t left;
+	int res;
+	u8 flags, id;
+	struct wpabuf *resp;
+	const u8 *pos;
+	struct eap_fast_data *data = priv;
+	struct wpabuf msg;
+
+	pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_FAST, ret,
+					reqData, &left, &flags);
+	if (pos == NULL)
+		return NULL;
+
+	req = wpabuf_head(reqData);
+	id = req->identifier;
+
+	if (flags & EAP_TLS_FLAGS_START) {
+		if (eap_fast_process_start(sm, data, flags, pos, left) < 0)
+			return NULL;
+
+		left = 0; /* A-ID is not used in further packet processing */
+	}
+
+	wpabuf_set(&msg, pos, left);
+
+	resp = NULL;
+	if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+	    !data->resuming) {
+		/* Process tunneled (encrypted) phase 2 data. */
+		res = eap_fast_decrypt(sm, data, ret, id, &msg, &resp);
+		if (res < 0) {
+			ret->methodState = METHOD_DONE;
+			ret->decision = DECISION_FAIL;
+			/*
+			 * Ack possible Alert that may have caused failure in
+			 * decryption.
+			 */
+			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,
+						  data->fast_version, id, pos,
+						  left, &resp);
+		if (res < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-FAST: TLS processing failed");
+			ret->methodState = METHOD_DONE;
+			ret->decision = DECISION_FAIL;
+			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,
+				   "EAP-FAST: TLS done, proceed to Phase 2");
+			if (data->provisioning &&
+			    (!(data->provisioning_allowed &
+			       EAP_FAST_PROV_AUTH) ||
+			     tls_get_cipher(sm->ssl_ctx, data->ssl.conn,
+					    cipher, sizeof(cipher)) < 0 ||
+			     os_strstr(cipher, "ADH-") ||
+			     os_strstr(cipher, "anon"))) {
+				wpa_printf(MSG_DEBUG, "EAP-FAST: Using "
+					   "anonymous (unauthenticated) "
+					   "provisioning");
+				data->anon_provisioning = 1;
+			} else {
+				data->anon_provisioning = 0;
+            }
+			data->resuming = 0;
+			if (eap_fast_derive_keys(sm, data) < 0) {
+				wpa_printf(MSG_DEBUG,
+					   "EAP-FAST: Could not derive keys");
+				ret->methodState = METHOD_DONE;
+				ret->decision = DECISION_FAIL;
+				wpabuf_free(resp);
+				return NULL;
+			}
+		}
+
+		if (res == 2) {
+			/*
+			 * Application data included in the handshake message.
+			 */
+			wpabuf_free(data->pending_phase2_req);
+			data->pending_phase2_req = resp;
+			resp = NULL;
+			res = eap_fast_decrypt(sm, data, ret, id, &msg, &resp);
+		}
+	}
+
+	if (res == 1) {
+		wpabuf_free(resp);
+		return eap_peer_tls_build_ack(id, EAP_TYPE_FAST,
+					      data->fast_version);
+	}
+
+	return resp;
+}
+
+
+#if 0 /* FIX */
+static Boolean eap_fast_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+	struct eap_fast_data *data = priv;
+	return tls_connection_established(sm->ssl_ctx, data->ssl.conn);
+}
+
+
+static void eap_fast_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+	struct eap_fast_data *data = priv;
+
+	if (data->phase2_priv && data->phase2_method &&
+	    data->phase2_method->deinit_for_reauth)
+		data->phase2_method->deinit_for_reauth(sm, data->phase2_priv);
+	os_free(data->key_block_p);
+	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;
+}
+
+
+static void * eap_fast_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+	struct eap_fast_data *data = priv;
+	if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
+		os_free(data);
+		return NULL;
+	}
+	os_memset(data->key_data, 0, EAP_FAST_KEY_LEN);
+	os_memset(data->emsk, 0, EAP_EMSK_LEN);
+	os_free(data->session_id);
+	data->session_id = NULL;
+	if (data->phase2_priv && data->phase2_method &&
+	    data->phase2_method->init_for_reauth)
+		data->phase2_method->init_for_reauth(sm, data->phase2_priv);
+	data->phase2_success = 0;
+	data->resuming = 1;
+	data->provisioning = 0;
+	data->anon_provisioning = 0;
+	data->simck_idx = 0;
+	return priv;
+}
+#endif
+
+
+static int eap_fast_get_status(struct eap_sm *sm, void *priv, char *buf,
+			       size_t buflen, int verbose)
+{
+	struct eap_fast_data *data = priv;
+	int len, ret;
+
+	len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
+	if (data->phase2_method) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "EAP-FAST Phase2 method=%s\n",
+				  data->phase2_method->name);
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+	}
+	return len;
+}
+
+
+static bool eap_fast_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+	struct eap_fast_data *data = priv;
+	return data->success;
+}
+
+
+static u8 * eap_fast_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_fast_data *data = priv;
+	u8 *key;
+
+	if (!data->success)
+		return NULL;
+
+	key = os_memdup(data->key_data, EAP_FAST_KEY_LEN);
+	if (key == NULL)
+		return NULL;
+
+	*len = EAP_FAST_KEY_LEN;
+
+	return key;
+}
+
+
+static u8 * eap_fast_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_fast_data *data = priv;
+	u8 *id;
+
+	if (!data->success || !data->session_id)
+		return NULL;
+
+	id = os_memdup(data->session_id, data->id_len);
+	if (id == NULL)
+		return NULL;
+
+	*len = data->id_len;
+
+	return id;
+}
+
+
+static u8 * eap_fast_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_fast_data *data = priv;
+	u8 *key;
+
+	if (!data->success)
+		return NULL;
+
+	key = os_memdup(data->emsk, EAP_EMSK_LEN);
+	if (key == NULL)
+		return NULL;
+
+	*len = EAP_EMSK_LEN;
+
+	return key;
+}
+
+
+int eap_peer_fast_register(void)
+{
+	struct eap_method *eap;
+
+	eap = eap_peer_method_alloc(EAP_VENDOR_IETF, EAP_TYPE_FAST, "FAST");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_fast_init;
+	eap->deinit = eap_fast_deinit;
+	eap->process = eap_fast_process;
+	eap->isKeyAvailable = eap_fast_isKeyAvailable;
+	eap->getKey = eap_fast_getKey;
+	eap->getSessionId = eap_fast_get_session_id;
+	eap->get_status = eap_fast_get_status;
+#if 0
+	eap->has_reauth_data = eap_fast_has_reauth_data;
+	eap->deinit_for_reauth = eap_fast_deinit_for_reauth;
+	eap->init_for_reauth = eap_fast_init_for_reauth;
+#endif
+	eap->get_emsk = eap_fast_get_emsk;
+
+	return eap_peer_method_register(eap);
+}

+ 270 - 0
components/wpa_supplicant/src/eap_peer/eap_fast_common.c

@@ -0,0 +1,270 @@
+/*
+ * EAP-FAST common helper functions (RFC 4851)
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "crypto/sha1.h"
+#include "tls/tls.h"
+#include "eap_peer/eap_defs.h"
+#include "eap_peer/eap_tlv_common.h"
+#include "eap_peer/eap_fast_common.h"
+
+
+void eap_fast_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len)
+{
+	struct pac_tlv_hdr hdr;
+	hdr.type = host_to_be16(type);
+	hdr.len = host_to_be16(len);
+	wpabuf_put_data(buf, &hdr, sizeof(hdr));
+}
+
+
+void eap_fast_put_tlv(struct wpabuf *buf, u16 type, const void *data,
+			     u16 len)
+{
+	eap_fast_put_tlv_hdr(buf, type, len);
+	wpabuf_put_data(buf, data, len);
+}
+
+
+void eap_fast_put_tlv_buf(struct wpabuf *buf, u16 type,
+				 const struct wpabuf *data)
+{
+	eap_fast_put_tlv_hdr(buf, type, wpabuf_len(data));
+	wpabuf_put_buf(buf, data);
+}
+
+
+struct wpabuf * eap_fast_tlv_eap_payload(struct wpabuf *buf)
+{
+	struct wpabuf *e;
+
+	if (buf == NULL)
+		return NULL;
+
+	/* Encapsulate EAP packet in EAP-Payload TLV */
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Add EAP-Payload TLV");
+	e = wpabuf_alloc(sizeof(struct pac_tlv_hdr) + wpabuf_len(buf));
+	if (e == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to allocate memory "
+			   "for TLV encapsulation");
+		wpabuf_free(buf);
+		return NULL;
+	}
+	eap_fast_put_tlv_buf(e,
+			     EAP_TLV_TYPE_MANDATORY | EAP_TLV_EAP_PAYLOAD_TLV,
+			     buf);
+	wpabuf_free(buf);
+	return e;
+}
+
+
+void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random,
+				   const u8 *client_random, u8 *master_secret)
+{
+#define TLS_RANDOM_LEN 32
+#define TLS_MASTER_SECRET_LEN 48
+	u8 seed[2 * TLS_RANDOM_LEN];
+
+	wpa_hexdump(MSG_DEBUG, "EAP-FAST: client_random",
+		    client_random, TLS_RANDOM_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-FAST: server_random",
+		    server_random, TLS_RANDOM_LEN);
+
+	/*
+	 * RFC 4851, Section 5.1:
+	 * master_secret = T-PRF(PAC-Key, "PAC to master secret label hash",
+	 *                       server_random + client_random, 48)
+	 */
+	os_memcpy(seed, server_random, TLS_RANDOM_LEN);
+	os_memcpy(seed + TLS_RANDOM_LEN, client_random, TLS_RANDOM_LEN);
+	sha1_t_prf(pac_key, EAP_FAST_PAC_KEY_LEN,
+		   "PAC to master secret label hash",
+		   seed, sizeof(seed), master_secret, TLS_MASTER_SECRET_LEN);
+
+	wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: master_secret",
+			master_secret, TLS_MASTER_SECRET_LEN);
+}
+
+
+u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn, size_t len)
+{
+	u8 *out;
+
+	out = os_malloc(len);
+	if (out == NULL)
+		return NULL;
+
+	if (tls_connection_get_eap_fast_key(ssl_ctx, conn, out, len)) {
+		os_free(out);
+		return NULL;
+	}
+
+	return out;
+}
+
+
+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)
+	 */
+
+	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;
+}
+
+
+int eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk)
+{
+	/*
+	 * RFC 4851, Section 5.4: EAP Master Session Key Genreration
+	 * EMSK = T-PRF(S-IMCK[j],
+	 *        "Extended Session Key Generating Function", 64)
+	 */
+
+	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;
+}
+
+
+int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv,
+		       int tlv_type, u8 *pos, size_t len)
+{
+	switch (tlv_type) {
+	case EAP_TLV_EAP_PAYLOAD_TLV:
+		wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: EAP-Payload TLV",
+			    pos, len);
+		if (tlv->eap_payload_tlv) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+				   "EAP-Payload TLV in the message");
+			tlv->iresult = EAP_TLV_RESULT_FAILURE;
+			return -2;
+		}
+		tlv->eap_payload_tlv = pos;
+		tlv->eap_payload_tlv_len = len;
+		break;
+	case EAP_TLV_RESULT_TLV:
+		wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Result TLV", pos, len);
+		if (tlv->result) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+				   "Result TLV in the message");
+			tlv->result = EAP_TLV_RESULT_FAILURE;
+			return -2;
+		}
+		if (len < 2) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+				   "Result TLV");
+			tlv->result = EAP_TLV_RESULT_FAILURE;
+			break;
+		}
+		tlv->result = WPA_GET_BE16(pos);
+		if (tlv->result != EAP_TLV_RESULT_SUCCESS &&
+		    tlv->result != EAP_TLV_RESULT_FAILURE) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown Result %d",
+				   tlv->result);
+			tlv->result = EAP_TLV_RESULT_FAILURE;
+		}
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Result: %s",
+			   tlv->result == EAP_TLV_RESULT_SUCCESS ?
+			   "Success" : "Failure");
+		break;
+	case EAP_TLV_INTERMEDIATE_RESULT_TLV:
+		wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Intermediate Result TLV",
+			    pos, len);
+		if (len < 2) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+				   "Intermediate-Result TLV");
+			tlv->iresult = EAP_TLV_RESULT_FAILURE;
+			break;
+		}
+		if (tlv->iresult) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+				   "Intermediate-Result TLV in the message");
+			tlv->iresult = EAP_TLV_RESULT_FAILURE;
+			return -2;
+		}
+		tlv->iresult = WPA_GET_BE16(pos);
+		if (tlv->iresult != EAP_TLV_RESULT_SUCCESS &&
+		    tlv->iresult != EAP_TLV_RESULT_FAILURE) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown Intermediate "
+				   "Result %d", tlv->iresult);
+			tlv->iresult = EAP_TLV_RESULT_FAILURE;
+		}
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Intermediate Result: %s",
+			   tlv->iresult == EAP_TLV_RESULT_SUCCESS ?
+			   "Success" : "Failure");
+		break;
+	case EAP_TLV_CRYPTO_BINDING_TLV:
+		wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV",
+			    pos, len);
+		if (tlv->crypto_binding) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+				   "Crypto-Binding TLV in the message");
+			tlv->iresult = EAP_TLV_RESULT_FAILURE;
+			return -2;
+		}
+		tlv->crypto_binding_len = sizeof(struct eap_tlv_hdr) + len;
+		if (tlv->crypto_binding_len < sizeof(*tlv->crypto_binding)) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+				   "Crypto-Binding TLV");
+			tlv->iresult = EAP_TLV_RESULT_FAILURE;
+			return -2;
+		}
+		tlv->crypto_binding = (struct eap_tlv_crypto_binding_tlv *)
+			(pos - sizeof(struct eap_tlv_hdr));
+		break;
+	case EAP_TLV_REQUEST_ACTION_TLV:
+		wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Request-Action TLV",
+			    pos, len);
+		if (tlv->request_action) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+				   "Request-Action TLV in the message");
+			tlv->iresult = EAP_TLV_RESULT_FAILURE;
+			return -2;
+		}
+		if (len < 2) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+				   "Request-Action TLV");
+			tlv->iresult = EAP_TLV_RESULT_FAILURE;
+			break;
+		}
+		tlv->request_action = WPA_GET_BE16(pos);
+		wpa_printf(MSG_DEBUG, "EAP-FAST: Request-Action: %d",
+			   tlv->request_action);
+		break;
+	case EAP_TLV_PAC_TLV:
+		wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: PAC TLV", pos, len);
+		if (tlv->pac) {
+			wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+				   "PAC TLV in the message");
+			tlv->iresult = EAP_TLV_RESULT_FAILURE;
+			return -2;
+		}
+		tlv->pac = pos;
+		tlv->pac_len = len;
+		break;
+	default:
+		/* Unknown TLV */
+		return -1;
+	}
+
+	return 0;
+}

+ 107 - 0
components/wpa_supplicant/src/eap_peer/eap_fast_common.h

@@ -0,0 +1,107 @@
+/*
+ * EAP-FAST definitions (RFC 4851)
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_FAST_H
+#define EAP_FAST_H
+
+#define EAP_FAST_VERSION 1
+#define EAP_FAST_KEY_LEN 64
+#define EAP_FAST_SIMCK_LEN 40
+#define EAP_FAST_SKS_LEN 40
+#define EAP_FAST_CMK_LEN 20
+
+#define TLS_EXT_PAC_OPAQUE 35
+
+/*
+ * RFC 5422: Section 4.2.1 - Formats for PAC TLV Attributes / Type Field
+ * Note: bit 0x8000 (Mandatory) and bit 0x4000 (Reserved) are also defined
+ * in the general PAC TLV format (Section 4.2).
+ */
+#define PAC_TYPE_PAC_KEY 1
+#define PAC_TYPE_PAC_OPAQUE 2
+#define PAC_TYPE_CRED_LIFETIME 3
+#define PAC_TYPE_A_ID 4
+#define PAC_TYPE_I_ID 5
+/*
+ * 6 was previous assigned for SERVER_PROTECTED_DATA, but
+ * draft-cam-winget-eap-fast-provisioning-02.txt changed this to Reserved.
+ */
+#define PAC_TYPE_A_ID_INFO 7
+#define PAC_TYPE_PAC_ACKNOWLEDGEMENT 8
+#define PAC_TYPE_PAC_INFO 9
+#define PAC_TYPE_PAC_TYPE 10
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct pac_tlv_hdr {
+	be16 type;
+	be16 len;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+#define EAP_FAST_PAC_KEY_LEN 32
+
+/* RFC 5422: 4.2.6 PAC-Type TLV */
+#define PAC_TYPE_TUNNEL_PAC 1
+/* Application Specific Short Lived PACs (only in volatile storage) */
+/* User Authorization PAC */
+#define PAC_TYPE_USER_AUTHORIZATION 3
+/* Application Specific Long Lived PACs */
+/* Machine Authentication PAC */
+#define PAC_TYPE_MACHINE_AUTHENTICATION 2
+
+
+/*
+ * RFC 5422:
+ * Section 3.3 - Key Derivations Used in the EAP-FAST Provisioning Exchange
+ */
+struct eap_fast_key_block_provisioning {
+	/* Extra key material after TLS key_block */
+	u8 session_key_seed[EAP_FAST_SKS_LEN];
+	u8 server_challenge[16]; /* MSCHAPv2 ServerChallenge */
+	u8 client_challenge[16]; /* MSCHAPv2 ClientChallenge */
+};
+
+
+struct wpabuf;
+struct tls_connection;
+
+struct eap_fast_tlv_parse {
+	u8 *eap_payload_tlv;
+	size_t eap_payload_tlv_len;
+	struct eap_tlv_crypto_binding_tlv *crypto_binding;
+	size_t crypto_binding_len;
+	int iresult;
+	int result;
+	int request_action;
+	u8 *pac;
+	size_t pac_len;
+};
+
+void eap_fast_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len);
+void eap_fast_put_tlv(struct wpabuf *buf, u16 type, const void *data,
+		      u16 len);
+void eap_fast_put_tlv_buf(struct wpabuf *buf, u16 type,
+			  const struct wpabuf *data);
+struct wpabuf * eap_fast_tlv_eap_payload(struct wpabuf *buf);
+void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random,
+				   const u8 *client_random, u8 *master_secret);
+u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn,
+			 size_t len);
+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);
+
+#endif /* EAP_FAST_H */

+ 932 - 0
components/wpa_supplicant/src/eap_peer/eap_fast_pac.c

@@ -0,0 +1,932 @@
+/*
+ * EAP peer method: EAP-FAST PAC file processing
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include "os.h"
+
+#include "utils/common.h"
+#include "eap_peer/eap_config.h"
+#include "eap_peer/eap_i.h"
+#include "eap_peer/eap_fast_pac.h"
+
+/* TODO: encrypt PAC-Key in the PAC file */
+
+
+/* Text data format */
+static const char *pac_file_hdr =
+	"wpa_supplicant EAP-FAST PAC file - version 1";
+
+/*
+ * Binary data format
+ * 4-octet magic value: 6A E4 92 0C
+ * 2-octet version (big endian)
+ * <version specific data>
+ *
+ * version=0:
+ * Sequence of PAC entries:
+ *   2-octet PAC-Type (big endian)
+ *   32-octet PAC-Key
+ *   2-octet PAC-Opaque length (big endian)
+ *   <variable len> PAC-Opaque data (length bytes)
+ *   2-octet PAC-Info length (big endian)
+ *   <variable len> PAC-Info data (length bytes)
+ */
+
+#define EAP_FAST_PAC_BINARY_MAGIC 0x6ae4920c
+#define EAP_FAST_PAC_BINARY_FORMAT_VERSION 0
+
+
+/**
+ * eap_fast_free_pac - Free PAC data
+ * @pac: Pointer to the PAC entry
+ *
+ * Note that the PAC entry must not be in a list since this function does not
+ * remove the list links.
+ */
+void eap_fast_free_pac(struct eap_fast_pac *pac)
+{
+	os_free(pac->pac_opaque);
+	os_free(pac->pac_info);
+	os_free(pac->a_id);
+	os_free(pac->i_id);
+	os_free(pac->a_id_info);
+	os_free(pac);
+}
+
+
+/**
+ * eap_fast_get_pac - Get a PAC entry based on A-ID
+ * @pac_root: Pointer to root of the PAC list
+ * @a_id: A-ID to search for
+ * @a_id_len: Length of A-ID
+ * @pac_type: PAC-Type to search for
+ * Returns: Pointer to the PAC entry, or %NULL if A-ID not found
+ */
+struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_pac *pac_root,
+				       const u8 *a_id, size_t a_id_len,
+				       u16 pac_type)
+{
+	struct eap_fast_pac *pac = pac_root;
+
+	while (pac) {
+		if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
+		    os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
+			return pac;
+		}
+		pac = pac->next;
+	}
+	return NULL;
+}
+
+
+static void eap_fast_remove_pac(struct eap_fast_pac **pac_root,
+				struct eap_fast_pac **pac_current,
+				const u8 *a_id, size_t a_id_len, u16 pac_type)
+{
+	struct eap_fast_pac *pac, *prev;
+
+	pac = *pac_root;
+	prev = NULL;
+
+	while (pac) {
+		if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
+		    os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
+			if (prev == NULL)
+				*pac_root = pac->next;
+			else
+				prev->next = pac->next;
+			if (*pac_current == pac)
+				*pac_current = NULL;
+			eap_fast_free_pac(pac);
+			break;
+		}
+		prev = pac;
+		pac = pac->next;
+	}
+}
+
+
+static int eap_fast_copy_buf(u8 **dst, size_t *dst_len,
+			     const u8 *src, size_t src_len)
+{
+	if (src) {
+		*dst = os_memdup(src, src_len);
+		if (*dst == NULL)
+			return -1;
+		*dst_len = src_len;
+	}
+	return 0;
+}
+
+
+/**
+ * eap_fast_add_pac - Add a copy of a PAC entry to a list
+ * @pac_root: Pointer to PAC list root pointer
+ * @pac_current: Pointer to the current PAC pointer
+ * @entry: New entry to clone and add to the list
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function makes a clone of the given PAC entry and adds this copied
+ * entry to the list (pac_root). If an old entry for the same A-ID is found,
+ * it will be removed from the PAC list and in this case, pac_current entry
+ * is set to %NULL if it was the removed entry.
+ */
+int eap_fast_add_pac(struct eap_fast_pac **pac_root,
+		     struct eap_fast_pac **pac_current,
+		     struct eap_fast_pac *entry)
+{
+	struct eap_fast_pac *pac;
+
+	if (entry == NULL || entry->a_id == NULL)
+		return -1;
+
+	/* Remove a possible old entry for the matching A-ID. */
+	eap_fast_remove_pac(pac_root, pac_current,
+			    entry->a_id, entry->a_id_len, entry->pac_type);
+
+	/* Allocate a new entry and add it to the list of PACs. */
+	pac = os_zalloc(sizeof(*pac));
+	if (pac == NULL)
+		return -1;
+
+	pac->pac_type = entry->pac_type;
+	os_memcpy(pac->pac_key, entry->pac_key, EAP_FAST_PAC_KEY_LEN);
+	if (eap_fast_copy_buf(&pac->pac_opaque, &pac->pac_opaque_len,
+			      entry->pac_opaque, entry->pac_opaque_len) < 0 ||
+	    eap_fast_copy_buf(&pac->pac_info, &pac->pac_info_len,
+			      entry->pac_info, entry->pac_info_len) < 0 ||
+	    eap_fast_copy_buf(&pac->a_id, &pac->a_id_len,
+			      entry->a_id, entry->a_id_len) < 0 ||
+	    eap_fast_copy_buf(&pac->i_id, &pac->i_id_len,
+			      entry->i_id, entry->i_id_len) < 0 ||
+	    eap_fast_copy_buf(&pac->a_id_info, &pac->a_id_info_len,
+			      entry->a_id_info, entry->a_id_info_len) < 0) {
+		eap_fast_free_pac(pac);
+		return -1;
+	}
+
+	pac->next = *pac_root;
+	*pac_root = pac;
+
+	return 0;
+}
+
+
+struct eap_fast_read_ctx {
+	FILE *f;
+	const char *pos;
+	const char *end;
+	int line;
+	char *buf;
+	size_t buf_len;
+};
+
+static int eap_fast_read_line(struct eap_fast_read_ctx *rc, char **value)
+{
+	char *pos;
+
+	rc->line++;
+	if (rc->f) {
+		if (fgets(rc->buf, rc->buf_len, rc->f) == NULL)
+			return -1;
+	} else {
+		const char *l_end;
+		size_t len;
+		if (rc->pos >= rc->end)
+			return -1;
+		l_end = rc->pos;
+		while (l_end < rc->end && *l_end != '\n')
+			l_end++;
+		len = l_end - rc->pos;
+		if (len >= rc->buf_len)
+			len = rc->buf_len - 1;
+		os_memcpy(rc->buf, rc->pos, len);
+		rc->buf[len] = '\0';
+		rc->pos = l_end + 1;
+	}
+
+	rc->buf[rc->buf_len - 1] = '\0';
+	pos = rc->buf;
+	while (*pos != '\0') {
+		if (*pos == '\n' || *pos == '\r') {
+			*pos = '\0';
+			break;
+		}
+		pos++;
+	}
+
+	pos = os_strchr(rc->buf, '=');
+	if (pos)
+		*pos++ = '\0';
+	*value = pos;
+
+	return 0;
+}
+
+
+static u8 * eap_fast_parse_hex(const char *value, size_t *len)
+{
+	int hlen;
+	u8 *buf;
+
+	if (value == NULL)
+		return NULL;
+	hlen = os_strlen(value);
+	if (hlen & 1)
+		return NULL;
+	*len = hlen / 2;
+	buf = os_malloc(*len);
+	if (buf == NULL)
+		return NULL;
+	if (hexstr2bin(value, buf, *len)) {
+		os_free(buf);
+		return NULL;
+	}
+	return buf;
+}
+
+
+static int eap_fast_init_pac_data(struct eap_sm *sm, const char *pac_file,
+				  struct eap_fast_read_ctx *rc)
+{
+	os_memset(rc, 0, sizeof(*rc));
+
+	rc->buf_len = 2048;
+	rc->buf = os_malloc(rc->buf_len);
+	if (rc->buf == NULL)
+		return -1;
+
+	if (os_strncmp(pac_file, "blob://", 7) == 0) {
+		const struct wpa_config_blob *blob;
+		blob = eap_get_config_blob(sm, pac_file);
+		if (blob == NULL) {
+			wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - "
+				   "assume no PAC entries have been "
+				   "provisioned", pac_file);
+			os_free(rc->buf);
+			return -1;
+		}
+		rc->pos = (char *) blob->data;
+		rc->end = (char *) blob->data + blob->len;
+	} else {
+		rc->f = fopen(pac_file, "rb");
+		if (rc->f == NULL) {
+			wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - "
+				   "assume no PAC entries have been "
+				   "provisioned", pac_file);
+			os_free(rc->buf);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+static void eap_fast_deinit_pac_data(struct eap_fast_read_ctx *rc)
+{
+	os_free(rc->buf);
+	if (rc->f)
+		fclose(rc->f);
+}
+
+
+static const char * eap_fast_parse_start(struct eap_fast_pac **pac)
+{
+	if (*pac)
+		return "START line without END";
+
+	*pac = os_zalloc(sizeof(struct eap_fast_pac));
+	if (*pac == NULL)
+		return "No memory for PAC entry";
+	(*pac)->pac_type = PAC_TYPE_TUNNEL_PAC;
+	return NULL;
+}
+
+
+static const char * eap_fast_parse_end(struct eap_fast_pac **pac_root,
+				       struct eap_fast_pac **pac)
+{
+	if (*pac == NULL)
+		return "END line without START";
+	if (*pac_root) {
+		struct eap_fast_pac *end = *pac_root;
+		while (end->next)
+			end = end->next;
+		end->next = *pac;
+	} else
+		*pac_root = *pac;
+
+	*pac = NULL;
+	return NULL;
+}
+
+
+static const char * eap_fast_parse_pac_type(struct eap_fast_pac *pac,
+					    char *pos)
+{
+	if (!pos)
+		return "Cannot parse pac type";
+	pac->pac_type = atoi(pos);
+	if (pac->pac_type != PAC_TYPE_TUNNEL_PAC &&
+	    pac->pac_type != PAC_TYPE_USER_AUTHORIZATION &&
+	    pac->pac_type != PAC_TYPE_MACHINE_AUTHENTICATION)
+		return "Unrecognized PAC-Type";
+
+	return NULL;
+}
+
+
+static const char * eap_fast_parse_pac_key(struct eap_fast_pac *pac, char *pos)
+{
+	u8 *key;
+	size_t key_len;
+
+	key = eap_fast_parse_hex(pos, &key_len);
+	if (key == NULL || key_len != EAP_FAST_PAC_KEY_LEN) {
+		os_free(key);
+		return "Invalid PAC-Key";
+	}
+
+	os_memcpy(pac->pac_key, key, EAP_FAST_PAC_KEY_LEN);
+	os_free(key);
+
+	return NULL;
+}
+
+
+static const char * eap_fast_parse_pac_opaque(struct eap_fast_pac *pac,
+					      char *pos)
+{
+	os_free(pac->pac_opaque);
+	pac->pac_opaque = eap_fast_parse_hex(pos, &pac->pac_opaque_len);
+	if (pac->pac_opaque == NULL)
+		return "Invalid PAC-Opaque";
+	return NULL;
+}
+
+
+static const char * eap_fast_parse_a_id(struct eap_fast_pac *pac, char *pos)
+{
+	os_free(pac->a_id);
+	pac->a_id = eap_fast_parse_hex(pos, &pac->a_id_len);
+	if (pac->a_id == NULL)
+		return "Invalid A-ID";
+	return NULL;
+}
+
+
+static const char * eap_fast_parse_i_id(struct eap_fast_pac *pac, char *pos)
+{
+	os_free(pac->i_id);
+	pac->i_id = eap_fast_parse_hex(pos, &pac->i_id_len);
+	if (pac->i_id == NULL)
+		return "Invalid I-ID";
+	return NULL;
+}
+
+
+static const char * eap_fast_parse_a_id_info(struct eap_fast_pac *pac,
+					     char *pos)
+{
+	os_free(pac->a_id_info);
+	pac->a_id_info = eap_fast_parse_hex(pos, &pac->a_id_info_len);
+	if (pac->a_id_info == NULL)
+		return "Invalid A-ID-Info";
+	return NULL;
+}
+
+
+/**
+ * eap_fast_load_pac - Load PAC entries (text format)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @pac_root: Pointer to root of the PAC list (to be filled)
+ * @pac_file: Name of the PAC file/blob to load
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root,
+		      const char *pac_file)
+{
+	struct eap_fast_read_ctx rc;
+	struct eap_fast_pac *pac = NULL;
+	int count = 0;
+	char *pos;
+	const char *err = NULL;
+
+	if (pac_file == NULL)
+		return -1;
+
+	if (eap_fast_init_pac_data(sm, pac_file, &rc) < 0)
+		return 0;
+
+	if (eap_fast_read_line(&rc, &pos) < 0) {
+		/* empty file - assume it is fine to overwrite */
+        printf("\n\nassuming it is fine to overwrite... \n\n");
+		eap_fast_deinit_pac_data(&rc);
+		return 0;
+	}
+    printf("\n\nPAC FILE =\n%s", rc.pos);
+	if (os_strcmp(pac_file_hdr, rc.buf) != 0)
+		err = "Unrecognized header line";
+
+	while (!err && eap_fast_read_line(&rc, &pos) == 0) {
+		if (os_strcmp(rc.buf, "START") == 0)
+			err = eap_fast_parse_start(&pac);
+		else if (os_strcmp(rc.buf, "END") == 0) {
+			err = eap_fast_parse_end(pac_root, &pac);
+			count++;
+		} else if (!pac)
+			err = "Unexpected line outside START/END block";
+		else if (os_strcmp(rc.buf, "PAC-Type") == 0)
+			err = eap_fast_parse_pac_type(pac, pos);
+		else if (os_strcmp(rc.buf, "PAC-Key") == 0)
+			err = eap_fast_parse_pac_key(pac, pos);
+		else if (os_strcmp(rc.buf, "PAC-Opaque") == 0)
+			err = eap_fast_parse_pac_opaque(pac, pos);
+		else if (os_strcmp(rc.buf, "A-ID") == 0)
+			err = eap_fast_parse_a_id(pac, pos);
+		else if (os_strcmp(rc.buf, "I-ID") == 0)
+			err = eap_fast_parse_i_id(pac, pos);
+		else if (os_strcmp(rc.buf, "A-ID-Info") == 0)
+			err = eap_fast_parse_a_id_info(pac, pos);
+	}
+
+	if (pac) {
+		if (!err)
+			err = "PAC block not terminated with END";
+		eap_fast_free_pac(pac);
+	}
+
+	eap_fast_deinit_pac_data(&rc);
+
+	if (err) {
+		wpa_printf(MSG_INFO, "EAP-FAST: %s in '%s:%d'",
+			   err, pac_file, rc.line);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Read %d PAC entries from '%s'",
+		   count, pac_file);
+
+	return 0;
+}
+
+
+static void eap_fast_write(char **buf, char **pos, size_t *buf_len,
+			   const char *field, const u8 *data,
+			   size_t len, int txt)
+{
+	size_t i, need;
+	int ret;
+	char *end;
+
+	if (data == NULL || buf == NULL || *buf == NULL ||
+	    pos == NULL || *pos == NULL || *pos < *buf)
+		return;
+
+	need = os_strlen(field) + len * 2 + 30;
+	if (txt)
+		need += os_strlen(field) + len + 20;
+
+	if (*pos - *buf + need > *buf_len) {
+		char *nbuf = os_realloc(*buf, *buf_len + need);
+		if (nbuf == NULL) {
+			os_free(*buf);
+			*buf = NULL;
+			return;
+		}
+		*pos = nbuf + (*pos - *buf);
+		*buf = nbuf;
+		*buf_len += need;
+	}
+	end = *buf + *buf_len;
+
+	ret = os_snprintf(*pos, end - *pos, "%s=", field);
+	if (os_snprintf_error(end - *pos, ret))
+		return;
+	*pos += ret;
+	*pos += wpa_snprintf_hex(*pos, end - *pos, data, len);
+	ret = os_snprintf(*pos, end - *pos, "\n");
+	if (os_snprintf_error(end - *pos, ret))
+		return;
+	*pos += ret;
+
+	if (txt) {
+		ret = os_snprintf(*pos, end - *pos, "%s-txt=", field);
+		if (os_snprintf_error(end - *pos, ret))
+			return;
+		*pos += ret;
+		for (i = 0; i < len; i++) {
+			ret = os_snprintf(*pos, end - *pos, "%c", data[i]);
+			if (os_snprintf_error(end - *pos, ret))
+				return;
+			*pos += ret;
+		}
+		ret = os_snprintf(*pos, end - *pos, "\n");
+		if (os_snprintf_error(end - *pos, ret))
+			return;
+		*pos += ret;
+	}
+}
+
+
+static int eap_fast_write_pac(struct eap_sm *sm, const char *pac_file,
+			      char *buf, size_t len)
+{
+	if (os_strncmp(pac_file, "blob://", 7) == 0) {
+		struct wpa_config_blob *blob;
+		blob = os_zalloc(sizeof(*blob));
+		if (blob == NULL)
+			return -1;
+		blob->data = (u8 *) buf;
+		blob->len = len;
+		buf = NULL;
+		blob->name = os_strdup(pac_file + 7);
+		if (blob->name == NULL) {
+			os_free(blob);
+			return -1;
+		}
+		eap_set_config_blob(sm, blob);
+	} else {
+		FILE *f;
+		f = fopen(pac_file, "wb");
+		if (f == NULL) {
+			wpa_printf(MSG_INFO, "EAP-FAST: Failed to open PAC "
+				   "file '%s' for writing", pac_file);
+			return -1;
+		}
+		if (fwrite(buf, 1, len, f) != len) {
+			wpa_printf(MSG_INFO, "EAP-FAST: Failed to write all "
+				   "PACs into '%s'", pac_file);
+			fclose(f);
+			return -1;
+		}
+		os_free(buf);
+		fclose(f);
+	}
+
+	return 0;
+}
+
+
+static int eap_fast_add_pac_data(struct eap_fast_pac *pac, char **buf,
+				 char **pos, size_t *buf_len)
+{
+	int ret;
+
+	ret = os_snprintf(*pos, *buf + *buf_len - *pos,
+			  "START\nPAC-Type=%d\n", pac->pac_type);
+	if (os_snprintf_error(*buf + *buf_len - *pos, ret))
+		return -1;
+
+	*pos += ret;
+	eap_fast_write(buf, pos, buf_len, "PAC-Key",
+		       pac->pac_key, EAP_FAST_PAC_KEY_LEN, 0);
+	eap_fast_write(buf, pos, buf_len, "PAC-Opaque",
+		       pac->pac_opaque, pac->pac_opaque_len, 0);
+	eap_fast_write(buf, pos, buf_len, "PAC-Info",
+		       pac->pac_info, pac->pac_info_len, 0);
+	eap_fast_write(buf, pos, buf_len, "A-ID",
+		       pac->a_id, pac->a_id_len, 0);
+	eap_fast_write(buf, pos, buf_len, "I-ID",
+		       pac->i_id, pac->i_id_len, 1);
+	eap_fast_write(buf, pos, buf_len, "A-ID-Info",
+		       pac->a_id_info, pac->a_id_info_len, 1);
+	if (*buf == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-FAST: No memory for PAC "
+			   "data");
+		return -1;
+	}
+	ret = os_snprintf(*pos, *buf + *buf_len - *pos, "END\n");
+	if (os_snprintf_error(*buf + *buf_len - *pos, ret))
+		return -1;
+	*pos += ret;
+
+	return 0;
+}
+
+
+/**
+ * eap_fast_save_pac - Save PAC entries (text format)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @pac_root: Root of the PAC list
+ * @pac_file: Name of the PAC file/blob
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_pac *pac_root,
+		      const char *pac_file)
+{
+	struct eap_fast_pac *pac;
+	int ret, count = 0;
+	char *buf, *pos;
+	size_t buf_len;
+
+	if (pac_file == NULL)
+		return -1;
+
+	buf_len = 1024;
+	pos = buf = os_malloc(buf_len);
+	if (buf == NULL)
+		return -1;
+
+	ret = os_snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr);
+	if (os_snprintf_error(buf + buf_len - pos, ret)) {
+		os_free(buf);
+		return -1;
+	}
+	pos += ret;
+
+	pac = pac_root;
+	while (pac) {
+		if (eap_fast_add_pac_data(pac, &buf, &pos, &buf_len)) {
+			os_free(buf);
+			return -1;
+		}
+		count++;
+		pac = pac->next;
+	}
+
+	if (eap_fast_write_pac(sm, pac_file, buf, pos - buf)) {
+		os_free(buf);
+		return -1;
+	}
+
+    wpa_printf(MSG_DEBUG, "PAC file: %s", (sm->blob[3].data));
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %d PAC entries into '%s'",
+		   count, pac_file);
+
+	return 0;
+}
+
+
+/**
+ * eap_fast_pac_list_truncate - Truncate a PAC list to the given length
+ * @pac_root: Root of the PAC list
+ * @max_len: Maximum length of the list (>= 1)
+ * Returns: Number of PAC entries removed
+ */
+size_t eap_fast_pac_list_truncate(struct eap_fast_pac *pac_root,
+				  size_t max_len)
+{
+	struct eap_fast_pac *pac, *prev;
+	size_t count;
+
+	pac = pac_root;
+	prev = NULL;
+	count = 0;
+
+	while (pac) {
+		count++;
+		if (count > max_len)
+			break;
+		prev = pac;
+		pac = pac->next;
+	}
+
+	if (count <= max_len || prev == NULL)
+		return 0;
+
+	count = 0;
+	prev->next = NULL;
+
+	while (pac) {
+		prev = pac;
+		pac = pac->next;
+		eap_fast_free_pac(prev);
+		count++;
+	}
+
+	return count;
+}
+
+
+static void eap_fast_pac_get_a_id(struct eap_fast_pac *pac)
+{
+	u8 *pos, *end;
+	u16 type, len;
+
+	pos = pac->pac_info;
+	end = pos + pac->pac_info_len;
+
+	while (end - pos > 4) {
+		type = WPA_GET_BE16(pos);
+		pos += 2;
+		len = WPA_GET_BE16(pos);
+		pos += 2;
+		if (len > (unsigned int) (end - pos))
+			break;
+
+		if (type == PAC_TYPE_A_ID) {
+			os_free(pac->a_id);
+			pac->a_id = os_memdup(pos, len);
+			if (pac->a_id == NULL)
+				break;
+			pac->a_id_len = len;
+		}
+
+		if (type == PAC_TYPE_A_ID_INFO) {
+			os_free(pac->a_id_info);
+			pac->a_id_info = os_memdup(pos, len);
+			if (pac->a_id_info == NULL)
+				break;
+			pac->a_id_info_len = len;
+		}
+
+		pos += len;
+	}
+}
+
+
+/**
+ * eap_fast_load_pac_bin - Load PAC entries (binary format)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @pac_root: Pointer to root of the PAC list (to be filled)
+ * @pac_file: Name of the PAC file/blob to load
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root,
+			  const char *pac_file)
+{
+	const struct wpa_config_blob *blob = NULL;
+	u8 *buf, *end, *pos;
+	size_t len = 0;
+    size_t count = 0;
+	struct eap_fast_pac *pac, *prev;
+
+	*pac_root = NULL;
+
+	if (pac_file == NULL)
+		return -1;
+
+	if (sm->config.pac_file != NULL) { //if (os_strncmp(pac_file, "blob://", 7) == 0) {
+		blob = eap_get_config_blob(sm, PAC_FILE_NAME);
+		if (blob == NULL) {
+			wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - "
+				   "assume no PAC entries have been "
+				   "provisioned", pac_file);
+			return 0;
+		}
+		buf = (u8 *) blob->data;
+		len = blob->len;
+	} else {
+		buf = (u8 *) sm->blob[3].data; //(u8 *) os_readfile(pac_file, &len);
+		if (buf == NULL) {
+			wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - "
+				   "assume no PAC entries have been "
+				   "provisioned", pac_file);
+			return 0;
+		}
+	}
+
+	if (len == 0) {
+		if (blob == NULL)
+			os_free(buf);
+		return 0;
+	}
+
+	if (len < 6 || WPA_GET_BE32(buf) != EAP_FAST_PAC_BINARY_MAGIC ||
+	    WPA_GET_BE16(buf + 4) != EAP_FAST_PAC_BINARY_FORMAT_VERSION) {
+		wpa_printf(MSG_INFO, "EAP-FAST: Invalid PAC file '%s' (bin)",
+			   pac_file);
+		if (blob == NULL)
+			os_free(buf);
+		return -1;
+	}
+
+	pac = prev = NULL;
+	pos = buf + 6;
+	end = buf + len;
+	while (pos < end) {
+		u16 val;
+
+		if (end - pos < 2 + EAP_FAST_PAC_KEY_LEN + 2 + 2) {
+			pac = NULL;
+			goto parse_fail;
+		}
+
+		pac = os_zalloc(sizeof(*pac));
+		if (pac == NULL)
+			goto parse_fail;
+
+		pac->pac_type = WPA_GET_BE16(pos);
+		pos += 2;
+		os_memcpy(pac->pac_key, pos, EAP_FAST_PAC_KEY_LEN);
+		pos += EAP_FAST_PAC_KEY_LEN;
+		val = WPA_GET_BE16(pos);
+		pos += 2;
+		if (val > end - pos)
+			goto parse_fail;
+		pac->pac_opaque_len = val;
+		pac->pac_opaque = os_memdup(pos, pac->pac_opaque_len);
+		if (pac->pac_opaque == NULL)
+			goto parse_fail;
+		pos += pac->pac_opaque_len;
+		if (2 > end - pos)
+			goto parse_fail;
+		val = WPA_GET_BE16(pos);
+		pos += 2;
+		if (val > end - pos)
+			goto parse_fail;
+		pac->pac_info_len = val;
+		pac->pac_info = os_memdup(pos, pac->pac_info_len);
+		if (pac->pac_info == NULL)
+			goto parse_fail;
+		pos += pac->pac_info_len;
+		eap_fast_pac_get_a_id(pac);
+
+		count++;
+		if (prev)
+			prev->next = pac;
+		else
+			*pac_root = pac;
+		prev = pac;
+	}
+
+	if (blob == NULL)
+		os_free(buf);
+
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Read %lu PAC entries from '%s' (bin)",
+		   (unsigned long) count, pac_file);
+
+	return 0;
+
+parse_fail:
+	wpa_printf(MSG_INFO, "EAP-FAST: Failed to parse PAC file '%s' (bin)",
+		   pac_file);
+	if (blob == NULL)
+		os_free(buf);
+	if (pac)
+		eap_fast_free_pac(pac);
+	return -1;
+}
+
+
+/**
+ * eap_fast_save_pac_bin - Save PAC entries (binary format)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @pac_root: Root of the PAC list
+ * @pac_file: Name of the PAC file/blob
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_fast_save_pac_bin(struct eap_sm *sm, struct eap_fast_pac *pac_root,
+			  const char *pac_file)
+{
+	size_t len, count = 0;
+	struct eap_fast_pac *pac;
+	u8 *buf, *pos;
+
+	len = 6;
+	pac = pac_root;
+	while (pac) {
+		if (pac->pac_opaque_len > 65535 ||
+		    pac->pac_info_len > 65535)
+			return -1;
+		len += 2 + EAP_FAST_PAC_KEY_LEN + 2 + pac->pac_opaque_len +
+			2 + pac->pac_info_len;
+		pac = pac->next;
+	}
+
+	buf = os_malloc(len);
+	if (buf == NULL)
+		return -1;
+
+	pos = buf;
+	WPA_PUT_BE32(pos, EAP_FAST_PAC_BINARY_MAGIC);
+	pos += 4;
+	WPA_PUT_BE16(pos, EAP_FAST_PAC_BINARY_FORMAT_VERSION);
+	pos += 2;
+
+	pac = pac_root;
+	while (pac) {
+		WPA_PUT_BE16(pos, pac->pac_type);
+		pos += 2;
+		os_memcpy(pos, pac->pac_key, EAP_FAST_PAC_KEY_LEN);
+		pos += EAP_FAST_PAC_KEY_LEN;
+		WPA_PUT_BE16(pos, pac->pac_opaque_len);
+		pos += 2;
+		os_memcpy(pos, pac->pac_opaque, pac->pac_opaque_len);
+		pos += pac->pac_opaque_len;
+		WPA_PUT_BE16(pos, pac->pac_info_len);
+		pos += 2;
+		os_memcpy(pos, pac->pac_info, pac->pac_info_len);
+		pos += pac->pac_info_len;
+
+		pac = pac->next;
+		count++;
+	}
+
+	if (eap_fast_write_pac(sm, pac_file, (char *) buf, len)) {
+		os_free(buf);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %lu PAC entries into '%s' "
+		   "(bin)", (unsigned long) count, pac_file);
+
+	return 0;
+}

+ 50 - 0
components/wpa_supplicant/src/eap_peer/eap_fast_pac.h

@@ -0,0 +1,50 @@
+/*
+ * EAP peer method: EAP-FAST PAC file processing
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_FAST_PAC_H
+#define EAP_FAST_PAC_H
+
+#include "eap_peer/eap_fast_common.h"
+
+struct eap_fast_pac {
+	struct eap_fast_pac *next;
+
+	u8 pac_key[EAP_FAST_PAC_KEY_LEN];
+	u8 *pac_opaque;
+	size_t pac_opaque_len;
+	u8 *pac_info;
+	size_t pac_info_len;
+	u8 *a_id;
+	size_t a_id_len;
+	u8 *i_id;
+	size_t i_id_len;
+	u8 *a_id_info;
+	size_t a_id_info_len;
+	u16 pac_type;
+};
+
+
+void eap_fast_free_pac(struct eap_fast_pac *pac);
+struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_pac *pac_root,
+				       const u8 *a_id, size_t a_id_len,
+				       u16 pac_type);
+int eap_fast_add_pac(struct eap_fast_pac **pac_root,
+		     struct eap_fast_pac **pac_current,
+		     struct eap_fast_pac *entry);
+int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root,
+		      const char *pac_file);
+int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_pac *pac_root,
+		      const char *pac_file);
+size_t eap_fast_pac_list_truncate(struct eap_fast_pac *pac_root,
+				  size_t max_len);
+int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root,
+			  const char *pac_file);
+int eap_fast_save_pac_bin(struct eap_sm *sm, struct eap_fast_pac *pac_root,
+			  const char *pac_file);
+
+#endif /* EAP_FAST_PAC_H */

+ 11 - 1
components/wpa_supplicant/src/eap_peer/eap_i.h

@@ -90,13 +90,15 @@ struct eap_method {
 	void (*deinit_for_reauth)(struct eap_sm *sm, void *priv);
 	void * (*init_for_reauth)(struct eap_sm *sm, void *priv);
 	u8 * (*getSessionId)(struct eap_sm *sm, void *priv, size_t *len);
+	u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len);
 };
 
 #define CLIENT_CERT_NAME	"CLC"
 #define CA_CERT_NAME		"CAC"
 #define PRIVATE_KEY_NAME	"PVK"
+#define PAC_FILE_NAME		"PAC"
 #define BLOB_NAME_LEN		3
-#define BLOB_NUM		3
+#define BLOB_NUM		4
 
 enum SIG_WPA2 {
     SIG_WPA2_START = 0,
@@ -111,6 +113,7 @@ enum SIG_WPA2 {
 struct eap_sm {
 	void *eap_method_priv;
 
+	void *msg_ctx;
 	void *ssl_ctx;
 
 	unsigned int workaround;
@@ -126,6 +129,12 @@ struct eap_sm {
 	u8 finish_state;
 
 	int init_phase2;
+	/* Optional challenges generated in Phase 1 (EAP-FAST) */
+	u8 *peer_challenge, *auth_challenge;
+
+	unsigned int expected_failure:1;
+	unsigned int ext_cert_check:1;
+	unsigned int waiting_ext_cert_check:1;
 	bool peap_done;
 
 	u8 *eapKeyData;
@@ -144,6 +153,7 @@ const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len);
 const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash);
 const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len);
 struct eap_peer_config * eap_get_config(struct eap_sm *sm);
+void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob);
 const struct wpa_config_blob * eap_get_config_blob(struct eap_sm *sm, const char *name);
 bool wifi_sta_get_enterprise_disable_time_check(void);
 

+ 1 - 0
components/wpa_supplicant/src/eap_peer/eap_methods.h

@@ -30,6 +30,7 @@ void eap_peer_unregister_methods(void);
 int eap_peer_tls_register(void);
 int eap_peer_peap_register(void);
 int eap_peer_ttls_register(void);
+int eap_peer_fast_register(void);
 int eap_peer_mschapv2_register(void);
 
 void eap_peer_unregister_methods(void);

+ 280 - 80
components/wpa_supplicant/src/eap_peer/eap_mschapv2.c

@@ -21,6 +21,7 @@
 #include "eap_peer/eap_config.h"
 #include "eap_peer/mschapv2.h"
 #include "eap_peer/eap_methods.h"
+#include "common/wpa_ctrl.h"
 
 #define MSCHAPV2_OP_CHALLENGE		1
 #define MSCHAPV2_OP_RESPONSE		2
@@ -104,6 +105,24 @@ eap_mschapv2_init(struct eap_sm *sm)
 	if (data == NULL)
 		return NULL;
 
+	if (sm->peer_challenge) {
+		data->peer_challenge = os_memdup(sm->peer_challenge,
+						 MSCHAPV2_CHAL_LEN);
+		if (data->peer_challenge == NULL) {
+			eap_mschapv2_deinit(sm, data);
+			return NULL;
+		}
+	}
+
+	if (sm->auth_challenge) {
+		data->auth_challenge = os_memdup(sm->auth_challenge,
+						 MSCHAPV2_CHAL_LEN);
+		if (data->auth_challenge == NULL) {
+			eap_mschapv2_deinit(sm, data);
+			return NULL;
+		}
+	}
+
 	data->phase2 = sm->init_phase2;
 
 	return data;
@@ -139,8 +158,15 @@ eap_mschapv2_challenge_reply(
 	ms = wpabuf_put(resp, sizeof(*ms));
 	ms->op_code = MSCHAPV2_OP_RESPONSE;
 	ms->mschapv2_id = mschapv2_id;
-	if (data->prev_error)
+	if (data->prev_error) {
+		/*
+		 * TODO: this does not seem to be enough when processing two
+		 * or more failure messages. IAS did not increment mschapv2_id
+		 * in its own packets, but it seemed to expect the peer to
+		 * increment this for all packets(?).
+		 */
 		ms->mschapv2_id++;
+	}
 	WPA_PUT_BE16(ms->ms_length, ms_len);
 	wpabuf_put_u8(resp, sizeof(*r));
 
@@ -148,33 +174,53 @@ eap_mschapv2_challenge_reply(
 	r = wpabuf_put(resp, sizeof(*r));
 	peer_challenge = r->peer_challenge;
 	if (data->peer_challenge) {
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge generated "
+			   "in Phase 1");
 		peer_challenge = data->peer_challenge;
-	os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN);
+		os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN);
 	} else if (random_get_bytes(peer_challenge, MSCHAPV2_CHAL_LEN)) {
 		wpabuf_free(resp);
 		return NULL;
 	}
 	os_memset(r->reserved, 0, 8);
-	if (data->auth_challenge)
+	if (data->auth_challenge) {
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge generated "
+			   "in Phase 1");
 		auth_challenge = data->auth_challenge;
+	}
 	if (mschapv2_derive_response(identity, identity_len, password,
 				     password_len, pwhash, auth_challenge,
 				     peer_challenge, r->nt_response,
 				     data->auth_response, data->master_key)) {
+		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to derive "
+			   "response");
 		wpabuf_free(resp);
 		return NULL;
 	}
 	data->auth_response_valid = 1;
 	data->master_key_valid = 1;
 
-	r->flags = 0;
+	r->flags = 0; /* reserved, must be zero */
 
 	wpabuf_put_data(resp, identity, identity_len);
+	wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
+		   "(response)", id, ms->mschapv2_id);
 	return resp;
 }
 
-static struct wpabuf * 
-eap_mschapv2_challenge(
+
+/**
+ * eap_mschapv2_process - Process an EAP-MSCHAPv2 challenge message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Pointer to private EAP method data from eap_mschapv2_init()
+ * @ret: Return values from EAP request validation and processing
+ * @req: Pointer to EAP-MSCHAPv2 header from the request
+ * @req_len: Length of the EAP-MSCHAPv2 data
+ * @id: EAP identifier used in the request
+ * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
+ * no reply available
+ */
+static struct wpabuf * eap_mschapv2_challenge(
 	struct eap_sm *sm, struct eap_mschapv2_data *data,
 	struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req,
 	size_t req_len, u8 id)
@@ -186,7 +232,10 @@ eap_mschapv2_challenge(
 	    eap_get_config_password(sm, &len) == NULL)
 		return NULL;
 
+	wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received challenge");
 	if (req_len < sizeof(*req) + 1) {
+		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge data "
+			   "(len %lu)", (unsigned long) req_len);
 		ret->ignore = true;
 		return NULL;
 	}
@@ -194,21 +243,30 @@ eap_mschapv2_challenge(
 	challenge_len = *pos++;
 	len = req_len - sizeof(*req) - 1;
 	if (challenge_len != MSCHAPV2_CHAL_LEN) {
+		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid challenge length "
+			   "%lu", (unsigned long) challenge_len);
 		ret->ignore = true;
 		return NULL;
 	}
 
 	if (len < challenge_len) {
+		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge"
+			   " packet: len=%lu challenge_len=%lu",
+			   (unsigned long) len, (unsigned long) challenge_len);
 		ret->ignore = true;
 		return NULL;
 	}
 
-	if (data->passwd_change_challenge_valid)
+	if (data->passwd_change_challenge_valid) {
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using challenge from the "
+			   "failure message");
 		challenge = data->passwd_change_challenge;
-	else
+	} else
 		challenge = pos;
 	pos += challenge_len;
 	len -= challenge_len;
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Authentication Servername",
+		    pos, len);
 
 	ret->ignore = false;
 	ret->methodState = METHOD_MAY_CONT;
@@ -225,9 +283,13 @@ eap_mschapv2_password_changed(struct eap_sm *sm,
 {
 	struct eap_peer_config *config = eap_get_config(sm);
 	if (config && config->new_password) {
+		wpa_msg(sm->msg_ctx, MSG_INFO,
+			WPA_EVENT_PASSWORD_CHANGED
+			"EAP-MSCHAPV2: Password changed successfully");
 		data->prev_error = 0;
 		os_free(config->password);
 		if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
+			/* TODO: update external storage */
 		} else if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) {
 			config->password = os_malloc(16);
 			config->password_len = 16;
@@ -257,11 +319,14 @@ eap_mschapv2_success(struct eap_sm *sm,
 	const u8 *pos;
 	size_t len;
 
+	wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received success");
 	len = req_len - sizeof(*req);
-	pos = (const u8 *)(req + 1);
+	pos = (const u8 *) (req + 1);
 	if (!data->auth_response_valid ||
 	    mschapv2_verify_auth_response(data->auth_response, pos, len)) {
-		ret->methodState = METHOD_NONE;
+		wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: Invalid authenticator "
+			   "response in success request");
+		ret->methodState = METHOD_DONE;
 		ret->decision = DECISION_FAIL;
 		return NULL;
 	}
@@ -271,15 +336,23 @@ eap_mschapv2_success(struct eap_sm *sm,
 		pos++;
 		len--;
 	}
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Success message",
+			  pos, len);
+	wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Authentication succeeded");
 
+	/* Note: Only op_code of the EAP-MSCHAPV2 header is included in success
+	 * message. */
 	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
 			     EAP_CODE_RESPONSE, id);
 	if (resp == NULL) {
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Failed to allocate "
+			   "buffer for success response");
 		ret->ignore = true;
 		return NULL;
 	}
 
-	wpabuf_put_u8(resp, MSCHAPV2_OP_SUCCESS);
+	wpabuf_put_u8(resp, MSCHAPV2_OP_SUCCESS); /* op_code */
+
 	ret->methodState = METHOD_DONE;
 	ret->decision = DECISION_UNCOND_SUCC;
 	ret->allowNotifications = false;
@@ -291,19 +364,25 @@ eap_mschapv2_success(struct eap_sm *sm,
 	return resp;
 }
 
-static int 
-eap_mschapv2_failure_txt(struct eap_sm *sm,
-			 struct eap_mschapv2_data *data, char *txt)
+
+static int eap_mschapv2_failure_txt(struct eap_sm *sm,
+				    struct eap_mschapv2_data *data, char *txt)
 {
-	char *pos; 
+	char *pos = "";
 	int retry = 1;
 	struct eap_peer_config *config = eap_get_config(sm);
 
+	/* For example:
+	 * E=691 R=1 C=<32 octets hex challenge> V=3 M=Authentication Failure
+	 */
+
 	pos = txt;
 
 	if (pos && os_strncmp(pos, "E=", 2) == 0) {
 		pos += 2;
 		data->prev_error = atoi(pos);
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: error %d",
+			   data->prev_error);
 		pos = (char *)os_strchr(pos, ' ');
 		if (pos)
 			pos++;
@@ -312,6 +391,8 @@ eap_mschapv2_failure_txt(struct eap_sm *sm,
 	if (pos && os_strncmp(pos, "R=", 2) == 0) {
 		pos += 2;
 		retry = atoi(pos);
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: retry is %sallowed",
+			   retry == 1 ? "" : "not ");
 		pos = (char *)os_strchr(pos, ' ');
 		if (pos)
 			pos++;
@@ -324,19 +405,32 @@ eap_mschapv2_failure_txt(struct eap_sm *sm,
 		if (hex_len == PASSWD_CHANGE_CHAL_LEN * 2) {
 			if (hexstr2bin(pos, data->passwd_change_challenge,
 				       PASSWD_CHANGE_CHAL_LEN)) {
-				wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: invalid failure challenge\n");
+				wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid "
+					   "failure challenge");
 			} else {
+				wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: failure "
+					    "challenge",
+					    data->passwd_change_challenge,
+					    PASSWD_CHANGE_CHAL_LEN);
 				data->passwd_change_challenge_valid = 1;
 			}
 		} else {
-			wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: required challenge field "
-				  "was not present in failure message\n");
+			wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid failure "
+				   "challenge len %d", hex_len);
 		}
+		pos = os_strchr(pos, ' ');
+		if (pos)
+			pos++;
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: required challenge field "
+			   "was not present in failure message");
 	}
 
 	if (pos && os_strncmp(pos, "V=", 2) == 0) {
 		pos += 2;
 		data->passwd_change_version = atoi(pos);
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: password changing "
+			   "protocol version %d", data->passwd_change_version);
 		pos = (char *)os_strchr(pos, ' ');
 		if (pos)
 			pos++;
@@ -345,24 +439,38 @@ eap_mschapv2_failure_txt(struct eap_sm *sm,
 	if (pos && os_strncmp(pos, "M=", 2) == 0) {
 		pos += 2;
 	}
+	if (data->prev_error == ERROR_AUTHENTICATION_FAILURE && retry &&
+	    config && config->phase2 &&
+	    os_strstr(config->phase2, "mschapv2_retry=0")) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-MSCHAPV2: mark password retry disabled based on local configuration");
+		retry = 0;
+	}
 	if (data->prev_error == ERROR_PASSWD_EXPIRED &&
 	    data->passwd_change_version == 3 && config) {
 		if (config->new_password == NULL) {
-			wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Password expired - "
-				  "password change reqired\n");
+			wpa_msg(sm->msg_ctx, MSG_INFO,
+				"EAP-MSCHAPV2: Password expired - password "
+				"change required");
+			eap_sm_request_new_password(sm);
 		}
 	} else if (retry == 1 && config) {
+		/* TODO: could prevent the current password from being used
+		 * again at least for some period of time */
 		if (!config->mschapv2_retry)
+			eap_sm_request_identity(sm);
+		eap_sm_request_password(sm);
 		config->mschapv2_retry = 1;
 	} else if (config) {
+		/* TODO: prevent retries using same username/password */
 		config->mschapv2_retry = 0;
 	}
 
 	return retry == 1;
 }
 
-static struct wpabuf * 
-eap_mschapv2_change_password(
+
+static struct wpabuf * eap_mschapv2_change_password(
 	struct eap_sm *sm, struct eap_mschapv2_data *data,
 	struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, u8 id)
 {
@@ -393,46 +501,67 @@ eap_mschapv2_change_password(
 			     EAP_CODE_RESPONSE, id);
 	if (resp == NULL)
 		return NULL;
+
 	ms = wpabuf_put(resp, sizeof(*ms));
 	ms->op_code = MSCHAPV2_OP_CHANGE_PASSWORD;
 	ms->mschapv2_id = req->mschapv2_id + 1;
 	WPA_PUT_BE16(ms->ms_length, ms_len);
 	cp = wpabuf_put(resp, sizeof(*cp));
 
+	/* Encrypted-Password */
 	if (pwhash) {
 		if (encrypt_pw_block_with_password_hash(
-			new_password, new_password_len,
-			password, cp->encr_password))
+			    new_password, new_password_len,
+			    password, cp->encr_password))
 			goto fail;
 	} else {
 		if (new_password_encrypted_with_old_nt_password_hash(
-			new_password, new_password_len,
-			password, password_len, cp->encr_password))
+			    new_password, new_password_len,
+			    password, password_len, cp->encr_password))
 			goto fail;
 	}
 
+	/* Encrypted-Hash */
 	if (pwhash) {
 		u8 new_password_hash[16];
-		nt_password_hash(new_password, new_password_len,
-				 new_password_hash);
-		nt_password_hash_encrypted_with_block(password,
-						      new_password_hash,
-						      cp->encr_hash);
+		if (nt_password_hash(new_password, new_password_len,
+				     new_password_hash) ||
+		    nt_password_hash_encrypted_with_block(password,
+							  new_password_hash,
+							  cp->encr_hash))
+			goto fail;
 	} else {
-		old_nt_password_hash_encrypted_with_new_nt_password_hash(
-				new_password, new_password_len,
-				password, password_len, cp->encr_hash);
+		if (old_nt_password_hash_encrypted_with_new_nt_password_hash(
+			    new_password, new_password_len,
+			    password, password_len, cp->encr_hash))
+			goto fail;
 	}
 
+	/* Peer-Challenge */
 	if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN))
 		goto fail;
 
+	/* Reserved, must be zero */
 	os_memset(cp->reserved, 0, 8);
 
+	/* NT-Response */
+	wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge",
+		    data->passwd_change_challenge, PASSWD_CHANGE_CHAL_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge",
+		    cp->peer_challenge, MSCHAPV2_CHAL_LEN);
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username",
+			  username, username_len);
+	wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: new password",
+			      new_password, new_password_len);
 	generate_nt_response(data->passwd_change_challenge, cp->peer_challenge,
-			     username, username_len, new_password,
-			     new_password_len, cp->nt_response);
-
+			     username, username_len,
+			     new_password, new_password_len,
+			     cp->nt_response);
+	wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: NT-Response",
+		    cp->nt_response, MSCHAPV2_NT_RESPONSE_LEN);
+
+	/* Authenticator response is not really needed yet, but calculate it
+	 * here so that challenges need not be saved. */
 	generate_authenticator_response(new_password, new_password_len,
 					cp->peer_challenge,
 					data->passwd_change_challenge,
@@ -440,13 +569,23 @@ eap_mschapv2_change_password(
 					cp->nt_response, data->auth_response);
 	data->auth_response_valid = 1;
 
-	nt_password_hash(new_password, new_password_len, password_hash);
-	hash_nt_password_hash(password_hash, password_hash_hash);
-	get_master_key(password_hash_hash, cp->nt_response, data->master_key);
+	/* Likewise, generate master_key here since we have the needed data
+	 * available. */
+	if (nt_password_hash(new_password, new_password_len, password_hash) ||
+	    hash_nt_password_hash(password_hash, password_hash_hash) ||
+	    get_master_key(password_hash_hash, cp->nt_response,
+			   data->master_key)) {
+		data->auth_response_valid = 0;
+		goto fail;
+	}
 	data->master_key_valid = 1;
 
+	/* Flags */
 	os_memset(cp->flags, 0, 2);
 
+	wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
+		   "(change pw)", id, ms->mschapv2_id);
+
 	return resp;
 
 fail:
@@ -454,19 +593,38 @@ fail:
 	return NULL;
 }
 
-static struct wpabuf * 
-eap_mschapv2_failure(struct eap_sm *sm,
-		     struct eap_mschapv2_data *data,
-		     struct eap_method_ret *ret,
-		     const struct eap_mschapv2_hdr *req,
-		     size_t req_len, u8 id)
+
+/**
+ * eap_mschapv2_process - Process an EAP-MSCHAPv2 failure message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Pointer to private EAP method data from eap_mschapv2_init()
+ * @ret: Return values from EAP request validation and processing
+ * @req: Pointer to EAP-MSCHAPv2 header from the request
+ * @req_len: Length of the EAP-MSCHAPv2 data
+ * @id: EAP identifier used in th erequest
+ * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
+ * no reply available
+ */
+static struct wpabuf * eap_mschapv2_failure(struct eap_sm *sm,
+					    struct eap_mschapv2_data *data,
+					    struct eap_method_ret *ret,
+					    const struct eap_mschapv2_hdr *req,
+					    size_t req_len, u8 id)
 {
 	struct wpabuf *resp;
-	const u8 *msdata = (const u8 *)(req + 1);
+	const u8 *msdata = (const u8 *) (req + 1);
 	char *buf;
 	size_t len = req_len - sizeof(*req);
 	int retry = 0;
 
+	wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received failure");
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Failure data",
+			  msdata, len);
+	/*
+	 * eap_mschapv2_failure_txt() expects a nul terminated string, so we
+	 * must allocate a large enough temporary buffer to create that since
+	 * the received message does not include nul termination.
+	 */
 	buf = (char *)dup_binstr(msdata, len);
 	if (buf) {
 		retry = eap_mschapv2_failure_txt(sm, data, buf);
@@ -482,23 +640,31 @@ eap_mschapv2_failure(struct eap_sm *sm,
 	    data->passwd_change_version == 3) {
 		struct eap_peer_config *config = eap_get_config(sm);
 		if (config && config->new_password)
-			return eap_mschapv2_change_password(sm, data, ret,
-							    req, id);
+			return eap_mschapv2_change_password(sm, data, ret, req,
+							    id);
+		if (config && config->pending_req_new_password)
+			return NULL;
 	} else if (retry && data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
+		/* TODO: could try to retry authentication, e.g, after having
+		 * changed the username/password. In this case, EAP MS-CHAP-v2
+		 * Failure Response would not be sent here. */
 		return NULL;
 	}
 
+	/* Note: Only op_code of the EAP-MSCHAPV2 header is included in failure
+	 * message. */
 	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
 			     EAP_CODE_RESPONSE, id);
 	if (resp == NULL)
 		return NULL;
 
-	wpabuf_put_u8(resp, MSCHAPV2_OP_FAILURE);
+	wpabuf_put_u8(resp, MSCHAPV2_OP_FAILURE); /* op_code */
+
 	return resp;
 }
 
-static int
-eap_mschapv2_check_config(struct eap_sm *sm)
+
+static int eap_mschapv2_check_config(struct eap_sm *sm)
 {
 	struct eap_peer_config *config = eap_get_config(sm);
 
@@ -520,19 +686,24 @@ eap_mschapv2_check_config(struct eap_sm *sm)
 	return 0; 
 }
 
-static int 
-eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len,
-		         const struct eap_mschapv2_hdr *ms)
+
+static int eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len,
+				    const struct eap_mschapv2_hdr *ms)
 {
 	size_t ms_len = WPA_GET_BE16(ms->ms_length);
 
 	if (ms_len == len)
 		return 0;
 
+	wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%lu "
+		   "ms_len=%lu", (unsigned long) len, (unsigned long) ms_len);
 	if (sm->workaround) {
-		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Workaround, ignore Invalid"
-			  " header len=%lu ms_len=%lu\n",
-			  (unsigned long)len, (unsigned long)ms_len);
+		/* Some authentication servers use invalid ms_len,
+		 * ignore it for interoperability. */
+		wpa_printf(MSG_INFO, "EAP-MSCHAPV2: workaround, ignore"
+			   " invalid ms_len %lu (len %lu)",
+			   (unsigned long) ms_len,
+			   (unsigned long) len);
 		return 0;
 	}
 	wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Invalid header len=%lu ms_len=%lu\n",
@@ -541,18 +712,31 @@ eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len,
 	return -1;
 }
 
-static void 
-eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data,
-			    const struct wpabuf *reqData)
+
+static void eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data,
+					const struct wpabuf *reqData)
 {
+	/*
+	 * Store a copy of the challenge message, so that it can be processed
+	 * again in case retry is allowed after a possible failure.
+	 */
 	wpabuf_free(data->prev_challenge);
 	data->prev_challenge = wpabuf_dup(reqData);
 }
 
-static struct wpabuf * 
-eap_mschapv2_process(struct eap_sm *sm, void *priv,
-		     struct eap_method_ret *ret,
-		     const struct wpabuf *reqData)
+
+/**
+ * eap_mschapv2_process - Process an EAP-MSCHAPv2 request
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_mschapv2_init()
+ * @ret: Return values from EAP request validation and processing
+ * @reqData: EAP request to be processed (eapReqData)
+ * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
+ * no reply available
+ */
+static struct wpabuf * eap_mschapv2_process(struct eap_sm *sm, void *priv,
+					    struct eap_method_ret *ret,
+					    const struct wpabuf *reqData)
 {
 	u8 id;
 	size_t len;
@@ -569,27 +753,31 @@ eap_mschapv2_process(struct eap_sm *sm, void *priv,
 
 	if (config->mschapv2_retry && data->prev_challenge &&
 	    data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
+		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Replacing pending packet "
+			   "with the previous challenge");
+
 		reqData = data->prev_challenge;
 		using_prev_challenge = 1;
 		config->mschapv2_retry = 0;
 	}
 
-	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
-			       reqData, &len);
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, reqData,
+			       &len);
 	if (pos == NULL || len < sizeof(*ms) + 1) {
 		ret->ignore = true;
 		return NULL;
 	}
 
-	ms = (const struct eap_mschapv2_hdr *)pos;
+	ms = (const struct eap_mschapv2_hdr *) pos;
 	if (eap_mschapv2_check_mslen(sm, len, ms)) {
 		ret->ignore = true;
 		return NULL;
 	}
 
 	id = eap_get_id(reqData);
-	wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d\n",
-		  id, ms->mschapv2_id);
+	wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d",
+		   id, ms->mschapv2_id);
+
 	switch (ms->op_code) {
 	case MSCHAPV2_OP_CHALLENGE:
 		if (!using_prev_challenge)
@@ -602,19 +790,20 @@ eap_mschapv2_process(struct eap_sm *sm, void *priv,
 	default:
 		wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Unknow op code %d -ignored\n",
 			  ms->op_code);
+		ret->ignore = TRUE;
 		return NULL;
 	}
 }
 
-static bool 
-eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv)
+
+static bool eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv)
 {
 	struct eap_mschapv2_data *data = priv;
 	return data->success && data->master_key_valid;
 }
 
-static u8 * 
-eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
+
+static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
 {
 	struct eap_mschapv2_data *data = priv;
 	u8 *key;
@@ -626,20 +815,31 @@ eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
 	key_len = 2 * MSCHAPV2_KEY_LEN;
 
 	key = os_malloc(key_len);
+	if (key == NULL)
+		return NULL;
 
-	/* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key,
-	 *	 peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */
-	get_asymetric_start_key(data->master_key, key,
-				MSCHAPV2_KEY_LEN, 1, 0);
+	/* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key, i.e.,
+	 *	peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */
+	get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1, 0);
 	get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
 				MSCHAPV2_KEY_LEN, 0, 0);
 
+	wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key",
+			key, key_len);
+
 	*len = key_len;
 	return key;
 }
 
-int 
-eap_peer_mschapv2_register(void)
+
+/**
+ * eap_peer_mschapv2_register - Register EAP-MSCHAPv2 peer method
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to register EAP-MSCHAPv2 peer method into the EAP
+ * method list.
+ */
+int eap_peer_mschapv2_register(void)
 {
 	struct eap_method *eap;
 	int ret;

+ 17 - 13
components/wpa_supplicant/src/eap_peer/eap_tls.c

@@ -16,6 +16,10 @@
 #include "eap_peer/eap_config.h"
 #include "eap_peer/eap_methods.h"
 
+
+static void eap_tls_deinit(struct eap_sm *sm, void *priv);
+
+
 struct eap_tls_data {
 	struct eap_ssl_data ssl;
 	u8 *key_data;
@@ -26,19 +30,6 @@ struct eap_tls_data {
 };
 
 
-
-static void eap_tls_deinit(struct eap_sm *sm, void *priv)
-{
-	struct eap_tls_data *data = priv;
-	if (data == NULL)
-		return;
-	eap_peer_tls_ssl_deinit(sm, &data->ssl);
-	os_free(data->key_data);
-	os_free(data->session_id);
-	os_free(data);
-}
-
-
 static void * eap_tls_init(struct eap_sm *sm)
 {
 	struct eap_tls_data *data;
@@ -66,6 +57,19 @@ static void * eap_tls_init(struct eap_sm *sm)
 	return data;
 }
 
+
+static void eap_tls_deinit(struct eap_sm *sm, void *priv)
+{
+	struct eap_tls_data *data = priv;
+	if (data == NULL)
+		return;
+	eap_peer_tls_ssl_deinit(sm, &data->ssl);
+	os_free(data->key_data);
+	os_free(data->session_id);
+	os_free(data);
+}
+
+
 static struct wpabuf * eap_tls_failure(struct eap_sm *sm,
 				       struct eap_tls_data *data,
 				       struct eap_method_ret *ret, int res,

+ 5 - 1
components/wpa_supplicant/src/eap_peer/eap_tls_common.c

@@ -102,6 +102,10 @@ static int eap_tls_params_from_conf(struct eap_sm *sm,
 
 	wpa_printf(MSG_DEBUG, "TLS: using phase1 config options");
 	eap_tls_params_from_conf1(params, config);
+    if (data->eap_type == EAP_TYPE_FAST) {
+        wpa_printf(MSG_DEBUG, "EAP-TYPE == EAP-FAST #####################################");
+        params->flags |= TLS_CONN_EAP_FAST;
+    }
 
 	/*
 	 * Use blob data, if available. Otherwise, leave reference to external
@@ -254,7 +258,7 @@ u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
 	if (out == NULL)
 		return NULL;
 
-	if (tls_connection_export_key(data->ssl_ctx, data->conn, label, out,
+	if (tls_connection_export_key(data->ssl_ctx, data->conn, label, 0, 0, out,
 				len)) {
 		os_free(out);
 		return NULL;

+ 39 - 13
components/wpa_supplicant/src/eap_peer/mschapv2.c

@@ -14,10 +14,11 @@ const u8 * mschapv2_remove_domain(const u8 *username, size_t *len)
 	size_t i;
 
 	/*
-	 * MSCHAPV2 does not include optional domain name in the
+	 * MSCHAPv2 does not include optional domain name in the
 	 * challenge-response calculation, so remove domain prefix
-	 * (if present)
+	 * (if present).
 	 */
+
 	for (i = 0; i < *len; i++) {
 		if (username[i] == '\\') {
 			*len -= i + 1;
@@ -28,31 +29,48 @@ const u8 * mschapv2_remove_domain(const u8 *username, size_t *len)
 	return username;
 }
 
+
 int mschapv2_derive_response(const u8 *identity, size_t identity_len,
-			 const u8 *password, size_t password_len,
-			 int pwhash,
-			 const u8 *auth_challenge,
-			 const u8 *peer_challenge,
-			 u8 *nt_response, u8 *auth_response,
-			 u8 *master_key)
+			     const u8 *password, size_t password_len,
+			     int pwhash,
+			     const u8 *auth_challenge,
+			     const u8 *peer_challenge,
+			     u8 *nt_response, u8 *auth_response,
+			     u8 *master_key)
 {
 	const u8 *username;
 	size_t username_len;
 	u8 password_hash[16], password_hash_hash[16];
 
+	wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Identity",
+			  identity, identity_len);
 	username_len = identity_len;
 	username = mschapv2_remove_domain(identity, &username_len);
+	wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Username",
+			  username, username_len);
 
+	wpa_hexdump(MSG_DEBUG, "MSCHAPV2: auth_challenge",
+		    auth_challenge, MSCHAPV2_CHAL_LEN);
+	wpa_hexdump(MSG_DEBUG, "MSCHAPV2: peer_challenge",
+		    peer_challenge, MSCHAPV2_CHAL_LEN);
+	wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: username",
+			  username, username_len);
+	/* Authenticator response is not really needed yet, but calculate it
+	 * here so that challenges need not be saved. */
 	if (pwhash) {
+		wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: password hash",
+				password, password_len);
 		if (generate_nt_response_pwhash(auth_challenge, peer_challenge,
 						username, username_len,
 						password, nt_response) ||
 		    generate_authenticator_response_pwhash(
-				password, peer_challenge, auth_challenge,
-				username, username_len, nt_response,
-				auth_response))
+			    password, peer_challenge, auth_challenge,
+			    username, username_len, nt_response,
+			    auth_response))
 			return -1;
 	} else {
+		wpa_hexdump_ascii_key(MSG_DEBUG, "MSCHAPV2: password",
+				      password, password_len);
 		if (generate_nt_response(auth_challenge, peer_challenge,
 					 username, username_len,
 					 password, password_len,
@@ -65,7 +83,12 @@ int mschapv2_derive_response(const u8 *identity, size_t identity_len,
 						    auth_response))
 			return -1;
 	}
+	wpa_hexdump(MSG_DEBUG, "MSCHAPV2: NT Response",
+		    nt_response, MSCHAPV2_NT_RESPONSE_LEN);
+	wpa_hexdump(MSG_DEBUG, "MSCHAPV2: Auth Response",
+		    auth_response, MSCHAPV2_AUTH_RESPONSE_LEN);
 
+	/* Generate master_key here since we have the needed data available. */
 	if (pwhash) {
 		if (hash_nt_password_hash(password, password_hash_hash))
 			return -1;
@@ -76,17 +99,20 @@ int mschapv2_derive_response(const u8 *identity, size_t identity_len,
 	}
 	if (get_master_key(password_hash_hash, nt_response, master_key))
 		return -1;
+	wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: Master Key",
+			master_key, MSCHAPV2_MASTER_KEY_LEN);
 
 	return 0;
 }
 
+
 int mschapv2_verify_auth_response(const u8 *auth_response,
-			      const u8 *buf, size_t buf_len)
+				  const u8 *buf, size_t buf_len)
 {
 	u8 recv_response[MSCHAPV2_AUTH_RESPONSE_LEN];
 	if (buf_len < 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN ||
 	    buf[0] != 'S' || buf[1] != '=' ||
-	    hexstr2bin((char *)(buf + 2), recv_response,
+	    hexstr2bin((char *) (buf + 2), recv_response,
 		       MSCHAPV2_AUTH_RESPONSE_LEN) ||
 	    os_memcmp(auth_response, recv_response,
 		      MSCHAPV2_AUTH_RESPONSE_LEN) != 0)

+ 62 - 0
components/wpa_supplicant/src/esp_supplicant/esp_wpa2.c

@@ -51,6 +51,15 @@
 #define DATA_MUTEX_TAKE() xSemaphoreTakeRecursive(s_wpa2_data_lock,portMAX_DELAY)
 #define DATA_MUTEX_GIVE() xSemaphoreGiveRecursive(s_wpa2_data_lock)
 
+//length of the string "fast_provisioning={0/1/2} "
+#define FAST_PROVISIONING_CONFIG_STR_LEN 20
+//length of the string "fast_max_pac_list_len=(int < 100) "
+#define FAST_MAX_PAC_LIST_CONFIG_STR_LEN 25
+//length of the string "fast_pac_format=binary"
+#define FAST_PAC_FORMAT_STR_LEN 22
+//Total
+#define PHASE1_PARAM_STRING_LEN FAST_PROVISIONING_CONFIG_STR_LEN + FAST_MAX_PAC_LIST_CONFIG_STR_LEN + FAST_PAC_FORMAT_STR_LEN
+
 static void *s_wpa2_data_lock = NULL;
 
 static struct eap_sm *gEapSm = NULL;
@@ -1149,3 +1158,56 @@ esp_err_t esp_wifi_sta_wpa2_ent_get_disable_time_check(bool *disable)
     return ESP_OK;
 }
 
+esp_err_t esp_wifi_sta_wpa2_ent_set_pac_file(const unsigned char *pac_file, int pac_file_len)
+{
+    if (pac_file && pac_file_len > -1) {
+        if (pac_file_len < 512) { // The file contains less than 1 pac and is to be rewritten later
+            g_wpa_pac_file = (u8 *)os_zalloc(512);
+            if (g_wpa_pac_file == NULL) {
+                return ESP_ERR_NO_MEM;
+            }
+            g_wpa_pac_file_len = 0;
+        } else { // The file contains pac data
+            g_wpa_pac_file = (u8 *)os_zalloc(pac_file_len);
+            if (g_wpa_pac_file == NULL) {
+                return ESP_ERR_NO_MEM;
+            }
+            os_memcpy(g_wpa_pac_file, pac_file, pac_file_len);
+            g_wpa_pac_file_len = pac_file_len;
+        }
+    } else {
+        return ESP_FAIL;
+    }
+
+    return ESP_OK;
+}
+
+esp_err_t esp_wifi_sta_wpa2_ent_set_fast_phase1_params(esp_eap_fast_config config)
+{
+    char config_for_supplicant[PHASE1_PARAM_STRING_LEN] = "";
+    if ((config.fast_provisioning > -1) && (config.fast_provisioning <= 2)) {
+        os_sprintf((char *) &config_for_supplicant, "fast_provisioning=%d ", config.fast_provisioning);
+    } else {
+        return ESP_ERR_INVALID_ARG;
+    }
+    if (config.fast_max_pac_list_len && config.fast_max_pac_list_len < 100) {
+        os_sprintf((char *) &config_for_supplicant + strlen(config_for_supplicant), "fast_max_pac_list_len=%d ", config.fast_max_pac_list_len);
+    } else if (config.fast_max_pac_list_len >= 100) {
+        return ESP_ERR_INVALID_ARG;
+    }
+    if (config.fast_pac_format_binary) {
+        os_strcat((char *) &config_for_supplicant, (const char *) "fast_pac_format=binary");
+    }
+
+    // Free the old buffer if it already exists
+    if (g_wpa_phase1_options != NULL) {
+        os_free(g_wpa_phase1_options);
+    }
+    g_wpa_phase1_options = (char *)os_zalloc(sizeof(config_for_supplicant));
+    if (g_wpa_phase1_options == NULL) {
+        return ESP_ERR_NO_MEM;
+    }
+    os_memcpy(g_wpa_phase1_options, &config_for_supplicant, sizeof(config_for_supplicant));
+    return ESP_OK;
+
+}

+ 1 - 1
components/wpa_supplicant/src/rsn_supp/wpa.c

@@ -1,4 +1,3 @@
-
 /*
  * WPA Supplicant - WPA state machine and EAPOL-Key processing
  * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi>
@@ -12,6 +11,7 @@
  *
  * See README and COPYING for more details.
  */
+
 #include "utils/includes.h"
 
 #include "utils/common.h"

+ 37 - 5
components/wpa_supplicant/src/tls/tls.h

@@ -82,6 +82,8 @@ struct tls_config {
 #define TLS_CONN_DISABLE_SESSION_TICKET BIT(2)
 #define TLS_CONN_REQUEST_OCSP BIT(3)
 #define TLS_CONN_REQUIRE_OCSP BIT(4)
+#define TLS_CONN_SUITEB BIT(11)
+#define TLS_CONN_EAP_FAST BIT(7)
 
 /**
  * struct tls_connection_params - Parameters for TLS connection
@@ -278,17 +280,23 @@ int __must_check tls_global_set_verify(void *tls_ctx, int check_crl);
  * @tls_ctx: TLS context data from tls_init()
  * @conn: Connection context data from tls_connection_init()
  * @verify_peer: 1 = verify peer certificate
+ * @flags: Connection flags (TLS_CONN_*)
+ * @session_ctx: Session caching context or %NULL to use default
+ * @session_ctx_len: Length of @session_ctx in bytes.
  * Returns: 0 on success, -1 on failure
  */
-int __must_check tls_connection_set_verify(void *tls_ctx,
+int tls_connection_set_verify(void *tls_ctx,
 					   struct tls_connection *conn,
-					   int verify_peer);
+					   int verify_peer,
+					   unsigned int flags,
+					   const u8 *session_ctx,
+					   size_t session_ctx_len);
 
 /**
  * tls_connection_get_random - Get random data from TLS connection
  * @tls_ctx: TLS context data from tls_init()
  * @conn: Connection context data from tls_connection_init()
- * @keys: Structure of key/random data (filled on success)
+ * @data: Structure of client/server random data (filled on success)
  * Returns: 0 on success, -1 on failure
  */
 int __must_check tls_connection_get_random(void *tls_ctx,
@@ -300,17 +308,39 @@ int __must_check tls_connection_get_random(void *tls_ctx,
  * @tls_ctx: TLS context data from tls_init()
  * @conn: Connection context data from tls_connection_init()
  * @label: Label (e.g., description of the key) for PRF
+ * @context: Optional extra upper-layer context (max len 2^16)
+ * @context_len: The length of the context value
  * @out: Buffer for output data from TLS-PRF
  * @out_len: Length of the output buffer
  * Returns: 0 on success, -1 on failure
  *
- * Exports keying material using the mechanism described in RFC 5705.
+ * Exports keying material using the mechanism described in RFC 5705. If
+ * context is %NULL, context is not provided; otherwise, context is provided
+ * (including the case of empty context with context_len == 0).
  */
 int __must_check tls_connection_export_key(void *tls_ctx,
 					   struct tls_connection *conn,
 					   const char *label,
+					   const u8 *context,
+					   size_t context_len,
 					   u8 *out, size_t out_len);
 
+/**
+ * tls_connection_get_eap_fast_key - Derive key material for EAP-FAST
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @out: Buffer for output data from TLS-PRF
+ * @out_len: Length of the output buffer
+ * Returns: 0 on success, -1 on failure
+ *
+ * Exports key material after the normal TLS key block for use with
+ * EAP-FAST. Most callers will want tls_connection_export_key(), but EAP-FAST
+ * uses a different legacy mechanism.
+ */
+int __must_check tls_connection_get_eap_fast_key(void *tls_ctx,
+						 struct tls_connection *conn,
+						 u8 *out, size_t out_len);
+
 /**
  * tls_connection_handshake - Process TLS handshake (client side)
  * @tls_ctx: TLS context data from tls_init()
@@ -412,7 +442,9 @@ enum {
 	TLS_CIPHER_RC4_SHA /* 0x0005 */,
 	TLS_CIPHER_AES128_SHA /* 0x002f */,
 	TLS_CIPHER_RSA_DHE_AES128_SHA /* 0x0031 */,
-	TLS_CIPHER_ANON_DH_AES128_SHA /* 0x0034 */
+	TLS_CIPHER_ANON_DH_AES128_SHA /* 0x0034 */,
+	TLS_CIPHER_RSA_DHE_AES256_SHA /* 0x0039 */,
+	TLS_CIPHER_AES256_SHA /* 0x0035 */,
 };
 
 /**

+ 27 - 9
components/wpa_supplicant/src/tls/tls_internal.c

@@ -267,7 +267,8 @@ int tls_global_set_verify(void *tls_ctx, int check_crl)
 
 
 int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
-			      int verify_peer)
+			      int verify_peer, unsigned int flags,
+			      const u8 *session_ctx, size_t session_ctx_len)
 {
 #ifdef CONFIG_TLS_INTERNAL_SERVER
 	if (conn->server)
@@ -276,6 +277,7 @@ int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
 	return -1;
 }
 
+
 int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn,
 			    struct tls_random *data)
 {
@@ -290,6 +292,7 @@ int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn,
 	return -1;
 }
 
+
 static int tls_get_keyblock_size(struct tls_connection *conn)
 {
 #ifdef CONFIG_TLS_INTERNAL_CLIENT
@@ -303,8 +306,10 @@ static int tls_get_keyblock_size(struct tls_connection *conn)
 	return -1;
 }
 
+
 static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
-			      const char *label, int server_random_first,
+			      const char *label, const u8 *context,
+			      size_t context_len, int server_random_first,
 			      int skip_keyblock, u8 *out, size_t out_len)
 {
 	int ret = -1, skip = 0;
@@ -320,33 +325,46 @@ static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
 			return -1;
 		_out = tmp_out;
 	}
+
 #ifdef CONFIG_TLS_INTERNAL_CLIENT
 	if (conn->client) {
 		ret = tlsv1_client_prf(conn->client, label,
-					server_random_first,
-					out, out_len);
+				       server_random_first,
+				       _out, skip + out_len);
 	}
 #endif /* CONFIG_TLS_INTERNAL_CLIENT */
 #ifdef CONFIG_TLS_INTERNAL_SERVER
 	if (conn->server) {
 		ret = tlsv1_server_prf(conn->server, label,
-					server_random_first,
-					out, out_len);
+				       server_random_first,
+				       _out, skip + out_len);
 	}
 #endif /* CONFIG_TLS_INTERNAL_SERVER */
 	if (ret == 0 && skip_keyblock)
 		os_memcpy(out, _out + skip, out_len);
-	wpa_bin_clear_free(tmp_out, skip);
+	bin_clear_free(tmp_out, skip);
 
 	return ret;
 }
 
+
 int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
-			      const char *label, u8 *out, size_t out_len)
+			      const char *label, const u8 *context,
+			      size_t context_len, u8 *out, size_t out_len)
 {
-	return tls_connection_prf(tls_ctx, conn, label, 0, 0, out, out_len);
+	return tls_connection_prf(tls_ctx, conn, label, context, context_len,
+				  0, 0, out, out_len);
 }
 
+
+int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
+				    u8 *out, size_t out_len)
+{
+	return tls_connection_prf(tls_ctx, conn, "key expansion", NULL, 0,
+				  1, 1, out, out_len);
+}
+
+
 struct wpabuf * tls_connection_handshake(void *tls_ctx,
 					 struct tls_connection *conn,
 					 const struct wpabuf *in_data,

+ 9 - 0
components/wpa_supplicant/src/utils/wpabuf.c

@@ -202,6 +202,15 @@ void wpabuf_free(struct wpabuf *buf)
 }
 
 
+void wpabuf_clear_free(struct wpabuf *buf)
+{
+	if (buf) {
+		os_memset(wpabuf_mhead(buf), 0, wpabuf_len(buf));
+		wpabuf_free(buf);
+	}
+}
+
+
 void * wpabuf_put(struct wpabuf *buf, size_t len)
 {
 	void *tmp = wpabuf_mhead_u8(buf) + wpabuf_len(buf);

+ 9 - 0
examples/wifi/wifi_eap_fast/CMakeLists.txt

@@ -0,0 +1,9 @@
+
+# (Automatically converted from project Makefile by convert_to_cmake.py.)
+
+# The following lines of boilerplate have to be in your project's CMakeLists
+# in this exact order for cmake to work correctly
+cmake_minimum_required(VERSION 3.5)
+
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(wifi_eap_fast)

+ 8 - 0
examples/wifi/wifi_eap_fast/Makefile

@@ -0,0 +1,8 @@
+#
+# This is a project Makefile. It is assumed the directory this Makefile resides in is a
+# project subdirectory.
+#
+
+PROJECT_NAME := wifi_eap_fast
+
+include $(IDF_PATH)/make/project.mk

+ 67 - 0
examples/wifi/wifi_eap_fast/README.md

@@ -0,0 +1,67 @@
+# WPA2 Enterprise Example
+
+This example shows how ESP32 connects to AP with Wi-Fi enterprise encryption using the EAP-FAST method. The example does the following steps:
+
+1. Install CA certificate which is optional.
+2. Set user name and password and identity.
+3. Set the PAC file which may be empty.
+4. Enable wpa2 enterprise.
+5. Connect to AP.
+
+*Note:* 1. EAP-FAST is not supported with `CONFIG_WPA_MBEDTLS_CRYPTO` and so is disabled by default.
+        2. Setting the config `fast_provisioning` to methods 0 and 1 do not support saving the PAC credentials in case of a restart or loss of power.
+        3. The certificates present in the `examples/wifi/wifi_eap_fast/main` folder contain server certificates which have the corresponding CA as well. These can be used for server validation which is opptional.
+        4. The expiration date of these certificates is 2027/06/05.
+
+### Configuration
+
+```
+idf.py menuconfig
+```
+* Set SSID of Access Point to connect in Example Configuration.
+* Enter EAP-ID.
+* Enter Username and Password.
+* Enable or disable Validate Server option.
+
+### Build and Flash the project.
+
+```
+idf.py -p PORT flash monitor
+```
+
+### Example output
+
+Here is an example of wpa2 enterprise (FAST method) console output.
+```
+I (690) example: Setting WiFi configuration SSID wpa2_test...
+I (690) phy_init: phy_version 4670,719f9f6,Feb 18 2021,17:07:07
+I (800) wifi:mode : sta (24:6f:28:80:41:78)
+I (800) wifi:enable tsf
+I (1410) wifi:new:<6,0>, old:<1,0>, ap:<255,255>, sta:<6,0>, prof:1
+I (2410) wifi:state: init -> auth (b0)
+I (2420) wifi:state: auth -> assoc (0)
+E (2420) wifi:Association refused temporarily, comeback time 3072 mSec
+I (5500) wifi:state: assoc -> assoc (0)
+I (5500) wifi:state: assoc -> init (6c0)
+I (5500) wifi:new:<6,0>, old:<6,0>, ap:<255,255>, sta:<6,0>, prof:1
+I (7560) wifi:new:<6,0>, old:<6,0>, ap:<255,255>, sta:<6,0>, prof:1
+I (7560) wifi:state: init -> auth (b0)
+I (7560) wifi:state: auth -> assoc (0)
+I (7570) wifi:state: assoc -> run (10)
+I (7770) wifi:connected with wpa2_test, aid = 1, channel 6, BW20, bssid = 24:4b:fe:ab:be:99
+I (7770) wifi:security: WPA2-ENT, phy: bg, rssi: -80
+I (7780) wifi:pm start, type: 1
+
+I (7800) example: ~~~~~~~~~~~
+I (7800) example: IP:0.0.0.0
+I (7800) example: MASK:0.0.0.0
+I (7800) example: GW:0.0.0.0
+I (7800) example: ~~~~~~~~~~~
+I (7870) wifi:AP's beacon interval = 102400 us, DTIM period = 1
+I (8580) esp_netif_handlers: sta ip: 192.168.5.3, mask: 255.255.255.0, gw: 192.168.5.1
+I (12800) example: ~~~~~~~~~~~
+I (12800) example: IP:192.168.5.3
+I (12800) example: MASK:255.255.255.0
+I (12800) example: GW:192.168.5.1
+I (12800) example: ~~~~~~~~~~~
+```

+ 4 - 0
examples/wifi/wifi_eap_fast/main/CMakeLists.txt

@@ -0,0 +1,4 @@
+# Embed CA, certificate & key directly into binary
+idf_component_register(SRCS "wifi_eap_fast_main.c"
+                    INCLUDE_DIRS "."
+                    EMBED_TXTFILES ca.pem pac_file.pac)

+ 33 - 0
examples/wifi/wifi_eap_fast/main/Kconfig.projbuild

@@ -0,0 +1,33 @@
+menu "Example Configuration"
+
+    config EXAMPLE_WIFI_SSID
+        string "WiFi SSID"
+        default "wpa2_test"
+        help
+            SSID (network name) for the example to connect to.
+
+    config EXAMPLE_VALIDATE_SERVER_CERT
+        bool "Validate server"
+        default y
+        help
+            Validate the servers' certificate using CA cert.
+
+    config EXAMPLE_EAP_ID
+        string "EAP ID"
+        default "example@espressif.com"
+        help
+            Identity in phase 1 of EAP procedure.
+
+    config EXAMPLE_EAP_USERNAME
+        string "EAP USERNAME"
+        default "espressif"
+        help
+            Username for EAP method.
+
+    config EXAMPLE_EAP_PASSWORD
+        string "EAP PASSWORD"
+        default "test11"
+        help
+            Password for EAP method.
+
+endmenu

+ 23 - 0
examples/wifi/wifi_eap_fast/main/ca.pem

@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIID3DCCA0WgAwIBAgIJAMnlgL1czsmjMA0GCSqGSIb3DQEBCwUAMIGTMQswCQYD
+VQQGEwJGUjEPMA0GA1UECAwGUmFkaXVzMRIwEAYDVQQHDAlTb21ld2hlcmUxFTAT
+BgNVBAoMDEV4YW1wbGUgSW5jLjEgMB4GCSqGSIb3DQEJARYRYWRtaW5AZXhhbXBs
+ZS5jb20xJjAkBgNVBAMMHUV4YW1wbGUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4X
+DTE3MDYwNzA4MDY0OVoXDTI3MDYwNTA4MDY0OVowgZMxCzAJBgNVBAYTAkZSMQ8w
+DQYDVQQIDAZSYWRpdXMxEjAQBgNVBAcMCVNvbWV3aGVyZTEVMBMGA1UECgwMRXhh
+bXBsZSBJbmMuMSAwHgYJKoZIhvcNAQkBFhFhZG1pbkBleGFtcGxlLmNvbTEmMCQG
+A1UEAwwdRXhhbXBsZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwgZ8wDQYJKoZIhvcN
+AQEBBQADgY0AMIGJAoGBALpWR23fn/TmHxsXsHdrydzPSd17fZkc71WsaicgQR66
+1tIVYb22UWGfj9KPM8THMsV74ew4ZkaQ39qvU0iuQIRrKARFHFok+vbaecgWMeWe
+vGIqdnmyB9gJYaFOKgtSkfXsu2ddsqdvLYwcDbczrq8X9yEXpN6mnxXeCcPG4F0p
+AgMBAAGjggE0MIIBMDAdBgNVHQ4EFgQUgigpdAUpONoDq0pQ3yfxrslCSpcwgcgG
+A1UdIwSBwDCBvYAUgigpdAUpONoDq0pQ3yfxrslCSpehgZmkgZYwgZMxCzAJBgNV
+BAYTAkZSMQ8wDQYDVQQIDAZSYWRpdXMxEjAQBgNVBAcMCVNvbWV3aGVyZTEVMBMG
+A1UECgwMRXhhbXBsZSBJbmMuMSAwHgYJKoZIhvcNAQkBFhFhZG1pbkBleGFtcGxl
+LmNvbTEmMCQGA1UEAwwdRXhhbXBsZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCCQDJ
+5YC9XM7JozAMBgNVHRMEBTADAQH/MDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly93
+d3cuZXhhbXBsZS5jb20vZXhhbXBsZV9jYS5jcmwwDQYJKoZIhvcNAQELBQADgYEA
+euxOBPInSJRKAIseMxPmAabtAqKNslZSmpG4He3lkKt+HM3jfznUt3psmD7j1hFW
+S4l7KXzzajvaGYybDq5N9MqrDjhGn3VXZqOLMUNDL7OQq96TzgqsTBT1dmVSbNlt
+PQgiAeKAk3tmH4lRRi9MTBSyJ6I92JYcS5H6Bs4ZwCc=
+-----END CERTIFICATE-----

+ 9 - 0
examples/wifi/wifi_eap_fast/main/component.mk

@@ -0,0 +1,9 @@
+#
+# "main" pseudo-component makefile.
+#
+# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
+
+# embed files from the "certs" directory as binary data symbols
+# in the app
+COMPONENT_EMBED_TXTFILES := ca.pem
+COMPONENT_EMBED_TXTFILES += pac_file.pac

+ 0 - 0
examples/wifi/wifi_eap_fast/main/pac_file.pac


+ 70 - 0
examples/wifi/wifi_eap_fast/main/server.crt

@@ -0,0 +1,70 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 47 (0x2f)
+    Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=FR, ST=Radius, L=Somewhere, O=Example Inc./emailAddress=admin@example.com, CN=Example Certificate Authority
+        Validity
+            Not Before: Jun  7 08:06:49 2017 GMT
+            Not After : Jun  5 08:06:49 2027 GMT
+        Subject: C=FR, ST=Radius, O=Example Inc., CN=Example Server Certificate/emailAddress=admin@example.com
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:c9:d8:e2:e0:75:91:83:87:d8:c8:80:c6:20:4d:
+                    e9:14:24:30:98:33:53:fa:56:0e:ec:9a:43:7f:87:
+                    a9:22:94:26:06:c7:ac:b5:d9:ec:55:06:81:b7:0d:
+                    c9:24:51:49:fa:47:fb:4b:4e:fc:ed:75:8a:e1:28:
+                    32:bc:c5:e0:4c:45:c4:58:60:15:67:1e:6b:40:19:
+                    3f:f0:ab:92:61:92:2d:71:10:2e:f2:eb:bc:81:2f:
+                    5a:3b:74:ca:5f:fd:e0:ee:d1:d9:07:6a:6c:20:c0:
+                    07:88:b4:8b:0f:ad:1e:c9:4f:7c:11:98:37:89:15:
+                    de:24:b1:11:1a:7c:97:4a:cf:f3:c8:cb:79:9e:9c:
+                    c3:71:da:a6:94:97:f5:95:fd:61:06:44:e2:3f:12:
+                    43:0b:1d:33:48:91:d2:ce:4f:97:a1:ed:6a:30:c7:
+                    5d:98:b5:6e:0a:b7:4f:d9:03:ec:80:76:09:b0:40:
+                    a1:a1:af:ab:2a:59:c4:0f:56:22:bc:be:14:be:18:
+                    df:10:7d:5d:22:bf:e5:04:77:7a:75:6b:3e:eb:6d:
+                    20:a1:a7:60:d4:f1:87:9d:9f:60:b9:d3:db:2c:25:
+                    f4:91:4a:f1:d2:40:e5:a1:10:88:a0:41:5a:98:40:
+                    ca:15:d7:e3:e6:3e:c0:6a:d5:46:b2:b4:90:b4:ae:
+                    3b:e3
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Extended Key Usage:
+                TLS Web Server Authentication
+            X509v3 CRL Distribution Points:
+
+                Full Name:
+                  URI:http://www.example.com/example_ca.crl
+
+    Signature Algorithm: sha1WithRSAEncryption
+         a4:25:21:51:0b:22:6c:63:8d:a9:c1:4f:04:33:69:79:34:f0:
+         36:dd:8f:6a:27:5f:07:a2:1d:ef:8b:f0:96:e6:e7:a3:b8:3b:
+         85:5e:3f:26:43:8a:8e:95:58:9c:a6:db:9c:51:bf:ea:53:16:
+         3e:c1:a8:11:1a:c6:cf:0e:a1:17:18:64:d2:05:f1:c0:9c:a6:
+         2b:16:c4:29:54:03:d2:17:bd:15:74:d6:ad:8a:8f:2d:cc:27:
+         3b:88:88:f2:ea:d0:a2:cb:e9:42:57:df:26:9f:8a:a2:02:2f:
+         35:b6:19:1d:26:43:44:af:12:4b:bc:b9:84:50:02:fd:1d:fa:
+         50:e8
+-----BEGIN CERTIFICATE-----
+MIIDWTCCAsKgAwIBAgIBLzANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UEBhMCRlIx
+DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYDVQQKDAxF
+eGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUuY29tMSYw
+JAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNzA2MDcw
+ODA2NDlaFw0yNzA2MDUwODA2NDlaMHwxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZS
+YWRpdXMxFTATBgNVBAoMDEV4YW1wbGUgSW5jLjEjMCEGA1UEAwwaRXhhbXBsZSBT
+ZXJ2ZXIgQ2VydGlmaWNhdGUxIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUu
+Y29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAydji4HWRg4fYyIDG
+IE3pFCQwmDNT+lYO7JpDf4epIpQmBsestdnsVQaBtw3JJFFJ+kf7S0787XWK4Sgy
+vMXgTEXEWGAVZx5rQBk/8KuSYZItcRAu8uu8gS9aO3TKX/3g7tHZB2psIMAHiLSL
+D60eyU98EZg3iRXeJLERGnyXSs/zyMt5npzDcdqmlJf1lf1hBkTiPxJDCx0zSJHS
+zk+Xoe1qMMddmLVuCrdP2QPsgHYJsEChoa+rKlnED1YivL4UvhjfEH1dIr/lBHd6
+dWs+620goadg1PGHnZ9gudPbLCX0kUrx0kDloRCIoEFamEDKFdfj5j7AatVGsrSQ
+tK474wIDAQABo08wTTATBgNVHSUEDDAKBggrBgEFBQcDATA2BgNVHR8ELzAtMCug
+KaAnhiVodHRwOi8vd3d3LmV4YW1wbGUuY29tL2V4YW1wbGVfY2EuY3JsMA0GCSqG
+SIb3DQEBBQUAA4GBAKQlIVELImxjjanBTwQzaXk08Dbdj2onXweiHe+L8Jbm56O4
+O4VePyZDio6VWJym25xRv+pTFj7BqBEaxs8OoRcYZNIF8cCcpisWxClUA9IXvRV0
+1q2Kjy3MJzuIiPLq0KLL6UJX3yafiqICLzW2GR0mQ0SvEku8uYRQAv0d+lDo
+-----END CERTIFICATE-----

+ 27 - 0
examples/wifi/wifi_eap_fast/main/server.key

@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAydji4HWRg4fYyIDGIE3pFCQwmDNT+lYO7JpDf4epIpQmBses
+tdnsVQaBtw3JJFFJ+kf7S0787XWK4SgyvMXgTEXEWGAVZx5rQBk/8KuSYZItcRAu
+8uu8gS9aO3TKX/3g7tHZB2psIMAHiLSLD60eyU98EZg3iRXeJLERGnyXSs/zyMt5
+npzDcdqmlJf1lf1hBkTiPxJDCx0zSJHSzk+Xoe1qMMddmLVuCrdP2QPsgHYJsECh
+oa+rKlnED1YivL4UvhjfEH1dIr/lBHd6dWs+620goadg1PGHnZ9gudPbLCX0kUrx
+0kDloRCIoEFamEDKFdfj5j7AatVGsrSQtK474wIDAQABAoIBAQC2kGDEPBJdMSW2
+VCLfXRiPixwYzXQLXIMrJWwfkQg9qlmqkDd6U50aWkRA2UswegW7RhfYSZ0i+cmf
+VMhvTVpOIlwwwtcY6b5/v1bBy60eaySGuuh79xQMlFO8qynQIMStvUfbGTqrdIRb
+9VBB4YeS9T12fILejtTZwv2BQ2dj1Y1SCay6Ri85UzJqSClRKgHISybvVdLNjPvP
+0TRFBr57zyjL6WE8teKiKchzQko2u86No5uBCdKGsrAkrsdcR0YqlM/pZxd3VKNm
++eny0k+dZZlvcPxzkzP4hEp9+Rw5rP9/s3s/cCwvuuC5JO32ATBWKCbTvPv/XPDb
+MdSJtOshAoGBAPzk0eswkcbFYtpnpBNmBAr1dtAdW1lfjUI2ucMMwt7Wns0P/tt+
+gq6Hi1wTaGP0l/dIECgeHwjtWj31ZJjQtFJ1y/kafxo4o9cA8vCydpdvSZaldAfg
+sbLlDTDYzEpelaDIbNQBBXFoC5U9JlBhBsIFCL5Z8ZuIeFPsb7t5wwuHAoGBAMxT
+jyWfNm1uNxp1xgCnrRsLPQPVnURrSFAqcHrECqRu3F7sozTN7q/cZViemxPvVDGQ
+p9c+9bHwaYvW4trO5qDHJ++gGwm5L52bMAY1VUfeTt67fqrey43XpdmzcTX1V9Uj
+QWawPUCSDzFjL1MjfCIejtyYf5ash53vj+T8r/vFAoGAA/OPVB1uKazr3n3AEo2F
+gqZTNO1AgCT+EArK3EFWyiSQVqPpV4SihheYFdg3yVgJB9QYbIgL9BfBUTaEW97m
+8mLkzP+c/Mvlw3ZAVYJ0V+llPPVY2saoACOUES9SAdd4fwqiqK1baGo3xB0wfBEI
+CgAKIu9E1ylKuAT5ufQtGAECgYEAtP/kU5h5N3El4QupTdU7VDSdZTMqsHw0v8cI
+gsf9AXKvRmtrnBA8u46KPHmruHoO5CVXeSZtsaXdaaH+rYQQ6yXg67WxnehtFLlv
+TmCaXiLBTS9cYvMf8FOyuGnsBLeEietEOTov2G5KhR5uwsAxa2wUc7endor5S9/2
+YQuyvV0CgYALbiFpILd5l1ip65eE6JdA3hfttUbV2j2NSW12ej69vqbeOfaSgNse
+uYCcXFsBbQPhNPwA+4d1oCe8SyXZg1f7gE812z2Tyr/3vdVnNZlitoxhsHmGiyS7
+gZdaTYCb78l9z0EBdaCVvA16owEle4SR6f9eCwzSI0WPOUra+x/hrA==
+-----END RSA PRIVATE KEY-----

+ 57 - 0
examples/wifi/wifi_eap_fast/main/server.pem

@@ -0,0 +1,57 @@
+Bag Attributes
+    localKeyID: 63 3B C1 EE 3A 4A 9B 3E FF 9E E7 BC 17 50 D7 F7 B7 7E 3B C0
+subject=/C=FR/ST=Radius/O=Example Inc./CN=Example Server Certificate/emailAddress=admin@example.com
+issuer=/C=FR/ST=Radius/L=Somewhere/O=Example Inc./emailAddress=admin@example.com/CN=Example Certificate Authority
+-----BEGIN CERTIFICATE-----
+MIIDWTCCAsKgAwIBAgIBLzANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UEBhMCRlIx
+DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYDVQQKDAxF
+eGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUuY29tMSYw
+JAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNzA2MDcw
+ODA2NDlaFw0yNzA2MDUwODA2NDlaMHwxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZS
+YWRpdXMxFTATBgNVBAoMDEV4YW1wbGUgSW5jLjEjMCEGA1UEAwwaRXhhbXBsZSBT
+ZXJ2ZXIgQ2VydGlmaWNhdGUxIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUu
+Y29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAydji4HWRg4fYyIDG
+IE3pFCQwmDNT+lYO7JpDf4epIpQmBsestdnsVQaBtw3JJFFJ+kf7S0787XWK4Sgy
+vMXgTEXEWGAVZx5rQBk/8KuSYZItcRAu8uu8gS9aO3TKX/3g7tHZB2psIMAHiLSL
+D60eyU98EZg3iRXeJLERGnyXSs/zyMt5npzDcdqmlJf1lf1hBkTiPxJDCx0zSJHS
+zk+Xoe1qMMddmLVuCrdP2QPsgHYJsEChoa+rKlnED1YivL4UvhjfEH1dIr/lBHd6
+dWs+620goadg1PGHnZ9gudPbLCX0kUrx0kDloRCIoEFamEDKFdfj5j7AatVGsrSQ
+tK474wIDAQABo08wTTATBgNVHSUEDDAKBggrBgEFBQcDATA2BgNVHR8ELzAtMCug
+KaAnhiVodHRwOi8vd3d3LmV4YW1wbGUuY29tL2V4YW1wbGVfY2EuY3JsMA0GCSqG
+SIb3DQEBBQUAA4GBAKQlIVELImxjjanBTwQzaXk08Dbdj2onXweiHe+L8Jbm56O4
+O4VePyZDio6VWJym25xRv+pTFj7BqBEaxs8OoRcYZNIF8cCcpisWxClUA9IXvRV0
+1q2Kjy3MJzuIiPLq0KLL6UJX3yafiqICLzW2GR0mQ0SvEku8uYRQAv0d+lDo
+-----END CERTIFICATE-----
+Bag Attributes
+    localKeyID: 63 3B C1 EE 3A 4A 9B 3E FF 9E E7 BC 17 50 D7 F7 B7 7E 3B C0
+Key Attributes: <No Attributes>
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIzOJcU8NzjKACAggA
+MBQGCCqGSIb3DQMHBAjdK4dY0QgcrgSCBMgCv4euS7bxaRTMSXYliVA+UrAI0iJL
+oQgZ6cGFqXqasKu7K6BltLXaKJLX321gV2ofHHAxa1CWFwIgrv/bhci10cJr6BaL
+a3+5L5e/EO0eRKVzoplput9mTP27pcJSu8jN5jOu054oamzFKtgGcBuL9mRCAfVq
+JSCwbSUHPrwZX6jVDsLggrvE78SIXEYAQDfUrbvBY1kGNNV+U9v410SREYXI3q+8
+a4Paotf2HN5G0nvr0BSJUbY0NUe4AP1NfOM1VE8UiLL3LfZE3ZbOm6XsM8Mxf8rV
+BkDOzhErF/dXOE65b5xG4KfoXy78WN7OeJpjNgNUDUl1/3+bc++8f7ZBzOtVAjP7
+diDYU/UrQeLUrLVxTLD6C9J9HvbW5JCEa1FN+pKQKxeaQ9j/dDhJ7XI4ctaIIQui
+zrOTLEhBqC10TaNabLNVsLFCgFQMREBVYZoBYbFIZLT4FuRk5yaAYRDCrRKgqlu6
+61E0jOD/hDZIbBO/Vf94d+VaypeFonoz6SdrWgf4heN530d5fd5nOxElRl8LmUcH
+LriwJIXB7XCfwgjREceRQpjiqmWPl8hcfe9E2NzwldbE+eLN0mlXgRyLlU+eXova
+3r1u60Hb9ocLux3kHUXpZ+MomwkwqIn9qFC0U3KBGt7SmhUWypmoNUPiXP0U4Lno
+tynPBODzhDeRv2T0qxULc6OznDE3WOuy88B94/JXhB5D56bRb9FkkpHpmn8MP8SW
+fIV3cK/cBoGxgyk+IaL0aQbojFtyeyKbkrz1W3WnPtB3/kqd8USzpCvUBMBVG83D
+XzHcU/mRYO61BgyGv0VBXdBtPbmW1CK73eX7gc/uOtqhxUQc3g5hNZDL2rxPCpID
+alyGlSQxY8cPWJbYMPq2RHoJNDxnTbFhDm3mdOoTg5qK4+cUZMBqfq1kswXM9Z4p
+LSnIviRjpL4a7hhDVAJYOo29rBP+QNdt5CkyLNwjnDv+l8jHOMTP4t1RPvoQx6hc
+aOQOcSQnIqDgzot3//dnC2mBTQ66jsOnRPMMs9MQ4up6Wrc5PEt5avBi3B4AQC2Q
+TUjL6QAcMR5cJbkecp+5h7W5pUw3OYcYpg6G+unJVGSgZkye2lySGgNuRLmcYjVA
+gXfA9a8+HN/TFGHP668pZvHQrfV470ETnHhrh+NLN5AIDR+UFG8is7Xj7Ly2/5bN
+M3Q3AYSCb8P8/ZrV3Dfm3qoCoxuNYax0PYt/JBWXTtPG1SClUQg+yo8VMiNw877h
+IEdkg1QBxKWY5x+ThK79y+Cwub795ym9bYTwAtH8kpLmamoBT8UvgJumh0i8NGg3
+04B/oyg1P8/TxfbD0uaTPC+tmbIKVyHybTQL6/E0bs2knPp2Zyxno3yE3AI70msZ
+mGNuK9mkxITIUbijiRZyeQvZz/x/daYwQ0WuaXXpPQvKcFu7LyIEad8QpIqtSCax
+VS9Mtwe7zTCp0jmQDBGltlk1B2rUlQ5rxsFN6kJtBULdQc1TTj0NUp3ESeAE3A/l
+wE8fPzBht1OKghJqVndGoh01XhKJfpfaQ85CES0HzA6Nll8SQO0bn6u4SBZGEvBT
+pFarRrtS6KbulTDB3yBPrKgd1ZoBQv5/ScW8fPcLi55U6nhR28uu+zKy4yZFgOFR
+2KU=
+-----END ENCRYPTED PRIVATE KEY-----

+ 148 - 0
examples/wifi/wifi_eap_fast/main/wifi_eap_fast_main.c

@@ -0,0 +1,148 @@
+/*
+ * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: CC0-1.0
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/event_groups.h"
+#include "esp_wifi.h"
+#include "esp_wpa2.h"
+#include "esp_event.h"
+#include "esp_log.h"
+#include "esp_system.h"
+#include "nvs_flash.h"
+#include "esp_netif.h"
+
+/* The examples use simple WiFi configuration that you can set via
+   project configuration menu.
+
+   If you'd rather not, just change the below entries to strings with
+   the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
+
+*/
+#define EXAMPLE_WIFI_SSID CONFIG_EXAMPLE_WIFI_SSID
+
+#define EXAMPLE_EAP_ID CONFIG_EXAMPLE_EAP_ID
+#define EXAMPLE_EAP_USERNAME CONFIG_EXAMPLE_EAP_USERNAME
+#define EXAMPLE_EAP_PASSWORD CONFIG_EXAMPLE_EAP_PASSWORD
+
+/* FreeRTOS event group to signal when we are connected & ready to make a request */
+static EventGroupHandle_t wifi_event_group;
+
+/* esp netif object representing the WIFI station */
+static esp_netif_t *sta_netif = NULL;
+
+/* The event group allows multiple bits for each event,
+   but we only care about one event - are we connected
+   to the AP with an IP? */
+const int CONNECTED_BIT = BIT0;
+
+static const char *TAG = "example";
+
+/* CA cert, taken from ca.pem
+
+   To embed it in the app binary, the PEM, CRT and KEY file is named
+   in the component.mk COMPONENT_EMBED_TXTFILES variable.
+*/
+#if defined(CONFIG_EXAMPLE_VALIDATE_SERVER_CERT)
+extern uint8_t ca_pem_start[] asm("_binary_ca_pem_start");
+extern uint8_t ca_pem_end[]   asm("_binary_ca_pem_end");
+#endif
+extern uint8_t pac_file_pac_start[] asm("_binary_pac_file_pac_start");
+extern uint8_t pac_file_pac_end[] asm("_binary_pac_file_pac_end");
+
+static void event_handler(void* arg, esp_event_base_t event_base,
+                                int32_t event_id, void* event_data)
+{
+    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
+        esp_wifi_connect();
+    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
+        esp_wifi_connect();
+        xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
+    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
+        xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
+    }
+}
+
+static void initialise_wifi(void)
+{
+#if defined(CONFIG_EXAMPLE_VALIDATE_SERVER_CERT)
+    unsigned int ca_pem_bytes = ca_pem_end - ca_pem_start;
+#endif
+    unsigned int pac_file_bytes = pac_file_pac_end - pac_file_pac_start;
+
+    ESP_ERROR_CHECK(esp_netif_init());
+    wifi_event_group = xEventGroupCreate();
+    ESP_ERROR_CHECK(esp_event_loop_create_default());
+    sta_netif = esp_netif_create_default_wifi_sta();
+    assert(sta_netif);
+
+    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
+    ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
+    ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) );
+    ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL) );
+    ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
+    wifi_config_t wifi_config = {
+        .sta = {
+            .ssid = EXAMPLE_WIFI_SSID,
+#if defined(CONFIG_EXAMPLE_WPA3_ENTERPRISE)
+            .pmf_cfg = {
+                .capable = true,
+                .required = false
+            },
+#endif
+        },
+    };
+    ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
+    ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
+    ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
+    ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_set_identity((uint8_t *)EXAMPLE_EAP_ID, strlen(EXAMPLE_EAP_ID)) );
+
+#if defined(CONFIG_EXAMPLE_VALIDATE_SERVER_CERT) || \
+    defined(CONFIG_EXAMPLE_WPA3_ENTERPRISE)
+    ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_set_ca_cert(ca_pem_start, ca_pem_bytes) );
+#endif /* CONFIG_EXAMPLE_VALIDATE_SERVER_CERT */ /* EXAMPLE_WPA3_ENTERPRISE */
+
+    ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_set_username((uint8_t *)EXAMPLE_EAP_USERNAME, strlen(EXAMPLE_EAP_USERNAME)) );
+    ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_set_password((uint8_t *)EXAMPLE_EAP_PASSWORD, strlen(EXAMPLE_EAP_PASSWORD)) );
+    ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_set_pac_file(pac_file_pac_start, pac_file_bytes - 1) );
+    esp_eap_fast_config eap_fast_config = {
+        .fast_provisioning = 2,
+        .fast_max_pac_list_len = 0,
+        .fast_pac_format_binary = false
+    };
+    ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_set_fast_phase1_params(eap_fast_config) );
+
+    ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_enable() );
+    ESP_ERROR_CHECK( esp_wifi_start() );
+}
+
+static void wpa2_enterprise_example_task(void *pvParameters)
+{
+    esp_netif_ip_info_t ip;
+    memset(&ip, 0, sizeof(esp_netif_ip_info_t));
+    vTaskDelay(2000 / portTICK_PERIOD_MS);
+
+    while (1) {
+        vTaskDelay(5000 / portTICK_PERIOD_MS);
+
+        if (esp_netif_get_ip_info(sta_netif, &ip) == 0) {
+            ESP_LOGI(TAG, "~~~~~~~~~~~");
+            ESP_LOGI(TAG, "IP:"IPSTR, IP2STR(&ip.ip));
+            ESP_LOGI(TAG, "MASK:"IPSTR, IP2STR(&ip.netmask));
+            ESP_LOGI(TAG, "GW:"IPSTR, IP2STR(&ip.gw));
+            ESP_LOGI(TAG, "~~~~~~~~~~~");
+        }
+    }
+}
+
+void app_main(void)
+{
+    ESP_ERROR_CHECK( nvs_flash_init() );
+    initialise_wifi();
+    xTaskCreate(&wpa2_enterprise_example_task, "wpa2_enterprise_example_task", 4096, NULL, 5, NULL);
+}

+ 1 - 0
examples/wifi/wifi_eap_fast/sdkconfig.defaults

@@ -0,0 +1 @@
+CONFIG_WPA_MBEDTLS_CRYPTO=n